فهرست منبع

Merge branch 'candidate-5.4.0'

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 10 سال پیش
والد
کامیت
0789d51428

+ 1 - 0
cmake_modules/dependencies/vivid.cmake

@@ -0,0 +1 @@
+set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.55.0, libicu52, libxslt1.1, libxml2, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive13, rsync, libapr1, libaprutil1, python")

+ 5 - 1
common/remote/rmtfile.cpp

@@ -656,10 +656,14 @@ public:
     }
 };
 
-unsigned validateNodes(const SocketEndpointArray &eps,const char *dataDir, const char *mirrorDir, bool chkver, const char *script, unsigned scripttimeout, SocketEndpointArray &failures, UnsignedArray &failedcodes, StringArray &failedmessages, const char *filename)
+unsigned validateNodes(const SocketEndpointArray &epso,const char *dataDir, const char *mirrorDir, bool chkver, const char *script, unsigned scripttimeout, SocketEndpointArray &failures, UnsignedArray &failedcodes, StringArray &failedmessages, const char *filename)
 {
     // used for detecting duff nodes
     IPointerArrayOf<ISocket> sockets;
+    // dedup nodes
+    SocketEndpointArray eps;
+    ForEachItemIn(i1,epso)
+        eps.appendUniq(epso.element(i1));
     unsigned to=30*1000;
     unsigned n=eps.ordinality();    // use approx log scale (timeout is long but only for failure situation)
     while (n>1) {

+ 2 - 4
common/remote/sockfile.cpp

@@ -2598,8 +2598,7 @@ void CRemoteFile::copyTo(IFile *dest, size32_t buffersize, ICopyFileProgress *pr
 
 unsigned getRemoteVersion(ISocket * socket, StringBuffer &ver)
 {
-    static CriticalSection sect;
-    CriticalBlock block(sect);
+    // used to have a global critical section here
     if (!socket)
         return 0;
     unsigned ret;
@@ -2638,8 +2637,7 @@ unsigned getRemoteVersion(ISocket * socket, StringBuffer &ver)
 
 extern unsigned stopRemoteServer(ISocket * socket)
 {
-    static CriticalSection sect;
-    CriticalBlock block(sect);
+    // used to have a global critical section here
     if (!socket)
         return 0;
     MemoryBuffer sendbuf;

+ 39 - 11
common/thorhelper/thorsoapcall.cpp

@@ -758,6 +758,9 @@ public:
         else
             timeLimitMS = (unsigned)(dval * 1000);
 
+        if (flags & SOAPFhttpheaders)
+            httpHeaders.set(s.setown(helper->getHttpHeaders()));
+
         if (wscType == STsoap)
         {
             soapaction.set(s.setown(helper->getSoapAction()));
@@ -817,7 +820,7 @@ public:
         {
             service.toUpperCase();  //GET/PUT/POST
             if (strcmp(service.str(), "GET"))
-                throw MakeStringException(0, "HTTPCALL Only 'GET' service supported");
+                throw MakeStringException(0, "HTTPCALL Only 'GET' http method currently supported");
             OwnedRoxieString acceptTypeSupplied(helper->getAcceptType()); // text/html, text/xml, etc
             acceptType.set(acceptTypeSupplied);
             acceptType.trim();
@@ -1045,6 +1048,7 @@ protected:
     StringAttr soapaction;
     StringAttr httpHeaderName;
     StringAttr httpHeaderValue;
+    StringAttr httpHeaders;
     StringAttr inputpath;
     StringBuffer service;
     StringBuffer acceptType;//for httpcall, text/plain, text/html, text/xml, etc
@@ -1273,7 +1277,18 @@ IWSCHelper * createHttpCallHelper(IWSCRowProvider *r, IEngineRowAllocator * outp
 }
 
 //=================================================================================================
-
+bool httpHeaderBlockContainsHeader(const char *httpheaders, const char *header)
+{
+    if (!httpheaders || !*httpheaders)
+        return false;
+    VStringBuffer match("\n%s:", header);
+    const char *matchStart = match.str()+1;
+    if (!strncmp(httpheaders, matchStart, strlen(matchStart)))
+        return true;
+    if (strstr(httpheaders, match))
+        return true;
+    return false;
+}
 class CWSCAsyncFor : implements IWSCAsyncFor, public CInterface, public CAsyncFor
 {
     class CSocketDataProvider : public CInterface
@@ -1362,30 +1377,43 @@ private:
         else
             request.clear().append(master->service).append(" ").append(url.path).append(" HTTP/1.1\r\n");
 
-        if (url.userPasswordPair.length() > 0)
+        const char *httpheaders = master->httpHeaders.get();
+        if (httpheaders && *httpheaders)
         {
-            StringBuffer authToken;
-            JBASE64_Encode(url.userPasswordPair.str(), url.userPasswordPair.length(), authToken);
-            request.append("Authorization: Basic ").append(authToken).append("\r\n");
+            if (soapTraceLevel > 6 || master->logXML)
+                master->logctx.CTXLOG("%s: Adding HTTP Headers(%s)",  master->wscCallTypeText(), httpheaders);
+            request.append(httpheaders);
         }
-        else if (master->authToken.length() > 0)
+
+        if (!httpHeaderBlockContainsHeader(httpheaders, "Authorization"))
         {
-            request.append("Authorization: Basic ").append(master->authToken).append("\r\n");
+            if (url.userPasswordPair.length() > 0)
+            {
+                StringBuffer authToken;
+                JBASE64_Encode(url.userPasswordPair.str(), url.userPasswordPair.length(), authToken);
+                request.append("Authorization: Basic ").append(authToken).append("\r\n");
+            }
+            else if (master->authToken.length() > 0)
+            {
+                request.append("Authorization: Basic ").append(master->authToken).append("\r\n");
+            }
         }
+
         if (master->wscType == STsoap)
         {
             if (master->soapaction.get())
                 request.append("SOAPAction: ").append(master->soapaction.get()).append("\r\n");
-
-            if (master->httpHeaderName.get() && master->httpHeaderValue.get())
+            if (master->httpHeaders.isEmpty() && master->httpHeaderName.get() && master->httpHeaderValue.get())
             {
+                //backward compatibility
                 StringBuffer hdr = master->httpHeaderName.get();
                 hdr.append(": ").append(master->httpHeaderValue);
                 if (soapTraceLevel > 6 || master->logXML)
                     master->logctx.CTXLOG("SOAPCALL: Adding HTTP Header(%s)", hdr.str());
                 request.append(hdr.append("\r\n"));
             }
-            request.append("Content-Type: text/xml\r\n");
+            if (!httpHeaderBlockContainsHeader(httpheaders, "Content-Type"))
+                request.append("Content-Type: text/xml\r\n");
         }
         else if(master->wscType == SThttp)
             request.append("Accept: ").append(master->acceptType).append("\r\n");

+ 10 - 0
dali/dfuplus/dfuplus.cpp

@@ -32,6 +32,7 @@
 #include "rmtfile.hpp"
 #include "sockfile.hpp"
 
+#include "jutil.hpp"
 
 static class CSecuritySettings
 {
@@ -192,6 +193,15 @@ CDfuPlusHelper::CDfuPlusHelper(IProperties* _globals,   CDfuPlusMessagerIntercep
 
     const char* username = globals->queryProp("username");
     const char* password = globals->queryProp("password");
+    if ( username && *username && (!password || !*password))
+    {
+        VStringBuffer prompt("%s's password: ", username);
+        StringBuffer passw;
+        passwordInput(prompt, passw);
+        globals->setProp("password",passw.str());
+        password = globals->queryProp("password");
+    }
+
     sprayclient->setUsernameToken(username, password, NULL);
     dfuclient->setUsernameToken(username, password, NULL);
 

+ 1 - 1
dali/dfuplus/dfuplus.hpp

@@ -34,7 +34,7 @@ class CDfuPlusHelper : public CInterface, implements IInterface
 {
 public:
     IMPLEMENT_IINTERFACE
-    
+
     CDfuPlusHelper(IProperties* _globals,   CDfuPlusMessagerIntercept *_msgintercept=NULL);
     virtual ~CDfuPlusHelper();
 

+ 0 - 67
dali/dfuplus/main.cpp

@@ -20,9 +20,6 @@
 #include "daftcfg.hpp"
 #include "dfuerror.hpp"
 #include "dfuplus.hpp"
-#if defined( __linux__) || defined(__FreeBSD__)
-#include "termios.h"
-#endif
 
 void printVersion()
 {
@@ -202,70 +199,6 @@ bool build_globals(int argc, const char *argv[], IProperties * globals)
     return true;
 }
 
-void promptFor(const char *prompt, const char *prop, bool hide, IProperties * globals)
-{
-    StringBuffer result;
-    fprintf(stdout, "%s", prompt);
-    fflush(stdout);
-    if (hide)
-    {
-#ifdef _WIN32
-        HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);   
-        DWORD dwInputMode;
-        GetConsoleMode(hStdIn, &dwInputMode);   
-        SetConsoleMode(hStdIn, dwInputMode & ~ENABLE_LINE_INPUT & ~ENABLE_ECHO_INPUT);
-        loop
-        {
-            /* read a character from the console input */   
-            char ch;
-            DWORD dwRead;
-            if (!ReadFile(hStdIn, &ch, sizeof(ch), &dwRead, NULL))
-                break;
-            if (ch == '\n' || ch=='\r' || !ch)
-                break;
-            result.append(ch);
-        }
-        SetConsoleMode(hStdIn, dwInputMode); 
-#else
-        int fn = fileno(stdin);
-#ifdef __linux__        
-        struct termio t;
-        /* If ioctl fails, we're probably not connected to a terminal. */
-        if(!ioctl(fn, TCGETA, &t))
-        {
-            t.c_lflag &= ~ECHO;
-            ioctl(fn, TCSETA, &t);
-        }
-#endif
-        loop
-        {
-            char ch = fgetc(stdin);
-            if (ch == '\n' || ch=='\r' || !ch)
-                break;
-            result.append(ch);
-        }
-#ifdef __linux__        
-        if(!ioctl(fn, TCGETA, &t))
-        {
-            t.c_lflag |= ECHO;
-            ioctl(fn, TCSETA, &t);
-        }
-#endif
-#endif
-        printf("\n");
-    }
-    else
-    {
-        char buf[100];
-        if (fgets(buf, 100, stdin))
-            result.append(buf);
-        if (result.length() && result.charAt(result.length()-1)=='\n')
-            result.remove(result.length()-1, 1);
-    }
-    globals->setProp(prop, result);
-}
-
-
 int main(int argc, const char* argv[])
 {
     InitModuleObjects();

+ 21 - 2
docs/ECLStandardLibraryReference/SLR-Mods/Double.xml

@@ -12,6 +12,10 @@
       <primary>Double</primary>
     </indexterm>( </emphasis>source <emphasis role="bold">)</emphasis></para>
 
+  <para><emphasis role="bold">STD.Metaphone3.Double<indexterm>
+      <primary>Std.Metaphone3.Double</primary>
+    </indexterm>( </emphasis>source <emphasis role="bold">)</emphasis></para>
+
   <informaltable colsep="1" frame="all" rowsep="1">
     <tgroup cols="2">
       <colspec colwidth="80.50pt" />
@@ -28,7 +32,7 @@
         <row>
           <entry>Return:<emphasis> </emphasis></entry>
 
-          <entry>Double returns a STRING value. </entry>
+          <entry>Double returns a STRING value.</entry>
         </row>
       </tbody>
     </tgroup>
@@ -37,7 +41,12 @@
   <para>The <emphasis role="bold">Double</emphasis> function returns a textual
   representation of the <emphasis>source</emphasis> data, similar to a Soundex
   code. This function returns both return values from the Double Metaphone
-  algorithm, concatenating the two into a single result string. </para>
+  algorithm, concatenating the two into a single result string.</para>
+
+  <para>The <emphasis role="bold">Metaphone3.Double</emphasis> function uses
+  the newer Metaphone 3 libraries which improve phonetic encoding of English
+  words, non-English words familiar to Americans, and first and last names
+  commonly found in the United States (Enterprise Edition only).</para>
 
   <para>Example:</para>
 
@@ -55,6 +64,16 @@ r XF(ProgGuide.Person.File L) := TRANSFORM
   SELF.Mboth  := STD.Metaphone.Double( L.LastName );
 END;
 
+// Example using Metaphone 3 (available in Enterprise Edition)
+/* 
+r XF(ProgGuide.Person.File L) := TRANSFORM 
+     SELF.source := L.LastName;
+     SELF.M1     := STD.Metaphone3.Primary( L.LastName ); 
+     SELF.M2     := STD.Metaphone3.Secondary( L.LastName ); 
+     SELF.Mboth  := STD.Metaphone3.Double( L.LastName );
+   END;
+*/
+
 ds := PROJECT(ProgGuide.Person.File,XF(LEFT)); 
 
 COUNT(ds);

+ 23 - 0
docs/ECLStandardLibraryReference/SLR-Mods/Primary.xml

@@ -12,6 +12,14 @@
       <primary>Primary</primary>
     </indexterm>( </emphasis>source<emphasis role="bold"> ) </emphasis></para>
 
+  <para><emphasis role="bold">STD.Metaphone3.Primary<indexterm>
+      <primary>Std.Metaphone.Primary</primary>
+    </indexterm><indexterm>
+      <primary>Metaphone3.Primary</primary>
+    </indexterm><indexterm>
+      <primary></primary>
+    </indexterm>( </emphasis>source<emphasis role="bold"> ) </emphasis></para>
+
   <para></para>
 
   <para><informaltable colsep="1" frame="all" rowsep="1">
@@ -41,6 +49,11 @@
   function returns the first return value from the Double Metaphone
   algorithm.</para>
 
+  <para>The <emphasis role="bold">Metaphone3.Primary</emphasis> function uses
+  the newer Metaphone 3 libraries which improve phonetic encoding of English
+  words, non-English words familiar to Americans, and first and last names
+  commonly found in the United States (Enterprise Edition only).</para>
+
   <para>Example:</para>
 
   <programlisting>r := RECORD 
@@ -57,6 +70,16 @@ r XF(ProgGuide.Person.File L) := TRANSFORM
   SELF.Mboth  := STD.Metaphone.Double( L.LastName );
 END;
 
+// Example using Metaphone 3 (available in Enterprise Edition)
+/* 
+r XF(ProgGuide.Person.File L) := TRANSFORM 
+     SELF.source := L.LastName;
+     SELF.M1     := STD.Metaphone3.Primary( L.LastName ); 
+     SELF.M2     := STD.Metaphone3.Secondary( L.LastName ); 
+     SELF.Mboth  := STD.Metaphone3.Double( L.LastName );
+   END;
+*/
+
 ds := PROJECT(ProgGuide.Person.File,XF(LEFT)); 
 
 COUNT(ds);

+ 22 - 2
docs/ECLStandardLibraryReference/SLR-Mods/Secondary.xml

@@ -12,6 +12,10 @@
       <primary>Secondary</primary>
     </indexterm>(</emphasis> source <emphasis role="bold">) </emphasis></para>
 
+  <para><emphasis role="bold">STD.Metaphone3.Secondary<indexterm>
+      <primary>Std.Metaphone3.Secondary</primary>
+    </indexterm>(</emphasis> source <emphasis role="bold">) </emphasis></para>
+
   <para></para>
 
   <informaltable colsep="1" frame="all" rowsep="1">
@@ -30,7 +34,7 @@
         <row>
           <entry>Return:<emphasis> </emphasis></entry>
 
-          <entry>Secondary returns a STRING value. </entry>
+          <entry>Secondary returns a STRING value.</entry>
         </row>
       </tbody>
     </tgroup>
@@ -39,7 +43,13 @@
   <para>The <emphasis role="bold">Secondary</emphasis> function returns a
   textual representation of the source data, similar to a Soundex code. This
   function returns the second return value from the Double Metaphone
-  algorithm. </para>
+  algorithm.</para>
+
+  <para>The <emphasis role="bold">Metaphone3.SecondaryPrimary</emphasis>
+  function uses the newer Metaphone 3 libraries which improve phonetic
+  encoding of English words, non-English words familiar to Americans, and
+  first and last names commonly found in the United States (Enterprise Edition
+  only).</para>
 
   <para>Example:</para>
 
@@ -57,6 +67,16 @@ r XF(ProgGuide.Person.File L) := TRANSFORM
   SELF.Mboth  := STD.Metaphone.Double( L.LastName );
 END;
 
+// Example using Metaphone 3 (available in Enterprise Edition)
+/* 
+r XF(ProgGuide.Person.File L) := TRANSFORM 
+     SELF.source := L.LastName;
+     SELF.M1     := STD.Metaphone3.Primary( L.LastName ); 
+     SELF.M2     := STD.Metaphone3.Secondary( L.LastName ); 
+     SELF.Mboth  := STD.Metaphone3.Double( L.LastName );
+   END;
+*/
+
 ds := PROJECT(ProgGuide.Person.File,XF(LEFT)); 
 
 COUNT(ds);

+ 5 - 5
docs/ECLStandardLibraryReference/SLR-includer.xml

@@ -353,12 +353,12 @@
                 xmlns:xi="http://www.w3.org/2001/XInclude" />
   </chapter>
 
-  <chapter id="DoubleMetaphoneSupport">
-    <title>Double Metaphone Support</title>
+  <chapter id="MetaphoneSupport">
+    <title>Metaphone Support</title>
 
-    <para>These functions provide a means to implement Double Metaphone
-    phonetic encoding or fuzzy-match algorithms which return a primary code, a
-    secondary code, or both for a given string.</para>
+    <para>These functions provide a means to implement Double Metaphone or
+    Metaphone 3 phonetic encoding or fuzzy-match algorithms which return a
+    primary code, a secondary code, or both for a given string.</para>
 
     <xi:include href="ECLStandardLibraryReference/SLR-Mods/Primary.xml"
                 xmlns:xi="http://www.w3.org/2001/XInclude" />

+ 2 - 0
ecl/hqlcpp/hqlcpp.ipp

@@ -1510,6 +1510,8 @@ public:
     ABoundActivity * doBuildActivityWorkunitRead(BuildCtx & ctx, IHqlExpression * expr);
     ABoundActivity * doBuildActivityXmlParse(BuildCtx & ctx, IHqlExpression * expr);
 
+    void doBuildHttpHeaderStringFunction(BuildCtx & ctx, IHqlExpression * expr);
+
     void doBuildTempTableFlags(BuildCtx & ctx, IHqlExpression * expr, bool isConstant);
 
     void doBuildXmlEncode(BuildCtx & ctx, const CHqlBoundTarget * tgt, IHqlExpression * expr, CHqlBoundExpr * result);

+ 33 - 6
ecl/hqlcpp/hqlhtcpp.cpp

@@ -17043,6 +17043,32 @@ void HqlCppTranslator::validateExprScope(BuildCtx & ctx, IHqlExpression * datase
         throwError2(HQLERR_OpArgDependsDataset, opName, argName);
 }
 
+void HqlCppTranslator::doBuildHttpHeaderStringFunction(BuildCtx &ctx, IHqlExpression * expr)
+{
+    HqlExprArray headerExprs;
+    gatherAttributes(headerExprs, httpHeaderAtom, expr);
+    if (headerExprs.length())
+    {
+        Owned<ITypeInfo> string2Type = makeStringType(2);
+        OwnedHqlExpr endName = createConstant(createStringValue(": ", LINK(string2Type)));
+        OwnedHqlExpr endLine = createConstant(createStringValue("\r\n", LINK(string2Type)));
+
+        HqlExprArray headerStringExprs;
+        ForEachItemIn(i, headerExprs)
+        {
+            IHqlExpression * httpHeader = &headerExprs.item(i);
+            headerStringExprs.append(*LINK(httpHeader->queryChild(0)));
+            headerStringExprs.append(*LINK(endName));
+            headerStringExprs.append(*LINK(httpHeader->queryChild(1)));
+            headerStringExprs.append(*LINK(endLine));
+        }
+
+        OwnedHqlExpr concatHeaders = createBalanced(no_concat, unknownVarStringType, headerStringExprs);
+        concatHeaders.setown(foldHqlExpression(concatHeaders));
+        doBuildVarStringFunction(ctx, "getHttpHeaders", concatHeaders);
+    }
+
+}
 
 ABoundActivity * HqlCppTranslator::doBuildActivitySOAP(BuildCtx & ctx, IHqlExpression * expr, bool isSink, bool isRoot)
 {
@@ -17125,12 +17151,7 @@ ABoundActivity * HqlCppTranslator::doBuildActivitySOAP(BuildCtx & ctx, IHqlExpre
     if (action)
         doBuildVarStringFunction(instance->startctx, "getSoapAction", action->queryChild(0));
 
-    IHqlExpression * httpHeader = expr->queryAttribute(httpHeaderAtom);
-    if (httpHeader)
-    {
-        doBuildVarStringFunction(instance->startctx, "getHttpHeaderName", httpHeader->queryChild(0));
-        doBuildVarStringFunction(instance->startctx, "getHttpHeaderValue", httpHeader->queryChild(1));
-    }
+    doBuildHttpHeaderStringFunction(instance->startctx, expr);
 
     IHqlExpression * proxyAddress = expr->queryAttribute(proxyAddressAtom);
     if (proxyAddress)
@@ -17181,6 +17202,8 @@ ABoundActivity * HqlCppTranslator::doBuildActivitySOAP(BuildCtx & ctx, IHqlExpre
             flags.append("|SOAPFlogmin");
         if (logText)
             flags.append("|SOAPFlogusermsg");
+        if (expr->hasAttribute(httpHeaderAtom))
+            flags.append("|SOAPFhttpheaders");
 
         if (flags.length())
             doBuildUnsignedFunction(instance->classctx, "getFlags", flags.str()+1);
@@ -17289,6 +17312,8 @@ ABoundActivity * HqlCppTranslator::doBuildActivityHTTP(BuildCtx & ctx, IHqlExpre
     //virtual void toXML(const byte * self, StringBuffer & out) = 0;
     buildHTTPtoXml(instance->startctx);
 
+    doBuildHttpHeaderStringFunction(instance->startctx, expr);
+
     //virtual const char * queryOutputIteratorPath()
     IHqlExpression * separator = expr->queryAttribute(separatorAtom);
     if (separator)
@@ -17332,6 +17357,8 @@ ABoundActivity * HqlCppTranslator::doBuildActivityHTTP(BuildCtx & ctx, IHqlExpre
             flags.append("|SOAPFlogmin");
         if (logText)
             flags.append("|SOAPFlogusermsg");
+        if (expr->hasAttribute(httpHeaderAtom))
+            flags.append("|SOAPFhttpheaders");
 
         if (flags.length())
             doBuildUnsignedFunction(instance->classctx, "getFlags", flags.str()+1);

+ 4 - 4
ecllibrary/std/Metaphone3.ecl

@@ -11,7 +11,7 @@ IMPORT lib_metaphone3;
  * Returns the primary metaphone value
  *
  * @param src           The string whose metphone is to be calculated.
- * @see                 http://en.wikipedia.org/wiki/Metaphone#Double_Metaphone
+ * @see                 http://en.wikipedia.org/wiki/Metaphone#Metaphone_3
  */
 
 EXPORT String primary(STRING src, boolean encodeVowels=false, boolean encodeExact=false, unsigned4 maxLength=0) :=
@@ -21,17 +21,17 @@ EXPORT String primary(STRING src, boolean encodeVowels=false, boolean encodeExac
  * Returns the secondary metaphone value
  *
  * @param src           The string whose metphone is to be calculated.
- * @see                 http://en.wikipedia.org/wiki/Metaphone#Double_Metaphone
+ * @see                 http://en.wikipedia.org/wiki/Metaphone#Metaphone_3
  */
 
 EXPORT String secondary(STRING src, boolean encodeVowels=false, boolean encodeExact=false, unsigned4 maxLength=0) :=
   lib_metaphone3.Metaphone3Lib.Metaphone3Alt(src, encodeVowels, encodeExact, maxLength);
 
 /**
- * Returns the double metaphone value (primary and secondary concatenated
+ * Returns the double metaphone value (primary and secondary concatenated)
  *
  * @param src           The string whose metphone is to be calculated.
- * @see                 http://en.wikipedia.org/wiki/Metaphone#Double_Metaphone
+ * @see                 http://en.wikipedia.org/wiki/Metaphone#Metaphone_3
  */
 
 EXPORT String double(STRING src, boolean encodeVowels=false, boolean encodeExact=false, unsigned4 maxLength=0) :=

+ 42 - 1
ecllibrary/teststd/uni/TestEditDistance.ecl

@@ -14,30 +14,71 @@ EXPORT TestEditDistance := MODULE
 
   EXPORT TestConst := MODULE
     EXPORT Test01 := ASSERT(Uni.EditDistance(U'',U'') = 0, CONST);
+    EXPORT Test01a := ASSERT(Uni.EditDistance(U'',U'','en') = 0, CONST);
     EXPORT Test02 := ASSERT(Uni.EditDistance(U'',U'                ') = 0, CONST);
+    EXPORT Test02a := ASSERT(Uni.EditDistance(U'',U'                ','en') = 0, CONST);
     EXPORT Test03 := ASSERT(Uni.EditDistance(U'                ',U'') = 0, CONST);
+    EXPORT Test03a := ASSERT(Uni.EditDistance(U'                ',U'','en') = 0, CONST);
     EXPORT Test04 := ASSERT(Uni.EditDistance(U'a ',U'                ') = 1, CONST);
+    EXPORT Test04a := ASSERT(Uni.EditDistance(U'a ',U'                ','en') = 1, CONST);
     //EXPORT Test05 := ASSERT(Uni.EditDistance(U' a ',U'   ') = 1, CONST);
     EXPORT Test06 := ASSERT(Uni.EditDistance(U'Aprs  ',U'APp') = 3, CONST);
+    EXPORT Test06a := ASSERT(Uni.EditDistance(U'Aprs  ',U'APp','en') = 3, CONST);
     EXPORT Test07 := ASSERT(Uni.EditDistance(U'abcd',U'acbd') = 2, CONST);
+    EXPORT Test07a := ASSERT(Uni.EditDistance(U'abcd',U'acbd','en') = 2, CONST);
     EXPORT Test08 := ASSERT(Uni.EditDistance(U'abcd',U'abd') = 1, CONST);
+    EXPORT Test08a := ASSERT(Uni.EditDistance(U'abcd',U'abd','en') = 1, CONST);
     EXPORT Test09 := ASSERT(Uni.EditDistance(U'abcd',U'abc') = 1, CONST);
+    EXPORT Test09a := ASSERT(Uni.EditDistance(U'abcd',U'abc','en') = 1, CONST);
     EXPORT Test10 := ASSERT(Uni.EditDistance(U'abcd',U'bcd') = 1, CONST);
+    EXPORT Test10a := ASSERT(Uni.EditDistance(U'abcd',U'bcd','en') = 1, CONST);
     EXPORT Test11 := ASSERT(Uni.EditDistance(U'abcd',U'abcde') = 1, CONST);
+    EXPORT Test11a := ASSERT(Uni.EditDistance(U'abcd',U'abcde','en') = 1, CONST);
     EXPORT Test12 := ASSERT(Uni.EditDistance(U'abcd',U'aabcd') = 1, CONST);
+    EXPORT Test12a := ASSERT(Uni.EditDistance(U'abcd',U'aabcd','en') = 1, CONST);
     EXPORT Test13 := ASSERT(Uni.EditDistance(U'abcd',U' abcd') = 1, CONST);
+    EXPORT Test13a := ASSERT(Uni.EditDistance(U'abcd',U' abcd','en') = 1, CONST);
     EXPORT Test14 := ASSERT(Uni.EditDistance(U'abcd',U'a bcd') = 1, CONST);
+    EXPORT Test14a := ASSERT(Uni.EditDistance(U'abcd',U'a bcd','en') = 1, CONST);
     EXPORT Test15 := ASSERT(Uni.EditDistance(U'abcd',U'adcd') = 1, CONST);
+    EXPORT Test15a := ASSERT(Uni.EditDistance(U'abcd',U'adcd','en') = 1, CONST);
     EXPORT Test16 := ASSERT(Uni.EditDistance(U'abcd',U'') = 4, CONST);
+    EXPORT Test16a := ASSERT(Uni.EditDistance(U'abcd',U'','en') = 4, CONST);
     EXPORT Test17 := ASSERT(Uni.EditDistance(alpha,U'') = 26, CONST);
+    EXPORT Test17a := ASSERT(Uni.EditDistance(alpha,U'','en') = 26, CONST);
     EXPORT Test18 := ASSERT(Uni.EditDistance(manyAlpha,U'') = 255, CONST);      //overflow
+    EXPORT Test18a := ASSERT(Uni.EditDistance(manyAlpha,U'','en') = 255, CONST);      //overflow
     EXPORT Test19 := ASSERT(Uni.EditDistance(alpha,digits) = 26, CONST);
+    EXPORT Test19a := ASSERT(Uni.EditDistance(alpha,digits,'en') = 26, CONST);
     EXPORT Test20 := ASSERT(Uni.EditDistance(manyAlpha,digits) = 255, CONST);   //overflow
+    EXPORT Test20a := ASSERT(Uni.EditDistance(manyAlpha,digits,'en') = 255, CONST);   //overflow
     EXPORT Test21 := ASSERT(Uni.EditDistance(manyAlpha,manyDigits) = 255, CONST);   //overflow
+    EXPORT Test21a := ASSERT(Uni.EditDistance(manyAlpha,manyDigits,'en') = 255, CONST);   //overflow
     EXPORT Test22 := ASSERT(Uni.EditDistance(alpha,manyDigits) = 250, CONST);
+    EXPORT Test22a := ASSERT(Uni.EditDistance(alpha,manyDigits,'en') = 250, CONST);
     EXPORT Test23 := ASSERT(Uni.EditDistance(alpha,manyDigits+U'12345') = 255, CONST);
+    EXPORT Test23a := ASSERT(Uni.EditDistance(alpha,manyDigits+U'12345','en') = 255, CONST);
     EXPORT Test24 := ASSERT(Uni.EditDistance(alpha,manyDigits+U'123456') = 255, CONST);
+    EXPORT Test24a := ASSERT(Uni.EditDistance(alpha,manyDigits+U'123456','en') = 255, CONST);
     EXPORT Test25 := ASSERT(Uni.EditDistance(U'123456789',U'987654321') = 8, CONST);
+    EXPORT Test25a := ASSERT(Uni.EditDistance(U'123456789',U'987654321','en') = 8, CONST);
+    EXPORT Test26 := ASSERT(Uni.EditDistance(U'AVILÉS',U'AVILES') = 1, CONST);
+    EXPORT Test26a := ASSERT(Uni.EditDistance(U'AVILÉS',U'AVILES','en') = 1, CONST);
+    EXPORT Test27 := ASSERT(Uni.EditDistance(U'MOMBRU',U'MOMBRÚ') = 1, CONST);
+    EXPORT Test27a := ASSERT(Uni.EditDistance(U'MOMBRU',U'MOMBRÚ','en') = 1, CONST);
+    EXPORT Test28 := ASSERT(Uni.EditDistance(U'BLVAREZ',U'ÁLVAREZ') = 1, CONST);
+    EXPORT Test28a := ASSERT(Uni.EditDistance(U'BLVAREZ',U'ÁLVAREZ','en') = 1, CONST);
+    // when character's encoding is from 0x00ffff - 0x10ffff range: 0x1D306 ; Description=TETRAGRAM FOR CENTER (Tai Xuan Jing Symbols)
+    // UTF-16 representation is xD834,xDF06 (2 16-bit surrogates)
+    EXPORT Test27 := ASSERT(Uni.EditDistance(U'\uD834\uDF06XXX',U'XXXX') = 1, CONST);
+    EXPORT Test27a := ASSERT(Uni.EditDistance(U'\uD834\uDF06XXX',U'XXXX','en') = 1, CONST);
+    // NFC (normalized form composed) for accented characters uses multiple 16-bit code units
+    // for example: Ḍ̛ is encoded as 0x1E0C,0x031B, and Ḍ̛̇ as 0x1E0C,0x031B,0x0307
+    // These are the cases where the fast function version (ToDo) does not work correctly, but this one does
+    EXPORT Test28 := ASSERT(Uni.EditDistance(U'\u1E0C\u031BDDD',U'DDDD') = 2, CONST);
+    EXPORT Test28a := ASSERT(Uni.EditDistance(U'\u1E0C\u031BDDD',U'DDDD','en') = 1, CONST);
+    // Lithuanian 'i dot acute' is encoded as 0069 0307 0301
+    EXPORT Test29 := ASSERT(Uni.EditDistance(U'\u0069\u0307\u0301DDD',U'DDDD') = 3, CONST);
+    EXPORT Test29a := ASSERT(Uni.EditDistance(U'\u0069\u0307\u0301DDD',U'DDDD','lt') = 1, CONST);
   END;
-
 END;

+ 56 - 1
ecllibrary/teststd/uni/TestEditDistanceWithinRadius.ecl

@@ -14,62 +14,117 @@ EXPORT TestEditDistanceWithinRadius := MODULE
 
   EXPORT TestConst := MODULE
     EXPORT Test01a := ASSERT(Uni.EditDistanceWithinRadius(U'',U'',0), CONST);
+    EXPORT Test01b := ASSERT(Uni.EditDistanceWithinRadius(U'',U'',0,'en'), CONST);
     EXPORT Test02a := ASSERT(Uni.EditDistanceWithinRadius(U'',U'                ',0), CONST);
+    EXPORT Test02b := ASSERT(Uni.EditDistanceWithinRadius(U'',U'                ',0,'en'), CONST);
     EXPORT Test03a := ASSERT(Uni.EditDistanceWithinRadius(U'                ',U'',0), CONST);
+    EXPORT Test03b := ASSERT(Uni.EditDistanceWithinRadius(U'                ',U'',0,'en'), CONST);
     EXPORT Test04a := ASSERT(Uni.EditDistanceWithinRadius(U'a ',U'                ',1), CONST);
     EXPORT Test04b := ASSERT(NOT Uni.EditDistanceWithinRadius(U'a ',U'                ',0), CONST);
+    EXPORT Test04c := ASSERT(Uni.EditDistanceWithinRadius(U'a ',U'                ',1,'en'), CONST);
+    EXPORT Test04d := ASSERT(NOT Uni.EditDistanceWithinRadius(U'a ',U'                ',0,'en'), CONST);
 //    EXPORT Test05a := ASSERT(Uni.EditDistanceWithinRadius(U' a ',U'  ', 1), CONST);
     EXPORT Test05b := ASSERT(NOT Uni.EditDistanceWithinRadius(U' a ',U'  ', 0), CONST);
+    EXPORT Test05c := ASSERT(NOT Uni.EditDistanceWithinRadius(U' a ',U'  ', 0,'en'), CONST);
     EXPORT Test06a := ASSERT(Uni.EditDistanceWithinRadius(U'Aprs  ',U'APp',3), CONST);
     EXPORT Test06b := ASSERT(NOT Uni.EditDistanceWithinRadius(U'Aprs  ',U'APp',2), CONST);
+    EXPORT Test06c := ASSERT(Uni.EditDistanceWithinRadius(U'Aprs  ',U'APp',3,'en'), CONST);
+    EXPORT Test06d := ASSERT(NOT Uni.EditDistanceWithinRadius(U'Aprs  ',U'APp',2,'en'), CONST);
     EXPORT Test07a := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'acbd',2), CONST);
     EXPORT Test07b := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'acbd',1), CONST);
+    EXPORT Test07c := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'acbd',2,'en'), CONST);
+    EXPORT Test07d := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'acbd',1,'en'), CONST);
     EXPORT Test08a := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'abd',1), CONST);
     EXPORT Test08b := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'abd',0), CONST);
+    EXPORT Test08c := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'abd',1,'en'), CONST);
+    EXPORT Test08d := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'abd',0,'en'), CONST);
     EXPORT Test09a := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'abc',1), CONST);
     EXPORT Test09b := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'abc',0), CONST);
