This is an automated email from the ASF dual-hosted git repository.

andy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git

commit ba2c4bcbd09aaeab34f215fcb1576339ac89d9f1
Author: Andy Seaborne <[email protected]>
AuthorDate: Mon Apr 21 17:27:04 2025 +0100

    NodeValueDigest - Refactor digest evaluation
---
 .../java/org/apache/jena/sparql/ARQConstants.java  |   4 +
 .../org/apache/jena/sparql/expr/ExprDigest.java    |  57 +----------
 .../java/org/apache/jena/sparql/expr/ExprNode.java |   2 +-
 .../apache/jena/sparql/expr/ExprTripleTerm.java    |   3 +-
 .../sparql/expr/nodevalue/NodeValueDigest.java     | 114 +++++++++++++++++++++
 .../apache/jena/sparql/expr/TestFunctions2.java    |   2 +-
 6 files changed, 123 insertions(+), 59 deletions(-)

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 627dade41f..97f99c1860 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
@@ -53,6 +53,9 @@ public class ARQConstants
     /** RDFS namespace prefix */
     public static final String rdfsPrefix = 
"http://www.w3.org/2000/01/rdf-schema#";; //RDFS.getURI() ;
 
+    /** SPARQL namespace prefix */
+    public static final String sparqlPrefix = "https://www.w3.org/ns/sparql#";;
+
     /** OWL namespace prefix */
     public static final String owlPrefix = "http://www.w3.org/2002/07/owl#";; 
//OWL.getURI() ;
 
@@ -128,6 +131,7 @@ public class ARQConstants
         globalPrefixMap.setNsPrefix("xsd",  xsdPrefix) ;
         globalPrefixMap.setNsPrefix("owl" , owlPrefix) ;
         globalPrefixMap.setNsPrefix("fn" ,  fnPrefix) ;
+        globalPrefixMap.setNsPrefix("sparql",  sparqlPrefix) ;
         // Treat op: as fn: (op: has no namespace in XSD F&O).
         globalPrefixMap.setNsPrefix("op" ,  fnPrefix) ;
         globalPrefixMap.setNsPrefix("math" ,  mathPrefix) ;
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprDigest.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprDigest.java
index 59a8b273f9..228d05705f 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprDigest.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprDigest.java
@@ -18,16 +18,9 @@
 
 package org.apache.jena.sparql.expr;
 
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-import org.apache.jena.atlas.lib.Bytes;
 import org.apache.jena.atlas.lib.Cache;
 import org.apache.jena.atlas.lib.CacheFactory;
-import org.apache.jena.datatypes.xsd.XSDDatatype;
-import org.apache.jena.graph.Node;
-import org.apache.jena.sparql.ARQInternalErrorException;
+import org.apache.jena.sparql.expr.nodevalue.NodeValueDigest;
 import org.apache.jena.sparql.serializer.SerializationContext;
 
 public abstract class ExprDigest extends ExprFunction1
@@ -35,37 +28,11 @@ public abstract class ExprDigest extends ExprFunction1
     private final String digestName;
     // Historically, MD5 and SH* have been printed upper case.
     private final String printName;
