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)
