Browse Source

Merge remote-tracking branch 'origin/closedown-4.0.x'

Conflicts:
	dali/ft/daftformat.cpp

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 11 years ago
parent
commit
fdd6171fc4
33 changed files with 556 additions and 321 deletions
  1. 12 0
      build-config.h.cmake
  2. 1 0
      common/fileview2/CMakeLists.txt
  3. 75 18
      dali/ft/daftformat.cpp
  4. 1 1
      dali/ft/daftformat.ipp
  5. 23 5
      docs/ECLLanguageReference/ECLR_mods/Basics-AttributeVisibility.xml
  6. 21 11
      docs/ECLLanguageReference/ECLR_mods/BltInFunc-DENORMALIZE.xml
  7. 6 1
      docs/ECLLanguageReference/ECLR_mods/ParSppt-PARSPattrn.xml
  8. 42 12
      docs/ECLLanguageReference/ECLR_mods/SpecStruc-FuncTionMacro.xml
  9. 32 3
      ecl/ecl-bundle/ecl-bundle.cpp
  10. 4 3
      ecl/hql/hql.hpp
  11. 2 0
      ecl/hql/hqlerrors.hpp
  12. 1 1
      ecl/hql/hqlfold.cpp
  13. 3 3
      ecl/hql/hqlgram.y
  14. 2 2
      ecl/hql/hqlgram2.cpp
  15. 1 1
      ecl/hql/hqlopt.cpp
  16. 13 4
      ecl/hql/hqlutil.cpp
  17. 1 1
      ecl/hql/hqlutil.hpp
  18. 1 1
      ecl/hqlcpp/hqlttcpp.cpp
  19. 13 51
      esp/files/scripts/DFUQueryWidget.js
  20. 109 0
      esp/files/scripts/FilterDropDownWidget.js
  21. 24 67
      esp/files/scripts/GetDFUWorkunitsWidget.js
  22. 28 68
      esp/files/scripts/WUQueryWidget.js
  23. 12 22
      esp/files/templates/DFUQueryWidget.html
  24. 17 0
      esp/files/templates/FilterDropDownWidget.html
  25. 6 16
      esp/files/templates/GetDFUWorkunitsWidget.html
  26. 2 2
      esp/files/templates/HPCCPlatformFilesWidget.html
  27. 14 24
      esp/files/templates/WUQueryWidget.html
  28. 1 0
      esp/services/ws_dfu/CMakeLists.txt
  29. 1 0
      esp/services/ws_workunits/CMakeLists.txt
  30. 12 3
      plugins/unicodelib/unicodelib.cpp
  31. 20 0
      testing/ecl/issue9879.ecl
  32. 45 0
      testing/ecl/key/issue9879.xml
  33. 11 1
      thorlcr/activities/hashdistrib/thhashdistribslave.cpp

+ 12 - 0
build-config.h.cmake

@@ -82,6 +82,18 @@
     #cmakedefine BUILD_TAG "${BUILD_TAG}"
     #cmakedefine BUILD_TAG "${BUILD_TAG}"
 #endif
 #endif
 
 
+#ifndef BUILD_VERSION_MAJOR
+    #define BUILD_VERSION_MAJOR ${HPCC_MAJOR}
+#endif
+
+#ifndef BUILD_VERSION_MINOR
+    #define BUILD_VERSION_MINOR ${HPCC_MINOR}
+#endif
+
+#ifndef BUILD_VERSION_POINT
+    #define BUILD_VERSION_POINT ${HPCC_POINT}
+#endif
+
 #ifndef BASE_BUILD_TAG
 #ifndef BASE_BUILD_TAG
     #cmakedefine BASE_BUILD_TAG "${BASE_BUILD_TAG}"
     #cmakedefine BASE_BUILD_TAG "${BASE_BUILD_TAG}"
 #endif
 #endif

+ 1 - 0
common/fileview2/CMakeLists.txt

@@ -60,6 +60,7 @@ set (    SRCS
     )
     )
 
 
 include_directories ( 
 include_directories ( 
+         ${CMAKE_BINARY_DIR}/oss
          ./../../system/mp 
          ./../../system/mp 
          ./../../system/jhtree 
          ./../../system/jhtree 
          ./../dllserver 
          ./../dllserver 

+ 75 - 18
dali/ft/daftformat.cpp

@@ -566,6 +566,10 @@ CCsvPartitioner::CCsvPartitioner(const FileFormat & _format) : CInputBasePartiti
     addActionList(matcher, format.separate.get() ? format.separate.get() : "\\,", SEPARATOR, &maxElementLength);
     addActionList(matcher, format.separate.get() ? format.separate.get() : "\\,", SEPARATOR, &maxElementLength);
     addActionList(matcher, format.quote.get() ? format.quote.get() : "'", QUOTE, &maxElementLength);
     addActionList(matcher, format.quote.get() ? format.quote.get() : "'", QUOTE, &maxElementLength);
     addActionList(matcher, format.terminate.get() ? format.terminate.get() : "\\n,\\r\\n", TERMINATOR, &maxElementLength);
     addActionList(matcher, format.terminate.get() ? format.terminate.get() : "\\n,\\r\\n", TERMINATOR, &maxElementLength);
+    const char * escape = format.escape.get();
+    if (escape && *escape)
+        addActionList(matcher,  escape, ESCAPE, &maxElementLength);
+
     matcher.queryAddEntry(1, " ", WHITESPACE);
     matcher.queryAddEntry(1, " ", WHITESPACE);
     matcher.queryAddEntry(1, "\t", WHITESPACE);
     matcher.queryAddEntry(1, "\t", WHITESPACE);
 }
 }
@@ -574,11 +578,14 @@ size32_t CCsvPartitioner::getSplitRecordSize(const byte * start, unsigned maxToR
 {
 {
     //more complicated processing of quotes etc....
     //more complicated processing of quotes etc....
     unsigned quote = 0;
     unsigned quote = 0;
+    unsigned quoteToStrip = 0;
     const byte * cur = start;
     const byte * cur = start;
     const byte * end = start + maxToRead;
     const byte * end = start + maxToRead;
-    const byte * startOfColumn = cur;
-
+    const byte * firstGood = start;
+    const byte * lastGood = start;
     const byte * last = start;
     const byte * last = start;
+    bool lastEscape = false;
+
     while (cur != end)
     while (cur != end)
     {
     {
         unsigned matchLen;
         unsigned matchLen;
@@ -587,53 +594,103 @@ size32_t CCsvPartitioner::getSplitRecordSize(const byte * start, unsigned maxToR
         {
         {
         case NONE:
         case NONE:
             cur++;          // matchLen == 0;
             cur++;          // matchLen == 0;
+            lastGood = cur;
             break;
             break;
         case WHITESPACE:
         case WHITESPACE:
             //Skip leading whitepace
             //Skip leading whitepace
-            if (!quote&&(cur == startOfColumn))
+            if (quote)
+                lastGood = cur+matchLen;
+            else if (cur == firstGood)
             {
             {
-                startOfColumn = cur+matchLen;
+                firstGood = cur+matchLen;
+                lastGood = cur+matchLen;
             }
             }
             break;
             break;
         case SEPARATOR:
         case SEPARATOR:
+            // Quoted separator
             if (quote == 0)
             if (quote == 0)
             {
             {
-                startOfColumn = cur + matchLen;     // NB: Can write one past end.
+                lastEscape = false;
+                quoteToStrip = 0;
+                firstGood = cur + matchLen;
             }
             }
+            lastGood = cur+matchLen;
             break;
             break;
         case TERMINATOR:
         case TERMINATOR:
-            if (quote == 0)
+            if (quote == 0) // Is this a good idea? Means a mismatched quote is not fixed by EOL
             {
             {
-                if(processFullBuffer)
-                {
-                    last = cur + matchLen;
-                }
-                else
-                {
-                    return cur + matchLen - start;
-                }
+               if (processFullBuffer)
+               {
+                   last = cur + matchLen;
+                   // Reset to process a new record
+                   lastEscape = false;
+                   quoteToStrip = 0;
+                   firstGood = cur + matchLen;
+               }
+               else
+               {
+                   return (size32_t)(cur + matchLen - start);
+               }
             }
             }
+            lastGood = cur+matchLen;
             break;
             break;
         case QUOTE:
         case QUOTE:
+            // Quoted quote
             if (quote == 0)
             if (quote == 0)
             {
             {
-                if (cur == startOfColumn)
+                if (cur == firstGood)
                 {
                 {
                     quote = match;
                     quote = match;
-                    startOfColumn = cur+matchLen;
+                    firstGood = cur+matchLen;
                 }
                 }
+                lastGood = cur+matchLen;
             }
             }
             else
             else
             {
             {
                 if (quote == match)
                 if (quote == match)
-                    quote = 0;
+                {
+                    const byte * next = cur + matchLen;
+                    //Check for double quotes
+                    if ((next != end))
+                    {
+                        unsigned nextMatchLen;
+                        unsigned nextMatch = matcher.getMatch((size32_t)(end-next), (const char *)next, nextMatchLen);
+                        if (nextMatch == quote)
+                        {
+                            quoteToStrip = quote;
+                            matchLen += nextMatchLen;
+                            lastGood = cur+matchLen;
+                        }
+                        else
+                            quote = 0;
+                    }
+                    else
+                        quote = 0;
+                }
+                else
+                    lastGood = cur+matchLen;
             }
             }
             break;
             break;
+        case ESCAPE:
+            lastEscape = true;
+            lastGood = cur+matchLen;
+            // If this escape is at the end, proceed to field range
+            if (lastGood == end)
+                break;
+
+            // Skip escape and ignore the next match
+            cur += matchLen;
+            match = matcher.getMatch((size32_t)(end-cur), (const char *)cur, matchLen);
+            if ((match & 255) == NONE)
+                matchLen = 1;
+            lastGood += matchLen;
+            break;
+
         }
         }
         cur += matchLen;
         cur += matchLen;
     }
     }
 
 
-    if(processFullBuffer && (last != start))
+    if (processFullBuffer && (last != start))
     {
     {
         return last - start;
         return last - start;
     }
     }

+ 1 - 1
dali/ft/daftformat.ipp

@@ -239,7 +239,7 @@ protected:
     }
     }
 
 
 protected:
 protected:
-    enum { NONE=0, SEPARATOR=1, TERMINATOR=2, WHITESPACE=3, QUOTE=4 };
+    enum { NONE=0, SEPARATOR=1, TERMINATOR=2, WHITESPACE=3, QUOTE=4, ESCAPE=5 };
     unsigned        maxElementLength;
     unsigned        maxElementLength;
     FileFormat      format;
     FileFormat      format;
     StringMatcher   matcher;
     StringMatcher   matcher;

+ 23 - 5
docs/ECLLanguageReference/ECLR_mods/Basics-AttributeVisibility.xml

@@ -84,7 +84,7 @@ EXPORT Definition3 := $.Definition2 + 5;
     are referenced by their definition name alone; no qualification is
     are referenced by their definition name alone; no qualification is
     needed.</para>
     needed.</para>
 
 
-    <programlisting>//then inside the Definition4.ecl file (in the sam efolder as Definition2) you have:
+    <programlisting>//then inside the Definition4.ecl file (in the same folder as Definition2) you have:
 IMPORT $;  
 IMPORT $;  
    //makes definitions from the current module available to this code, as needed
    //makes definitions from the current module available to this code, as needed
 
 
@@ -100,9 +100,27 @@ LocalDef2 := Definition4 + LocalDef;
   //or SHARED definition in the file are meaningless 
   //or SHARED definition in the file are meaningless 
   //since they can never be used by anything
   //since they can never be used by anything
 </programlisting>
 </programlisting>
-  </sect2>
 
 
-  <para>See Also: <link linkend="IMPORT">IMPORT</link>, <link
-  linkend="EXPORT">EXPORT</link>, <link linkend="SHARED">SHARED</link>, <link
-  linkend="MODULE_Structure">MODULE</link></para>
+    <para>The <emphasis role="bold">LOCAL</emphasis><indexterm>
+        <primary>LOCAL</primary>
+      </indexterm> keyword is valid for use within any nested structure, but
+    most useful within a FUNCTIONMACRO structure to clearly identify that the
+    scope of a definition is limited to the code generated within the
+    FUNCTIONMACRO.</para>
+
+    <programlisting>AddOne(num) := FUNCTIONMACRO
+  LOCAL numPlus := num + 1;
+  RETURN numPlus;
+ENDMACRO;
+
+numPlus := 'this is a syntax error without LOCAL in the FUNCTIONMACRO';
+numPlus;
+AddOne(5);
+</programlisting>
+
+    <para>See Also: <link linkend="IMPORT">IMPORT</link>, <link
+    linkend="EXPORT">EXPORT</link>, <link linkend="SHARED">SHARED</link>,
+    <link linkend="MODULE_Structure">MODULE</link>, <link
+    linkend="FUNCTIONMACRO_Structure">FUNCTIONMACRO</link></para>
+  </sect2>
 </sect1>
 </sect1>

+ 21 - 11
docs/ECLLanguageReference/ECLR_mods/BltInFunc-DENORMALIZE.xml

@@ -81,8 +81,8 @@
 
 
             <entry>Specifies grouping the <emphasis>childrecset</emphasis>
             <entry>Specifies grouping the <emphasis>childrecset</emphasis>
             records based on the join condition so all the related child
             records based on the join condition so all the related child
-            records are passed as a dataset parameter to the transform.
-            </entry>
+            records are passed as a dataset parameter to the
+            transform.</entry>
           </row>
           </row>
 
 
           <row>
           <row>
@@ -108,6 +108,11 @@
   order in which the <emphasis>childrecset</emphasis> records are sent to the
   order in which the <emphasis>childrecset</emphasis> records are sent to the
   <emphasis>transform</emphasis> is undefined.</para>
   <emphasis>transform</emphasis> is undefined.</para>
 
 
+  <para>Because DENORMALIZE is basically a specialized form of JOIN, the
+  various join types (LEFT OUTER, RIGHT OUTER, FULL OUTER, LEFT ONLY, RIGHT
+  ONLY, FULL ONLY) are also available for use on DENORMALIZE and act just as
+  they do with JOIN.</para>
+
   <sect2 id="TRANSFORM_Function_Requirements_Denormalize">
   <sect2 id="TRANSFORM_Function_Requirements_Denormalize">
     <title>DENORMALIZE TRANSFORM Function Requirements</title>
     <title>DENORMALIZE TRANSFORM Function Requirements</title>
 
 
@@ -136,10 +141,11 @@
     <emphasis>transform</emphasis> function must be a record set of the same
     <emphasis>transform</emphasis> function must be a record set of the same
     format as the LEFT record.</para>
     format as the LEFT record.</para>
 
 
-    <para>Example:</para>
+    <para><emphasis role="bold">Example:</emphasis></para>
+
+    <para>Form 1 example:</para>
 
 
-    <programlisting>//Form 1 example:
-NormRec := RECORD
+    <programlisting>NormRec := RECORD
   STRING20  thename;
   STRING20  thename;
   STRING20  addr;
   STRING20  addr;
 END;
 END;
@@ -172,9 +178,11 @@ DeNormedRecs := DENORMALIZE(NamesTable, NormAddrs,
                             LEFT.thename = RIGHT.thename,
                             LEFT.thename = RIGHT.thename,
                             DeNormThem(LEFT,RIGHT,COUNTER));
                             DeNormThem(LEFT,RIGHT,COUNTER));
 OUTPUT(DeNormedRecs);
 OUTPUT(DeNormedRecs);
+</programlisting>
 
 
-//Form 2 example:
-NormRec := RECORD
+    <para>Form 2 example:</para>
+
+    <programlisting>NormRec := RECORD
   STRING20  thename;
   STRING20  thename;
   STRING20  addr;
   STRING20  addr;
 END;
 END;
@@ -203,9 +211,11 @@ DeNormedRecs := DENORMALIZE(NamesTable, NormAddrs,
                            GROUP,
                            GROUP,
                            DeNormThem(LEFT,ROWS(RIGHT)));
                            DeNormThem(LEFT,ROWS(RIGHT)));
 OUTPUT(DeNormedRecs);
 OUTPUT(DeNormedRecs);