+    EXPORT Test09c := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'abc',1,'en'), CONST);
+    EXPORT Test09d := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'abc',0,'en'), CONST);
     EXPORT Test10a := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'bcd',1), CONST);
     EXPORT Test10b := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'bcd',0), CONST);
+    EXPORT Test10c := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'bcd',1,'en'), CONST);
+    EXPORT Test10d := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'bcd',0,'en'), CONST);
     EXPORT Test11a := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'abcde',1), CONST);
     EXPORT Test11b := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'abcde',0), CONST);
+    EXPORT Test11c := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'abcde',1,'en'), CONST);
+    EXPORT Test11d := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'abcde',0,'en'), CONST);
     EXPORT Test12a := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'aabcd',1), CONST);
     EXPORT Test12b := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'aabcd',0), CONST);
+    EXPORT Test12c := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'aabcd',1,'en'), CONST);
+    EXPORT Test12d := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'aabcd',0,'en'), CONST);
     EXPORT Test13a := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U' abcd',1), CONST);
     EXPORT Test13b := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U' abcd',0), CONST);
+    EXPORT Test13c := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U' abcd',1,'en'), CONST);
+    EXPORT Test13d := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U' abcd',0,'en'), CONST);
     EXPORT Test14a := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'a bcd',1), CONST);
     EXPORT Test14b := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'a bcd',0), CONST);
