You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2017/08/19 17:00:40 UTC

[2/5] jena git commit: JENA-1372: fn:apply and fn:collation-key

JENA-1372: fn:apply and fn:collation-key

Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/919a8215
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/919a8215
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/919a8215

Branch: refs/heads/master
Commit: 919a8215174d1820565144c230e8fbc1d7f77cf1
Parents: 1be131e
Author: Andy Seaborne <an...@apache.org>
Authored: Thu Aug 17 21:08:54 2017 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Thu Aug 17 21:21:51 2017 +0100

----------------------------------------------------------------------
 .../jena/sparql/function/FunctionBase.java      | 22 ++++-
 .../jena/sparql/function/StandardFunctions.java |  5 +
 .../jena/sparql/function/library/FN_Apply.java  | 96 ++++++++++++++++++++
 .../function/library/FN_CollationKey.java       | 52 +++++++++++
 .../java/org/apache/jena/sparql/TC_General.java |  2 +
 .../apache/jena/sparql/expr/TestFunctions2.java |  1 -
 .../jena/sparql/function/library/LibTest.java   | 49 ++++++++++
 .../function/library/TS_LibraryFunctions.java   | 31 +++++++
 .../function/library/TestFnFunctions.java       | 64 +++++++++++++
 .../function/library/TestFunctionCollation.java | 18 +---
 10 files changed, 320 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/919a8215/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionBase.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionBase.java
index c55a988..53177ff 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionBase.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/FunctionBase.java
@@ -27,7 +27,7 @@ import org.apache.jena.sparql.expr.Expr ;
 import org.apache.jena.sparql.expr.ExprList ;
 import org.apache.jena.sparql.expr.NodeValue ;
 