+</programlisting>
+
+    <para>NOSORT example:</para>
 
 
-// NOSORT example
-MyRec := RECORD
+    <programlisting>MyRec := RECORD
   STRING1 Value1;
   STRING1 Value1;
   STRING1 Value2;
   STRING1 Value2;
 END;
 END;
@@ -236,8 +246,8 @@ OUTPUT(DeNormedRecs);
  */
  */
 </programlisting>
 </programlisting>
 
 
-    <para>See Also: <link linkend="TRANSFORM_Structure">TRANSFORM
-    Structure</link>, <link
+    <para>See Also: <link linkend="JOIN">JOIN</link>, <link
+    linkend="TRANSFORM_Structure">TRANSFORM Structure</link>, <link
     linkend="RECORD_Structure">RECORD Structure</link>, <link
     linkend="RECORD_Structure">RECORD Structure</link>, <link
     linkend="NORMALIZE">NORMALIZE</link></para>
     linkend="NORMALIZE">NORMALIZE</link></para>
   </sect2>
   </sect2>

+ 6 - 1
docs/ECLLanguageReference/ECLR_mods/ParSppt-PARSPattrn.xml

@@ -409,7 +409,12 @@ the following supported syntax elements:
  [0-9abcdef]    A set of characters
  [0-9abcdef]    A set of characters
                         (may use ^ for exclusion list)
                         (may use ^ for exclusion list)
  (?=…) (?!...)     Look ahead assertion
  (?=…) (?!...)     Look ahead assertion
- (?&lt;=…) (?&lt;!...)   Look behind assertion</programlisting><!--*** Note this and the following row entries have been monospace optimized for PDF/HTML*** --></entry>
+ (?&lt;=…) (?&lt;!...)   Look behind assertion
+
+Escape sequences can be used to define UNICODE Character ranges. The encoding is UTF-16 Big Endian.  
+For example:
+PATTERN AnyChar    := PATTERN(U'[\u0001-\u7fff]');
+</programlisting><!--*** Note this and the following row entries have been monospace optimized for PDF/HTML*** --></entry>
           </row>
           </row>
 
 
           <row>
           <row>

+ 42 - 12
docs/ECLLanguageReference/ECLR_mods/SpecStruc-FuncTionMacro.xml

@@ -82,16 +82,25 @@
   </informaltable>
   </informaltable>
 
 
   <para>The <emphasis role="bold">FUNCTIONMACRO </emphasis>structure is a code
   <para>The <emphasis role="bold">FUNCTIONMACRO </emphasis>structure is a code
-  generation tool, like the MACRO structure, coupled with code encapsulation
-  benefits of the FUNCTION structure. This means that #UNIQUENAME is not
-  necessary to prevent definition name clashes -- the definitions in the
-  <emphasis>code</emphasis> are local within the FUNCTIONMACRO.</para>
-
-  <para>One additional advantage the FUNCTIONMACRO has over the MACRO
-  structure is that it may be called in an expression context, just like a
-  FUNCTION would be.</para>
-
-  <para>Example:</para>
+  generation tool, like the MACRO structure, coupled with the code
+  encapsulation benefits of the FUNCTION structure. One advantage the
+  FUNCTIONMACRO has over the MACRO structure is that it may be called in an
+  expression context, just like a FUNCTION would be.</para>
+
+  <para>Unlike the MACRO structure, #UNIQUENAME is not necessary to prevent
+  internal definition name clashes when the FUNCTIONMACRO is used multiple
+  times within the same visibility scope. However, the <emphasis
+  role="bold">LOCAL</emphasis> keyword must be explicitly used within the
+  FUNCTIONMACRO if a definition name in its <emphasis>code</emphasis> may also
+  have been defined outside the FUNCTIONMACRO and within the same visibility
+  scope -- LOCAL clearly identifies that the definition is limited to the
+  <emphasis>code</emphasis> within the FUNCTIONMACRO.</para>
+
+  <para><emphasis role="bold">Example:</emphasis></para>
+
+  <para>This example demonstrates the FUNCTIONMACRO used in an expression
+  context. It also shows how the FUNCTIONMACRO may be called multiple times
+  without name clashes from its internal definitions:</para>
 
 
   <programlisting>EXPORT Field_Population(infile,infield,compareval) := FUNCTIONMACRO
   <programlisting>EXPORT Field_Population(infile,infield,compareval) := FUNCTIONMACRO
   c1 := COUNT(infile(infield=compareval));
   c1 := COUNT(infile(infield=compareval));
@@ -106,7 +115,28 @@ ds1 := dataset([{'M'},{'M'},{'M'},{''},{''},{'M'},{''},{'M'},{'M'},{''}],{STRING
 ds2 := dataset([{''},{'M'},{'M'},{''},{''},{'M'},{''},{''},{'M'},{''}],{STRING1 Gender});
 ds2 := dataset([{''},{'M'},{'M'},{''},{''},{'M'},{''},{''},{'M'},{''}],{STRING1 Gender});
 
 
 OUTPUT(Field_Population(ds1,Gender,''));
 OUTPUT(Field_Population(ds1,Gender,''));
-OUTPUT(Field_Population(ds2,Gender,''));        </programlisting>
+OUTPUT(Field_Population(ds2,Gender,''));</programlisting>
+
+  <para>This example demonstrates use of the LOCAL keyword to prevent name
+  clashes with external definitions within the same visibility scope as the
+  FUNCTIONMACRO:</para>
+
+  <programlisting>numPlus := 'this creates a syntax error without LOCAL in the FUNCTIONMACRO';
+AddOne(num) := FUNCTIONMACRO
+  LOCAL numPlus := num + 1;   //LOCAL required here
+  RETURN numPlus;
+ENDMACRO;
+
+AddTwo(num) := FUNCTIONMACRO
+  LOCAL numPlus := num + 2;   //LOCAL required here
+  RETURN numPlus;
+ENDMACRO;
+
+numPlus;
+AddOne(5);
+AddTwo(8);</programlisting>
 
 
-  <para>See Also: <link linkend="FUNCTION_Structure">FUNCTION Structure</link>, <link linkend="MACRO_Structure">MACRO Structure</link></para>
+  <para>See Also: <link linkend="FUNCTION_Structure">FUNCTION
+  Structure</link>, <link linkend="MACRO_Structure">MACRO
+  Structure</link></para>
 </sect1>
 </sect1>

+ 32 - 3
ecl/ecl-bundle/ecl-bundle.cpp

@@ -197,6 +197,20 @@ static void extractValueFromEnvOutput(StringBuffer &path, const char *specs, con
     }
     }
 }
 }
 
 
