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 2018/01/13 10:52:46 UTC
[1/8] jena git commit: JENA-1461: JavaScript custom functions
Repository: jena
Updated Branches:
refs/heads/master 67898ffc0 -> 58ac14b7e
JENA-1461: JavaScript custom functions
Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/c4f79ee9
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/c4f79ee9
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/c4f79ee9
Branch: refs/heads/master
Commit: c4f79ee9f908f301028a4e13dfc7c2fc2de89cbf
Parents: 2933d71
Author: Andy Seaborne <an...@apache.org>
Authored: Sun Jan 7 19:50:12 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Sun Jan 7 21:16:48 2018 +0000
----------------------------------------------------------------------
.../org/apache/jena/sparql/ARQConstants.java | 7 +
.../org/apache/jena/sparql/expr/E_Function.java | 40 +++-
.../jena/sparql/expr/ExprUndefFunction.java | 32 +++
.../jena/sparql/function/FunctionBase.java | 2 +-
.../jena/sparql/function/js/EnvJavaScript.java | 134 ++++++++++++
.../sparql/function/js/FunctionJavaScript.java | 93 ++++++++
.../org/apache/jena/sparql/function/js/NV.java | 217 +++++++++++++++++++
.../apache/jena/sparql/function/js/RDFJS.java | 35 +++
.../jena/riot/langsuite/FactoryTestRiot.java | 1 -
.../java/org/apache/jena/sparql/TC_General.java | 2 +
.../jena/sparql/function/js/TS_FunctionJS.java | 31 +++
.../function/js/TestJavaScriptFunctions.java | 198 +++++++++++++++++
.../apache/jena/sparql/function/js/TestNV.java | 71 ++++++
.../jena/sparql/function/js/TestSPARQL_JS.java | 49 +++++
.../function/library/TS_LibraryFunctions.java | 2 +-
jena-arq/testing/ARQ/JS/data.ttl | 5 +
jena-arq/testing/ARQ/JS/js-query-1.rq | 4 +
jena-arq/testing/ARQ/JS/js-query-1.srj | 12 +
jena-arq/testing/ARQ/JS/js-query-2.rq | 4 +
jena-arq/testing/ARQ/JS/js-query-2.srj | 12 +
jena-arq/testing/ARQ/JS/js-query-3.rq | 4 +
jena-arq/testing/ARQ/JS/js-query-3.srj | 12 +
jena-arq/testing/ARQ/JS/js-query-4.rq | 9 +
jena-arq/testing/ARQ/JS/js-query-4.srj | 14 ++
jena-arq/testing/ARQ/JS/manifest.ttl | 50 +++++
jena-arq/testing/ARQ/JS/test-library.js | 14 ++
26 files changed, 1044 insertions(+), 10 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
index 322f3e6..2cf4e4d 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
@@ -67,6 +67,13 @@ public class ARQConstants
/** XML Schema namespace */
public static final String XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema#" ;
+ /** The URI prefix that triggers JavaScript functions */
+ public static final String JavaScriptURI = "http://jena/apache.org/ARQ/jsFunction#" ;
+
+ /** Function called when JaavScript is initialized. */
+ public static final String JavaScriptInitFunction = "arqJSinit";
+
+ /** URI scheme that triggers the loader to load a java class */
public static final String javaClassURIScheme = "java:" ;
/** The ARQ function library URI space */
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
index 7b1501e..2d097a1 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
@@ -21,12 +21,15 @@ package org.apache.jena.sparql.expr;
import java.util.List ;
import org.apache.jena.query.ARQ ;
+import org.apache.jena.sparql.ARQConstants;
import org.apache.jena.sparql.ARQInternalErrorException ;
import org.apache.jena.sparql.engine.binding.Binding ;
import org.apache.jena.sparql.function.Function ;
import org.apache.jena.sparql.function.FunctionEnv ;
import org.apache.jena.sparql.function.FunctionFactory ;
import org.apache.jena.sparql.function.FunctionRegistry ;
+import org.apache.jena.sparql.function.js.EnvJavaScript;
+import org.apache.jena.sparql.function.js.FunctionJavaScript;
import org.apache.jena.sparql.serializer.SerializationContext ;
import org.apache.jena.sparql.util.Context ;
import org.apache.jena.sparql.util.FmtUtils ;
@@ -38,6 +41,7 @@ public class E_Function extends ExprFunctionN
private static final String name = "function" ;
public static boolean WarnOnUnknownFunction = true ;
private String functionIRI ;
+ private String javaScriptFunction = null;
private Function function = null ;
private boolean functionBound = false ;
@@ -45,16 +49,33 @@ public class E_Function extends ExprFunctionN
public E_Function(String functionIRI, ExprList args) {
super(name, args) ;
this.functionIRI = functionIRI ;
+ if ( functionIRI.startsWith(ARQConstants.JavaScriptURI) )
+ javaScriptFunction = functionIRI.substring(ARQConstants.JavaScriptURI.length());
}
@Override
public String getFunctionIRI() { return functionIRI ; }
// The Function subsystem takes over evaluation via SpecialForms.
- // This allows a "function" to behave as special forms (this is discouraged).
+ // This allows a "function" to behave as a special form (this is discouraged).
@Override
public NodeValue evalSpecial(Binding binding, FunctionEnv env) {
+ if ( javaScriptFunction != null ) {
+ // JavaScript
+ EnvJavaScript jsEnv = EnvJavaScript.get();
+ // FunctionJavaScript : functions only : no special forms.
+ function = new FunctionJavaScript(javaScriptFunction, jsEnv);
+ try {
+ return function.exec(binding, args, getFunctionIRI(), env);
+ } catch (ExprUndefFunction ex) {
+ // Or check if there is a Java function registered - allows java replacement.
+ if ( WarnOnUnknownFunction )
+ ARQ.getExecLogger().warn("JavaScript function not found: "+ex.getFunctionName());
+ throw ex;
+ }
+ }
+
// Only needed because some tests call straight in.
// Otherwise, the buildFunction() calls should have done everything
if ( !functionBound )
@@ -72,23 +93,28 @@ public class E_Function extends ExprFunctionN
}
public void buildFunction(Context cxt) {
+ if ( javaScriptFunction != null )
+ return;
try { bindFunction(cxt) ; }
- catch (ExprException ex) {
+ catch (ExprUndefFunction ex) {
if ( WarnOnUnknownFunction )
ARQ.getExecLogger().warn("URI <"+functionIRI+"> has no registered function factory") ;
}
}
+ private FunctionFactory functionFactory(Context cxt) {
+ FunctionRegistry registry = chooseRegistry(cxt) ;
+ FunctionFactory ff = registry.get(functionIRI) ;
+ return ff;
+ }
+
private void bindFunction(Context cxt) {
if ( functionBound )
return ;
-
- FunctionRegistry registry = chooseRegistry(cxt) ;
- FunctionFactory ff = registry.get(functionIRI) ;
-
+ FunctionFactory ff = functionFactory(cxt);
if ( ff == null ) {
functionBound = true ;
- throw new ExprEvalException("URI <" + functionIRI + "> not found as a function") ;
+ throw new ExprUndefFunction("URI <" + functionIRI + "> not found as a function", functionIRI) ;
}
function = ff.create(functionIRI) ;
function.build(functionIRI, args) ;
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprUndefFunction.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprUndefFunction.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprUndefFunction.java
new file mode 100644
index 0000000..64467d1
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprUndefFunction.java
@@ -0,0 +1,32 @@
+/*
+ * 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.expr;
+
+/** Exception for an undefined function. */
+
+public class ExprUndefFunction extends ExprEvalException
+{
+ private final String fnName;
+
+ public ExprUndefFunction(String msg, String fnName) { super(msg) ; this.fnName = fnName;}
+
+ public String getFunctionName() {
+ return fnName;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/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 53177ff..4139f53 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
@@ -60,7 +60,7 @@ public abstract class FunctionBase implements Function {
try {
this.functionEnv = env ;
this.binding = binding;
- NodeValue nv = exec(evalArgs) ;
+ NodeValue nv = exec(evalArgs) ;
return nv ;
} finally {
this.functionEnv = null ;
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
new file mode 100644
index 0000000..f18d236
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
@@ -0,0 +1,134 @@
+/*
+ * 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.js;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Paths;
+
+import javax.script.*;
+
+import org.apache.jena.atlas.io.IO;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.riot.RiotNotFoundException;
+import org.apache.jena.sparql.ARQConstants;
+import org.apache.jena.sparql.ARQException;
+import org.apache.jena.sparql.SystemARQ;
+import org.apache.jena.sparql.sse.builders.ExprBuildException;
+import org.apache.jena.sparql.util.Context;
+import org.apache.jena.sparql.util.Symbol;
+
+/** Environment for executing a JavaScript function.
+ * <p>
+ * Functions are loaded from the file named in context setting
+ * {@link EnvJavaScript#symJavaScriptLibFile}.
+ * <p>
+ * Function are loaded from a string value in context setting
+ * {@link EnvJavaScript#symJavaScriptLib}.
+ * <p>
+ * If both are present, the file named by {@code EnvJavaScript.symJavaScriptLibFile} is loaded
+ * then the string from {@code EnvJavaScript.symJavaScriptLib}.
+ */
+public class EnvJavaScript {
+ /** JavaScript as a string value which is evaluated */
+ public static Symbol symJavaScriptLib = SystemARQ.allocSymbol("js-functions");
+ /** JavaScript library in a file as named */
+ public static Symbol symJavaScriptLibFile = SystemARQ.allocSymbol("js-library");
+
+ private final String scriptLib;
+ private final String scriptLibFile;
+
+ private final ScriptEngine scriptEngine;
+ private CompiledScript compiledScript;
+
+ private final Invocable invoc;
+
+ public static EnvJavaScript create(Context context) {
+ return new EnvJavaScript(context);
+ }
+
+ private static EnvJavaScript global = null;
+
+ /** Return */
+ public static EnvJavaScript get() {
+ if ( global == null ) {
+ Context context = ARQ.getContext();
+ if ( context.isDefined(symJavaScriptLib) || context.isDefined(symJavaScriptLibFile) )
+ global = create(ARQ.getContext());
+ }
+ return global ;
+ }
+
+ /** Reset the global EnvJavaScript */
+ public static void reset() {
+ reset(ARQ.getContext());
+ }
+
+ /** Reset the global EnvJavaScript */
+ public static void reset(Context context) {
+ global = create(context);
+ }
+
+ private EnvJavaScript(Context context) {
+ this.scriptLib = context.getAsString(symJavaScriptLib);
+ this.scriptLibFile = context.getAsString(symJavaScriptLibFile);
+ if ( this.scriptLib == null && this.scriptLibFile == null )
+ throw new ARQException("Both script string and script filename are null");
+ ScriptEngineManager manager = new ScriptEngineManager();
+ scriptEngine = manager.getEngineByName("nashorn");
+ // Add function to script engine.
+ invoc = (Invocable)scriptEngine;
+ if ( scriptLibFile != null ) {
+ try {
+ Reader reader = Files.newBufferedReader(Paths.get(scriptLibFile), StandardCharsets.UTF_8);
+ Object x = scriptEngine.eval(reader);
+ }
+ catch (NoSuchFileException | FileNotFoundException ex) {
+ throw new RiotNotFoundException("File: "+scriptLibFile);
+ }
+ catch (IOException ex) { IO.exception(ex); }
+ catch (ScriptException e) {
+ throw new ExprBuildException("Failed to load Javascript", e);
+ }
+ }
+ if ( scriptLib != null ) {
+ try {
+ Object x = scriptEngine.eval(scriptLib);
+ }
+ catch (ScriptException e) {
+ throw new ExprBuildException("Failed to load Javascript", e);
+ }
+ }
+ // Try to call the init function - ignore NoSuchMethodException
+ try {
+ invoc.invokeFunction(ARQConstants.JavaScriptInitFunction);
+ } catch (NoSuchMethodException ex) {}
+ catch (ScriptException ex) {
+ throw new ARQException("Failed to call JavaScript initialization function", ex);
+ }
+ }
+
+ public Invocable invoc() {
+ return invoc;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
new file mode 100644
index 0000000..4cb1cc0
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
@@ -0,0 +1,93 @@
+/*
+ * 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.js;
+import java.util.List;
+
+import javax.script.ScriptException;
+
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.ExprList;
+import org.apache.jena.sparql.expr.ExprUndefFunction;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.function.FunctionBase;
+
+/**
+ * Javascript implemented SPARQL custom functions for ARQ. The JavaScript function is
+ * called with arguments which are mapped so that XSD strings, numbers and booleans become the
+ * equivalent native JavaScript object, and anything else becomes a {@link NV}, a
+ * JavaScript object providing access to the RDF features such as datatype.
+ * {@link NV#toString} returns a string so a function working with URIs can treat URIs as
+ * strings which is natural in JavaScript and aligned to
+ * <a href="https://github.com/rdfjs/representation-task-force/"
+ * >rdfjs/representation-task-force</a>.
+ * <p>
+ * Functions are executed in {@link EnvJavaScript}. There is a global
+ * {@link EnvJavaScript} and it can also be set specifically for a query execution.
+ * See {@link EnvJavaScript} for details of configuration.
+ * <p>
+ * Note: there is an
+ * attempt to reconstruct the datatype of the result of the function into
+ * {@code xsd:integer} and {@code xsd:double}.
+ * <p>
+ * Functions that return null or undefined will resutl in a {@link ExprEvalException}.
+ *
+ * @see EnvJavaScript
+ * @see NV
+ */
+public class FunctionJavaScript extends FunctionBase {
+
+ private final EnvJavaScript envJS;
+ private final String functionName;
+
+ private boolean initialized = false;
+
+ public FunctionJavaScript(String functionName, EnvJavaScript env) {
+ this.functionName = functionName;
+ this.envJS = env;
+ }
+
+ @Override
+ public void checkBuild(String uri, ExprList args) {}
+
+ @Override
+ public NodeValue exec(List<NodeValue> args) {
+ if ( ! initialized )
+ checkBuild(null, null);
+
+ try {
+ // Convert NodeValues to types more akin to Javascript.
+ // Pass strings as string, and numbers as Number.
+ Object[] a = new Object[args.size()];
+ for ( int i = 0 ; i < args.size(); i++ )
+ a[i] = NV.fromNodeValue(args.get(i));
+ Object r = envJS.invoc().invokeFunction(functionName, a);
+ if ( r == null )
+ // Or string "undefined".
+ throw new ExprEvalException(functionName);
+ NodeValue nv = NV.toNodeValue(r);
+ return nv;
+ }
+ catch (NoSuchMethodException ex) {
+ throw new ExprUndefFunction("No such JavaScript function '"+functionName+"'", functionName);
+ }
+ catch (ScriptException e) {
+ throw new ExprEvalException("Failed to evaluate javascript function '"+functionName+"'", e);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java
new file mode 100644
index 0000000..9ea418b
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java
@@ -0,0 +1,217 @@
+/*
+ * 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.js;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URI;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+
+/**
+ * General representation of an {@link NodeValue} for JavaScript. Conversion is to native
+ * types where possible, otherwise {@code NV}. {@code NV.toString} of a URI returns the
+ * uri as a string so {@code NV} works naturally in Java/Nashorn.
+ *
+ * @see #fromNodeValue
+ * @see #toNodeValue
+ */
+
+public class NV implements RDFJS {
+ // Six data types that are primitives in JavaScript:
+ // Boolean
+ // Null
+ // Undefined
+ // Number
+ // String
+ // Symbol (new in ECMAScript 6; not in Nashorn/Java8).
+ // and Object
+
+ private NodeValue nv;
+ /**
+ * Enable restoring integer from doubles.
+ */
+ private final static boolean narrowDoubles = true;
+ /**
+ * Map an ARQ {@link NodeValue} to java/Nashorn representation of a JavaScript object.
+ * Native JaavScript types supported are null, string, number and boolean.
+ * Otherwise a {@link NV} is returned.
+ */
+ public static Object fromNodeValue(NodeValue nv) {
+ if ( nv == null )
+ return null;
+ if ( nv.isString() )
+ return nv.getString();
+ if ( nv.isNumber() ) {
+ if ( nv.isInteger())
+ return nv.getInteger();
+ if ( nv.isDecimal() )
+ return nv.getDecimal();
+ if ( nv.isDouble() )
+ return nv.getDouble();
+ }
+ if ( nv.isBoolean() )
+ return nv.getBoolean();
+ return new NV(nv);
+ }
+ /**
+ * Map a java/Nashorn representation of a JavaScript object to an ARQ
+ * {@link NodeValue}. Identified types are null, string, number and boolean and also
+ * {@code NV} returned by the JavaScript code.
+ */
+ public static NodeValue toNodeValue(Object r) {
+ if ( r == null )
+ return null;
+ if ( r instanceof NV )
+ return ((NV)r).nv();
+ if ( r instanceof NodeValue )
+ return (NodeValue)r;
+ // May not be a String. String.toString is very efficient!
+ // https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/javascript.html#A1147390
+ if ( r instanceof CharSequence )
+ return NodeValue.makeString(((CharSequence)r).toString());
+ if ( r instanceof Number )
+ return number2value((Number)r);
+ if ( r instanceof Boolean ) {
+ return NodeValue.makeBoolean((Boolean)r);
+ }
+ if ( r instanceof URI ) {
+ Node n = NodeFactory.createURI(((URI)r).toString());
+ return NodeValue.makeNode(n);
+ }
+ throw new ExprEvalException("Can't convert '"+r+"' to a NodeValue. r is of class "+r.getClass().getName());
+ }
+
+ // Convert the numeric values that Nashorn can return.
+ static NodeValue number2value(Number r) {
+ if ( r instanceof Integer )
+ return NodeValue.makeInteger((Integer)r);
+ if ( r instanceof Long )
+ return NodeValue.makeInteger((Long)r);
+ if ( r instanceof Double ) {
+ double d = (Double)r;
+ if ( narrowDoubles && Double.isFinite(d)) {
+ try {
+ long x = (long)d;
+ // Check for loss in conversion
+ if ( x == d )
+ return NodeValue.makeInteger(x);
+ // If is a very large integer (larger than long)?
+ if ( d == Math.floor(d) && Double.isFinite(d) ) {
+ BigInteger big = new BigInteger(Double.toString(x));
+ return NodeValue.makeInteger(big);
+ }
+ if ( false ) {
+ // This always works but it is confusing decimal and double.
+ // Leave doubles as doubles.
+ return NodeValue.makeDecimal(d);
+ }
+ } catch (NumberFormatException ex) {
+ // Shouldn't happen.
+ throw new ExprEvalException("Bad number format", ex);
+ }
+ }
+ return NodeValue.makeDouble(d);
+ }
+ // These are carried NV.value returns.
+ if ( r instanceof BigInteger )
+ return NodeValue.makeInteger((BigInteger)r);
+ if ( r instanceof BigDecimal )
+ return NodeValue.makeDecimal((BigDecimal)r);
+
+ throw new ExprEvalException("Unknown return type for number: "+r);
+ }
+
+ // ------ Object NV
+
+ // Javascript names and RDF extras.
+ public boolean isURI() { return nv.isIRI(); }
+ public boolean isBlank() { return nv.isBlank(); }
+ public boolean isNumber() { return nv.isNumber(); }
+ public boolean isLiteral() { return nv.isLiteral(); }
+
+ // -- rdfjs
+ @Override
+ public String getTermType() {
+ if ( isURI() )
+ return "NamedNode";
+ if ( isBlank() )
+ return "BlankNode";
+ if ( isLiteral() )
+ return "Literal";
+ return null;
+ }
+
+ @Override
+ public String getValue() {
+ if ( isURI() )
+ return getUri();
+ if ( isBlank() )
+ return getLabel();
+ if ( isLiteral() )
+ return getLex();
+ return null;
+ }
+ // -- rdfjs
+
+ public String getLabel() { return nv.asNode().getBlankNodeLabel(); }
+ public String getDT() { return nv.getDatatypeURI(); }
+ public String getDatatype() { return nv.getDatatypeURI(); }
+ public String getLanguage() { return nv.getLang(); }
+ public String getLang() { return nv.getLang().toLowerCase(); }
+ public String getLex() { return nv.getNode().getLiteralLexicalForm(); }
+ public String getUri() { return nv.getNode().getURI(); }
+
+ public NV(NodeValue nv) {
+ this.nv = nv;
+ }
+ public NodeValue nv() {
+ return nv;
+ }
+
+ @Override
+ public String toString() { return nv.asUnquotedString(); }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((nv == null) ? 0 : nv.hashCode());
+ return result;
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if ( this == obj )
+ return true;
+ if ( obj == null )
+ return false;
+ if ( getClass() != obj.getClass() )
+ return false;
+ NV other = (NV)obj;
+ if ( nv == null ) {
+ if ( other.nv != null )
+ return false;
+ } else if ( !nv.equals(other.nv) )
+ return false;
+ return true;
+ }
+ }
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/main/java/org/apache/jena/sparql/function/js/RDFJS.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/RDFJS.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/RDFJS.java
new file mode 100644
index 0000000..a4fdf92
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/RDFJS.java
@@ -0,0 +1,35 @@
+/*
+ * 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.js;
+
+/**
+ * The <a href="https://github.com/rdfjs/representation-task-force/">rdfjs/representation-task-force</a>
+ * API for RDF terms.
+ *
+ */
+
+
+public interface RDFJS {
+ public String getTermType();
+ public String getValue();
+
+// public interface RDFJS_NamedNode {}
+// public interface RDFJS_BlankNode {}
+// public interface RDFJS_Literal {}
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/test/java/org/apache/jena/riot/langsuite/FactoryTestRiot.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/riot/langsuite/FactoryTestRiot.java b/jena-arq/src/test/java/org/apache/jena/riot/langsuite/FactoryTestRiot.java
index 5e8bb2c..3c946ec 100644
--- a/jena-arq/src/test/java/org/apache/jena/riot/langsuite/FactoryTestRiot.java
+++ b/jena-arq/src/test/java/org/apache/jena/riot/langsuite/FactoryTestRiot.java
@@ -150,5 +150,4 @@ public class FactoryTestRiot extends TestFactoryManifest
baseIRI = baseIRI+x ;
return baseIRI ;
}
-
}
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/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 e4542bc..594ba60 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.js.TS_FunctionJS;
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 ;
@@ -60,6 +61,7 @@ import org.junit.runners.Suite ;
, TS_Expr.class
, TS_LibraryFunctions.class
, TS_UserFunctions.class
+ , TS_FunctionJS.class
, TS_PFunction.class
, TS_ResultSet.class
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java
new file mode 100644
index 0000000..59e8b48
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.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.js;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses( {
+ TestNV.class
+ , TestJavaScriptFunctions.class
+ , TestSPARQL_JS.class
+})
+public class TS_FunctionJS {}
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java
new file mode 100644
index 0000000..7f456ff
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java
@@ -0,0 +1,198 @@
+/*
+ * 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.js;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+
+import org.apache.jena.atlas.lib.StrUtils;
+import org.apache.jena.datatypes.xsd.XSDDatatype;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.sse.SSE;
+import org.apache.jena.sparql.util.Context;
+import org.junit.Test;
+
+public class TestJavaScriptFunctions {
+ static final String JS_LIB_FILE = "testing/ARQ/JS/test-library.js";
+
+ static final String CamelCaseJS =
+ StrUtils.strjoinNL
+ ("function toCamelCase(str) { return str.split(' ').map(cc).join('');}"
+ ,"function ucFirst(word) { return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();}"
+ ,"function lcFirst(word) { return word.toLowerCase(); }"
+ ,"function cc(word,index) { return (index == 0) ? lcFirst(word) : ucFirst(word); }"
+ );
+
+ private static EnvJavaScript setupJS() {
+ Context cxt = ARQ.getContext().copy();
+ cxt.set(EnvJavaScript.symJavaScriptLib, CamelCaseJS);
+ cxt.set(EnvJavaScript.symJavaScriptLibFile, JS_LIB_FILE);
+ return EnvJavaScript.create(cxt);
+ }
+
+ private EnvJavaScript envJS = setupJS();
+
+ @Test public void js_dt_boolean() {
+ NodeValue nv = eval("rtnBoolean");
+ assertTrue(nv.isBoolean());
+ }
+
+ @Test public void js_dt_string() {
+ NodeValue nv = eval("rtnString");
+ assertTrue(nv.isString());
+ }
+
+ //Note: NodeValue isDouble etc means "can be used as" so NodeValue integer isDouble
+ // Use assertDatatype.
+
+ @Test public void js_dt_integer() {
+ NodeValue nv = eval("rtnInteger");
+ assertDatatype(nv, XSDDatatype.XSDinteger);
+ }
+
+ @Test public void js_dt_double() {
+ NodeValue nv = eval("rtnDouble");
+ assertDatatype(nv, XSDDatatype.XSDdouble);
+ }
+
+ @Test(expected=ExprEvalException.class)
+ public void js_dt_undef() {
+ NodeValue nv = eval("rtnUndef");
+ }
+
+ @Test(expected=ExprEvalException.class)
+ public void js_dt_null() {
+ NodeValue nv = eval("rtnNull");
+ }
+
+// @Test public void js_dt_symbol() {
+// NodeValue nv = eval("rtnSymbol");
+// }
+
+ @Test public void js_dt_1() {
+ NodeValue nv = eval("identity", "'2018-01-06T17:56:41.293+00:00'^^xsd:dateTime");
+ }
+
+ @Test public void js_1() {
+ NodeValue nv = eval("identity", "1");
+ assertNotNull(nv);
+ }
+
+ @Test public void js_2() {
+ NodeValue nv = eval("value", "<http://example/xyz>");
+ assertNotNull(nv);
+ assertTrue(nv.isString());
+ }
+
+ //combine dynamics.
+
+ @Test public void js_3() {
+ NodeValue nv = eval("combine", "1", "2");
+ NodeValue nvx = nv("3");
+ assertTrue(nv.isNumber());
+ assertDatatype(nv, XSDDatatype.XSDinteger);
+ assertEquals(nvx, nv);
+ }
+
+ @Test public void js_4() {
+ NodeValue nv = eval("combine", "'a'", "2");
+ NodeValue nvx = nv("'a2'");
+ assertTrue(nv.isString());
+ assertEquals(nvx, nv);
+ }
+
+ @Test public void js_5() {
+ NodeValue nv = eval("combine", "2", "'a'");
+ NodeValue nvx = nv("'2a'");
+ assertTrue(nv.isString());
+ assertEquals(nvx, nv);
+ }
+
+ @Test public void js_6() {
+ NodeValue nv = eval("combine", "2", "2.5");
+ NodeValue nvx = nv("4.5e0");
+ assertDatatype(nv, XSDDatatype.XSDdouble);
+ assertEquals(nvx, nv);
+ }
+
+ // Narrow to integer
+ @Test public void js_7() {
+ NodeValue nv = eval("combine", "2.5", "3.5");
+ NodeValue nvx = nv("6");
+ assertDatatype(nv, XSDDatatype.XSDinteger);
+ assertEquals(nvx, nv);
+ }
+
+ @Test public void js_8() {
+ NodeValue nv = eval("toCamelCase", "'abc def ghi'");
+ NodeValue nvx = nv("'abcDefGhi'");
+ assertDatatype(nv, XSDDatatype.XSDstring);
+ assertEquals(nvx, nv);
+ }
+
+ private void assertDatatype(NodeValue nv, XSDDatatype xsdDatatype) {
+ assertEquals(nv.asNode().getLiteralDatatype(), xsdDatatype);
+ }
+
+ @Test(expected=ExprEvalException.class)
+ public void js_err_1() {
+ NodeValue nv = eval("no_such_function()");
+ }
+
+ // Wrong number of argument is OK in JavaScript- except "null" return becomes ExprEvalException.
+ @Test(expected=ExprEvalException.class)
+ public void js_err_2() {
+ NodeValue nv = eval("identity");
+ }
+
+ @Test
+ public void js_err_3() {
+ // Legal.
+ NodeValue nv = eval("combine", "'a'");
+ assertNotNull(nv);
+ }
+
+ @Test
+ public void js_err_4() {
+ // Legal.
+ NodeValue nv = eval("identity", "3", "2");
+ NodeValue nvx = nv("3");
+ assertEquals(nvx, nv);
+ }
+
+
+ private NodeValue eval(String fn, String ...args) {
+ NodeValue[] nvs = new NodeValue[args.length];
+ for ( int i = 0 ; i < args.length ; i++ ) {
+ nvs[i] = nv(args[i]);
+ }
+ FunctionJavaScript x = new FunctionJavaScript(fn, envJS);
+ NodeValue nvr = x.exec(Arrays.asList(nvs));
+ return nvr;
+ }
+
+ private static NodeValue nv(String str) {
+ return NodeValue.makeNode(SSE.parseNode(str));
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestNV.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestNV.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestNV.java
new file mode 100644
index 0000000..728172d
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestNV.java
@@ -0,0 +1,71 @@
+/*
+ * 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.js;
+
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.jena.sparql.sse.SSE;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class TestNV {
+
+ @Test public void nv_1() { test("'abc'"); }
+ @Test public void nv_2() { test("true"); }
+ @Test public void nv_3() { test("123"); }
+ @Test public void nv_4() { test("123.5"); }
+
+ // No conversion to JS - becomes an NV.
+ @Test public void nv_5() { test("'2018-01-06T17:56:41.293+00:00'^^xsd:dateTime"); }
+ @Test public void nv_6() { test("<http://jena/apche.org/>"); }
+ @Test public void nv_7() { test("_:abc123"); }
+
+ @Test public void nv_10() {
+ NodeValue nodeValue = nv("'abc'");
+ NV nv = new NV(nodeValue);
+ assertEquals("abc", nv.getLex());
+ assertEquals("abc", nv.getValue());
+ assertEquals("Literal", nv.getTermType());
+ }
+
+ @Test public void nv_12() {
+ NodeValue nodeValue = nv("<http://jena/apche.org/>");
+ NV nv = new NV(nodeValue);
+ assertEquals("http://jena/apche.org/", nv.getUri());
+ assertEquals("http://jena/apche.org/", nv.getValue());
+ assertEquals("NamedNode", nv.getTermType());
+ }
+
+ @Test public void nv_13() {
+ NodeValue nodeValue = nv("_:a");
+ NV nv = new NV(nodeValue);
+ assertEquals(nv.getLabel(), nv.getValue());
+ assertEquals("BlankNode", nv.getTermType());
+ }
+
+ private void test(String str) {
+ NodeValue nv = nv(str);
+ Object x = NV.fromNodeValue(nv);
+ NodeValue nv2 = NV.toNodeValue(x);
+ assertEquals(nv,nv2);
+ }
+
+ private static NodeValue nv(String str) {
+ return NodeValue.makeNode(SSE.parseNode(str));
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java
new file mode 100644
index 0000000..309e367
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.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.js;
+
+import junit.framework.TestSuite;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.junit.ScriptTestSuiteFactory;
+import org.apache.jena.sparql.util.Context;
+import org.apache.jena.system.JenaSystem;
+import org.junit.runner.RunWith;
+import org.junit.runners.AllTests;
+
+@RunWith(AllTests.class)
+public class TestSPARQL_JS {
+ static final String MANIFEST = "testing/ARQ/JS/manifest.ttl";
+ static final String JS_LIB_FILE = "testing/ARQ/JS/test-library.js";
+
+ private static void setupJS() {
+ Context cxt = ARQ.getContext();
+ cxt.set(EnvJavaScript.symJavaScriptLibFile, JS_LIB_FILE);
+ cxt.set(EnvJavaScript.symJavaScriptLib, "function inc(x) { return x+1 }");
+ EnvJavaScript.reset();
+ }
+
+ static public TestSuite suite() {
+ JenaSystem.init();
+ setupJS();
+ TestSuite ts = new TestSuite(TestSPARQL_JS.class.getName());
+ TestSuite ts2 = ScriptTestSuiteFactory.make(MANIFEST);
+ ts.addTest(ts2);
+ return ts;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/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
index 132b313..7d0830f 100644
--- 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
@@ -24,7 +24,7 @@ import org.junit.runners.Suite.SuiteClasses ;
@RunWith(Suite.class)
@SuiteClasses( {
- // A lot of the test ate in TestFunctions and TestFunctions2.
+ // A lot of the test are in TestFunctions and TestFunctions2.
TestFunctionCollation.class
, TestFnFunctions.class
})
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/data.ttl
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/data.ttl b/jena-arq/testing/ARQ/JS/data.ttl
new file mode 100644
index 0000000..12303ce
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/data.ttl
@@ -0,0 +1,5 @@
+PREFIX : <http://example/>
+
+:s1 :p1 1 .
+:s2 :p2 "one" .
+
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-1.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-1.rq b/jena-arq/testing/ARQ/JS/js-query-1.rq
new file mode 100644
index 0000000..ad90720
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-1.rq
@@ -0,0 +1,4 @@
+PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX : <http://example/>
+
+SELECT (js:identity(8) AS ?X) {}
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-1.srj
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-1.srj b/jena-arq/testing/ARQ/JS/js-query-1.srj
new file mode 100644
index 0000000..04b98bc
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-1.srj
@@ -0,0 +1,12 @@
+{
+ "head": {
+ "vars": [ "X" ]
+ } ,
+ "results": {
+ "bindings": [
+ {
+ "X": { "type": "literal" , "datatype": "http://www.w3.org/2001/XMLSchema#integer" , "value": "8" }
+ }
+ ]
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-2.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-2.rq b/jena-arq/testing/ARQ/JS/js-query-2.rq
new file mode 100644
index 0000000..bf5cc35
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-2.rq
@@ -0,0 +1,4 @@
+PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX : <http://example/>
+
+SELECT (js:inc(8) AS ?X) {}
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-2.srj
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-2.srj b/jena-arq/testing/ARQ/JS/js-query-2.srj
new file mode 100644
index 0000000..54e9a3e
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-2.srj
@@ -0,0 +1,12 @@
+{
+ "head": {
+ "vars": [ "X" ]
+ } ,
+ "results": {
+ "bindings": [
+ {
+ "X": { "type": "literal" , "datatype": "http://www.w3.org/2001/XMLSchema#integer" , "value": "9" }
+ }
+ ]
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-3.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-3.rq b/jena-arq/testing/ARQ/JS/js-query-3.rq
new file mode 100644
index 0000000..55bf329
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-3.rq
@@ -0,0 +1,4 @@
+PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX : <http://example/>
+
+SELECT (js:inc(js:inc(8)) AS ?X) {}
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-3.srj
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-3.srj b/jena-arq/testing/ARQ/JS/js-query-3.srj
new file mode 100644
index 0000000..19f24bd
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-3.srj
@@ -0,0 +1,12 @@
+{
+ "head": {
+ "vars": [ "X" ]
+ } ,
+ "results": {
+ "bindings": [
+ {
+ "X": { "type": "literal" , "datatype": "http://www.w3.org/2001/XMLSchema#integer" , "value": "10" }
+ }
+ ]
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-4.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-4.rq b/jena-arq/testing/ARQ/JS/js-query-4.rq
new file mode 100644
index 0000000..6e466c3
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-4.rq
@@ -0,0 +1,9 @@
+PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX : <http://example/>
+
+SELECT * {
+ ?s ?p ?o
+ FILTER(js:identity(?s) = ?s)
+ FILTER(js:identity(?p) = :p1 )
+ FILTER(sameTerm(js:identity(?o),1) )
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/js-query-4.srj
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-4.srj b/jena-arq/testing/ARQ/JS/js-query-4.srj
new file mode 100644
index 0000000..be95897
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-4.srj
@@ -0,0 +1,14 @@
+{
+ "head": {
+ "vars": [ "s" , "p" , "o" ]
+ } ,
+ "results": {
+ "bindings": [
+ {
+ "s": { "type": "uri" , "value": "http://example/s1" } ,
+ "p": { "type": "uri" , "value": "http://example/p1" } ,
+ "o": { "type": "literal" , "datatype": "http://www.w3.org/2001/XMLSchema#integer" , "value": "1" }
+ }
+ ]
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/manifest.ttl
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/manifest.ttl b/jena-arq/testing/ARQ/JS/manifest.ttl
new file mode 100644
index 0000000..7dd64ae
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/manifest.ttl
@@ -0,0 +1,50 @@
+# 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.
+
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
+@prefix qt: <http://www.w3.org/2001/sw/DataAccess/tests/test-query#> .
+
+<> rdf:type mf:Manifest ;
+ rdfs:label "JavaScript function" ;
+ rdfs:comment "JavaScript function" ;
+ mf:entries
+ (
+ [ mf:name "JS Call: file-defined function" ;
+ mf:action
+ [ qt:query <js-query-1.rq> ;
+ qt:data <data.ttl> ] ;
+ mf:result <js-query-1.srj>
+ ]
+ [ mf:name "JS Call: string-defined function" ;
+ mf:action
+ [ qt:query <js-query-2.rq> ;
+ qt:data <data.ttl> ] ;
+ mf:result <js-query-2.srj>
+ ]
+ [ mf:name "JS Call: nested functions" ;
+ mf:action
+ [ qt:query <js-query-3.rq> ;
+ qt:data <data.ttl> ] ;
+ mf:result <js-query-3.srj>
+ ]
+ [ mf:name "JS Call: FILTER functions" ;
+ mf:action
+ [ qt:query <js-query-4.rq> ;
+ qt:data <data.ttl> ] ;
+ mf:result <js-query-4.srj>
+ ]
+ ).
http://git-wip-us.apache.org/repos/asf/jena/blob/c4f79ee9/jena-arq/testing/ARQ/JS/test-library.js
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/test-library.js b/jena-arq/testing/ARQ/JS/test-library.js
new file mode 100644
index 0000000..60d5fad
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/test-library.js
@@ -0,0 +1,14 @@
+var barx = '||';
+function bar(x,y) { return barx+combine(x,y)+barx }
+function value(n) { return n.value ; }
+function combine(x,y) { return x+y }
+function identity(x) { return x }
+
+// The JavaScript types (number split).
+function rtnBoolean() { return true; }
+function rtnString() { return "foo"; }
+function rtnInteger() { return 57 }
+function rtnDouble() { return 5.7 }
+function rtnUndef() { return undefined }
+function rtnNull() { return null; }
+//function rtnSymbol() { return Symbol('foo') }
[2/8] jena git commit: JENA-1461: Nashorn and threading
Posted by an...@apache.org.
JENA-1461: Nashorn and threading
Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/a0108d77
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/a0108d77
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/a0108d77
Branch: refs/heads/master
Commit: a0108d77c46616e65285f410a2f0f6cdab842c4f
Parents: c4f79ee
Author: Andy Seaborne <an...@apache.org>
Authored: Wed Jan 10 11:21:15 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Wed Jan 10 11:58:26 2018 +0000
----------------------------------------------------------------------
.../jena/sparql/function/js/EnvJavaScript.java | 132 ++++++++++---------
.../sparql/function/js/FunctionJavaScript.java | 7 +-
.../jena/sparql/function/js/JSEngine.java | 109 +++++++++++++++
jena-arq/testing/ARQ/JS/data.ttl | 2 +-
4 files changed, 182 insertions(+), 68 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jena/blob/a0108d77/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
index f18d236..167848f 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
@@ -18,23 +18,13 @@
package org.apache.jena.sparql.function.js;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.Reader;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.Paths;
+import javax.script.ScriptException;
-import javax.script.*;
-
-import org.apache.jena.atlas.io.IO;
+import org.apache.jena.atlas.lib.Pool;
+import org.apache.jena.atlas.lib.PoolBase;
+import org.apache.jena.atlas.lib.PoolSync;
import org.apache.jena.query.ARQ;
-import org.apache.jena.riot.RiotNotFoundException;
-import org.apache.jena.sparql.ARQConstants;
-import org.apache.jena.sparql.ARQException;
import org.apache.jena.sparql.SystemARQ;
-import org.apache.jena.sparql.sse.builders.ExprBuildException;
import org.apache.jena.sparql.util.Context;
import org.apache.jena.sparql.util.Symbol;
@@ -50,85 +40,99 @@ import org.apache.jena.sparql.util.Symbol;
* then the string from {@code EnvJavaScript.symJavaScriptLib}.
*/
public class EnvJavaScript {
- /** JavaScript as a string value which is evaluated */
+ /** JavaScript functions as a string value which is evaluated. */
public static Symbol symJavaScriptLib = SystemARQ.allocSymbol("js-functions");
- /** JavaScript library in a file as named */
+ /** JavaScript library of functions in a file. */
public static Symbol symJavaScriptLibFile = SystemARQ.allocSymbol("js-library");
private final String scriptLib;
private final String scriptLibFile;
- private final ScriptEngine scriptEngine;
- private CompiledScript compiledScript;
-
- private final Invocable invoc;
-
public static EnvJavaScript create(Context context) {
return new EnvJavaScript(context);
}
private static EnvJavaScript global = null;
- /** Return */
- public static EnvJavaScript get() {
+ /**
+ * Return the global {@code EnvJavaScript}.
+ * Returns null if no JavaScript has been provided.
+ */
+ public static EnvJavaScript get() {
if ( global == null ) {
- Context context = ARQ.getContext();
- if ( context.isDefined(symJavaScriptLib) || context.isDefined(symJavaScriptLibFile) )
- global = create(ARQ.getContext());
+ synchronized(EnvJavaScript.class) {
+ Context context = ARQ.getContext();
+ if ( context.isDefined(symJavaScriptLib) || context.isDefined(symJavaScriptLibFile) )
+ global = create(ARQ.getContext());
+ }
}
return global ;
}
-
- /** Reset the global EnvJavaScript */
+
+ /** Reset the global {@code EnvJavaScript} based on the system-wide context */
public static void reset() {
reset(ARQ.getContext());
}
- /** Reset the global EnvJavaScript */
+ /** Reset the global {@code EnvJavaScript} */
public static void reset(Context context) {
global = create(context);
}
+ // ---- EnvJavaScript Object
+
+ // One script engine per thread, here done by one per usage.
+ // Nashorn script engines are thread safe but the script Bindings} must not be shared.
+ // Direct use of Nashorn, via the protected APIs of jdk.nashorn.api.scripting
+ // are needed to utilize this and having one compiled form and many execution units.
+ // But in Java8 is saving up problems for Java9 and the
+ // Nashorn subsystem is imporved at Java9 for this use case.
+ // For now, in combination with the implementation of JSEngine,
+ // we keep separate Nashorn script engines.
+
+ private Pool<JSEngine> pool = PoolSync.create(new PoolBase<JSEngine>());
+
private EnvJavaScript(Context context) {
this.scriptLib = context.getAsString(symJavaScriptLib);
this.scriptLibFile = context.getAsString(symJavaScriptLibFile);
- if ( this.scriptLib == null && this.scriptLibFile == null )
- throw new ARQException("Both script string and script filename are null");
- ScriptEngineManager manager = new ScriptEngineManager();
- scriptEngine = manager.getEngineByName("nashorn");
- // Add function to script engine.
- invoc = (Invocable)scriptEngine;
- if ( scriptLibFile != null ) {
- try {
- Reader reader = Files.newBufferedReader(Paths.get(scriptLibFile), StandardCharsets.UTF_8);
- Object x = scriptEngine.eval(reader);
- }
- catch (NoSuchFileException | FileNotFoundException ex) {
- throw new RiotNotFoundException("File: "+scriptLibFile);
- }
- catch (IOException ex) { IO.exception(ex); }
- catch (ScriptException e) {
- throw new ExprBuildException("Failed to load Javascript", e);
- }
- }
- if ( scriptLib != null ) {
- try {
- Object x = scriptEngine.eval(scriptLib);
- }
- catch (ScriptException e) {
- throw new ExprBuildException("Failed to load Javascript", e);
- }
- }
- // Try to call the init function - ignore NoSuchMethodException
+ // Put one in the pool.
+ pool.put(build());
+ }
+
+ private JSEngine build() {
+ return new JSEngine(scriptLib, scriptLibFile);
+ }
+
+ private JSEngine getEngine() {
+ JSEngine engine = pool.get();
+ if ( engine == null )
+ // Which will go into the pool when finished with.
+ engine = new JSEngine(scriptLib, scriptLibFile);
+ return engine;
+ }
+
+ private EnvJavaScript(String functions, String functionLibFile) {
+ this.scriptLib = functions;
+ this.scriptLibFile = functionLibFile;
+ }
+
+ public Object call(String functionName, Object[] args) throws NoSuchMethodException, ScriptException {
+ JSEngine engine = getEngine();
try {
- invoc.invokeFunction(ARQConstants.JavaScriptInitFunction);
- } catch (NoSuchMethodException ex) {}
- catch (ScriptException ex) {
- throw new ARQException("Failed to call JavaScript initialization function", ex);
+ return engine.call(functionName, args);
+ } finally {
+ pool.put(engine);
}
}
- public Invocable invoc() {
- return invoc;
- }
+// ---- ThreadLocal version,
+// private ThreadLocal<JSEngine> invocable = ThreadLocal.withInitial(()->build());
+//
+// private JSEngine build() {
+// return new JSEngine(scriptLib, scriptLibFile);
+// }
+// public Object call(String functionName, Object[] args) throws NoSuchMethodException, ScriptException {
+// return invocable.get().call(functionName, args);
+// }
+
}
http://git-wip-us.apache.org/repos/asf/jena/blob/a0108d77/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
index 4cb1cc0..bd4c25e 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
@@ -48,7 +48,7 @@ import org.apache.jena.sparql.function.FunctionBase;
* Functions that return null or undefined will resutl in a {@link ExprEvalException}.
*
* @see EnvJavaScript
- * @see NV
+ * @see NV
*/
public class FunctionJavaScript extends FunctionBase {
@@ -76,9 +76,10 @@ public class FunctionJavaScript extends FunctionBase {
Object[] a = new Object[args.size()];
for ( int i = 0 ; i < args.size(); i++ )
a[i] = NV.fromNodeValue(args.get(i));
- Object r = envJS.invoc().invokeFunction(functionName, a);
+ Object r = envJS.call(functionName, a);
if ( r == null )
- // Or string "undefined".
+ // null is used used to signal an ExprEvalException.
+ // NV.throwExprEvalException(....);
throw new ExprEvalException(functionName);
NodeValue nv = NV.toNodeValue(r);
return nv;
http://git-wip-us.apache.org/repos/asf/jena/blob/a0108d77/jena-arq/src/main/java/org/apache/jena/sparql/function/js/JSEngine.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/JSEngine.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/JSEngine.java
new file mode 100644
index 0000000..9084beb
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/JSEngine.java
@@ -0,0 +1,109 @@
+/*
+ * 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.js;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Paths;
+
+import javax.script.*;
+
+import org.apache.jena.atlas.io.IO;
+import org.apache.jena.riot.RiotNotFoundException;
+import org.apache.jena.sparql.ARQConstants;
+import org.apache.jena.sparql.ARQException;
+import org.apache.jena.sparql.sse.builders.ExprBuildException;
+
+/** Abstraction of a <em>per-thread</em> JavaScript execution system */
+public class JSEngine {
+ private ScriptEngine scriptEngine;
+ private CompiledScript compiledScript;
+
+ private final Invocable invoc;
+ private String functions;
+
+ private String functionLibFile;
+
+
+ /** Create a {@code JSEngine} from a string. */
+ public static JSEngine createFromString(String functions) {
+ return new JSEngine(functions, null);
+ }
+
+ /** Create a {@code JSEngine} from the contents of a file. */
+ public static JSEngine createFromFile(String functionLibFile) {
+ return new JSEngine(null, functionLibFile);
+ }
+
+ /*package*/ JSEngine(String functions, String functionLibFile) {
+ this.functions = functions;
+ this.functionLibFile = functionLibFile;
+ invoc = build(functions, functionLibFile);
+ }
+
+ private static Invocable build(String functions, String functionLibFile) {
+ if ( functions == null && functionLibFile == null )
+ throw new ARQException("Both script string and script filename are null");
+
+ ScriptEngineManager manager = new ScriptEngineManager();
+ ScriptEngine scriptEngine = manager.getEngineByName("nashorn");
+
+ Invocable invoc = (Invocable)scriptEngine;
+ if ( functionLibFile != null ) {
+ try {
+ Reader reader = Files.newBufferedReader(Paths.get(functionLibFile), StandardCharsets.UTF_8);
+ Object x = scriptEngine.eval(reader);
+ }
+ catch (NoSuchFileException | FileNotFoundException ex) {
+ throw new RiotNotFoundException("File: "+functionLibFile);
+ }
+ catch (IOException ex) { IO.exception(ex); }
+ catch (ScriptException e) {
+ throw new ExprBuildException("Failed to load Javascript", e);
+ }
+ }
+ if ( functions != null ) {
+ try {
+ Object x = scriptEngine.eval(functions);
+ }
+ catch (ScriptException e) {
+ throw new ExprBuildException("Failed to load Javascript", e);
+ }
+ }
+
+ // Try to call the init function - ignore NoSuchMethodException
+ try {
+ invoc.invokeFunction(ARQConstants.JavaScriptInitFunction);
+ } catch (NoSuchMethodException ex) {}
+ catch (ScriptException ex) {
+ throw new ARQException("Failed to call JavaScript initialization function", ex);
+ }
+ return invoc;
+ }
+
+ public Object call(String functionName, Object[] args) throws NoSuchMethodException, ScriptException {
+ return invoc.invokeFunction(functionName, args);
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/a0108d77/jena-arq/testing/ARQ/JS/data.ttl
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/data.ttl b/jena-arq/testing/ARQ/JS/data.ttl
index 12303ce..f99776a 100644
--- a/jena-arq/testing/ARQ/JS/data.ttl
+++ b/jena-arq/testing/ARQ/JS/data.ttl
@@ -1,5 +1,5 @@
PREFIX : <http://example/>
:s1 :p1 1 .
-:s2 :p2 "one" .
+[] :p2 "one" .
[5/8] jena git commit: Fix javadoc
Posted by an...@apache.org.
Fix javadoc
Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/65d42959
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/65d42959
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/65d42959
Branch: refs/heads/master
Commit: 65d42959b46897bd7037da1d4a0977b7847ffa71
Parents: be160bc
Author: Andy Seaborne <an...@apache.org>
Authored: Wed Jan 10 16:24:34 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Wed Jan 10 16:24:34 2018 +0000
----------------------------------------------------------------------
jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java | 2 +-
jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jena/blob/65d42959/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
index 652306f..d6792df 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
@@ -70,7 +70,7 @@ public class ARQConstants
/** The URI prefix that triggers JavaScript functions */
public static final String JavaScriptURI = "http://jena.apache.org/ARQ/jsFunction#" ;
- /** Function called when JaavScript is initialized. */
+ /** Function called when JavaScript is initialized. */
public static final String JavaScriptInitFunction = "arqJSinit";
/** URI scheme that triggers the loader to load a java class */
http://git-wip-us.apache.org/repos/asf/jena/blob/65d42959/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java
index 9ea418b..2abcad1 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/NV.java
@@ -53,7 +53,7 @@ public class NV implements RDFJS {
private final static boolean narrowDoubles = true;
/**
* Map an ARQ {@link NodeValue} to java/Nashorn representation of a JavaScript object.
- * Native JaavScript types supported are null, string, number and boolean.
+ * Native JavaScript types supported are null, string, number and boolean.
* Otherwise a {@link NV} is returned.
*/
public static Object fromNodeValue(NodeValue nv) {
[3/8] jena git commit: Fix namespace
Posted by an...@apache.org.
Fix namespace
Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/4d5faef3
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/4d5faef3
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/4d5faef3
Branch: refs/heads/master
Commit: 4d5faef30d9966d70256c6c73ea40fb2a8255cfe
Parents: a0108d7
Author: Andy Seaborne <an...@apache.org>
Authored: Wed Jan 10 16:20:05 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Wed Jan 10 16:20:30 2018 +0000
----------------------------------------------------------------------
jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java | 2 +-
jena-arq/testing/ARQ/JS/js-query-1.rq | 2 +-
jena-arq/testing/ARQ/JS/js-query-2.rq | 2 +-
jena-arq/testing/ARQ/JS/js-query-3.rq | 2 +-
jena-arq/testing/ARQ/JS/js-query-4.rq | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jena/blob/4d5faef3/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
index 2cf4e4d..652306f 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
@@ -68,7 +68,7 @@ public class ARQConstants
public static final String XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema#" ;
/** The URI prefix that triggers JavaScript functions */
- public static final String JavaScriptURI = "http://jena/apache.org/ARQ/jsFunction#" ;
+ public static final String JavaScriptURI = "http://jena.apache.org/ARQ/jsFunction#" ;
/** Function called when JaavScript is initialized. */
public static final String JavaScriptInitFunction = "arqJSinit";
http://git-wip-us.apache.org/repos/asf/jena/blob/4d5faef3/jena-arq/testing/ARQ/JS/js-query-1.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-1.rq b/jena-arq/testing/ARQ/JS/js-query-1.rq
index ad90720..51ad595 100644
--- a/jena-arq/testing/ARQ/JS/js-query-1.rq
+++ b/jena-arq/testing/ARQ/JS/js-query-1.rq
@@ -1,4 +1,4 @@
-PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX js: <http://jena.apache.org/ARQ/jsFunction#>
PREFIX : <http://example/>
SELECT (js:identity(8) AS ?X) {}
http://git-wip-us.apache.org/repos/asf/jena/blob/4d5faef3/jena-arq/testing/ARQ/JS/js-query-2.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-2.rq b/jena-arq/testing/ARQ/JS/js-query-2.rq
index bf5cc35..bc70e57 100644
--- a/jena-arq/testing/ARQ/JS/js-query-2.rq
+++ b/jena-arq/testing/ARQ/JS/js-query-2.rq
@@ -1,4 +1,4 @@
-PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX js: <http://jena.apache.org/ARQ/jsFunction#>
PREFIX : <http://example/>
SELECT (js:inc(8) AS ?X) {}
http://git-wip-us.apache.org/repos/asf/jena/blob/4d5faef3/jena-arq/testing/ARQ/JS/js-query-3.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-3.rq b/jena-arq/testing/ARQ/JS/js-query-3.rq
index 55bf329..0b9a663 100644
--- a/jena-arq/testing/ARQ/JS/js-query-3.rq
+++ b/jena-arq/testing/ARQ/JS/js-query-3.rq
@@ -1,4 +1,4 @@
-PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX js: <http://jena.apache.org/ARQ/jsFunction#>
PREFIX : <http://example/>
SELECT (js:inc(js:inc(8)) AS ?X) {}
http://git-wip-us.apache.org/repos/asf/jena/blob/4d5faef3/jena-arq/testing/ARQ/JS/js-query-4.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-4.rq b/jena-arq/testing/ARQ/JS/js-query-4.rq
index 6e466c3..c10eb74 100644
--- a/jena-arq/testing/ARQ/JS/js-query-4.rq
+++ b/jena-arq/testing/ARQ/JS/js-query-4.rq
@@ -1,4 +1,4 @@
-PREFIX js: <http://jena/apache.org/ARQ/jsFunction#>
+PREFIX js: <http://jena.apache.org/ARQ/jsFunction#>
PREFIX : <http://example/>
SELECT * {
[7/8] jena git commit: JENA-1461: Put contants into a public place.
Posted by an...@apache.org.
JENA-1461: Put contants into a public place.
Improve and add tests
Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/2eb211f8
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/2eb211f8
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/2eb211f8
Branch: refs/heads/master
Commit: 2eb211f80aebe37f54df246ef626cffd7f5b9fc8
Parents: 64fe8bd
Author: Andy Seaborne <an...@apache.org>
Authored: Fri Jan 12 16:05:16 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Fri Jan 12 16:05:16 2018 +0000
----------------------------------------------------------------------
.../main/java/org/apache/jena/query/ARQ.java | 10 ++++++++++
.../org/apache/jena/sparql/ARQConstants.java | 2 +-
.../org/apache/jena/sparql/expr/E_Function.java | 4 ++++
.../jena/sparql/function/js/EnvJavaScript.java | 11 +++++------
.../sparql/function/js/FunctionJavaScript.java | 3 ---
.../jena/sparql/function/js/TS_FunctionJS.java | 20 +++++++++++++++++++-
.../function/js/TestJavaScriptFunctions.java | 4 ++--
.../jena/sparql/function/js/TestSPARQL_JS.java | 4 ++--
jena-arq/testing/ARQ/JS/js-query-5.rq | 7 +++++++
jena-arq/testing/ARQ/JS/js-query-5.srj | 8 ++++++++
jena-arq/testing/ARQ/JS/manifest.ttl | 6 ++++++
11 files changed, 64 insertions(+), 15 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/src/main/java/org/apache/jena/query/ARQ.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/query/ARQ.java b/jena-arq/src/main/java/org/apache/jena/query/ARQ.java
index b023a1b..e319bc8 100644
--- a/jena-arq/src/main/java/org/apache/jena/query/ARQ.java
+++ b/jena-arq/src/main/java/org/apache/jena/query/ARQ.java
@@ -485,6 +485,16 @@ public class ARQ
* Expression evaluation without extension types (e.g. xsd:date, language tags)
*/
public static final Symbol extensionValueTypes = SystemARQ.allocSymbol("extensionValueTypesExpr") ;
+
+ /**
+ * Context symbol for JavaScript functions as a string value which is evaluated.
+ */
+ public static Symbol symJavaScriptFunctions = SystemARQ.allocSymbol("js-functions");
+
+ /**
+ * Context symbol for JavaScript library of functions defined in a file.
+ */
+ public static Symbol symJavaScriptLibFile = SystemARQ.allocSymbol("js-library");
/**
* Generate the ToList operation in the algebra (as ARQ is stream based, ToList is a non-op).
http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
index d6792df..1cfafb4 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/ARQConstants.java
@@ -67,7 +67,7 @@ public class ARQConstants
/** XML Schema namespace */
public static final String XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema#" ;
- /** The URI prefix that triggers JavaScript functions */
+ /** The URI prefix that triggers JavaScript functions */
public static final String JavaScriptURI = "http://jena.apache.org/ARQ/jsFunction#" ;
/** Function called when JavaScript is initialized. */
http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
index 2d097a1..558a174 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Function.java
@@ -64,6 +64,10 @@ public class E_Function extends ExprFunctionN
if ( javaScriptFunction != null ) {
// JavaScript
EnvJavaScript jsEnv = EnvJavaScript.get();
+ if ( jsEnv == null ) {
+ ARQ.getExecLogger().warn("JavaScript function found '"+javaScriptFunction+"' but no JavaScript library installed");
+ throw new ExprUndefFunction("No JavaScript function library", javaScriptFunction) ;
+ }
// FunctionJavaScript : functions only : no special forms.
function = new FunctionJavaScript(javaScriptFunction, jsEnv);
try {
http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
index 148fde4..259ba83 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
@@ -24,7 +24,6 @@ import org.apache.jena.atlas.lib.Pool;
import org.apache.jena.atlas.lib.PoolBase;
import org.apache.jena.atlas.lib.PoolSync;
import org.apache.jena.query.ARQ;
-import org.apache.jena.sparql.SystemARQ;
import org.apache.jena.sparql.util.Context;
import org.apache.jena.sparql.util.Symbol;
@@ -35,16 +34,16 @@ import org.apache.jena.sparql.util.Symbol;
* The file must be UTF-8 encoded.
* <p>
* Function are loaded from a string value in context setting
- * {@link EnvJavaScript#symJavaScriptLib}.
+ * {@link EnvJavaScript#symJavaScriptFunctions}.
* <p>
* If both are present, the file named by {@code EnvJavaScript.symJavaScriptLibFile} is loaded
* then the string from {@code EnvJavaScript.symJavaScriptLib}.
*/
public class EnvJavaScript {
/** JavaScript functions as a string value which is evaluated. */
- public static Symbol symJavaScriptLib = SystemARQ.allocSymbol("js-functions");
+ public static Symbol symJavaScriptFunctions = ARQ.symJavaScriptFunctions;
/** JavaScript library of functions in a file. */
- public static Symbol symJavaScriptLibFile = SystemARQ.allocSymbol("js-library");
+ public static Symbol symJavaScriptLibFile = ARQ.symJavaScriptLibFile;
private final String scriptLib;
private final String scriptLibFile;
@@ -63,7 +62,7 @@ public class EnvJavaScript {
if ( global == null ) {
synchronized(EnvJavaScript.class) {
Context context = ARQ.getContext();
- if ( context.isDefined(symJavaScriptLib) || context.isDefined(symJavaScriptLibFile) )
+ if ( context.isDefined(symJavaScriptFunctions) || context.isDefined(symJavaScriptLibFile) )
global = create(ARQ.getContext());
}
}
@@ -94,7 +93,7 @@ public class EnvJavaScript {
private Pool<JSEngine> pool = PoolSync.create(new PoolBase<JSEngine>());
private EnvJavaScript(Context context) {
- this.scriptLib = context.getAsString(symJavaScriptLib);
+ this.scriptLib = context.getAsString(symJavaScriptFunctions);
this.scriptLibFile = context.getAsString(symJavaScriptLibFile);
// Put one in the pool.
pool.put(build());
http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
index cda6d31..cacef8e 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
@@ -67,9 +67,6 @@ public class FunctionJavaScript extends FunctionBase {
@Override
public NodeValue exec(List<NodeValue> args) {
- if ( ! initialized )
- checkBuild(null, null);
-
try {
// Convert NodeValues to types more akin to Javascript.
// Pass strings as string, and numbers as Number.
http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java
index 59e8b48..9683082 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TS_FunctionJS.java
@@ -18,6 +18,10 @@
package org.apache.jena.sparql.function.js;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.sparql.expr.E_Function;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@@ -28,4 +32,18 @@ import org.junit.runners.Suite.SuiteClasses;
, TestJavaScriptFunctions.class
, TestSPARQL_JS.class
})
-public class TS_FunctionJS {}
+public class TS_FunctionJS {
+ static boolean b = false;
+
+ @BeforeClass public static void beforeClass() {
+ b = E_Function.WarnOnUnknownFunction;
+ E_Function.WarnOnUnknownFunction = false ;
+ }
+
+ @AfterClass public static void afterClass() {
+ ARQ.getContext().unset(ARQ.symJavaScriptFunctions);
+ ARQ.getContext().unset(ARQ.symJavaScriptLibFile);
+ E_Function.WarnOnUnknownFunction = b ;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java
index 7f456ff..12d8799 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestJavaScriptFunctions.java
@@ -46,7 +46,7 @@ public class TestJavaScriptFunctions {
private static EnvJavaScript setupJS() {
Context cxt = ARQ.getContext().copy();
- cxt.set(EnvJavaScript.symJavaScriptLib, CamelCaseJS);
+ cxt.set(EnvJavaScript.symJavaScriptFunctions, CamelCaseJS);
cxt.set(EnvJavaScript.symJavaScriptLibFile, JS_LIB_FILE);
return EnvJavaScript.create(cxt);
}
@@ -160,7 +160,7 @@ public class TestJavaScriptFunctions {
NodeValue nv = eval("no_such_function()");
}
- // Wrong number of argument is OK in JavaScript- except "null" return becomes ExprEvalException.
+ // Wrong number of argument is OK in JavaScript - "null" return becomes ExprEvalException.
@Test(expected=ExprEvalException.class)
public void js_err_2() {
NodeValue nv = eval("identity");
http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java
index 309e367..0e9e85b 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/function/js/TestSPARQL_JS.java
@@ -33,8 +33,8 @@ public class TestSPARQL_JS {
private static void setupJS() {
Context cxt = ARQ.getContext();
- cxt.set(EnvJavaScript.symJavaScriptLibFile, JS_LIB_FILE);
- cxt.set(EnvJavaScript.symJavaScriptLib, "function inc(x) { return x+1 }");
+ cxt.set(ARQ.symJavaScriptLibFile, JS_LIB_FILE);
+ cxt.set(ARQ.symJavaScriptFunctions, "function inc(x) { return x+1 }");
EnvJavaScript.reset();
}
http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/testing/ARQ/JS/js-query-5.rq
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-5.rq b/jena-arq/testing/ARQ/JS/js-query-5.rq
new file mode 100644
index 0000000..0757be0
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-5.rq
@@ -0,0 +1,7 @@
+PREFIX js: <http://jena.apache.org/ARQ/jsFunction#>
+PREFIX : <http://example/>
+
+SELECT * {
+ ?s ?p ?o
+ FILTER(js:nosuchFunction())
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/testing/ARQ/JS/js-query-5.srj
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/js-query-5.srj b/jena-arq/testing/ARQ/JS/js-query-5.srj
new file mode 100644
index 0000000..0fe0186
--- /dev/null
+++ b/jena-arq/testing/ARQ/JS/js-query-5.srj
@@ -0,0 +1,8 @@
+{
+ "head": {
+ "vars": [ "s" , "p" , "o" ]
+ } ,
+ "results": {
+ "bindings": []
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/2eb211f8/jena-arq/testing/ARQ/JS/manifest.ttl
----------------------------------------------------------------------
diff --git a/jena-arq/testing/ARQ/JS/manifest.ttl b/jena-arq/testing/ARQ/JS/manifest.ttl
index 7dd64ae..9381ebb 100644
--- a/jena-arq/testing/ARQ/JS/manifest.ttl
+++ b/jena-arq/testing/ARQ/JS/manifest.ttl
@@ -47,4 +47,10 @@
qt:data <data.ttl> ] ;
mf:result <js-query-4.srj>
]
+ [ mf:name "JS Call: FILTER functions" ;
+ mf:action
+ [ qt:query <js-query-5.rq> ;
+ qt:data <data.ttl> ] ;
+ mf:result <js-query-5.srj>
+ ]
).
[4/8] jena git commit: Note UTF-8 required
Posted by an...@apache.org.
Note UTF-8 required
Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/be160bcd
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/be160bcd
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/be160bcd
Branch: refs/heads/master
Commit: be160bcda6e4baef3c82f799e790a26e40f30739
Parents: 4d5faef
Author: Andy Seaborne <an...@apache.org>
Authored: Wed Jan 10 16:22:54 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Wed Jan 10 16:22:54 2018 +0000
----------------------------------------------------------------------
.../java/org/apache/jena/sparql/function/js/EnvJavaScript.java | 3 ++-
.../org/apache/jena/sparql/function/js/FunctionJavaScript.java | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jena/blob/be160bcd/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
index 167848f..148fde4 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/EnvJavaScript.java
@@ -31,7 +31,8 @@ import org.apache.jena.sparql.util.Symbol;
/** Environment for executing a JavaScript function.
* <p>
* Functions are loaded from the file named in context setting
- * {@link EnvJavaScript#symJavaScriptLibFile}.
+ * {@link EnvJavaScript#symJavaScriptLibFile}.
+ * The file must be UTF-8 encoded.
* <p>
* Function are loaded from a string value in context setting
* {@link EnvJavaScript#symJavaScriptLib}.
http://git-wip-us.apache.org/repos/asf/jena/blob/be160bcd/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
index bd4c25e..cda6d31 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/function/js/FunctionJavaScript.java
@@ -45,7 +45,7 @@ import org.apache.jena.sparql.function.FunctionBase;
* attempt to reconstruct the datatype of the result of the function into
* {@code xsd:integer} and {@code xsd:double}.
* <p>
- * Functions that return null or undefined will resutl in a {@link ExprEvalException}.
+ * Functions that return null or undefined will result in a {@link ExprEvalException}.
*
* @see EnvJavaScript
* @see NV
[8/8] jena git commit: Merge commit 'refs/pull/340/head' of
github.com:apache/jena
Posted by an...@apache.org.
Merge commit 'refs/pull/340/head' of github.com:apache/jena
This closes #340.
Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/58ac14b7
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/58ac14b7
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/58ac14b7
Branch: refs/heads/master
Commit: 58ac14b7ed1414d8465ab0e12b13edae23315149
Parents: 67898ff 2eb211f
Author: Andy Seaborne <an...@apache.org>
Authored: Sat Jan 13 10:20:00 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Sat Jan 13 10:20:00 2018 +0000
----------------------------------------------------------------------
.../main/java/org/apache/jena/query/ARQ.java | 10 +
.../org/apache/jena/sparql/ARQConstants.java | 7 +
.../org/apache/jena/sparql/expr/E_Function.java | 44 +++-
.../jena/sparql/expr/ExprUndefFunction.java | 32 +++
.../jena/sparql/function/FunctionBase.java | 2 +-
.../jena/sparql/function/js/EnvJavaScript.java | 138 ++++++++++++
.../sparql/function/js/FunctionJavaScript.java | 91 ++++++++
.../jena/sparql/function/js/JSEngine.java | 109 ++++++++++
.../org/apache/jena/sparql/function/js/NV.java | 217 +++++++++++++++++++
.../apache/jena/sparql/function/js/RDFJS.java | 35 +++
.../jena/sparql/util/graph/GraphUtils.java | 9 +-
.../jena/riot/langsuite/FactoryTestRiot.java | 1 -
.../java/org/apache/jena/sparql/TC_General.java | 2 +
.../jena/sparql/function/js/TS_FunctionJS.java | 49 +++++
.../function/js/TestJavaScriptFunctions.java | 198 +++++++++++++++++
.../apache/jena/sparql/function/js/TestNV.java | 71 ++++++
.../jena/sparql/function/js/TestSPARQL_JS.java | 49 +++++
.../function/library/TS_LibraryFunctions.java | 2 +-
jena-arq/testing/ARQ/JS/data.ttl | 5 +
jena-arq/testing/ARQ/JS/js-query-1.rq | 4 +
jena-arq/testing/ARQ/JS/js-query-1.srj | 12 +
jena-arq/testing/ARQ/JS/js-query-2.rq | 4 +
jena-arq/testing/ARQ/JS/js-query-2.srj | 12 +
jena-arq/testing/ARQ/JS/js-query-3.rq | 4 +
jena-arq/testing/ARQ/JS/js-query-3.srj | 12 +
jena-arq/testing/ARQ/JS/js-query-4.rq | 9 +
jena-arq/testing/ARQ/JS/js-query-4.srj | 14 ++
jena-arq/testing/ARQ/JS/js-query-5.rq | 7 +
jena-arq/testing/ARQ/JS/js-query-5.srj | 8 +
jena-arq/testing/ARQ/JS/manifest.ttl | 56 +++++
jena-arq/testing/ARQ/JS/test-library.js | 14 ++
.../apache/jena/fuseki/build/FusekiConfig.java | 51 ++---
.../apache/jena/fuseki/mgt/ActionDatasets.java | 2 +-
.../apache/jena/fuseki/server/FusekiSystem.java | 29 +--
.../jena/fuseki/embedded/FusekiServer.java | 28 ++-
35 files changed, 1259 insertions(+), 78 deletions(-)
----------------------------------------------------------------------
[6/8] jena git commit: JENA-1464: Refactor Fuseki configuration
processing.
Posted by an...@apache.org.
JENA-1464: Refactor Fuseki configuration processing.
Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/64fe8bd9
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/64fe8bd9
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/64fe8bd9
Branch: refs/heads/master
Commit: 64fe8bd9161bed88150e330d8808ed45ce35d957
Parents: 65d4295
Author: Andy Seaborne <an...@apache.org>
Authored: Fri Jan 12 15:18:43 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Fri Jan 12 16:01:38 2018 +0000
----------------------------------------------------------------------
.../jena/sparql/util/graph/GraphUtils.java | 9 ++--
.../apache/jena/fuseki/build/FusekiConfig.java | 51 ++++++--------------
.../apache/jena/fuseki/mgt/ActionDatasets.java | 2 +-
.../apache/jena/fuseki/server/FusekiSystem.java | 29 +++--------
.../jena/fuseki/embedded/FusekiServer.java | 28 ++++++++---
5 files changed, 51 insertions(+), 68 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jena/blob/64fe8bd9/jena-arq/src/main/java/org/apache/jena/sparql/util/graph/GraphUtils.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/util/graph/GraphUtils.java b/jena-arq/src/main/java/org/apache/jena/sparql/util/graph/GraphUtils.java
index d82dfeb..ce0243e 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/util/graph/GraphUtils.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/util/graph/GraphUtils.java
@@ -173,13 +173,16 @@ public class GraphUtils {
return null ;
return s.getResource() ;
}
+
+ public static List<Resource> listResourcesByType(Model model, Resource type) {
+ return Iter.toList(model.listSubjectsWithProperty(RDF.type, type)) ;
+ }
public static Resource getResourceByType(Model model, Resource type) {
- // See also
- StmtIterator sIter = model.listStatements(null, RDF.type, type) ;
+ ResIterator sIter = model.listSubjectsWithProperty(RDF.type, type) ;
if ( !sIter.hasNext() )
return null ;
- Resource r = sIter.nextStatement().getSubject() ;
+ Resource r = sIter.next();
if ( sIter.hasNext() )
throw new TypeNotUniqueException(r) ;
return r ;
http://git-wip-us.apache.org/repos/asf/jena/blob/64fe8bd9/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java
index 6e64632..614b721 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java
@@ -33,7 +33,6 @@ import java.util.Collections ;
import java.util.List ;
import org.apache.jena.assembler.JA ;
-import org.apache.jena.atlas.iterator.Iter ;
import org.apache.jena.atlas.lib.IRILib ;
import org.apache.jena.atlas.lib.StrUtils ;
import org.apache.jena.fuseki.Fuseki ;
@@ -47,7 +46,7 @@ import org.apache.jena.query.ResultSet ;
import org.apache.jena.rdf.model.* ;
import org.apache.jena.riot.Lang;
import org.apache.jena.sparql.core.assembler.AssemblerUtils ;
-import org.apache.jena.vocabulary.RDF ;
+import org.apache.jena.sparql.util.graph.GraphUtils;
import org.slf4j.Logger ;
public class FusekiConfig {
@@ -55,19 +54,13 @@ public class FusekiConfig {
private static Logger log = Fuseki.configLog ;
- /** Has side effects in server setup */
- public static List<DataAccessPoint> readServerConfigFile(String filename) {
- // Old-style config file.
- Model model = readAssemblerFile(filename) ;
- if ( model.size() == 0 )
- return Collections.emptyList() ;
- server(model) ;
- return servicesAndDatasets(model) ;
- }
-
- private static void server(Model model) {
+ /** Process the server section, if any, of a configuration file.
+ * This includes setting global context and ja:loadClass.
+ * It does not include services - see {@link #servicesAndDatasets}
+ */
+ public static void processServerConfig(Model model) {
// Find one server.
- List<Resource> servers = getByType(FusekiVocab.tServer, model) ;
+ List<Resource> servers = GraphUtils.listResourcesByType(model, FusekiVocab.tServer) ;
if ( servers.size() == 0 )
return ;
if ( servers.size() > 1 )
@@ -106,9 +99,13 @@ public class FusekiConfig {
}
}
- private static List<DataAccessPoint> servicesAndDatasets(Model model) {
+ /** Find and process datasets and services in a configuration file.
+ * This can be a Fuseki server configuration file or a services-only configuration file.
+ * It looks {@code fuseki:services ( .... )} then, if not found, all {@code rtdf:type fuseki:services}.
+ */
+ public static List<DataAccessPoint> servicesAndDatasets(Model model) {
// Old style configuration file : server to services.
- DatasetDescriptionRegistry dsDescMap = FusekiSystem.registryForBuild() ;
+ DatasetDescriptionRegistry dsDescMap = new DatasetDescriptionRegistry();
// ---- Services
ResultSet rs = FusekiLib.query("SELECT * { ?s fu:services [ list:member ?service ] }", model) ;
List<DataAccessPoint> accessPoints = new ArrayList<>() ;
@@ -124,7 +121,6 @@ public class FusekiConfig {
DataAccessPoint acc = FusekiBuilder.buildDataAccessPoint(svc, dsDescMap) ;
accessPoints.add(acc) ;
}
-
return accessPoints ;
}
@@ -147,12 +143,6 @@ public class FusekiConfig {
return AssemblerUtils.readAssemblerFile(filename) ;
}
- // XXX Move to a library
- private static List<Resource> getByType(Resource type, Model m) {
- ResIterator rIter = m.listSubjectsWithProperty(RDF.type, type) ;
- return Iter.toList(rIter) ;
- }
-
// ---- Directory of assemblers
/** Read service descriptions in the given directory */
@@ -177,7 +167,7 @@ public class FusekiConfig {
List<DataAccessPoint> dataServiceRef = new ArrayList<>() ;
try (DirectoryStream<Path> stream = Files.newDirectoryStream(pDir, filter)) {
for ( Path p : stream ) {
- DatasetDescriptionRegistry dsDescMap = FusekiSystem.registryForBuild() ;
+ DatasetDescriptionRegistry dsDescMap = new DatasetDescriptionRegistry() ;
String fn = IRILib.filenameToIRI(p.toString()) ;
log.info("Load configuration: "+fn);
Model m = readAssemblerFile(fn) ;
@@ -189,21 +179,12 @@ public class FusekiConfig {
return dataServiceRef ;
}
- /** Read and process one file */
- public static List<DataAccessPoint> readConfigurationFile(String fn) {
- List<DataAccessPoint> acc = new ArrayList<>() ;
- Model m = readAssemblerFile(fn) ;
- DatasetDescriptionRegistry dsDescMap = new DatasetDescriptionRegistry() ;
- readConfiguration(m, dsDescMap, acc) ;
- return acc ;
- }
-
/** Read a configuration in a model.
* Allow dataset descriptions to be carried over from another place.
* Add to a list.
*/
private static void readConfiguration(Model m, DatasetDescriptionRegistry dsDescMap, List<DataAccessPoint> dataServiceRef) {
- List<Resource> services = getByType(FusekiVocab.fusekiService, m) ;
+ List<Resource> services = GraphUtils.listResourcesByType(m, FusekiVocab.fusekiService) ;
if ( services.size() == 0 ) {
log.error("No services found") ;
@@ -219,7 +200,7 @@ public class FusekiConfig {
// ---- System database
/** Read the system database */
public static List<DataAccessPoint> readSystemDatabase(Dataset ds) {
- DatasetDescriptionRegistry dsDescMap = FusekiSystem.registryForBuild() ;
+ DatasetDescriptionRegistry dsDescMap = new DatasetDescriptionRegistry() ;
String qs = StrUtils.strjoinNL
(SystemState.PREFIXES ,
"SELECT * {" ,
http://git-wip-us.apache.org/repos/asf/jena/blob/64fe8bd9/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
index d59ea66..85e84af 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
@@ -111,7 +111,7 @@ public class ActionDatasets extends ActionContainerItem {
@Override
protected JsonValue execPostContainer(HttpAction action) {
JenaUUID uuid = JenaUUID.generate() ;
- DatasetDescriptionRegistry registry = FusekiSystem.registryForBuild() ;
+ DatasetDescriptionRegistry registry = new DatasetDescriptionRegistry() ;
ContentType ct = FusekiLib.getContentType(action) ;
http://git-wip-us.apache.org/repos/asf/jena/blob/64fe8bd9/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiSystem.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiSystem.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiSystem.java
index 78f6490..105f20a 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiSystem.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiSystem.java
@@ -45,6 +45,7 @@ import org.apache.jena.riot.Lang ;
import org.apache.jena.riot.RDFDataMgr ;
import org.apache.jena.riot.RDFLanguages ;
import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.sparql.core.assembler.AssemblerUtils;
import org.apache.jena.tdb.sys.Names ;
public class FusekiSystem
@@ -257,12 +258,17 @@ public class FusekiSystem
return Collections.emptyList();
}
Fuseki.configLog.info("Configuration file: " + configFilename) ;
- return FusekiConfig.readServerConfigFile(configFilename) ;
+ //return FusekiConfig.readServerConfigFile(configFilename);
+ Model model = AssemblerUtils.readAssemblerFile(configFilename) ;
+ if ( model.size() == 0 )
+ return Collections.emptyList() ;
+ FusekiConfig.processServerConfig(model);
+ return FusekiConfig.servicesAndDatasets(model) ;
}
private static DataAccessPoint configFromTemplate(String templateFile, String datasetPath,
boolean allowUpdate, Map<String, String> params) {
- DatasetDescriptionRegistry registry = FusekiSystem.registryForBuild() ;
+ DatasetDescriptionRegistry registry = new DatasetDescriptionRegistry() ;
// ---- Setup
if ( params == null ) {
params = new HashMap<>() ;
@@ -404,25 +410,6 @@ public class FusekiSystem
}
/**
- * <ul>
- * <li>GLOBAL: sharing across all descriptions
- * <li>FILE: sharing within files but not across files.
- * </ul>
- */
- enum DatasetDescriptionScope { GLOBAL, FILE }
- private static DatasetDescriptionRegistry globalDatasets = new DatasetDescriptionRegistry() ;
- private static DatasetDescriptionScope policyDatasetDescriptionScope = DatasetDescriptionScope.FILE ;
-
- /** Call this once per configuration file. */
- public static DatasetDescriptionRegistry registryForBuild() {
- switch (policyDatasetDescriptionScope) {
- case FILE : return new DatasetDescriptionRegistry() ;
- case GLOBAL : return globalDatasets ;
- default: throw new InternalErrorException() ;
- }
- }
-
- /**
* Dataset set name to configuration file name. Return a configuration file name -
* existing one or ".ttl" form if new
*/
http://git-wip-us.apache.org/repos/asf/jena/blob/64fe8bd9/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/FusekiServer.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/FusekiServer.java b/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/FusekiServer.java
index 5bc9102..c88509e 100644
--- a/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/FusekiServer.java
+++ b/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/FusekiServer.java
@@ -18,9 +18,12 @@
package org.apache.jena.fuseki.embedded;
-import java.util.*;
import static java.util.Objects.requireNonNull;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
@@ -32,16 +35,17 @@ import org.apache.jena.fuseki.build.FusekiBuilder;
import org.apache.jena.fuseki.build.FusekiConfig;
import org.apache.jena.fuseki.jetty.FusekiErrorHandler1;
import org.apache.jena.fuseki.mgt.ActionStats;
-import org.apache.jena.fuseki.server.DataAccessPoint;
-import org.apache.jena.fuseki.server.DataAccessPointRegistry;
-import org.apache.jena.fuseki.server.DataService;
-import org.apache.jena.fuseki.server.Operation;
-import org.apache.jena.fuseki.servlets.ServiceDispatchRegistry;
+import org.apache.jena.fuseki.server.*;
import org.apache.jena.fuseki.servlets.ActionService;
import org.apache.jena.fuseki.servlets.FusekiFilter;
+import org.apache.jena.fuseki.servlets.ServiceDispatchRegistry;
import org.apache.jena.query.Dataset;
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.Resource;
import org.apache.jena.riot.WebContent;
import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.assembler.AssemblerUtils;
+import org.apache.jena.sparql.util.graph.GraphUtils;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.server.HttpConnectionFactory;
@@ -297,9 +301,17 @@ public class FusekiServer {
*/
public Builder parseConfigFile(String filename) {
requireNonNull(filename, "filename");
- List<DataAccessPoint> x = FusekiConfig.readConfigurationFile(filename);
+ Model model = AssemblerUtils.readAssemblerFile(filename);
+
+ // Process server context
+ Resource server = GraphUtils.getResourceByType(model, FusekiVocab.tServer);
+ if ( server != null )
+ AssemblerUtils.setContext(server, Fuseki.getContext()) ;
+
+ // Process services, whether via server ja:services or, if absent, by finding by type.
+ List<DataAccessPoint> x = FusekiConfig.servicesAndDatasets(model);
// Unbundle so that they accumulate.
- x.forEach(dap-> add(dap.getName(), dap.getDataService()));
+ x.forEach(dap->add(dap.getName(), dap.getDataService()));
return this;
}