Jelajahi Sumber

Merge pull request #6388 from richardkchapman/java-iterator-signature

HPCC-12148 Accept more standard signature for java iterator parameters
Gavin Halliday 10 tahun lalu
induk
melakukan
35c6b74652
2 mengubah file dengan 48 tambahan dan 27 penghapusan
  1. 4 1
      initfiles/examples/embed/java-stream.ecl
  2. 44 26
      plugins/javaembed/javaembed.cpp

+ 4 - 1
initfiles/examples/embed/java-stream.ecl

@@ -47,11 +47,14 @@ OUTPUT(jpassrec(ret));  // Passes an ECL record to a Java function
 // where the fields of the object in question are mapped by name to the fields in the ECL record.
 // When passing an iterator, we use a modified form of the function signature in the IMPORT statement, using a < to indicate "Iterator of"
 // followed by the name of the class of the objects that the iterator is to return. This is the one case where the output of javap -s cannot be used
-// directly to provide the signature of the java function being called.
+// directly to provide the signature of the java function being called. We can also use the "extended" signature of the method that is output by
+// javap -s -v, of the form 'JavaCat.passDataset:(Ljava/util/Iterator<LJavaCat;>;)I'
+//
 // To return a dataset, an iterator must be returned.
 
 INTEGER passDataset(LINKCOUNTED DATASET(jret) d) :=
   IMPORT(java, 'JavaCat.passDataset:(<LJavaCat;)I'); // Calls int passDataset(Iterator<JavaCat> d)
+// Note we could also use 'Ljava/util/Iterator<LJavaCat;>;)I' as the signature, but not 'Ljava/util/Iterator;)I' which is the signature output by javap -s
 
 DATASET(jret) passDataset2(LINKCOUNTED DATASET(jret) d) :=
   IMPORT(java, 'JavaCat.passDataset2:([LJavaCat;)Ljava/util/Iterator;'); // Calls Iterator<JavaCat> passDataset2(JavaCat d[])

+ 44 - 26
plugins/javaembed/javaembed.cpp

@@ -1314,10 +1314,17 @@ public:
             {
                 if (*finger == '<')
                 {
-                    javaSignature.append("Ljava/util/Iterator;");
-                    finger = strchr(finger, ';');
-                    if (!finger)
-                        throw MakeStringException(MSGAUD_user, 0, "javaembed: Invalid java function signature %s", signature);
+                    // If there is a corresponding >, assume it's the 'extended' form and just strip out the bit from < to >
+                    const char *close = strchr(finger, '>');
+                    if (close)
+                        finger = close;
+                    else
+                    {
+                        javaSignature.append("Ljava/util/Iterator;");
+                        finger = strchr(finger, ';');
+                        if (!finger)
+                            throw MakeStringException(MSGAUD_user, 0, "javaembed: Invalid java function signature %s", signature);
+                    }
                 }
                 else
                     javaSignature.append(*finger);
@@ -2096,18 +2103,40 @@ public:
     virtual void bindDatasetParam(const char *name, IOutputMetaData & metaVal, IRowStream * val)
     {
         jvalue v;
-        if (*argsig == '[')
+        char argsigStart = *argsig;
+        switch (argsigStart)
         {
+        case '[':
+        case '<':
             ++argsig;
-            // At present, dataset parameters have to map to arrays. May support iterators later
-            if (*argsig != 'L')  // should tell us the type of the object we need to create to pass in
+            break;
+        case 'L':
+            if (strncmp(argsig, "Ljava/util/Iterator<", 20) == 0)
+            {
+                argsig += 20;
+                break;
+            }
+            /* no break */
+        default:
+            typeError("DATASET");
+        }
+        if (*argsig != 'L')  // should tell us the type of the object we need to create to pass in
+            typeError("DATASET");
+        // Class name is from the char after the L up to the first ;
+        const char *tail = strchr(argsig, ';');
+        if (!tail)
+            typeError("RECORD");
+        StringAttr className(argsig+1, tail - (argsig+1));
+        argsig = tail+1;
+        if (argsigStart=='L')
+        {
+            if (argsig[0] != '>' || argsig[1] != ';')
                 typeError("DATASET");
-            // Class name is from the char after the L up to the first ;
-            const char *tail = strchr(argsig, ';');
-            if (!tail)
-                typeError("RECORD");
-            StringAttr className(argsig+1, tail - (argsig+1));
-            argsig = tail+1;
+            argsig += 2;
+        }
+        if (argsigStart=='[')
+        {
+            // Pass in an array of objects
             PointerArrayOf<_jobject> allRows;
             const RtlTypeInfo *typeInfo = metaVal.queryTypeInfo();
             assertex(typeInfo);
@@ -2129,18 +2158,9 @@ public:
             }
             v.l = array;
         }
-        else if (*argsig == '<') // An extension to the Java signatures, used to indicate
-                                  // that we will pass an iterator that generates objects of the specified type
+        else
         {
-            ++argsig;
-            if (*argsig != 'L')  // should tell us the type of the object we need to create to pass in
-                typeError("DATASET");
-            // Class name is from the char after the L up to the first ;
-            const char *tail = strchr(argsig, ';');
-            if (!tail)
-                typeError("RECORD");
-            StringAttr className(argsig+1, tail - (argsig+1));
-            argsig = tail+1;
+            // Pass in an iterator
             // Create a java object of type com.HPCCSystems.HpccUtils - this acts as a proxy for the iterator
             sharedCtx->JNIenv->ExceptionClear();
             jclass proxyClass = sharedCtx->JNIenv->FindClass("com/HPCCSystems/HpccUtils");
@@ -2156,8 +2176,6 @@ public:
             sharedCtx->checkException();
             v.l = proxy;
         }
-        else
-            typeError("DATASET");
         addArg(v);
     }