+bool directoryContainsBundleFile(IFile *dir)
+{
+    Owned<IDirectoryIterator> files = dir->directoryFiles(NULL, false, false);
+    ForEach(*files)
+    {
+        IFile *thisFile = &files->query();
+        StringBuffer fileName;
+        splitFilename(thisFile->queryFilename(), NULL, NULL, &fileName, &fileName);
+        if (stricmp(fileName.str(), "bundle.ecl")==0)
+            return true;
+    }
+    return false;
+}
+
 void recursiveRemoveDirectory(IFile *dir, bool isDryRun)
 void recursiveRemoveDirectory(IFile *dir, bool isDryRun)
 {
 {
     Owned<IDirectoryIterator> files = dir->directoryFiles(NULL, false, true);
     Owned<IDirectoryIterator> files = dir->directoryFiles(NULL, false, true);
@@ -280,10 +294,19 @@ public:
             Owned<IFile> bundleFile = createIFile(bundle);
             Owned<IFile> bundleFile = createIFile(bundle);
             if (bundleFile->exists())
             if (bundleFile->exists())
             {
             {
+                StringBuffer cleanedParam(bundle);
+                removeTrailingPathSepChar(cleanedParam);
                 StringBuffer drive, path;
                 StringBuffer drive, path;
-                splitFilename(bundle, &drive, &path, &bundleName, NULL, true);
+                splitFilename(cleanedParam, &drive, &path, &bundleName, NULL, true);
                 if (bundleFile->isDirectory())
                 if (bundleFile->isDirectory())
-                    eclOpts.appendf(" -I%s", bundle);
+                {
+                    // Distinguish a directory containing a single file (zipped single module case) from a
+                    // directory containing multiple exported files. Somehow.
+                    if (directoryContainsBundleFile(bundleFile))
+                        eclOpts.appendf(" -I%s%s", drive.str(), path.str());
+                    else
+                        eclOpts.appendf(" -I%s", bundle);
+                }
                 else if (drive.length() + path.length())
                 else if (drive.length() + path.length())
                     eclOpts.appendf(" -I%s%s", drive.str(), path.str());
                     eclOpts.appendf(" -I%s%s", drive.str(), path.str());
                 else
                 else
@@ -878,7 +901,7 @@ protected:
     }
     }
     bool isFromFile() const
     bool isFromFile() const
     {
     {
-        // If a supplied bundle id contains pathsep or ., assume a filename is being supplied
+        // If a supplied bundle id contains pathsep or ., assume a filename or directory is being supplied
         return strchr(optBundle, PATHSEPCHAR) != NULL || strchr(optBundle, '.') != NULL;
         return strchr(optBundle, PATHSEPCHAR) != NULL || strchr(optBundle, '.') != NULL;
     }
     }
     StringAttr optBundle;
     StringAttr optBundle;
@@ -1160,6 +1183,12 @@ public:
                 throw MakeStringException(0, "Cannot create bundle version directory %s", versionPath.str());
                 throw MakeStringException(0, "Cannot create bundle version directory %s", versionPath.str());
             if (bundleFile->isDirectory() == foundYes) // could also be an archive, acting as a directory
             if (bundleFile->isDirectory() == foundYes) // could also be an archive, acting as a directory
             {
             {
+                if (directoryContainsBundleFile(bundleFile))
+                {
+                    versionPath.append(PATHSEPCHAR).append(bundle->queryCleanName());
+                    if (!optDryRun && !recursiveCreateDirectory(versionPath))
+                        throw MakeStringException(0, "Cannot create bundle version directory %s", versionPath.str());
+                }
                 copyDirectory(bundleFile, versionPath);
                 copyDirectory(bundleFile, versionPath);
             }
             }
             else
             else

+ 4 - 3
ecl/hql/hql.hpp

@@ -27,6 +27,7 @@
 #define HQL_API
 #define HQL_API
 #endif
 #endif
 #include "hqlatoms.hpp"
 #include "hqlatoms.hpp"
+#include "build-config.h"
 
 
 #define stringify(x) # x
 #define stringify(x) # x
 #define estringify(x) stringify(x)
 #define estringify(x) stringify(x)
@@ -47,9 +48,9 @@ Relevant changes include
 
 
 */
 */
 
 
-#define LANGUAGE_VERSION_MAJOR      4
-#define LANGUAGE_VERSION_MINOR      0
-#define LANGUAGE_VERSION_SUB        0
+#define LANGUAGE_VERSION_MAJOR      BUILD_VERSION_MAJOR
+#define LANGUAGE_VERSION_MINOR      BUILD_VERSION_MINOR
+#define LANGUAGE_VERSION_SUB        BUILD_VERSION_POINT
 
 
 #define LANGUAGE_VERSION   estringify(LANGUAGE_VERSION_MAJOR) "." estringify(LANGUAGE_VERSION_MINOR) "." estringify(LANGUAGE_VERSION_SUB)
 #define LANGUAGE_VERSION   estringify(LANGUAGE_VERSION_MAJOR) "." estringify(LANGUAGE_VERSION_MINOR) "." estringify(LANGUAGE_VERSION_SUB)
 
 

+ 2 - 0
ecl/hql/hqlerrors.hpp

@@ -464,6 +464,7 @@
 #define HQLERR_UnexpectedType                   3126
 #define HQLERR_UnexpectedType                   3126
 #define HQLERR_PayloadMismatch                  3127
 #define HQLERR_PayloadMismatch                  3127
 #define HQLERR_MemberXContainsVirtualRef        3128
 #define HQLERR_MemberXContainsVirtualRef        3128
+#define HQLERR_FieldHasNoDefaultValue           3129
 
 
 #define HQLERR_DedupFieldNotFound_Text          "Field removed from dedup could not be found"
 #define HQLERR_DedupFieldNotFound_Text          "Field removed from dedup could not be found"
 #define HQLERR_CycleWithModuleDefinition_Text   "Module definition contain an illegal cycle/recursive definition %s"
 #define HQLERR_CycleWithModuleDefinition_Text   "Module definition contain an illegal cycle/recursive definition %s"
@@ -494,6 +495,7 @@
 #define HQLWRN_CouldNotConstantFoldIf_Text      "Could not constant fold the condition on a IFBLOCK for a inline table"
 #define HQLWRN_CouldNotConstantFoldIf_Text      "Could not constant fold the condition on a IFBLOCK for a inline table"
 #define HQLERR_PayloadMismatch_Text             "Mismatched => in inline dictionary definition"
 #define HQLERR_PayloadMismatch_Text             "Mismatched => in inline dictionary definition"
 #define HQLERR_MemberXContainsVirtualRef_Text   "Member %s contains virtual references but not supported as virtual"
 #define HQLERR_MemberXContainsVirtualRef_Text   "Member %s contains virtual references but not supported as virtual"
+#define HQLERR_FieldHasNoDefaultValue_Text      "Field '%s' doesn't have a defined value"
 
 
 /* parser error */
 /* parser error */
 #define ERR_PARSER_CANNOTRECOVER    3005  /* The parser can not recover from previous error(s) */
 #define ERR_PARSER_CANNOTRECOVER    3005  /* The parser can not recover from previous error(s) */

+ 1 - 1
ecl/hql/hqlfold.cpp

@@ -4811,7 +4811,7 @@ IHqlExpression * CExprFolderTransformer::doFoldTransformed(IHqlExpression * unfo
             {
             {
                 ECLlocation dummyLocation(0, 0, 0, NULL);
                 ECLlocation dummyLocation(0, 0, 0, NULL);
                 ThrowingErrorReceiver errorReporter;
                 ThrowingErrorReceiver errorReporter;
-                OwnedHqlExpr inlineTable = convertTempTableToInlineTable(&errorReporter, dummyLocation, expr);
+                OwnedHqlExpr inlineTable = convertTempTableToInlineTable(errorReporter, dummyLocation, expr);
                 if (expr != inlineTable)
                 if (expr != inlineTable)
                     return inlineTable.getClear();
                     return inlineTable.getClear();
             }
             }

+ 3 - 3
ecl/hql/hqlgram.y

@@ -7204,7 +7204,7 @@ simpleDictionary
                         {
                         {
                             HqlExprArray values;  // Empty list
                             HqlExprArray values;  // Empty list
                             OwnedHqlExpr table = createDataset(no_temptable, createValue(no_recordlist, NULL, values), $6.getExpr());
                             OwnedHqlExpr table = createDataset(no_temptable, createValue(no_recordlist, NULL, values), $6.getExpr());
-                            OwnedHqlExpr ds = convertTempTableToInlineTable(parser->errorHandler, $4.pos, table);
+                            OwnedHqlExpr ds = convertTempTableToInlineTable(*parser->errorHandler, $4.pos, table);
                             $$.setExpr(createDictionary(no_createdictionary, ds.getClear()), $1);
                             $$.setExpr(createDictionary(no_createdictionary, ds.getClear()), $1);
                         }
                         }
     | DICTIONARY '(' '[' beginList inlineDatasetValueList ']' ',' recordDef ')'
     | DICTIONARY '(' '[' beginList inlineDatasetValueList ']' ',' recordDef ')'
@@ -7212,7 +7212,7 @@ simpleDictionary
                             HqlExprArray values;
                             HqlExprArray values;
                             parser->endList(values);
                             parser->endList(values);
                             OwnedHqlExpr table = createDataset(no_temptable, createValue(no_recordlist, NULL, values), $8.getExpr());
                             OwnedHqlExpr table = createDataset(no_temptable, createValue(no_recordlist, NULL, values), $8.getExpr());
-                            OwnedHqlExpr ds = convertTempTableToInlineTable(parser->errorHandler, $5.pos, table);
+                            OwnedHqlExpr ds = convertTempTableToInlineTable(*parser->errorHandler, $5.pos, table);
                             $$.setExpr(createDictionary(no_createdictionary, ds.getClear()), $1);
                             $$.setExpr(createDictionary(no_createdictionary, ds.getClear()), $1);
                         }
                         }
     | '(' dictionary  ')'  {
     | '(' dictionary  ')'  {
@@ -8486,7 +8486,7 @@ simpleDataSet
                             HqlExprArray values;
                             HqlExprArray values;
                             parser->endList(values);
                             parser->endList(values);
                             OwnedHqlExpr table = createDataset(no_temptable, createValue(no_recordlist, NULL, values), $8.getExpr());
                             OwnedHqlExpr table = createDataset(no_temptable, createValue(no_recordlist, NULL, values), $8.getExpr());
-                            $$.setExpr(convertTempTableToInlineTable(parser->errorHandler, $5.pos, table));
+                            $$.setExpr(convertTempTableToInlineTable(*parser->errorHandler, $5.pos, table));
                             $$.setPosition($1);
                             $$.setPosition($1);
                         }
                         }
     | DATASET '(' '[' beginList transformList ']' ')'
     | DATASET '(' '[' beginList transformList ']' ')'

+ 2 - 2
ecl/hql/hqlgram2.cpp

@@ -5282,7 +5282,7 @@ IHqlExpression * HqlGram::createDatasetFromList(attribute & listAttr, attribute
     {
     {
         OwnedHqlExpr list = createValue(no_null);
         OwnedHqlExpr list = createValue(no_null);
         OwnedHqlExpr table = createDataset(no_temptable, LINK(list), record.getClear());
         OwnedHqlExpr table = createDataset(no_temptable, LINK(list), record.getClear());
-        return convertTempTableToInlineTable(errorHandler, listAttr.pos, table);
+        return convertTempTableToInlineTable(*errorHandler, listAttr.pos, table);
         return createDataset(no_null, LINK(record));
         return createDataset(no_null, LINK(record));
     }
     }
 
 
@@ -5323,7 +5323,7 @@ IHqlExpression * HqlGram::createDatasetFromList(attribute & listAttr, attribute
         reportError(ERR_RECORD_NOT_MATCH_SET, recordAttr, "The field in the record does not match the type of the set elements");
         reportError(ERR_RECORD_NOT_MATCH_SET, recordAttr, "The field in the record does not match the type of the set elements");
     
     
     OwnedHqlExpr table = createDataset(no_temptable, LINK(list), record.getClear());
     OwnedHqlExpr table = createDataset(no_temptable, LINK(list), record.getClear());
-    return convertTempTableToInlineTable(errorHandler, listAttr.pos, table);
+    return convertTempTableToInlineTable(*errorHandler, listAttr.pos, table);
 }
 }
 
 
 
 

+ 1 - 1
ecl/hql/hqlopt.cpp

@@ -2492,7 +2492,7 @@ IHqlExpression * CTreeOptimizer::doCreateTransformed(IHqlExpression * transforme
             {
             {
                 ECLlocation dummyLocation(0, 0, 0, NULL);
                 ECLlocation dummyLocation(0, 0, 0, NULL);
                 ThrowingErrorReceiver errorReporter;
                 ThrowingErrorReceiver errorReporter;
-                OwnedHqlExpr inlineTable = convertTempTableToInlineTable(&errorReporter, dummyLocation, transformed);
+                OwnedHqlExpr inlineTable = convertTempTableToInlineTable(errorReporter, dummyLocation, transformed);
                 if (transformed != inlineTable)
                 if (transformed != inlineTable)
                     return inlineTable.getClear();
                     return inlineTable.getClear();
             }
             }

+ 13 - 4
ecl/hql/hqlutil.cpp

@@ -5596,7 +5596,7 @@ IHqlExpression * convertTempRowToCreateRow(IErrorReceiver * errors, ECLlocation
     return expr->cloneAllAnnotations(ret);
     return expr->cloneAllAnnotations(ret);
 }
 }
 
 
-static IHqlExpression * convertTempTableToInline(IErrorReceiver * errors, ECLlocation & location, IHqlExpression * expr)
+static IHqlExpression * convertTempTableToInline(IErrorReceiver & errors, ECLlocation & location, IHqlExpression * expr)
 {
 {
     IHqlExpression * oldValues = expr->queryChild(0);
     IHqlExpression * oldValues = expr->queryChild(0);
     IHqlExpression * record = expr->queryChild(1);
     IHqlExpression * record = expr->queryChild(1);
@@ -5609,7 +5609,7 @@ static IHqlExpression * convertTempTableToInline(IErrorReceiver * errors, ECLloc
     if ((valueOp != no_recordlist) && (valueOp != no_list))
     if ((valueOp != no_recordlist) && (valueOp != no_list))
         return LINK(expr);
         return LINK(expr);
 
 
-    TempTableTransformer transformer(errors, location);
+    TempTableTransformer transformer(&errors, location);
     HqlExprArray transforms;
     HqlExprArray transforms;
     ForEachChild(idx, values)
     ForEachChild(idx, values)
     {
     {
@@ -5623,7 +5623,16 @@ static IHqlExpression * convertTempTableToInline(IErrorReceiver * errors, ECLloc
             {
             {
                 IHqlExpression * field = cur->queryChild(idx);
                 IHqlExpression * field = cur->queryChild(idx);
                 if (field->getOperator() == no_field)
                 if (field->getOperator() == no_field)
-                    row.append(*LINK(field->queryChild(0)));
+                {
+                    IHqlExpression * value = queryRealChild(field, 0);
+                    if (value)
+                        row.append(*LINK(value));
+                    else
+                    {
+                        VStringBuffer msg(HQLERR_FieldHasNoDefaultValue_Text, field->queryName()->str());
+                        errors.reportError(HQLERR_FieldHasNoDefaultValue, msg.str(), location.sourcePath->str(), location.lineno, location.column, location.position);
+                    }
+                }
             }
             }
             cur.setown(createValue(no_rowvalue, makeNullType(), row));
             cur.setown(createValue(no_rowvalue, makeNullType(), row));
         }
         }
@@ -5637,7 +5646,7 @@ static IHqlExpression * convertTempTableToInline(IErrorReceiver * errors, ECLloc
     return expr->cloneAllAnnotations(ret);
     return expr->cloneAllAnnotations(ret);
 }
 }
 
 
-IHqlExpression * convertTempTableToInlineTable(IErrorReceiver * errors, ECLlocation & location, IHqlExpression * expr)
+IHqlExpression * convertTempTableToInlineTable(IErrorReceiver & errors, ECLlocation & location, IHqlExpression * expr)
 {
 {
     return convertTempTableToInline(errors, location, expr);
     return convertTempTableToInline(errors, location, expr);
 }
 }

+ 1 - 1
ecl/hql/hqlutil.hpp

@@ -461,7 +461,7 @@ extern HQL_API IHqlExpression * createSelectMapRow(IErrorReceiver * errors, ECLl
 extern HQL_API IHqlExpression * createINDictExpr(IErrorReceiver * errors, ECLlocation & location, IHqlExpression *expr, IHqlExpression *dict);
 extern HQL_API IHqlExpression * createINDictExpr(IErrorReceiver * errors, ECLlocation & location, IHqlExpression *expr, IHqlExpression *dict);
 extern HQL_API IHqlExpression *createINDictRow(IErrorReceiver * errors, ECLlocation & location, IHqlExpression *row, IHqlExpression *dict);
 extern HQL_API IHqlExpression *createINDictRow(IErrorReceiver * errors, ECLlocation & location, IHqlExpression *row, IHqlExpression *dict);
 extern HQL_API IHqlExpression * convertTempRowToCreateRow(IErrorReceiver * errors, ECLlocation & location, IHqlExpression * expr);
 extern HQL_API IHqlExpression * convertTempRowToCreateRow(IErrorReceiver * errors, ECLlocation & location, IHqlExpression * expr);
-extern HQL_API IHqlExpression * convertTempTableToInlineTable(IErrorReceiver * errors, ECLlocation & location, IHqlExpression * expr);
+extern HQL_API IHqlExpression * convertTempTableToInlineTable(IErrorReceiver & errors, ECLlocation & location, IHqlExpression * expr);
 extern HQL_API void setPayloadAttribute(HqlExprArray &args);
 extern HQL_API void setPayloadAttribute(HqlExprArray &args);
 
 
 extern HQL_API bool areTypesComparable(ITypeInfo * leftType, ITypeInfo * rightType);
 extern HQL_API bool areTypesComparable(ITypeInfo * leftType, ITypeInfo * rightType);

+ 1 - 1
ecl/hqlcpp/hqlttcpp.cpp

@@ -10837,7 +10837,7 @@ IHqlExpression * HqlTreeNormalizer::transformTempTable(IHqlExpression * expr)
 {
 {
     ECLlocation dummyLocation(0, 0, 0, NULL);
     ECLlocation dummyLocation(0, 0, 0, NULL);
     AbortingErrorReceiver errorReporter(errors);
     AbortingErrorReceiver errorReporter(errors);
-    OwnedHqlExpr inlineTable = convertTempTableToInlineTable(&errorReporter, dummyLocation, expr);
+    OwnedHqlExpr inlineTable = convertTempTableToInlineTable(errorReporter, dummyLocation, expr);
     if (expr != inlineTable)
     if (expr != inlineTable)
         return transform(inlineTable);
         return transform(inlineTable);
 
 

+ 13 - 51
esp/files/scripts/DFUQueryWidget.js

@@ -50,6 +50,7 @@ define([
     "hpcc/SFDetailsWidget",
     "hpcc/SFDetailsWidget",
     "hpcc/DFUWUDetailsWidget",
     "hpcc/DFUWUDetailsWidget",
     "hpcc/TargetSelectWidget",
     "hpcc/TargetSelectWidget",
+    "hpcc/FilterDropDownWidget",
 
 
     "dojo/text!../templates/DFUQueryWidget.html",
     "dojo/text!../templates/DFUQueryWidget.html",
 
 
@@ -69,7 +70,7 @@ define([
 ], function (declare, lang, arrayUtil, dom, domAttr, domConstruct, domClass, domForm, date, on,
 ], function (declare, lang, arrayUtil, dom, domAttr, domConstruct, domClass, domForm, date, on,
                 registry, Dialog, Menu, MenuItem, MenuSeparator, PopupMenuItem, Textarea,
                 registry, Dialog, Menu, MenuItem, MenuSeparator, PopupMenuItem, Textarea,
                 Grid, Keyboard, Selection, selector, ColumnResizer, DijitRegistry, Pagination,
                 Grid, Keyboard, Selection, selector, ColumnResizer, DijitRegistry, Pagination,
-                _TabContainerWidget, WsDfu, ESPUtil, ESPLogicalFile, ESPDFUWorkunit, LFDetailsWidget, SFDetailsWidget, DFUWUDetailsWidget, TargetSelectWidget,
+                _TabContainerWidget, WsDfu, ESPUtil, ESPLogicalFile, ESPDFUWorkunit, LFDetailsWidget, SFDetailsWidget, DFUWUDetailsWidget, TargetSelectWidget, FilterDropDownWidget,
                 template) {
                 template) {
     return declare("DFUQueryWidget", [_TabContainerWidget, ESPUtil.FormHelper], {
     return declare("DFUQueryWidget", [_TabContainerWidget, ESPUtil.FormHelper], {
         templateString: template,
         templateString: template,
@@ -78,11 +79,12 @@ define([
         workunitsTab: null,
         workunitsTab: null,
         workunitsGrid: null,
         workunitsGrid: null,
 
 
-        validateDialog: null,
+        filter: null,
 
 
         postCreate: function (args) {
         postCreate: function (args) {
             this.inherited(arguments);
             this.inherited(arguments);
             this.workunitsTab = registry.byId(this.id + "_Workunits");
             this.workunitsTab = registry.byId(this.id + "_Workunits");
+            this.filter = registry.byId(this.id + "Filter");
             this.clusterTargetSelect = registry.byId(this.id + "ClusterTargetSelect");
             this.clusterTargetSelect = registry.byId(this.id + "ClusterTargetSelect");
             this.desprayTargetSelect = registry.byId(this.id + "DesprayTargetSelect");
             this.desprayTargetSelect = registry.byId(this.id + "DesprayTargetSelect");
         },
         },
@@ -178,20 +180,6 @@ define([
             registry.byId(this.id + "DesprayDropDown").closeDropDown();
             registry.byId(this.id + "DesprayDropDown").closeDropDown();
         },
         },
 
 
-        _onFilterApply: function (event) {
-            registry.byId(this.id + "FilterDropDown").closeDropDown();
-            if (this.hasFilter()) {
-                this.applyFilter();
-            } else {
-                this.validateDialog.show();
-            }
-        },
-
-        _onFilterClear: function(event) {
-            this.clearFilter();
-            this.applyFilter();
-        },
-
         _onRowDblClick: function (item) {
         _onRowDblClick: function (item) {
             var wuTab = this.ensureLFPane(this.id + "_" + item.Name, item);
             var wuTab = this.ensureLFPane(this.id + "_" + item.Name, item);
             this.selectChild(wuTab);
             this.selectChild(wuTab);
@@ -218,24 +206,8 @@ define([
         },
         },
 
 
         //  Implementation  ---
         //  Implementation  ---
-        clearFilter: function () {
-            arrayUtil.forEach(registry.byId(this.id + "FilterForm").getDescendants(), function (item, idx) {
-                item.set('value', null);
-            });
-        },
-
-        hasFilter: function () {
-            var filter = domForm.toObject(this.id + "FilterForm");
-            for (var key in filter) {
-                if (filter[key] != "") {
-                    return true;
-                }
-            }
-            return false;
-        },
-
         getFilter: function () {
         getFilter: function () {
-            var retVal = domForm.toObject(this.id + "FilterForm");
+            var retVal = this.filter.toObject();
             lang.mixin(retVal, {
             lang.mixin(retVal, {
                 StartDate: this.getISOString("FromDate", "FromTime"),
                 StartDate: this.getISOString("FromDate", "FromTime"),
                 EndDate: this.getISOString("ToDate", "ToTime")
                 EndDate: this.getISOString("ToDate", "ToTime")
@@ -249,10 +221,6 @@ define([
             return retVal;
             return retVal;
         },
         },
 
 
-        applyFilter: function () {
-            this.refreshGrid();
-        },
-
         //  Implementation  ---
         //  Implementation  ---
         init: function (params) {
         init: function (params) {
             if (this.inherited(arguments))
             if (this.inherited(arguments))
@@ -319,23 +287,24 @@ define([
                 var pSubMenu = new Menu();
                 var pSubMenu = new Menu();
                 this.menuFilterOwner = this.addMenuItem(pSubMenu, {
                 this.menuFilterOwner = this.addMenuItem(pSubMenu, {
                     onClick: function (args) {
                     onClick: function (args) {
-                        context.clearFilter();
-                        registry.byId(context.id + "Owner").set("value", context.menuFilterOwner.get("hpcc_value"));
-                        context.applyFilter();
+                        context.filter.clear();
+                        context.filter.setValue(context.id + "Owner", context.menuFilterOwner.get("hpcc_value"));
+                        context.refreshGrid();
                     }
                     }
                 });
                 });
                 this.menuFilterCluster = this.addMenuItem(pSubMenu, {
                 this.menuFilterCluster = this.addMenuItem(pSubMenu, {
                     onClick: function (args) {
                     onClick: function (args) {
-                        context.clearFilter();
-                        registry.byId(context.id + "ClusterTargetSelect").set("value", context.menuFilterCluster.get("hpcc_value"));
-                        context.applyFilter();
+                        context.filter.clear();
+                        context.filter.setValue(context.id + "ClusterTargetSelect", context.menuFilterOwner.get("hpcc_value"));
+                        context.refreshGrid();
                     }
                     }
                 });
                 });
                 pSubMenu.addChild(new MenuSeparator());
                 pSubMenu.addChild(new MenuSeparator());
                 this.menuFilterClearFilter = this.addMenuItem(pSubMenu, {
                 this.menuFilterClearFilter = this.addMenuItem(pSubMenu, {
                     label: "Clear",
                     label: "Clear",
                     onClick: function () {
                     onClick: function () {
-                        context._onFilterClear();
+                        context.filter.clear();
+                        context.refreshGrid();
                     }
                     }
                 });
                 });
 
 
@@ -470,13 +439,6 @@ define([
                     }, sourceDiv);
                     }, sourceDiv);
                 });
                 });
             }
             }
-
-            this.refreshFilterState();
-        },
-
-        refreshFilterState: function () {
-            var hasFilter = this.hasFilter();
-            dom.byId(this.id + "IconFilter").src = hasFilter ? "img/filter.png" : "img/noFilter.png";
         },
         },
 
 
         ensureDFUWUPane: function (id, params) {
         ensureDFUWUPane: function (id, params) {

+ 109 - 0
esp/files/scripts/FilterDropDownWidget.js

@@ -0,0 +1,109 @@
+/*##############################################################################
+#   HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+############################################################################## */
+define([
+    "dojo/_base/declare",
+    "dojo/_base/array",
+    "dojo/dom",
+    "dojo/dom-form",
+    "dojo/on",
+
+    "dijit/registry",
+
+    "hpcc/_Widget",
+
+    "dojo/text!../templates/FilterDropDownWidget.html",
+
+    "dijit/form/DropDownButton",
+    "dijit/TooltipDialog",
+    "dijit/form/Form",
+    "dijit/form/Button",
+
+    "dojox/layout/TableContainer"
+
+], function (declare, arrayUtil, dom, domForm, on,
+                registry,
+                _Widget,
+                template) {
+    return declare("FilterDropDownWidget", [_Widget], {
+        templateString: template,
+        baseClass: "FilterDropDownWidget",
+
+        iconFilter: null,
+        filterDropDown: null,
+        filterForm: null,
+
+        postCreate: function (args) {
+            this.inherited(arguments);
+            this.filterDropDown = registry.byId(this.id + "FilterDropDown");
+            this.filterForm = registry.byId(this.id + "FilterForm");
+        },
+
+        startup: function (args) {
+            this.inherited(arguments);
+            this.iconFilter = dom.byId(this.id + "IconFilter");
+        },
+
+        //  Hitched actions  ---
+        _onFilterClear: function (event) {
+            this.clear();
+            this.filterDropDown.closeDropDown();
+            this.emit("clear");
+            this.refreshState();
+        },
+
+        _onFilterApply: function (event) {
+            this.filterDropDown.closeDropDown();
+            this.emit("apply");
+            this.refreshState();
+        },
+
+        //  Implementation  ---
+        clear: function () {
+            arrayUtil.forEach(this.filterForm.getDescendants(), function (item, idx) {
+                item.set("value", null);
+            });
+        },
+
+        setValue: function (id, value) {
+            registry.byId(id).set("value", value);
+            this.refreshState();
+        },
+
+        exists: function () {
+            var filter = this.toObject();
+            for (var key in filter) {
+                if (filter[key] != "") {
+                    return true;
+                }
+            }
+            return false;
+        },
+
+        toObject: function () {
+            var retVal = domForm.toObject(this.filterForm.id);
+            return retVal;
+        },
+
+        init: function (params) {
+            if (this.inherited(arguments))
+                return;
+        },
+
+        refreshState: function () {
+            this.iconFilter.src = this.exists() ? "img/filter.png" : "img/noFilter.png";
+        }
+    });
+});

+ 24 - 67
esp/files/scripts/GetDFUWorkunitsWidget.js

@@ -43,6 +43,7 @@ define([
     "hpcc/FileSpray",
     "hpcc/FileSpray",
     "hpcc/DFUWUDetailsWidget",
     "hpcc/DFUWUDetailsWidget",
     "hpcc/TargetSelectWidget",
     "hpcc/TargetSelectWidget",
+    "hpcc/FilterDropDownWidget",
 
 
     "dojo/text!../templates/GetDFUWorkunitsWidget.html",
     "dojo/text!../templates/GetDFUWorkunitsWidget.html",
 
 
@@ -62,7 +63,7 @@ define([
 ], function (declare, arrayUtil,dom, domClass, domForm, date, on,
 ], function (declare, arrayUtil,dom, domClass, domForm, date, on,
                 registry, Dialog, Menu, MenuItem, MenuSeparator, PopupMenuItem,
                 registry, Dialog, Menu, MenuItem, MenuSeparator, PopupMenuItem,
                 Grid, Keyboard, Selection, selector, ColumnResizer, DijitRegistry, Pagination,
                 Grid, Keyboard, Selection, selector, ColumnResizer, DijitRegistry, Pagination,
-                _TabContainerWidget, ESPUtil, ESPDFUWorkunit, FileSpray, DFUWUDetailsWidget, TargetSelectWidget,
+                _TabContainerWidget, ESPUtil, ESPDFUWorkunit, FileSpray, DFUWUDetailsWidget, TargetSelectWidget, FilterDropDownWidget,
                 template) {
                 template) {
     return declare("GetDFUWorkunitsWidget", [_TabContainerWidget], {
     return declare("GetDFUWorkunitsWidget", [_TabContainerWidget], {
         templateString: template,
         templateString: template,
@@ -70,19 +71,20 @@ define([
 
 
         workunitsTab: null,
         workunitsTab: null,
         workunitsGrid: null,
         workunitsGrid: null,
+        filter: null,
         clusterTargetSelect: null,
         clusterTargetSelect: null,
         stateTargetSelect: null,
         stateTargetSelect: null,
 
 
         postCreate: function (args) {
         postCreate: function (args) {
             this.inherited(arguments);
             this.inherited(arguments);
             this.workunitsTab = registry.byId(this.id + "_Workunits");
             this.workunitsTab = registry.byId(this.id + "_Workunits");
+            this.filter = registry.byId(this.id + "Filter");
             this.clusterTargetSelect = registry.byId(this.id + "ClusterTargetSelect");
             this.clusterTargetSelect = registry.byId(this.id + "ClusterTargetSelect");
             this.stateSelect = registry.byId(this.id + "StateSelect");
             this.stateSelect = registry.byId(this.id + "StateSelect");
         },
         },
 
 
         startup: function (args) {
         startup: function (args) {
             this.inherited(arguments);
             this.inherited(arguments);
-            this.initFilter();
         },
         },
 
 
         getTitle: function () {
         getTitle: function () {
@@ -133,20 +135,6 @@ define([
             FileSpray.DFUWorkunitsAction(this.workunitsGrid.getSelected(), "Unprotect");
             FileSpray.DFUWorkunitsAction(this.workunitsGrid.getSelected(), "Unprotect");
         },
         },
 
 
-        _onFilterApply: function (event) {
-            registry.byId(this.id + "FilterDropDown").closeDropDown();
-            if (this.hasFilter()) {
-                this.applyFilter();
-            } else {
-                this.validateDialog.show();
-            }
-        },
-
-        _onFilterClear: function(event) {
-            this.clearFilter();
-            this.applyFilter();
-        },
-
         _onRowDblClick: function (id) {
         _onRowDblClick: function (id) {
             var wuTab = this.ensurePane(this.id + "_" + id, {
             var wuTab = this.ensurePane(this.id + "_" + id, {
                 Wuid: id
                 Wuid: id
@@ -185,31 +173,6 @@ define([
             }
             }
         },
         },
 
 
-        clearFilter: function () {
-            arrayUtil.forEach(registry.byId(this.id + "FilterForm").getDescendants(), function (item, idx) {
-                item.set('value', null);
-            });
-        },
-
-        hasFilter: function () {
-            var filter = domForm.toObject(this.id + "FilterForm");
-            for (var key in filter) {
-                if (filter[key] != "") {
-                    return true;
-                }
-            }
-            return false;
-        },
-
-        getFilter: function () {
-            var retVal = domForm.toObject(this.id + "FilterForm");
-            return retVal;
-        },
-
-        applyFilter: function () {
-            this.refreshGrid();
-        },
-
         //  Implementation  ---
         //  Implementation  ---
         init: function (params) {
         init: function (params) {
             if (this.inherited(arguments))
             if (this.inherited(arguments))
@@ -229,6 +192,14 @@ define([
                 includeBlank: true
                 includeBlank: true
             });
             });
             this.selectChild(this.workunitsTab, true);
             this.selectChild(this.workunitsTab, true);
+
+            var context = this;
+            this.filter.on("clear", function (evt) {
+                context.refreshGrid();
+            });
+            this.filter.on("apply", function (evt) {
+                context.refreshGrid();
+            });
         },
         },
 
 
         initTab: function () {
         initTab: function () {
@@ -292,30 +263,31 @@ define([
                 });*/
                 });*/
                 this.menuFilterJobname = this.addMenuItem(pSubMenu, {
                 this.menuFilterJobname = this.addMenuItem(pSubMenu, {
                     onClick: function (args) {
                     onClick: function (args) {
-                        context.clearFilter();
-                        registry.byId(context.id + "Jobname").set("value", context.menuFilterJobname.get("hpcc_value"));
-                        context.applyFilter();
+                        context.filter.clear();
+                        context.filter.setValue(context.id + "Jobname", context.menuFilterOwner.get("hpcc_value"));
+                        context.refreshGrid();
                     }
                     }
                 });
                 });
                 this.menuFilterCluster = this.addMenuItem(pSubMenu, {
                 this.menuFilterCluster = this.addMenuItem(pSubMenu, {
                     onClick: function (args) {
                     onClick: function (args) {
-                        context.clearFilter();
-                        registry.byId(context.id + "ClusterTargetSelect").set("value", context.menuFilterCluster.get("hpcc_value"));
-                        context.applyFilter();
+                        context.filter.clear();
+                        context.filter.setValue(context.id + "ClusterTargetSelect", context.menuFilterOwner.get("hpcc_value"));
+                        context.refreshGrid();
                     }
                     }
                 });
                 });
                 this.menuFilterState = this.addMenuItem(pSubMenu, {
                 this.menuFilterState = this.addMenuItem(pSubMenu, {
                     onClick: function (args) {
                     onClick: function (args) {
-                        context.clearFilter();
-                        registry.byId(context.id + "StateSelect").set("value", context.menuFilterState.get("hpcc_value"));
-                        context.applyFilter();
+                        context.filter.clear();
+                        context.filter.setValue(context.id + "StateSelect", context.menuFilterOwner.get("hpcc_value"));
+                        context.refreshGrid();
                     }
                     }
                 });
                 });
                 pSubMenu.addChild(new MenuSeparator());
                 pSubMenu.addChild(new MenuSeparator());
                 this.menuFilterClearFilter = this.addMenuItem(pSubMenu, {
                 this.menuFilterClearFilter = this.addMenuItem(pSubMenu, {
                     label: "Clear",
                     label: "Clear",
                     onClick: function () {
                     onClick: function () {
-                        context._onFilterClear();
+                        context.filter.clear();
+                        context.refreshGrid();
                     }
                     }
                 });
                 });
 
 
@@ -414,15 +386,8 @@ define([
             this.workunitsGrid.startup();
             this.workunitsGrid.startup();
         },
         },
 
 
-        initFilter: function () {
-            this.validateDialog = new Dialog({
-                title: "Filter",
-                content: "No filter criteria specified."
-            });
-        },
-
         refreshGrid: function (args) {
         refreshGrid: function (args) {
-            this.workunitsGrid.set("query", this.getFilter());
+            this.workunitsGrid.set("query", this.filter.toObject());
         },
         },
 
 
         refreshActionState: function () {
         refreshActionState: function () {
@@ -432,7 +397,6 @@ define([
             var hasNotProtected = false;
             var hasNotProtected = false;
             var hasFailed = false;
             var hasFailed = false;
             var hasNotFailed = false;
             var hasNotFailed = false;
-            var hasFilter = this.hasFilter();
             for (var i = 0; i < selection.length; ++i) {
             for (var i = 0; i < selection.length; ++i) {
                 hasSelection = true;
                 hasSelection = true;
                 if (selection[i] && selection[i].isProtected && selection[i].isProtected != "0") {
                 if (selection[i] && selection[i].isProtected && selection[i].isProtected != "0") {
@@ -452,13 +416,6 @@ define([
             registry.byId(this.id + "SetToFailed").set("disabled", !hasNotProtected);
             registry.byId(this.id + "SetToFailed").set("disabled", !hasNotProtected);
             registry.byId(this.id + "Protect").set("disabled", !hasNotProtected);
             registry.byId(this.id + "Protect").set("disabled", !hasNotProtected);
             registry.byId(this.id + "Unprotect").set("disabled", !hasProtected);
             registry.byId(this.id + "Unprotect").set("disabled", !hasProtected);
-
-            this.refreshFilterState();
-        },
-
-        refreshFilterState: function () {
-            var hasFilter = this.hasFilter();
-            dom.byId(this.id + "IconFilter").src = hasFilter ? "img/filter.png" : "img/noFilter.png";
         },
         },
 
 
         ensurePane: function (id, params) {
         ensurePane: function (id, params) {

+ 28 - 68
esp/files/scripts/WUQueryWidget.js

@@ -24,7 +24,6 @@ define([
     "dojo/on",
     "dojo/on",
 
 
     "dijit/registry",
     "dijit/registry",
-    "dijit/Dialog",
     "dijit/Menu",
     "dijit/Menu",
     "dijit/MenuItem",
     "dijit/MenuItem",
     "dijit/MenuSeparator",
     "dijit/MenuSeparator",
@@ -44,6 +43,7 @@ define([
     "hpcc/ESPWorkunit",
     "hpcc/ESPWorkunit",
     "hpcc/WUDetailsWidget",
     "hpcc/WUDetailsWidget",
     "hpcc/TargetSelectWidget",
     "hpcc/TargetSelectWidget",
+    "hpcc/FilterDropDownWidget",
 
 
     "dojo/text!../templates/WUQueryWidget.html",
     "dojo/text!../templates/WUQueryWidget.html",
 
 
@@ -62,9 +62,9 @@ define([
     "dojox/layout/TableContainer"
     "dojox/layout/TableContainer"
 
 
 ], function (declare, lang, arrayUtil, dom, domClass, domForm, date, on,
 ], function (declare, lang, arrayUtil, dom, domClass, domForm, date, on,
-                registry, Dialog, Menu, MenuItem, MenuSeparator, PopupMenuItem,
+                registry, Menu, MenuItem, MenuSeparator, PopupMenuItem,
                 Grid, Keyboard, Selection, selector, ColumnResizer, DijitRegistry, Pagination,
                 Grid, Keyboard, Selection, selector, ColumnResizer, DijitRegistry, Pagination,
-                _TabContainerWidget, WsWorkunits, ESPUtil, ESPWorkunit, WUDetailsWidget, TargetSelectWidget,
+                _TabContainerWidget, WsWorkunits, ESPUtil, ESPWorkunit, WUDetailsWidget, TargetSelectWidget, FilterDropDownWidget,
                 template) {
                 template) {
     return declare("WUQueryWidget", [_TabContainerWidget, ESPUtil.FormHelper], {
     return declare("WUQueryWidget", [_TabContainerWidget, ESPUtil.FormHelper], {
         templateString: template,
         templateString: template,
@@ -72,14 +72,14 @@ define([
 
 
         workunitsTab: null,
         workunitsTab: null,
         workunitsGrid: null,
         workunitsGrid: null,
+        filter: null,
         clusterTargetSelect: null,
         clusterTargetSelect: null,
         stateSelect: null,
         stateSelect: null,
 
 
-        validateDialog: null,
-
         postCreate: function (args) {
         postCreate: function (args) {
             this.inherited(arguments);
             this.inherited(arguments);
             this.workunitsTab = registry.byId(this.id + "_Workunits");
             this.workunitsTab = registry.byId(this.id + "_Workunits");
+            this.filter = registry.byId(this.id + "Filter");
             this.clusterTargetSelect = registry.byId(this.id + "ClusterTargetSelect");
             this.clusterTargetSelect = registry.byId(this.id + "ClusterTargetSelect");
             this.stateSelect = registry.byId(this.id + "StateSelect");
             this.stateSelect = registry.byId(this.id + "StateSelect");
             this.logicalFileSearchTypeSelect = registry.byId(this.id + "LogicalFileSearchType");
             this.logicalFileSearchTypeSelect = registry.byId(this.id + "LogicalFileSearchType");
@@ -88,7 +88,6 @@ define([
         startup: function (args) {
         startup: function (args) {
             this.inherited(arguments);
             this.inherited(arguments);
             this.initContextMenu();
             this.initContextMenu();
-            this.initFilter();
         },
         },
 
 
         getTitle: function () {
         getTitle: function () {
@@ -150,20 +149,6 @@ define([
         _onDeschedule: function (event) {
         _onDeschedule: function (event) {
         },
         },
 
 
-        _onFilterApply: function (event) {
-            registry.byId(this.id + "FilterDropDown").closeDropDown();
-            if (this.hasFilter()) {
-                this.applyFilter();
-            } else {
-                this.validateDialog.show();
-            }
-        },
-
-        _onFilterClear: function (event) {
-            this.clearFilter();
-            this.applyFilter();
-        },
-
         _onRowDblClick: function (wuid) {
         _onRowDblClick: function (wuid) {
             var wuTab = this.ensurePane(this.id + "_" + wuid, {
             var wuTab = this.ensurePane(this.id + "_" + wuid, {
                 Wuid: wuid
                 Wuid: wuid
@@ -207,24 +192,8 @@ define([
         },
         },
 
 
         //  Implementation  ---
         //  Implementation  ---
-        clearFilter: function () {
-            arrayUtil.forEach(registry.byId(this.id + "FilterForm").getDescendants(), function (item, idx) {
-                item.set("value", null);
-            });
-        },
-
-        hasFilter: function () {
-            var filter = domForm.toObject(this.id + "FilterForm");
-            for (var key in filter) {
-                if (filter[key] != "") {
-                    return true;
-                }
-            }
-            return false;
-        },
-
         getFilter: function () {
         getFilter: function () {
-            var retVal = domForm.toObject(this.id + "FilterForm");
+            var retVal = this.filter.toObject();
             lang.mixin(retVal, {
             lang.mixin(retVal, {
                 StartDate: this.getISOString("FromDate", "FromTime"),
                 StartDate: this.getISOString("FromDate", "FromTime"),
                 EndDate: this.getISOString("ToDate", "ToTime")
                 EndDate: this.getISOString("ToDate", "ToTime")
@@ -240,10 +209,6 @@ define([
             return retVal;
             return retVal;
         },
         },
 
 
-        applyFilter: function () {
-            this.refreshGrid();
-        },
-
         //  Implementation  ---
         //  Implementation  ---
         init: function (params) {
         init: function (params) {
             if (this.inherited(arguments))
             if (this.inherited(arguments))
@@ -267,6 +232,14 @@ define([
 
 
             this.initWorkunitsGrid();
             this.initWorkunitsGrid();
             this.selectChild(this.workunitsTab, true);
             this.selectChild(this.workunitsTab, true);
+
+            var context = this;
+            this.filter.on("clear", function (evt) {
+                context.refreshGrid();
+            });
+            this.filter.on("apply", function (evt) {
+                context.refreshGrid();
+            });
         },
         },
 
 
         initTab: function () {
         initTab: function () {
@@ -327,37 +300,38 @@ define([
                 var pSubMenu = new Menu();
                 var pSubMenu = new Menu();
                 this.menuFilterOwner = this.addMenuItem(pSubMenu, {
                 this.menuFilterOwner = this.addMenuItem(pSubMenu, {
                     onClick: function (args) {
                     onClick: function (args) {
-                        context.clearFilter();
-                        registry.byId(context.id + "Owner").set("value", context.menuFilterOwner.get("hpcc_value"));
-                        context.applyFilter();
+                        context.filter.clear();
+                        context.filter.setValue(context.id + "Owner", context.menuFilterOwner.get("hpcc_value"));
+                        context.refreshGrid();
                     }
                     }
                 });
                 });
                 this.menuFilterJobname = this.addMenuItem(pSubMenu, {
                 this.menuFilterJobname = this.addMenuItem(pSubMenu, {
                     onClick: function (args) {
                     onClick: function (args) {
-                        context.clearFilter();
-                        registry.byId(context.id + "Jobname").set("value", context.menuFilterJobname.get("hpcc_value"));
-                        context.applyFilter();
+                        context.filter.clear();
+                        context.filter.setValue(context.id + "Jobname", context.menuFilterJobname.get("hpcc_value"));
+                        context.refreshGrid();
                     }
                     }
                 });
                 });
                 this.menuFilterCluster = this.addMenuItem(pSubMenu, {
                 this.menuFilterCluster = this.addMenuItem(pSubMenu, {
                     onClick: function (args) {
                     onClick: function (args) {
-                        context.clearFilter();
-                        registry.byId(context.id + "ClusterTargetSelect").set("value", context.menuFilterCluster.get("hpcc_value"));
-                        context.applyFilter();
+                        context.filter.clear();
+                        context.filter.setValue(context.id + "ClusterTargetSelect", context.menuFilterCluster.get("hpcc_value"));
+                        context.refreshGrid();
                     }
                     }
                 });
                 });
                 this.menuFilterState = this.addMenuItem(pSubMenu, {
                 this.menuFilterState = this.addMenuItem(pSubMenu, {
                     onClick: function (args) {
                     onClick: function (args) {
-                        context.clearFilter();
-                        registry.byId(context.id + "StateSelect").set("value", context.menuFilterState.get("hpcc_value"));
-                        context.applyFilter();
+                        context.filter.clear();
+                        context.filter.setValue(context.id + "StateSelect", context.menuFilterState.get("hpcc_value"));
+                        context.refreshGrid();
                     }
                     }
                 });
                 });
                 pSubMenu.addChild(new MenuSeparator());
                 pSubMenu.addChild(new MenuSeparator());
                 this.menuFilterClearFilter = this.addMenuItem(pSubMenu, {
                 this.menuFilterClearFilter = this.addMenuItem(pSubMenu, {
                     label: "Clear",
                     label: "Clear",
                     onClick: function () {
                     onClick: function () {
-                        context._onFilterClear();
+                        context.filter.clear();
+                        context.refreshGrid();
                     }
                     }
                 });
                 });
                 pMenu.addChild(new PopupMenuItem({
                 pMenu.addChild(new PopupMenuItem({
@@ -446,13 +420,6 @@ define([
             this.workunitsGrid.startup();
             this.workunitsGrid.startup();
         },
         },
 
 
-        initFilter: function () {
-            this.validateDialog = new Dialog({
-                title: "Filter",
-                content: "No filter criteria specified."
-            });
-        },
-
         refreshGrid: function (args) {
         refreshGrid: function (args) {
             this.workunitsGrid.set("query", this.getFilter());
             this.workunitsGrid.set("query", this.getFilter());
         },
         },
@@ -500,13 +467,6 @@ define([
 
 
             this.menuProtect.set("disabled", !hasNotProtected);
             this.menuProtect.set("disabled", !hasNotProtected);
             this.menuUnprotect.set("disabled", !hasProtected);
             this.menuUnprotect.set("disabled", !hasProtected);
-
-            this.refreshFilterState();
-        },
-
-        refreshFilterState: function () {
-            var hasFilter = this.hasFilter();
-            dom.byId(this.id + "IconFilter").src = hasFilter ? "img/filter.png" : "img/noFilter.png";
         },
         },
 
 
         ensurePane: function (id, params) {
         ensurePane: function (id, params) {

+ 12 - 22
esp/files/templates/DFUQueryWidget.html

@@ -43,28 +43,18 @@
                         </div>
                         </div>
                     </div>
                     </div>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
-                    <img id="${id}IconFilter" src="img/noFilter.png" class="iconNoFilter"/>
-                    <div id="${id}FilterDropDown" data-dojo-type="dijit.form.DropDownButton">
-                        <span>Filter</span>
-                        <div data-dojo-type="dijit.TooltipDialog">
-                            <div id="${id}FilterForm" style="width:430px" data-dojo-type="dijit.form.Form">
-                                <div data-dojo-props="cols:2" data-dojo-type="dojox.layout.TableContainer">
-                                    <input id="${id}Name" title="Name:" name="LogicalName" colspan="2" style="width:100%;" data-dojo-props="trim: true, placeHolder:'*::somefile*'" data-dojo-type="dijit.form.TextBox" />
-                                    <input id="${id}Description" title="Description:" name="Description" colspan="2" style="width:100%;" data-dojo-props="trim: true, placeHolder:'Some*Description'" data-dojo-type="dijit.form.TextBox" />
-                                    <input id="${id}Owner" title="Owner:" name="Owner" colspan="2" data-dojo-props="trim: true, placeHolder:'JSmith*'" data-dojo-type="dijit.form.TextBox" />
-                                    <input id="${id}ClusterTargetSelect" title="Cluster:" name="ClusterName" colspan="2" style="display: inline-block; vertical-align: middle" data-dojo-type="TargetSelectWidget" />
-                                    <input id="${id}FromSize" title="From&nbsp;Sizes:" name="FileSizeFrom" colspan="2" data-dojo-props="trim: true, placeHolder:'4096'" data-dojo-type="dijit.form.TextBox" />
-                                    <input id="${id}ToSize" title="To&nbsp;Sizes:" name="FileSizeTo" colspan="2" data-dojo-props="trim: true, placeHolder:'16777216'" data-dojo-type="dijit.form.TextBox" />
-                                    <input id="${id}FromDate" title="From&nbsp;Date:" name="StartDate" data-dojo-props="trim: true, placeHolder:'7/28/2013'" data-dojo-type="dijit.form.DateTextBox" />
-                                    <input id="${id}FromTime" title="" name="FromTime" data-dojo-props="trim: true, placeHolder:'7:30 AM'" data-dojo-type="dijit.form.TimeTextBox" />
-                                    <input id="${id}ToDate" title="To&nbsp;Date:" name="EndDate" data-dojo-props="trim: true, placeHolder:'7/28/2013'" data-dojo-type="dijit.form.DateTextBox" />
-                                    <input id="${id}ToTime" title="" name="ToTime" data-dojo-props="trim: true, placeHolder:'7:30 PM'" data-dojo-type="dijit.form.TimeTextBox" />
-                                    <input id="${id}LastNDays" title="Last&nbsp;N&nbsp;Days:" name="FirstN" colspan="2" data-dojo-props="dtrim: true, placeHolder:'2'" data-dojo-type="dijit.form.TextBox" />
-                                </div>
-                                <button data-dojo-attach-event="onClick:_onFilterApply" data-dojo-type="dijit.form.Button">Apply</button>
-                                <button data-dojo-attach-event="onClick:_onFilterClear" data-dojo-type="dijit.form.Button">Clear</button>
-                            </div>
-                        </div>
+                    <div id="${id}Filter" data-dojo-type="FilterDropDownWidget">
+                        <input id="${id}Name" title="Name:" name="LogicalName" colspan="2" style="width:100%;" data-dojo-props="trim: true, placeHolder:'*::somefile*'" data-dojo-type="dijit.form.TextBox" />
+                        <input id="${id}Description" title="Description:" name="Description" colspan="2" style="width:100%;" data-dojo-props="trim: true, placeHolder:'Some*Description'" data-dojo-type="dijit.form.TextBox" />
+                        <input id="${id}Owner" title="Owner:" name="Owner" colspan="2" data-dojo-props="trim: true, placeHolder:'JSmith*'" data-dojo-type="dijit.form.TextBox" />
+                        <input id="${id}ClusterTargetSelect" title="Cluster:" name="ClusterName" colspan="2" style="display: inline-block; vertical-align: middle" data-dojo-type="TargetSelectWidget" />
+                        <input id="${id}FromSize" title="From&nbsp;Sizes:" name="FileSizeFrom" colspan="2" data-dojo-props="trim: true, placeHolder:'4096'" data-dojo-type="dijit.form.TextBox" />
+                        <input id="${id}ToSize" title="To&nbsp;Sizes:" name="FileSizeTo" colspan="2" data-dojo-props="trim: true, placeHolder:'16777216'" data-dojo-type="dijit.form.TextBox" />
+                        <input id="${id}FromDate" title="From&nbsp;Date:" name="StartDate" data-dojo-props="trim: true, placeHolder:'7/28/2013'" data-dojo-type="dijit.form.DateTextBox" />
+                        <input id="${id}FromTime" title="" name="FromTime" data-dojo-props="trim: true, placeHolder:'7:30 AM'" data-dojo-type="dijit.form.TimeTextBox" />
+                        <input id="${id}ToDate" title="To&nbsp;Date:" name="EndDate" data-dojo-props="trim: true, placeHolder:'7/28/2013'" data-dojo-type="dijit.form.DateTextBox" />
+                        <input id="${id}ToTime" title="" name="ToTime" data-dojo-props="trim: true, placeHolder:'7:30 PM'" data-dojo-type="dijit.form.TimeTextBox" />
+                        <input id="${id}LastNDays" title="Last&nbsp;N&nbsp;Days:" name="FirstN" colspan="2" data-dojo-props="dtrim: true, placeHolder:'2'" data-dojo-type="dijit.form.TextBox" />
                     </div>
                     </div>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <div id="${id}NewPage" class="right" data-dojo-attach-event="onClick:_onNewPage" data-dojo-props="iconClass:'iconNewPage', showLabel:false" data-dojo-type="dijit.form.Button">Open in New Page</div>
                     <div id="${id}NewPage" class="right" data-dojo-attach-event="onClick:_onNewPage" data-dojo-props="iconClass:'iconNewPage', showLabel:false" data-dojo-type="dijit.form.Button">Open in New Page</div>

+ 17 - 0
esp/files/templates/FilterDropDownWidget.html

@@ -0,0 +1,17 @@
+<span class="${baseClass}">
+    <img id="${id}IconFilter" src="img/noFilter.png" class="iconNoFilter"/>
+    <div id="${id}FilterDropDown" data-dojo-type="dijit.form.DropDownButton">
+        <span>Filter</span>
+        <div class="toolTip" data-dojo-type="dijit.TooltipDialog">
+            <div id="${id}FilterForm" style="width:460px" data-dojo-type="dijit.form.Form">
+                <div class="dijitDialogPaneContentArea" data-dojo-props="cols:2" data-dojo-type="dojox.layout.TableContainer">
+                    <span data-dojo-attach-point="containerNode"></span>
+                </div>
+                <div class="dijitDialogPaneActionBar">
+                    <button id="${id}FilterApply" data-dojo-attach-event="onClick:_onFilterApply" data-dojo-type="dijit.form.Button">Apply</button>
+                    <button id="${id}FilterClear" data-dojo-attach-event="onClick:_onFilterClear" data-dojo-type="dijit.form.Button">Clear</button>
+                </div>
+            </div>
+        </div>
+    </div>
+</span>

+ 6 - 16
esp/files/templates/GetDFUWorkunitsWidget.html

@@ -12,22 +12,12 @@
                     <div id="${id}Protect" data-dojo-attach-event="onClick:_onProtect" data-dojo-type="dijit.form.Button">Protect</div>
                     <div id="${id}Protect" data-dojo-attach-event="onClick:_onProtect" data-dojo-type="dijit.form.Button">Protect</div>
                     <div id="${id}Unprotect" data-dojo-attach-event="onClick:_onUnprotect" data-dojo-type="dijit.form.Button">Unprotect</div>
                     <div id="${id}Unprotect" data-dojo-attach-event="onClick:_onUnprotect" data-dojo-type="dijit.form.Button">Unprotect</div>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
-                    <img id="${id}IconFilter" src="img/noFilter.png" class="iconNoFilter"/>
-                    <div id="${id}FilterDropDown" data-dojo-type="dijit.form.DropDownButton">
-                        <span>Filter</span>
-                        <div data-dojo-type="dijit.TooltipDialog">
-                            <div id="${id}FilterForm" style="width:360px" data-dojo-type="dijit.form.Form">
-                                <div data-dojo-props="cols:2" data-dojo-type="dojox.layout.TableContainer">
-                                    <input id="${id}Owner" title="Owner:" name="Owner" colspan="2" data-dojo-props="trim: true, placeHolder:'jsmi*'" data-dojo-type="dijit.form.TextBox" />
-                                    <input id="${id}Jobname" title="Jobname:" name="Jobname" colspan="2" data-dojo-props="trim: true, placeHolder:'log_analysis_1*'" data-dojo-type="dijit.form.TextBox" />
-                                    <input id="${id}ClusterTargetSelect" title="Cluster:" name="Cluster" colspan="2" data-dojo-props="trim: true, placeHolder:'r?x*'" data-dojo-type="TargetSelectWidget" />
-                                    <input id="${id}StateSelect" title="State" name="StateReq" colspan="2" data-dojo-props="trim: true, placeHolder:'Created'" data-dojo-type="TargetSelectWidget" />
-                                    <input id="${id}Type" title="Archived&nbsp;Only" name="Type" value="archived workunits" colspan="2" data-dojo-type="dijit.form.CheckBox" />
-                                </div>
-                                <button data-dojo-attach-event="onClick:_onFilterApply" data-dojo-type="dijit.form.Button">Apply</button>
-                                <button data-dojo-attach-event="onClick:_onFilterClear" data-dojo-type="dijit.form.Button">Clear</button>
-                            </div>
-                        </div>
+                    <div id="${id}Filter" data-dojo-type="FilterDropDownWidget">
+                        <input id="${id}Owner" title="Owner:" name="Owner" colspan="2" data-dojo-props="trim: true, placeHolder:'jsmi*'" data-dojo-type="dijit.form.TextBox" />
+                        <input id="${id}Jobname" title="Jobname:" name="Jobname" colspan="2" data-dojo-props="trim: true, placeHolder:'log_analysis_1*'" data-dojo-type="dijit.form.TextBox" />
+                        <input id="${id}ClusterTargetSelect" title="Cluster:" name="Cluster" colspan="2" data-dojo-props="trim: true, placeHolder:'r?x*'" data-dojo-type="TargetSelectWidget" />
+                        <input id="${id}StateSelect" title="State" name="StateReq" colspan="2" data-dojo-props="trim: true, placeHolder:'Created'" data-dojo-type="TargetSelectWidget" />
+                        <input id="${id}Type" title="Archived&nbsp;Only" name="Type" value="archived workunits" colspan="2" data-dojo-type="dijit.form.CheckBox" />
                     </div>
                     </div>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <div id="${id}NewPage" class="right" data-dojo-attach-event="onClick:_onNewPage" data-dojo-props="iconClass:'iconNewPage', showLabel:false" data-dojo-type="dijit.form.Button">Open in New Page</div>
                     <div id="${id}NewPage" class="right" data-dojo-attach-event="onClick:_onNewPage" data-dojo-props="iconClass:'iconNewPage', showLabel:false" data-dojo-type="dijit.form.Button">Open in New Page</div>

+ 2 - 2
esp/files/templates/HPCCPlatformFilesWidget.html

@@ -4,12 +4,12 @@
             <div id="${id}StackController" style="width: 100%" data-dojo-props="containerId:'${id}TabContainer'" data-dojo-type="dijit.layout.StackController"></div>
             <div id="${id}StackController" style="width: 100%" data-dojo-props="containerId:'${id}TabContainer'" data-dojo-type="dijit.layout.StackController"></div>
         </div>
         </div>
         <div id="${id}TabContainer" data-dojo-props="region: 'center', tabPosition: 'top'" style="width: 100%; height: 100%" data-dojo-type="dijit.layout.StackContainer">
         <div id="${id}TabContainer" data-dojo-props="region: 'center', tabPosition: 'top'" style="width: 100%; height: 100%" data-dojo-type="dijit.layout.StackContainer">
-            <div id="${id}_Workunits" title="Workunits" data-dojo-type="GetDFUWorkunitsWidget">
-            </div>
             <div id="${id}_LogicalFiles" title="Logical Files" data-dojo-type="DFUQueryWidget">
             <div id="${id}_LogicalFiles" title="Logical Files" data-dojo-type="DFUQueryWidget">
             </div>
             </div>
             <div id="${id}_LandingZones" title="Landing Zones" data-dojo-type="LZBrowseWidget">
             <div id="${id}_LandingZones" title="Landing Zones" data-dojo-type="LZBrowseWidget">
             </div>
             </div>
+            <div id="${id}_Workunits" title="Workunits" data-dojo-type="GetDFUWorkunitsWidget">
+            </div>
             <div id="${id}_XRef" title="XRef" data-dojo-type="dijit.layout.ContentPane">
             <div id="${id}_XRef" title="XRef" data-dojo-type="dijit.layout.ContentPane">
             </div>
             </div>
         </div>
         </div>

+ 14 - 24
esp/files/templates/WUQueryWidget.html

@@ -17,30 +17,20 @@
                     <div id="${id}Reschedule" data-dojo-attach-event="onClick:_onReschedule" data-dojo-type="dijit.form.Button">Reschedule</div>
                     <div id="${id}Reschedule" data-dojo-attach-event="onClick:_onReschedule" data-dojo-type="dijit.form.Button">Reschedule</div>
                     <div id="${id}Deschedule" data-dojo-attach-event="onClick:_onDeschedule" data-dojo-type="dijit.form.Button">Deschedule</div>
                     <div id="${id}Deschedule" data-dojo-attach-event="onClick:_onDeschedule" data-dojo-type="dijit.form.Button">Deschedule</div>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
-                    <img id="${id}IconFilter" src="img/noFilter.png" class="iconNoFilter"/>
-                    <div id="${id}FilterDropDown" data-dojo-type="dijit.form.DropDownButton">
-                        <span>Filter</span>
-                        <div data-dojo-type="dijit.TooltipDialog" class="toolTip">
-                            <div id="${id}FilterForm" style="width:460px" data-dojo-type="dijit.form.Form">
-                                <div data-dojo-props="cols:2" data-dojo-type="dojox.layout.TableContainer">
-                                    <input id="${id}Wuid" title="WUID:" name="Wuid" colspan="2" data-dojo-props="trim: true, placeHolder:'W20130222-171723'" data-dojo-type="dijit.form.TextBox" />
-                                    <input id="${id}Owner" title="Owner:" name="Owner" colspan="2" data-dojo-props="trim: true, placeHolder:'jsmi*'" data-dojo-type="dijit.form.TextBox" />
-                                    <input id="${id}Jobname" title="Job&nbsp;Name:" name="Jobname" colspan="2" data-dojo-props="trim: true, placeHolder:'log_analysis_1*'" data-dojo-type="dijit.form.TextBox" />
-                                    <input id="${id}ClusterTargetSelect" title="Cluster:" name="Cluster" colspan="2" data-dojo-props="trim: true, placeHolder:'r?x*'" data-dojo-type="TargetSelectWidget" />
-                                    <input id="${id}StateSelect" title="State:" name="State" colspan="2" data-dojo-props="trim: true, placeHolder:'Created'" data-dojo-type="TargetSelectWidget" />
-                                    <input id="${id}ECL" title="ECL:" name="ECL" colspan="2" data-dojo-props="trim: true, placeHolder:':=dataset*'" data-dojo-type="dijit.form.TextBox" />
-                                    <input id="${id}LogicalFile" title="Logical&nbsp;File" name="LogicalFile" colspan="2" data-dojo-props="trim: true, placeHolder:'*::somefile'" data-dojo-type="dijit.form.TextBox" />
-                                    <input id="${id}LogicalFileSearchType" title="Logical&nbsp;File&nbsp;Type" name="LogicalFileSearchType" colspan="2" data-dojo-type="TargetSelectWidget" />
-                                    <input id="${id}FromDate" title="From&nbsp;Date:" name="StartDate" data-dojo-props="trim: true, placeHolder:'7/28/2013'" data-dojo-type="dijit.form.DateTextBox" />
-                                    <input id="${id}FromTime" name="FromTime" data-dojo-props="trim: true, placeHolder:'7:30 AM'" data-dojo-type="dijit.form.TimeTextBox" />
-                                    <input id="${id}ToDate" title="To&nbsp;Date:" name="EndDate" data-dojo-props="trim: true, placeHolder:'7/28/2013'" data-dojo-type="dijit.form.DateTextBox" />
-                                    <input id="${id}ToTime" name="ToTime" data-dojo-props="trim: true, placeHolder:'7:30 PM'" data-dojo-type="dijit.form.TimeTextBox" />
-                                    <input id="${id}LastNDays" title="Last&nbsp;N&nbsp;Days:" name="LastNDays" data-dojo-props="dtrim: true, placeHolder:'2'" data-dojo-type="dijit.form.TextBox" />
-                                </div>
-                                <button id="${id}FilterApply" data-dojo-attach-event="onClick:_onFilterApply" data-dojo-type="dijit.form.Button">Apply</button>
-                                <button id="${id}FilterClear" data-dojo-attach-event="onClick:_onFilterClear" data-dojo-type="dijit.form.Button">Clear</button>
-                            </div>
-                        </div>
+                    <div id="${id}Filter" data-dojo-type="FilterDropDownWidget">
+                        <input id="${id}Wuid" title="WUID:" name="Wuid" colspan="2" style="width:100%" data-dojo-props="trim: true, placeHolder:'W20130222-171723'" data-dojo-type="dijit.form.TextBox" />
+                        <input id="${id}Owner" title="Owner:" name="Owner" colspan="2" data-dojo-props="trim: true, placeHolder:'jsmi*'" data-dojo-type="dijit.form.TextBox" />
+                        <input id="${id}Jobname" title="Job&nbsp;Name:" name="Jobname" colspan="2" data-dojo-props="trim: true, placeHolder:'log_analysis_1*'" data-dojo-type="dijit.form.TextBox" />
+                        <input id="${id}ClusterTargetSelect" title="Cluster:" name="Cluster" colspan="2" data-dojo-props="trim: true, placeHolder:'r?x*'" data-dojo-type="TargetSelectWidget" />
+                        <input id="${id}StateSelect" title="State:" name="State" colspan="2" data-dojo-props="trim: true, placeHolder:'Created'" data-dojo-type="TargetSelectWidget" />
+                        <input id="${id}ECL" title="ECL:" name="ECL" colspan="2" data-dojo-props="trim: true, placeHolder:':=dataset*'" data-dojo-type="dijit.form.TextBox" />
+                        <input id="${id}LogicalFile" title="Logical&nbsp;File" name="LogicalFile" colspan="2" data-dojo-props="trim: true, placeHolder:'*::somefile'" data-dojo-type="dijit.form.TextBox" />
+                        <input id="${id}LogicalFileSearchType" title="Logical&nbsp;File&nbsp;Type" name="LogicalFileSearchType" colspan="2" data-dojo-type="TargetSelectWidget" />
+                        <input id="${id}FromDate" title="From&nbsp;Date:" name="StartDate" data-dojo-props="trim: true, placeHolder:'7/28/2013'" data-dojo-type="dijit.form.DateTextBox" />
+                        <input id="${id}FromTime" name="FromTime" data-dojo-props="trim: true, placeHolder:'7:30 AM'" data-dojo-type="dijit.form.TimeTextBox" />
+                        <input id="${id}ToDate" title="To&nbsp;Date:" name="EndDate" data-dojo-props="trim: true, placeHolder:'7/28/2013'" data-dojo-type="dijit.form.DateTextBox" />
+                        <input id="${id}ToTime" name="ToTime" data-dojo-props="trim: true, placeHolder:'7:30 PM'" data-dojo-type="dijit.form.TimeTextBox" />
+                        <input id="${id}LastNDays" title="Last&nbsp;N&nbsp;Days:" name="LastNDays" data-dojo-props="dtrim: true, placeHolder:'2'" data-dojo-type="dijit.form.TextBox" />
                     </div>
                     </div>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <div id="${id}NewPage" class="right" data-dojo-attach-event="onClick:_onNewPage" data-dojo-props="iconClass:'iconNewPage', showLabel:false" data-dojo-type="dijit.form.Button">Open in New Page</div>
                     <div id="${id}NewPage" class="right" data-dojo-attach-event="onClick:_onNewPage" data-dojo-props="iconClass:'iconNewPage', showLabel:false" data-dojo-type="dijit.form.Button">Open in New Page</div>

+ 1 - 0
esp/services/ws_dfu/CMakeLists.txt

@@ -39,6 +39,7 @@ set (    SRCS
     )
     )
 
 
 include_directories ( 
 include_directories ( 
+         ${CMAKE_BINARY_DIR}/oss
          ./../../../dali/dfu 
          ./../../../dali/dfu 
          ./../../../system/mp 
          ./../../../system/mp 
          ./../../platform 
          ./../../platform 

+ 1 - 0
esp/services/ws_workunits/CMakeLists.txt

@@ -44,6 +44,7 @@ set (    SRCS
     )
     )
 
 
 include_directories (
 include_directories (
+         ${CMAKE_BINARY_DIR}/oss
          ./../../esplib
          ./../../esplib
          ./../../../system/mp
          ./../../../system/mp
          ./../../platform
          ./../../platform

+ 12 - 3
plugins/unicodelib/unicodelib.cpp

@@ -744,6 +744,9 @@ UNICODELIB_API unsigned UNICODELIB_CALL ulUnicodeLocaleFind(unsigned srcLen, UCh
 
 
 UNICODELIB_API unsigned UNICODELIB_CALL ulUnicodeLocaleFindAtStrength(unsigned srcLen, UChar const * src, unsigned hitLen, UChar const * hit, unsigned instance, char const * localename, char strength)
 UNICODELIB_API unsigned UNICODELIB_CALL ulUnicodeLocaleFindAtStrength(unsigned srcLen, UChar const * src, unsigned hitLen, UChar const * hit, unsigned instance, char const * localename, char strength)
 {
 {
+    //Very strange behaviour - if source or pattern lengths are 0 search->getCollator() is invalid
+    if (srcLen == 0 || hitLen == 0)
+        return 0;
     UnicodeString const source(src, srcLen);
     UnicodeString const source(src, srcLen);
     UnicodeString const pattern(hit, hitLen);
     UnicodeString const pattern(hit, hitLen);
     UErrorCode error = U_ZERO_ERROR;
     UErrorCode error = U_ZERO_ERROR;
@@ -976,9 +979,15 @@ UNICODELIB_API void UNICODELIB_CALL ulUnicodeLocaleFindReplace(unsigned & tgtLen
 UNICODELIB_API void UNICODELIB_CALL ulUnicodeLocaleFindAtStrengthReplace(unsigned & tgtLen, UChar * & tgt, unsigned srcLen, UChar const * src, unsigned stokLen, UChar const * stok, unsigned rtokLen, UChar const * rtok, char const * localename, char strength)
 UNICODELIB_API void UNICODELIB_CALL ulUnicodeLocaleFindAtStrengthReplace(unsigned & tgtLen, UChar * & tgt, unsigned srcLen, UChar const * src, unsigned stokLen, UChar const * stok, unsigned rtokLen, UChar const * rtok, char const * localename, char strength)
 {
 {
     UnicodeString source(src, srcLen);
     UnicodeString source(src, srcLen);
-    UnicodeString const pattern(stok, stokLen);
-    UnicodeString const replace(rtok, rtokLen);
-    doUnicodeLocaleFindAtStrengthReplace(source, pattern, replace, localename, strength);
+
+    //Very strange behaviour - if source or pattern lengths are 0 search->getCollator() is invalid
+    if (srcLen && stokLen)
+    {
+        UnicodeString const pattern(stok, stokLen);
+        UnicodeString const replace(rtok, rtokLen);
+        doUnicodeLocaleFindAtStrengthReplace(source, pattern, replace, localename, strength);
+    }
+
     tgtLen = source.length();
     tgtLen = source.length();
     tgt = (UChar *)CTXMALLOC(parentCtx, tgtLen*2);
     tgt = (UChar *)CTXMALLOC(parentCtx, tgtLen*2);
     source.extract(0, tgtLen, tgt);
     source.extract(0, tgtLen, tgt);

+ 20 - 0
testing/ecl/issue9879.ecl

@@ -0,0 +1,20 @@
+IMPORT Std;
+
+STD.Uni.LocaleFind(u'',u'abc',1,'');
+STD.Uni.LocaleFind(u'abc',u'',1,'');
+STD.Uni.LocaleFind(u'abc',u'b',1,'');
+
+STD.Uni.LocaleFindAtStrength(u'',u'abc',1,'',1);
+STD.Uni.LocaleFindAtStrength(u'abc',u'',1,'',1);
+STD.Uni.LocaleFindAtStrength(u'abc',u'b',1,'',1);
+
+STD.Uni.LocaleFindReplace(u'',u'abc',u'x','');
+STD.Uni.LocaleFindReplace(u'abc',u'',u'x','');
+STD.Uni.LocaleFindReplace(u'abc',u'B',u'x','');
+STD.Uni.LocaleFindReplace(u'abc',u'b',u'x','');
+
+STD.Uni.LocaleFindAtStrengthReplace(u'',u'abc',u'x','',1);
+STD.Uni.LocaleFindAtStrengthReplace(u'abc',u'',u'x','',1);
+STD.Uni.LocaleFindAtStrengthReplace(u'abc',u'B',u'x','',0);
+STD.Uni.LocaleFindAtStrengthReplace(u'abc',u'B',u'x','',1);
+STD.Uni.LocaleFindAtStrengthReplace(u'abc',u'B',u'x','',2);

+ 45 - 0
testing/ecl/key/issue9879.xml

@@ -0,0 +1,45 @@
+<Dataset name='Result 1'>
+ <Row><Result_1>0</Result_1></Row>
+</Dataset>
+<Dataset name='Result 2'>
+ <Row><Result_2>0</Result_2></Row>
+</Dataset>
+<Dataset name='Result 3'>
+ <Row><Result_3>2</Result_3></Row>
+</Dataset>
+<Dataset name='Result 4'>
+ <Row><Result_4>0</Result_4></Row>
+</Dataset>
+<Dataset name='Result 5'>
+ <Row><Result_5>0</Result_5></Row>
+</Dataset>
+<Dataset name='Result 6'>
+ <Row><Result_6>2</Result_6></Row>
+</Dataset>
+<Dataset name='Result 7'>
+ <Row><Result_7></Result_7></Row>
+</Dataset>
+<Dataset name='Result 8'>
+ <Row><Result_8>abc</Result_8></Row>
+</Dataset>
+<Dataset name='Result 9'>
+ <Row><Result_9>abc</Result_9></Row>
+</Dataset>
+<Dataset name='Result 10'>
+ <Row><Result_10>axc</Result_10></Row>
+</Dataset>
+<Dataset name='Result 11'>
+ <Row><Result_11></Result_11></Row>
+</Dataset>
+<Dataset name='Result 12'>
+ <Row><Result_12>abc</Result_12></Row>
+</Dataset>
+<Dataset name='Result 13'>
+ <Row><Result_13>abc</Result_13></Row>
+</Dataset>
+<Dataset name='Result 14'>
+ <Row><Result_14>axc</Result_14></Row>
+</Dataset>
+<Dataset name='Result 15'>
+ <Row><Result_15>axc</Result_15></Row>
+</Dataset>

+ 11 - 1
thorlcr/activities/hashdistrib/thhashdistribslave.cpp

@@ -1091,11 +1091,21 @@ public:
     }
     }
     bool sendRecv(ICommunicator &comm, CMessageBuffer &mb, rank_t r, mptag_t tag)
     bool sendRecv(ICommunicator &comm, CMessageBuffer &mb, rank_t r, mptag_t tag)
     {
     {
+        mptag_t replyTag = createReplyTag();
         loop
         loop
         {
         {
             if (aborted)
             if (aborted)
                 return false;
                 return false;
-            if (comm.sendRecv(mb, r, tag, MEDIUMTIMEOUT))
+            mb.setReplyTag(replyTag);
+            if (comm.send(mb, r, tag, MEDIUMTIMEOUT))
+                break;
+            // try again
+        }
+        loop
+        {
+            if (aborted)
+                return false;
+            if (comm.recv(mb, r, replyTag, NULL, MEDIUMTIMEOUT))
                 return true;
                 return true;
             // try again
             // try again
         }
         }