+    EXPORT Test14c := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'a bcd',1,'en'), CONST);
+    EXPORT Test14d := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'a bcd',0,'en'), CONST);
     EXPORT Test15a := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'adcd',1), CONST);
     EXPORT Test15b := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'adcd',0), CONST);
+    EXPORT Test15c := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'adcd',1,'en'), CONST);
+    EXPORT Test15d := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'adcd',0,'en'), CONST);
     EXPORT Test16a := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'',4), CONST);
     EXPORT Test16b := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'',3), CONST);
+    EXPORT Test16c := ASSERT(Uni.EditDistanceWithinRadius(U'abcd',U'',4,'en'), CONST);
+    EXPORT Test16d := ASSERT(NOT Uni.EditDistanceWithinRadius(U'abcd',U'',3,'en'), CONST);
     EXPORT Test17a := ASSERT(Uni.EditDistanceWithinRadius(alpha,U'',26), CONST);
     EXPORT Test17b := ASSERT(NOT Uni.EditDistanceWithinRadius(alpha,U'',25), CONST);
+    EXPORT Test17c := ASSERT(Uni.EditDistanceWithinRadius(alpha,U'',26,'en'), CONST);
+    EXPORT Test17d := ASSERT(NOT Uni.EditDistanceWithinRadius(alpha,U'',25,'en'), CONST);
     EXPORT Test18a := ASSERT(Uni.EditDistanceWithinRadius(manyAlpha,U'',255), CONST);
     EXPORT Test18b := ASSERT(NOT Uni.EditDistanceWithinRadius(manyAlpha,U'',254), CONST);