-    private MessageDigest digestCache;
 
     public ExprDigest(Expr expr, String symbol, String printName, String 
digestName) {
         super(expr, symbol);
         this.digestName = digestName;
         this.printName = printName;
-        try {
-            digestCache = MessageDigest.getInstance(digestName);
-        } catch (NoSuchAlgorithmException e) {
-            throw new ARQInternalErrorException("Digest not provided in this 
Java system: " + digestName);
-        }
-    }
-
-    private MessageDigest getDigest() {
-        if ( digestCache != null ) {
-            MessageDigest digest2 = null;
-            try {
-                digest2 = (MessageDigest)digestCache.clone();
-                return digest2;
-            } catch (CloneNotSupportedException ex) {
-                // Can't clone - remove cache copy.
-                digestCache = null;
-            }
-        }
-        return createDigest();
-    }
-
-    private MessageDigest createDigest()
-    {
-        try { return MessageDigest.getInstance(digestName); }
-        catch (Exception ex2) { throw new ARQInternalErrorException(ex2); }
     }
 
     private final Cache<NodeValue, NodeValue> cache = 
CacheFactory.createOneSlotCache();
@@ -76,27 +43,7 @@ public abstract class ExprDigest extends ExprFunction1
     }
 
     private NodeValue calculate(NodeValue v) {
-        Node n = v.asNode();
-        if ( !n.isLiteral() )
-            throw new ExprEvalException("Not a literal: " + v);
-        if ( n.getLiteralLanguage() != null && 
!n.getLiteralLanguage().equals("") )
-            throw new ExprEvalException("Can't make a digest of an RDF term 
with a language tag");
-        // Literal, no language tag.
-        if ( n.getLiteralDatatype() != null && 
!XSDDatatype.XSDstring.equals(n.getLiteralDatatype()) )
-            throw new ExprEvalException("Not a simple literal nor an XSD 
string");
-
-        try {
-            MessageDigest digest = getDigest();
-            String x = n.getLiteralLexicalForm();
-            byte b[] = x.getBytes(StandardCharsets.UTF_8);
-            byte d[] = digest.digest(b);
-            String y = Bytes.asHexLC(d);
-            NodeValue result = NodeValue.makeString(y);
-            return result;
-
-        } catch (Exception ex2) {
-            throw new ARQInternalErrorException(ex2);
-        }
+        return NodeValueDigest.calculateDigest(v, digestName);
     }
 
     @Override
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprNode.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprNode.java
index 01c9aa6fbc..03249a637b 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprNode.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprNode.java
@@ -29,7 +29,7 @@ import org.apache.jena.sparql.graph.NodeTransform;
 import org.apache.jena.sparql.sse.writers.WriterExpr;
 
 /**
- * A node that is a constraint expression that can be evaluated
+ * A node that is a constraint expression that can be evaluated.
  * An {@link Expr} is already a Constraint - ExprNode is the base 
implementation
  * of all {@link Expr} classes that provides the Constraint machinery.
  */
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprTripleTerm.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprTripleTerm.java
index 878f810dc2..e518e8b5d8 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprTripleTerm.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprTripleTerm.java
@@ -31,8 +31,7 @@ import org.apache.jena.sparql.function.FunctionEnv ;
 import org.apache.jena.sparql.graph.NodeTransform ;
 
 /**
- * RDF-star triple term in an expression (AKA quoted triple).
- * It can still have variables in it.
+ * RDF 1.2 triple term in an expression.
  */
 public class ExprTripleTerm extends ExprNode {
 
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueDigest.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueDigest.java
new file mode 100644
index 0000000000..08c7a96f16
--- /dev/null
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/nodevalue/NodeValueDigest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.nodevalue;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.jena.atlas.lib.Bytes;
+import org.apache.jena.atlas.lib.Cache;
+import org.apache.jena.atlas.lib.CacheFactory;
+import org.apache.jena.atlas.lib.Lib;
+import org.apache.jena.datatypes.xsd.XSDDatatype;
+import org.apache.jena.graph.Node;
+import org.apache.jena.sparql.ARQInternalErrorException;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.expr.NodeValue;
+
+public class NodeValueDigest {
+
+    /**
+     * Calculate digest of a string literals.
+     * Pass in the digest name exactly, upper case and with a dash e.g. 
"SHA-1")
+     */
+    public static NodeValue calculateDigest(NodeValue nv, String digestName) {
+        // Exact digest name.
+        MessageDigest digest = createDigest(digestName);
+        return calculate(nv, digest);
+    }
+
+    /**
+     * Calculate digest of a string literals by
+     * function name or namespace fragment without the @code #}.
+     */
+    public static NodeValue function(NodeValue nv, String functionName) {
+        if ( ! digestFunctionName.contains(functionName) )
+            throw new ExprEvalException("Digest not supported: " + 
functionName);
+
+        functionName = Lib.lowercase(functionName);
+        String digestName = translateDigestFunctionNames.get(functionName);
+        return calculateDigest(nv, digestName);
+    }
+
+    // SPARQL name (lower case) to Java digest name.
+    private static Map<String, String> translateDigestFunctionNames = 
Map.of("md5",     "MD-5",
+                                                                             
"sha1",    "SHA-1",
+                                                                             
"sha224",  "SHA-224",
+                                                                             
"sha256",  "SHA-256",
+                                                                             
"sha384",  "SHA-384",
+                                                                             
"sha512",  "SHA-512");
+
+    private static Set<String> digestFunctionName = Set.of("md5", "sha1", 
"sha224", "sha256", "sha384", "sha512");
+
+    private static Cache<String, MessageDigest> digtests = 
CacheFactory.createCache(20);
+
+    private static MessageDigest getDigest(String digestName) {
+        MessageDigest digest = digtests.get(digestName, 
NodeValueDigest::createDigest);
+        MessageDigest digest2 = null;
+        try {
+            digest2 = (MessageDigest)digest.clone();
+            return digest2;
+        } catch (CloneNotSupportedException ex) {
+            // This should not happen. All the supported digests are cloneable.
+            digtests.remove(digestName);
+            return digest;
+        }
+    }
+
+    private static MessageDigest createDigest(String digestName) {
+        try {
+            return MessageDigest.getInstance(digestName);
+        } catch (Exception ex2) {
+            throw new ARQInternalErrorException("Digest not provided in this 
Java system: " + digestName);
+        }
+    }
+
+    private static NodeValue calculate(NodeValue nv, MessageDigest digest) {
+        Node n = nv.asNode();
+        if ( !n.isLiteral() )
+            throw new ExprEvalException("Not a literal: " + nv);
+        if ( n.getLiteralLanguage() != null && 
!n.getLiteralLanguage().equals("") )
+            throw new ExprEvalException("Can't make a digest of an RDF term 
with a language tag");
+        // Literal, no language tag.
+        if ( n.getLiteralDatatype() != null && 
!XSDDatatype.XSDstring.equals(n.getLiteralDatatype()) )
+            throw new ExprEvalException("Not a simple literal nor an XSD 
string");
+        try {
+            String x = n.getLiteralLexicalForm();
+            byte b[] = x.getBytes(StandardCharsets.UTF_8);
+            byte d[] = digest.digest(b);
+            String y = Bytes.asHexLC(d);
+            NodeValue result = NodeValue.makeString(y);
+            return result;
+        } catch (Exception ex2) {
+            throw new ARQInternalErrorException(ex2);
+        }
+    }
+}
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions2.java 
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions2.java
index 5c2baf8b8f..1812d2b5e5 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions2.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestFunctions2.java
@@ -526,7 +526,7 @@ public class TestFunctions2
     // SHA1
 
     @Test public void sha1_01()      { 
test("sha1('abcd')","'81fe8bfe87576c3ecb22426f8e57847382917acf'") ; }
-    @Test public void sha1_02()            { 
test("sha1('abcd'^^xsd:string)","'81fe8bfe87576c3ecb22426f8e57847382917acf'") ; 
}
+    @Test public void sha1_02()      { 
test("sha1('abcd'^^xsd:string)","'81fe8bfe87576c3ecb22426f8e57847382917acf'") ; 
}
     @Test(expected=ExprEvalException.class)
     public void sha1_03()            { 
test("sha1('abcd'@en)","'81fe8bfe87576c3ecb22426f8e57847382917acf'") ; }
     @Test(expected=ExprEvalException.class)

Reply via email to