-/** Impleemntation root for custom function evaluation. */  
+/** Implementation root for custom function evaluation. */  
 public abstract class FunctionBase implements Function {
 
     @Override
@@ -36,6 +36,13 @@ public abstract class FunctionBase implements Function {
         checkBuild(uri, args) ;
     }
 
+    // Valid during execution.
+    // Only specialised uses need these values 
+    // e.g. fn:apply which is a meta-function - it looks up a URI to get a function to call.
+    protected FunctionEnv functionEnv = null;
+    // Not needed so hide but keep for debugging.
+    private Binding binding = null;
+    
     @Override
     public NodeValue exec(Binding binding, ExprList args, String uri, FunctionEnv env) {
         if ( args == null )
@@ -49,8 +56,17 @@ public abstract class FunctionBase implements Function {
             evalArgs.add( x );
         }
         
-        NodeValue nv =  exec(evalArgs) ;
-        return nv ;
+        // Cature
+        try {
+            this.functionEnv = env ;
+            this.binding = binding;
+            NodeValue nv =  exec(evalArgs) ;
+            return nv ;
+        } finally {
+            this.functionEnv = null ;
+            this.binding = null;
+        }
+        
     }
     
     /** Function call to a list of evaluated argument values */ 

http://git-wip-us.apache.org/repos/asf/jena/blob/919a8215/jena-arq/src/main/java/org/apache/jena/sparql/function/StandardFunctions.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/StandardFunctions.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/StandardFunctions.java
index 145b1b0..e5d4337 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/StandardFunctions.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/StandardFunctions.java
@@ -232,6 +232,11 @@ public class StandardFunctions
         
         add(registry, math+"atan2",     Math_atan2.class) ;
         
+        // F&O 3.1
+        add(registry, xfn+"apply",           FN_Apply.class);
+        add(registry, xfn+"collation-key",   FN_CollationKey.class);
+
+        
         // And add op:'s
 //        4.2.1 op:numeric-add
 //        4.2.2 op:numeric-subtract

http://git-wip-us.apache.org/repos/asf/jena/blob/919a8215/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Apply.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Apply.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Apply.java
new file mode 100644
index 0000000..4f766fa
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_Apply.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.jena.sparql.function.library;
+
+import java.util.List ;
+
+import org.apache.jena.atlas.lib.Cache ;
+import org.apache.jena.atlas.lib.CacheFactory ;
+import org.apache.jena.graph.Node ;
+import org.apache.jena.sparql.expr.ExprEvalException ;
+import org.apache.jena.sparql.expr.ExprList ;
+import org.apache.jena.sparql.expr.NodeValue ;
+import org.apache.jena.sparql.function.Function ;
+import org.apache.jena.sparql.function.FunctionBase ;
+import org.apache.jena.sparql.function.FunctionFactory ;
+import org.apache.jena.sparql.function.FunctionRegistry ;
+import org.apache.jena.sparql.sse.builders.ExprBuildException ;
+import org.apache.jena.sparql.util.Context ;
+
+/** XPath and XQuery Functions and Operators 3.1
+ * <p> 
+ * {@code fn:apply(function, args)}
+ */
+public class FN_Apply extends FunctionBase {
+    // Assumes one object per use site. 
+    private Cache<String, Function> cache1 = CacheFactory.createOneSlotCache();
+    
+    @Override
+    public void checkBuild(String uri, ExprList args) {
+        if ( args.isEmpty() )
+            throw new ExprBuildException("fn:apply: no function to call (minimum number of args is one)");
+    }
+    @Override
+    public NodeValue exec(List<NodeValue> args) {
+        if ( args.isEmpty() )
+            throw new ExprBuildException("fn:apply: no function to call (minimum number of args is one)");
+        NodeValue functionId = args.get(0);
+        List<NodeValue> argExprs = args.subList(1,args.size()) ; 
+        ExprList exprs = new ExprList();
+        argExprs.forEach((a)->exprs.add(a));
+        Node fnNode = functionId.asNode();
+        
+        if ( fnNode.isBlank() ) 
+            throw new ExprBuildException("fn:apply: function id is a blank node (must be a URI)");        
+        if ( fnNode.isLiteral() )
+            throw new ExprBuildException("fn:apply: function id is a literal (must be a URI)");
+        if ( fnNode.isVariable() )
+            // Should not happen ... but ...
+            throw new ExprBuildException("fn:apply: function id is an unbound variable (must be a URI)");
+        if ( fnNode.isURI() ) {
+            String functionIRI = fnNode.getURI();
+            Function function = cache1.getOrFill(functionIRI, ()->buildFunction(functionIRI));
+            if ( function instanceof FunctionBase ) {
+                // Fast track.
+                return ((FunctionBase)function).exec(argExprs);
+            }
+            function.build(functionIRI, exprs);
+            // Eval'ed arguments.
+            return function.exec(null, exprs, functionIRI, null);
+        }
+        
+        throw new ExprEvalException("fn:apply: Weird function argument (arg 1): "+functionId);
+    }
+    
+    private Function buildFunction(String functionIRI) {
+        FunctionRegistry registry = chooseRegistry(functionEnv.getContext()) ;
+        FunctionFactory ff = registry.get(functionIRI) ;
+        if ( ff == null )
+            return null;
+        return ff.create(functionIRI) ;
+    }
+
+    private FunctionRegistry chooseRegistry(Context context) {
+        FunctionRegistry registry = FunctionRegistry.get(context) ;
+        if ( registry == null )
+            registry = FunctionRegistry.get() ;
+        return registry ;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/919a8215/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_CollationKey.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_CollationKey.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_CollationKey.java
new file mode 100644
index 0000000..6f98f25
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/FN_CollationKey.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.jena.sparql.function.library;
+
+import java.nio.charset.StandardCharsets ;
+import java.util.Base64 ;
+import java.util.Locale ;
+
+import org.apache.jena.datatypes.xsd.XSDDatatype ;
+import org.apache.jena.sparql.expr.NodeValue ;
+import org.apache.jena.sparql.function.FunctionBase2 ;
+
+/** XPath and XQuery Functions and Operators 3.1
+ * <p> 
+ * {@code fn:collation-key($key as xs:string, $collation as xs:string) as xs:base64Binary}
+ */
+public class FN_CollationKey extends FunctionBase2 {
+
+    // The function2 variant
+    // Function1 is default collation -> codepoint. 
+    
+    @Override
+    public NodeValue exec(NodeValue v1, NodeValue v2) {
+        //fn:collation-key($key as xs:string, $collation as xs:string) as xs:base64Binary
+        if ( ! v1.isString() ) {}
+        if ( ! v2.isString() ) {}
+        String collation = v2 == null ? "" : v2.getString().toLowerCase(Locale.ROOT); 
+        
+        // The irony of using the lexical form of old rdf:plainLiteral (RDF 1.1 does not
+        // need rdf:plainLiteral and rdf:plainLiteral should never appear in RDF)
+        String x = v1.getString()+"@"+v2.getString();
+        byte[] b = x.getBytes(StandardCharsets.UTF_8);
+        String s = Base64.getMimeEncoder().encodeToString(b);
+        return NodeValue.makeNode(s, XSDDatatype.XSDbase64Binary);
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/919a8215/jena-arq/src/test/java/org/apache/jena/sparql/TC_General.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/TC_General.java b/jena-arq/src/test/java/org/apache/jena/sparql/TC_General.java
index e8d46ea..e4542bc 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/TC_General.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/TC_General.java
@@ -31,6 +31,7 @@ import org.apache.jena.sparql.engine.join.TS_Join ;
 import org.apache.jena.sparql.expr.E_Function ;
 import org.apache.jena.sparql.expr.NodeValue ;
 import org.apache.jena.sparql.expr.TS_Expr ;
+import org.apache.jena.sparql.function.library.TS_LibraryFunctions ;
 import org.apache.jena.sparql.function.user.TS_UserFunctions ;
 import org.apache.jena.sparql.graph.TS_Graph ;
 import org.apache.jena.sparql.lang.TS_Lang ;
@@ -57,6 +58,7 @@ import org.junit.runners.Suite ;
     , TS_Util.class
     
     , TS_Expr.class
+    , TS_LibraryFunctions.class
     , TS_UserFunctions.class
     , TS_PFunction.class
     

http://git-wip-us.apache.org/repos/asf/jena/blob/919a8215/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions2.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions2.java b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions2.java
index bd937d5..bc5985a 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions2.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions2.java
@@ -34,7 +34,6 @@ import org.junit.Test ;
 
 public class TestFunctions2 extends BaseTest
 {
-    // 3->2
     // Some overlap with TestFunctions except those are direct function calls and these are via SPARQL 1.1 syntax.
     // Better too many tests than too few.
     

http://git-wip-us.apache.org/repos/asf/jena/blob/919a8215/jena-arq/src/test/java/org/apache/jena/sparql/function/library/LibTest.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/LibTest.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/LibTest.java
new file mode 100644
index 0000000..cfff012
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/LibTest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.jena.sparql.function.library;
+
+import static org.junit.Assert.assertEquals ;
+import static org.junit.Assert.assertTrue ;
+
+import org.apache.jena.graph.Node ;
+import org.apache.jena.shared.PrefixMapping ;
+import org.apache.jena.sparql.ARQConstants ;
+import org.apache.jena.sparql.expr.Expr ;
+import org.apache.jena.sparql.expr.NodeValue ;
+import org.apache.jena.sparql.function.FunctionEnvBase ;
+import org.apache.jena.sparql.util.ExprUtils ;
+import org.apache.jena.sparql.util.NodeFactoryExtra ;
+
+public class LibTest {
+    private static PrefixMapping pmap = ARQConstants.getGlobalPrefixMap() ;
+
+    static void test(String string) {
+        test(string, "true");
+    }
+
+    static void test(String string, String result) {
+        Expr expr = ExprUtils.parse(string, pmap) ;
+        NodeValue nv = expr.eval(null, new FunctionEnvBase()) ;
+        Node r = NodeFactoryExtra.parseNode(result) ;
+        NodeValue nvr = NodeValue.makeNode(r) ;
+        assertTrue("Not same value: Expected: " + nvr + " : Actual = " + nv, NodeValue.sameAs(nvr, nv)) ;
+        // test result must be lexical form exact.
+        assertEquals(r, nv.asNode()) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/919a8215/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TS_LibraryFunctions.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TS_LibraryFunctions.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TS_LibraryFunctions.java
new file mode 100644
index 0000000..132b313
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TS_LibraryFunctions.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.jena.sparql.function.library;
+
+import org.junit.runner.RunWith ;
+import org.junit.runners.Suite ;
+import org.junit.runners.Suite.SuiteClasses ;
+
+@RunWith(Suite.class)
+@SuiteClasses( {
+    // A lot of the test ate in TestFunctions and TestFunctions2.
+    TestFunctionCollation.class
+    , TestFnFunctions.class
+})
+public class TS_LibraryFunctions {}

http://git-wip-us.apache.org/repos/asf/jena/blob/919a8215/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctions.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctions.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctions.java
new file mode 100644
index 0000000..493f501
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFnFunctions.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.jena.sparql.function.library;
+
+import static org.apache.jena.sparql.function.library.LibTest.test;
+
+import org.apache.jena.sparql.expr.ExprEvalException ;
+import org.apache.jena.sparql.expr.VariableNotBoundException ;
+import org.apache.jena.sparql.sse.builders.ExprBuildException ;
+import org.apache.jena.system.JenaSystem ;
+import org.junit.Test ;
+
+public class TestFnFunctions {
+
+    static { JenaSystem.init(); }
+    
+    @Test public void apply_1() {
+        test("fn:apply(math:sqrt, 9)", "3.0e0");
+    }
+    
+    // Under arity
+    @Test(expected=ExprEvalException.class)
+    public void apply_2() {
+        test("fn:apply(math:sqrt)", "3.0e0");
+    }
+
+    // Over arity
+    @Test(expected=ExprEvalException.class)
+    public void apply_3() {
+        test("fn:apply(math:sqrt, 9, 10)", "3.0e0");
+    }
+
+    // Not a URI.
+    @Test(expected=ExprBuildException.class)
+    public void apply_4() {
+        test("fn:apply('bicycle', 9, 10)", "3.0e0");
+    }
+
+    @Test(expected=VariableNotBoundException.class)
+    public void apply_5() {
+        test("fn:apply(?var)", "3.0e0");
+    }
+    
+    @Test
+    public void collationKey_1() {
+        test("fn:collation-key('foo', 'en') = 'Zm9vQGVu'^^xsd:base64Binary");
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/919a8215/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFunctionCollation.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFunctionCollation.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFunctionCollation.java
index 7d69cd2..208d7fc 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFunctionCollation.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/library/TestFunctionCollation.java
@@ -20,28 +20,19 @@ package org.apache.jena.sparql.function.library;
 
 import static org.junit.Assert.assertArrayEquals;
 
-import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.List;
 
 import org.apache.jena.sparql.expr.NodeValue;
-import org.junit.Before;
 import org.junit.Test;
 
 /**
  * Tests for {@link FN_Collation}.
  */
 public class TestFunctionCollation {
-
-    private FN_Collation function = null;
-
-    @Before
-    public void setUp() {
-        function = new FN_Collation();
-    }
-
     @Test
     public void testFunctionCollationExec() {
+        FN_Collation function = new FN_Collation();
         NodeValue collation = NodeValue.makeString("fi");
         
         final String[] unordered = new String[]
@@ -54,12 +45,7 @@ public class TestFunctionCollation {
         for (String string : unordered) {
             nodeValues.add(function.exec(collation, NodeValue.makeString(string)));
         }
-        nodeValues.sort(new Comparator<NodeValue>() {
-            @Override
-            public int compare(NodeValue o1, NodeValue o2) {
-                return NodeValue.compare(o1, o2);
-            }
-        });
+        nodeValues.sort((NodeValue o1, NodeValue o2) -> NodeValue.compare(o1, o2) );
         List<String> result = new LinkedList<>();
         for (NodeValue nv : nodeValues) {
             String s = nv.toString();