+    EXPORT Test18c := ASSERT(Uni.EditDistanceWithinRadius(manyAlpha,U'',255,'en'), CONST);
+    EXPORT Test18d := ASSERT(NOT Uni.EditDistanceWithinRadius(manyAlpha,U'',254,'en'), CONST);
     EXPORT Test19a := ASSERT(Uni.EditDistanceWithinRadius(alpha,digits,26), CONST);
     EXPORT Test19b := ASSERT(NOT Uni.EditDistanceWithinRadius(alpha,digits,25), CONST);
+    EXPORT Test19c := ASSERT(Uni.EditDistanceWithinRadius(alpha,digits,26,'en'), CONST);
+    EXPORT Test19d := ASSERT(NOT Uni.EditDistanceWithinRadius(alpha,digits,25,'en'), CONST);
     EXPORT Test20a := ASSERT(Uni.EditDistanceWithinRadius(manyAlpha,digits,255),CONST);         //overflow
     EXPORT Test20b := ASSERT(NOT Uni.EditDistanceWithinRadius(manyAlpha,digits,254),CONST);     //overflow
+    EXPORT Test20c := ASSERT(Uni.EditDistanceWithinRadius(manyAlpha,digits,255,'en'),CONST);         //overflow
+    EXPORT Test20d := ASSERT(NOT Uni.EditDistanceWithinRadius(manyAlpha,digits,254,'en'),CONST);     //overflow
     EXPORT Test21a := ASSERT(Uni.EditDistanceWithinRadius(manyAlpha,manyDigits,255),CONST);     //overflow
     EXPORT Test21b := ASSERT(NOT Uni.EditDistanceWithinRadius(manyAlpha,manyDigits,254),CONST); //overflow
+    EXPORT Test21c := ASSERT(Uni.EditDistanceWithinRadius(manyAlpha,manyDigits,255,'en'),CONST);     //overflow
+    EXPORT Test21d := ASSERT(NOT Uni.EditDistanceWithinRadius(manyAlpha,manyDigits,254,'en'),CONST); //overflow
     EXPORT Test22a := ASSERT(Uni.EditDistanceWithinRadius(alpha,manyDigits,250), CONST);
     EXPORT Test22b := ASSERT(NOT Uni.EditDistanceWithinRadius(alpha,manyDigits,249), CONST);
+    EXPORT Test22c := ASSERT(Uni.EditDistanceWithinRadius(alpha,manyDigits,250,'en'), CONST);
+    EXPORT Test22d := ASSERT(NOT Uni.EditDistanceWithinRadius(alpha,manyDigits,249,'en'), CONST);
     EXPORT Test23a := ASSERT(Uni.EditDistanceWithinRadius(alpha,manyDigits+U'12345',255), CONST);
     EXPORT Test23b := ASSERT(NOT Uni.EditDistanceWithinRadius(alpha,manyDigits+U'12345',254), CONST);
+    EXPORT Test23c := ASSERT(Uni.EditDistanceWithinRadius(alpha,manyDigits+U'12345',255,'en'), CONST);
+    EXPORT Test23d := ASSERT(NOT Uni.EditDistanceWithinRadius(alpha,manyDigits+U'12345',254,'en'), CONST);
     EXPORT Test24a := ASSERT(Uni.EditDistanceWithinRadius(alpha,manyDigits+U'123456',255), CONST);
     EXPORT Test24b := ASSERT(NOT Uni.EditDistanceWithinRadius(alpha,manyDigits+U'123456',254), CONST);
+    EXPORT Test24c := ASSERT(Uni.EditDistanceWithinRadius(alpha,manyDigits+U'123456',255,'en'), CONST);
+    EXPORT Test24d := ASSERT(NOT Uni.EditDistanceWithinRadius(alpha,manyDigits+U'123456',254,'en'), CONST);
     EXPORT Test25a := ASSERT(Uni.EditDistanceWithinRadius(U'123456789',U'987654321',8), CONST);
     EXPORT Test25b := ASSERT(NOT Uni.EditDistanceWithinRadius(U'123456789',U'987654321',7), CONST);
+    EXPORT Test25c := ASSERT(Uni.EditDistanceWithinRadius(U'123456789',U'987654321',8,'en'), CONST);
+    EXPORT Test25d := ASSERT(NOT Uni.EditDistanceWithinRadius(U'123456789',U'987654321',7,'en'), CONST);
     EXPORT Test26a := ASSERT(Uni.EditDistanceWithinRadius(U'AVILÉS',U'AVILES',1), CONST);
     EXPORT Test26b := ASSERT(Uni.EditDistanceWithinRadius(U'MOMBRU',U'MOMBRÚ',1), CONST);
     EXPORT Test26c := ASSERT(Uni.EditDistanceWithinRadius(U'BLVAREZ',U'ÁLVAREZ',1), CONST);
+    EXPORT Test26aa := ASSERT(Uni.EditDistanceWithinRadius(U'AVILÉS',U'AVILES',1, 'en'), CONST);
+    EXPORT Test26bb := ASSERT(Uni.EditDistanceWithinRadius(U'MOMBRU',U'MOMBRÚ',1, 'en'), CONST);
+    EXPORT Test26cc := ASSERT(Uni.EditDistanceWithinRadius(U'BLVAREZ',U'ÁLVAREZ',1, 'en'), CONST);
     // when character's encoding is from 0x00ffff - 0x10ffff range: 0x1D306 ; Description=TETRAGRAM FOR CENTER (Tai Xuan Jing Symbols)
     // UTF-16 representation is xD834,xDF06 (2 16-bit surrogates)
     EXPORT Test27a := ASSERT(Uni.EditDistanceWithinRadius(U'\uD834\uDF06XXX',U'XXXX',1), CONST);
+    EXPORT Test27b := ASSERT(Uni.EditDistanceWithinRadius(U'\uD834\uDF06XXX',U'XXXX',1, 'en'), CONST);
     // NFC (normalized form composed) for accented characters uses multiple 16-bit code units
     // for example: Ḍ̛ is encoded as 0x1E0C,0x031B, and Ḍ̛̇ as 0x1E0C,0x031B,0x0307
     // These are the cases where the fast function version (ToDo) does not work correctly, but this one does
-    EXPORT Test27b := ASSERT(Uni.EditDistanceWithinRadius(U'\u1E0C\u031BDDD',U'DDDD',1), CONST);
+    EXPORT Test28a := ASSERT(NOT Uni.EditDistanceWithinRadius(U'\u1E0C\u031BDDD',U'DDDD',1), CONST);
+    EXPORT Test28b := ASSERT(Uni.EditDistanceWithinRadius(U'\u1E0C\u031BDDD',U'DDDD',1, 'en'), CONST);
+    // Lithuanian 'i dot acute' is encoded as 0069 0307 0301
+    EXPORT Test29a := ASSERT(NOT Uni.EditDistanceWithinRadius(U'\u0069\u0307\u0301DDD',U'DDDD',1), CONST);
+    EXPORT Test29b := ASSERT(Uni.EditDistanceWithinRadius(U'\u0069\u0307\u0301DDD',U'DDDD',1, 'lt'), CONST);
+
   END;
 
 END;

+ 57 - 0
esp/services/ws_smc/ws_smcService.cpp

@@ -1975,6 +1975,63 @@ bool CWsSMCEx::onBrowseResources(IEspContext &context, IEspBrowseResourcesReques
     return true;
 }
 
+int CWsSMCSoapBindingEx::onHttpEcho(CHttpRequest* request,  CHttpResponse* response)
+{
+    StringBuffer xml;
+    xml.append(
+        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+        "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
+          "<soap:Body>"
+            "<HttpEchoResponse xmlns='urn:hpccsystems:ws:httpecho'>");
+
+    appendXMLTag(xml, "Method", request->queryMethod());
+    appendXMLTag(xml, "UrlPath", request->queryPath());
+    appendXMLTag(xml, "UrlParameters", request->queryParamStr());
+
+    appendXMLOpenTag(xml, "Headers");
+    StringArray &headers = request->queryHeaders();
+    headers.sortAscii(false);
+    ForEachItemIn(i, headers)
+    {
+        const char *h = headers.item(i);
+        if (strnicmp(h, "Authorization", 13))
+            appendXMLTag(xml, "Header", h);
+    }
+    appendXMLCloseTag(xml, "Headers");
+
+    const char *content = request->queryContent();
+    if (content && *content)
+        appendXMLTag(xml, "Content", content);
+    xml.append("</HttpEchoResponse></soap:Body></soap:Envelope>");
+
+    response->setContent(xml);
+    response->setContentType("text/xml");
+    response->send();
+    return 0;
+}
+
+
+int CWsSMCSoapBindingEx::onGet(CHttpRequest* request,  CHttpResponse* response)
+{
+    const char *operation = request->queryServiceMethod();
+    if (!operation || !strieq(operation, "HttpEcho"))
+        return CWsSMCSoapBinding::onGet(request, response);
+
+    return onHttpEcho(request, response);
+}
+
+void CWsSMCSoapBindingEx::handleHttpPost(CHttpRequest *request, CHttpResponse *response)
+{
+    sub_service sstype;
+    StringBuffer operation;
+    request->getEspPathInfo(sstype, NULL, NULL, &operation, false);
+    if (!operation || !strieq(operation, "HttpEcho"))
+        CWsSMCSoapBinding::handleHttpPost(request, response);
+    else
+        onHttpEcho(request, response);
+}
+
+
 int CWsSMCSoapBindingEx::onGetForm(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service, const char *method)
 {
     try

+ 5 - 0
esp/services/ws_smc/ws_smcService.hpp

@@ -258,6 +258,11 @@ public:
             return NULL;
         return "stub.htm";
     }
+    virtual int onGet(CHttpRequest* request,  CHttpResponse* response);
+    void handleHttpPost(CHttpRequest *request, CHttpResponse *response);
+    int onHttpEcho(CHttpRequest* request,  CHttpResponse* response);
+
+
     virtual int onGetRoot(IEspContext &context, CHttpRequest* request,  CHttpResponse* response)
     {
         return  onGetInstantQuery(context, request, response, "WsSMC", "Activity");

+ 50 - 17
plugins/unicodelib/unicodelib.cpp

@@ -277,6 +277,12 @@ inline unsigned char min3(unsigned char a, unsigned char b, unsigned char c)
     return (min<c)? min:c;
 }
 
+// returns the length of Unicode Code Point in 16-bit Code Units
+inline int ucpLength(UChar32 c)
+{
+    return U16_IS_SINGLE(c)?1:2;
+}
+
 #define DISTANCE_ON_ERROR 999
 class UPCList // User perceived character list
 {
@@ -302,12 +308,27 @@ private:
         }
         if (U_FAILURE(status)) { length_ = 0; capacity_ = 0; invalid_ = true; }
     }
+    void doCreateUPCList() {
+        if (!capacity_) {
+             capacity_ = ustring_.length();
+         }
+        next_ = new uint32_t[capacity_+1]; // the number of characters is always less or equal to the string length
+        unsigned index=0;
+        next_[index] = 0;
+        int32_t end = 0;
+        while (end < capacity_)
+        {
+            end = end+ucpLength(ustring_[end]);
+            next_[++index] = end;
+        }
+        length_ = index;
+    }
 
 public:
-    UPCList(BreakIterator& cbi, const UnicodeString & source, uint32_t capacity=0)
+    UPCList(BreakIterator* cbi, const UnicodeString & source, uint32_t capacity=0)
         : length_(0), capacity_(capacity),ustring_(source), invalid_(false)
     {
-        doCreateUPCList(cbi);
+        !cbi?doCreateUPCList():doCreateUPCList(*cbi);
     }
 
     ~UPCList()
@@ -582,7 +603,7 @@ unsigned unicodeEditDistanceV3(UnicodeString & left, UnicodeString & right, unsi
 
 //This function is based on the unicodeEditDistanceV3 to pickup optimizations;
 // It replaces RuleBasedCollator with the CharacterIterator
-unsigned unicodeEditDistanceV4(UnicodeString & left, UnicodeString & right, unsigned radius, BreakIterator& bi)
+unsigned unicodeEditDistanceV4(UnicodeString & left, UnicodeString & right, unsigned radius, BreakIterator* bi)
 {
     if (radius >= 255)
         return 255;
@@ -593,9 +614,13 @@ unsigned unicodeEditDistanceV4(UnicodeString & left, UnicodeString & right, unsi
     unsigned leftLen = left.length();
     unsigned rightLen = right.length();
 
-    unsigned minED = (leftLen < rightLen)? rightLen - leftLen: leftLen - rightLen;
-    if (minED > radius)
-        return minED;
+    // this shortcut is not applicable in the bi mode because unicode characters could take more than 2 UChars
+    if (!bi)
+    {
+        unsigned minED = (leftLen < rightLen)? rightLen - leftLen: leftLen - rightLen;
+        if (minED > radius)
+            return minED>255?255:minED;
+    }
 
     if (leftLen > 255)
         leftLen = 255;
@@ -613,7 +638,7 @@ unsigned unicodeEditDistanceV4(UnicodeString & left, UnicodeString & right, unsi
     UPCList leftCs(bi, left, leftLen);
     UPCList rightCs(bi, right, rightLen);
     if (leftCs.isInvalid() || rightCs.isInvalid())
-        return false;
+        return DISTANCE_ON_ERROR;
 
     leftLen = leftCs.length();
     rightLen = rightCs.length();
@@ -1326,28 +1351,36 @@ UNICODELIB_API void UNICODELIB_CALL ulUnicodeCleanAccents(unsigned & tgtLen, UCh
 
 UNICODELIB_API unsigned UNICODELIB_CALL ulUnicodeLocaleEditDistance(unsigned leftLen, UChar const * left, unsigned rightLen, UChar const * right, char const * localename)
 {
-    RuleBasedCollator* rbc = queryRBCollator(localename);
-    if (!rbc)
-        return DISTANCE_ON_ERROR;
+    BreakIterator* bi = 0;
+    if (localename && *localename)
+    {
+        bi = queryCharacterBreakIterator(localename);
+        if (!bi)
+            return DISTANCE_ON_ERROR;
+    }
 
-    UnicodeString uLeft(left, leftLen);
-    UnicodeString uRight(right, rightLen);
+    UnicodeString uLeft(false, left, leftLen); // Readonly-aliasing UChar* constructor.
+    UnicodeString uRight(false, right, rightLen);
 
-    unsigned distance = nsUnicodelib::unicodeEditDistanceV2(uLeft, uRight, *rbc);
+    unsigned distance = nsUnicodelib::unicodeEditDistanceV4(uLeft, uRight, 254, bi);
     return distance;
 }
 
 
 UNICODELIB_API bool UNICODELIB_CALL ulUnicodeLocaleEditDistanceWithinRadius(unsigned leftLen, UChar const * left, unsigned rightLen, UChar const * right, unsigned radius, char const * localename)
 {
-    BreakIterator* bi = queryCharacterBreakIterator(localename);
-    if (!bi)
-        return false;
+    BreakIterator* bi = 0;
+    if (localename && *localename)
+    {
+        bi = queryCharacterBreakIterator(localename);
+        if (!bi)
+            return false;
+    }
 
     UnicodeString uLeft(false, left, leftLen); // Readonly-aliasing UChar* constructor.
     UnicodeString uRight(false, right, rightLen);
 
-    unsigned distance = nsUnicodelib::unicodeEditDistanceV4(uLeft, uRight, radius, *bi);
+    unsigned distance = nsUnicodelib::unicodeEditDistanceV4(uLeft, uRight, radius, bi);
     return distance <= radius;
 }
 

+ 0 - 1
roxie/ccd/ccdqueue.cpp

@@ -2466,7 +2466,6 @@ void LocalMessagePacker::flush(bool last_message)
 CLocalMessageCollator::CLocalMessageCollator(IRowManager *_rowManager, ruid_t _ruid) 
     : rowManager(_rowManager), id(_ruid)
 {
-    id = 0;
     totalBytesReceived = 0;
 }
 

+ 2 - 0
rtl/include/eclhelper.hpp

@@ -2138,6 +2138,7 @@ enum
     SOAPFpreserveSpace  = 0x0080,
     SOAPFlogmin         = 0x0100,
     SOAPFlogusermsg     = 0x0200,
+    SOAPFhttpheaders    = 0x0400
 };
 
 struct IHThorWebServiceCallActionArg : public IHThorArg
@@ -2162,6 +2163,7 @@ struct IHThorWebServiceCallActionArg : public IHThorArg
     virtual const char * getHttpHeaderValue()         { return NULL; }
     virtual const char * getProxyAddress()            { return NULL; }
     virtual const char * getAcceptType()              { return NULL; }
+    virtual const char * getHttpHeaders()             { return NULL; }
 };
 typedef IHThorWebServiceCallActionArg IHThorSoapActionArg ;
 typedef IHThorWebServiceCallActionArg IHThorHttpActionArg ;

+ 2 - 2
rtl/include/eclhelper_base.hpp

@@ -2539,9 +2539,9 @@ class CThorSoapActionArg : public CThorArg, implements IHThorSoapActionArg
     virtual const char * getSoapAction()              { return NULL; }
     virtual const char * getNamespaceName()           { return NULL; }
     virtual const char * getNamespaceVar()            { return NULL; }
-
     virtual const char * getHttpHeaderName()          { return NULL; }
     virtual const char * getHttpHeaderValue()         { return NULL; }
+    virtual const char * getHttpHeaders()             { return NULL; }
     virtual const char * getProxyAddress()            { return NULL; }
     virtual const char * getAcceptType()              { return NULL; }
     virtual void getLogText(size32_t & lenText, char * & text, const void * left) { lenText =0; text = NULL; }
@@ -2584,9 +2584,9 @@ class CThorSoapCallArg : public CThorArg, implements IHThorSoapCallArg
     virtual const char * getSoapAction()              { return NULL; }
     virtual const char * getNamespaceName()           { return NULL; }
     virtual const char * getNamespaceVar()            { return NULL; }
-
     virtual const char * getHttpHeaderName()          { return NULL; }
     virtual const char * getHttpHeaderValue()         { return NULL; }
+    virtual const char * getHttpHeaders()             { return NULL; }
     virtual const char * getProxyAddress()            { return NULL; }
     virtual const char * getAcceptType()              { return NULL; }
     virtual void getLogText(size32_t & lenText, char * & text, const void * left) { lenText =0; text = NULL; }

+ 20 - 0
testing/regress/ecl/httpcall_multiheader.ecl

@@ -0,0 +1,20 @@
+string TargetIP := '.' : stored('TargetIP');
+string storedHeader := 'StoredHeaderDefault' : stored('StoredHeader');
+
+
+httpEchoServiceResponseRecord :=
+    RECORD
+        string method{xpath('Method')};
+        string path{xpath('UrlPath')};
+        string parameters{xpath('UrlParameters')};
+        set of string headers{xpath('Headers/Header')};
+        string content{xpath('Content')};
+    END;
+
+string TargetURL := 'http://' + TargetIP + ':8010/WsSmc/HttpEcho?name=doe,joe&number=1';
+
+string constHeader := 'constHeaderValue';
+
+httpcallResult := HTTPCALL(TargetURL,'GET', 'text/xml', httpEchoServiceResponseRecord, xpath('Envelope/Body/HttpEchoResponse'),httpheader('literalHeader','literalValue'), httpheader('constHeader','constHeaderValue'), httpheader('storedHeader', storedHeader));
+
+output(httpcallResult);

+ 3 - 0
testing/regress/ecl/key/httpcall_multiheader.xml

@@ -0,0 +1,3 @@
+<Dataset name='Result 1'>
+ <Row><Method>GET</Method><UrlPath>/WsSmc/HttpEcho</UrlPath><UrlParameters>name=doe,joe&amp;number=1</UrlParameters><Headers><Header>Accept: text/xml</Header><Header>constHeader: constHeaderValue</Header><Header>literalHeader: literalValue</Header><Header>storedHeader: StoredHeaderDefault</Header></Headers><Content></Content></Row>
+</Dataset>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 6 - 0
testing/regress/ecl/key/soapcall_multihttpheader.xml


+ 27 - 0
testing/regress/ecl/soapcall_multihttpheader.ecl

@@ -0,0 +1,27 @@
+string TargetIP := '.' : stored('TargetIP');
+string storedHeader := 'StoredHeaderDefault' : stored('storedHeader');
+
+httpEchoServiceResponseRecord :=
+    RECORD
+        string method{xpath('Method')};
+        string path{xpath('UrlPath')};
+        string parameters{xpath('UrlParameters')};
+        set of string headers{xpath('Headers/Header')};
+        string content{xpath('Content')};
+    END;
+
+string TargetURL := 'http://' + TargetIP + ':8010/WsSmc/HttpEcho?name=doe,joe&number=1';
+
+
+httpEchoServiceRequestRecord :=
+    RECORD
+       string Name{xpath('Name')} := 'Doe, Joe',
+       unsigned id{xpath('ADL')} := 999999,
+       real8 score := 88.88,
+    END;
+
+string constHeader := 'constHeaderValue';
+
+soapcallResult := SOAPCALL(TargetURL, 'HttpEcho', httpEchoServiceRequestRecord, DATASET(httpEchoServiceResponseRecord), LITERAL, xpath('HttpEchoResponse'), httpheader('StoredHeader', storedHeader), httpheader('literalHeader', 'literalHeaderValue'), httpheader('constHeader', constHeader));
+
+output(soapcallResult);