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 abd21173a8808d3621a743f0651c653e0ef739bd Author: Andy Seaborne <[email protected]> AuthorDate: Fri Dec 5 12:38:25 2025 +0000 GH-3618: Node.sameTermAs(Node) and Node.sameValueAs(Node) --- .../src/main/java/org/apache/jena/graph/Node.java | 23 +++--- .../java/org/apache/jena/graph/Node_Literal.java | 2 +- .../jena/reasoner/rulesys/Node_RuleVariable.java | 2 +- .../reasoner/rulesys/impl/RETEClauseFilter.java | 88 ++++++++++++---------- .../java/org/apache/jena/graph/test/TestNode.java | 14 +++- 5 files changed, 74 insertions(+), 55 deletions(-) diff --git a/jena-core/src/main/java/org/apache/jena/graph/Node.java b/jena-core/src/main/java/org/apache/jena/graph/Node.java index 35d1d20679..80d5ceb4bd 100644 --- a/jena-core/src/main/java/org/apache/jena/graph/Node.java +++ b/jena-core/src/main/java/org/apache/jena/graph/Node.java @@ -21,6 +21,7 @@ package org.apache.jena.graph; import java.io.IOException; import java.io.ObjectStreamException; import java.io.Serializable; +import java.util.Objects; import java.util.function.Function; import org.apache.jena.datatypes.RDFDatatype ; @@ -250,22 +251,26 @@ public abstract class Node implements Serializable { * <p> * The {@link Node} argument must not be null. */ - public boolean sameTermAs(Object node) { + public boolean sameTermAs(Node node) { + Objects.requireNonNull(node); return equals(node); } /** - * Test that two nodes are semantically equivalent. - * In some cases this may be the same as equals, in others - * equals is stricter. For example, two xsd:int literals with - * the same value but different lexical form are semantically - * equivalent but distinguished by the java equals function. - * <p>Default implementation is to use {@link #equals} - * subclasses should override this.</p> + * Test that two nodes represent the same value. * <p> + * In some cases this may be the same as {@link #sameTermAs}. For example, two + * xsd:int literals with the same value but different lexical form are + * semantically equivalent but distinguished by the java equals function. + * </p> + * <p> + * The default implementation is to use {@link #equals}; subclasses should + * override this. + * </p> * The {@link Node} argument must not be null. */ - public boolean sameValueAs(Object node) { + public boolean sameValueAs(Node node) { + Objects.requireNonNull(node); return equals(node); } diff --git a/jena-core/src/main/java/org/apache/jena/graph/Node_Literal.java b/jena-core/src/main/java/org/apache/jena/graph/Node_Literal.java index 92050a7aed..70e2353132 100644 --- a/jena-core/src/main/java/org/apache/jena/graph/Node_Literal.java +++ b/jena-core/src/main/java/org/apache/jena/graph/Node_Literal.java @@ -126,7 +126,7 @@ public class Node_Literal extends Node } @Override - public boolean sameValueAs(Object other) { + public boolean sameValueAs(Node other) { if ( other instanceof Node_Literal otherLiteral ) return label.sameValueAs( otherLiteral.getLiteral() ); return false; diff --git a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/Node_RuleVariable.java b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/Node_RuleVariable.java index 5f54e3c2dd..7fa62177cf 100755 --- a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/Node_RuleVariable.java +++ b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/Node_RuleVariable.java @@ -174,7 +174,7 @@ public class Node_RuleVariable extends Node_Variable { * Test that two nodes are semantically equivalent. */ @Override - public boolean sameValueAs(Object other) { + public boolean sameValueAs(Node other) { return other instanceof Node_RuleVariable; } diff --git a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/RETEClauseFilter.java b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/RETEClauseFilter.java index f864eab2aa..f16ce4488c 100644 --- a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/RETEClauseFilter.java +++ b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/RETEClauseFilter.java @@ -31,47 +31,47 @@ import org.apache.jena.reasoner.rulesys.* ; * and bindings are implemented using a simple byte-coded interpreter. */ public class RETEClauseFilter implements RETESourceNode { - + /** Contains the set of byte-coded instructions and argument pointers */ protected byte[] instructions; - + /** Contains the object arguments referenced from the instructions array */ protected Object[] args; - + /** The network node to receive any created tokens */ protected RETESinkNode continuation; - + /** Instruction code: Check triple entry (arg1) against literal value (arg2). */ public static final byte TESTValue = 0x01; - + /** Instruction code: Check literal value is a functor of name arg1 */ public static final byte TESTFunctorName = 0x02; - + /** Instruction code: Cross match two triple entries (arg1, arg2) */ public static final byte TESTIntraMatch = 0x03; - + /** Instruction code: Create a result environment of length arg1. */ public static final byte CREATEToken = 0x04; - + /** Instruction code: Bind a node (arg1) to a place in the rules token (arg2). */ public static final byte BIND = 0x05; - + /** Instruction code: Final entry - dispatch to the network. */ public static final byte END = 0x06; - + /** Argument addressing code: triple subject */ public static final byte ADDRSubject = 0x10; - + /** Argument addressing code: triple predicate */ public static final byte ADDRPredicate = 0x20; - + /** Argument addressing code: triple object as a whole */ public static final byte ADDRObject = 0x30; - - /** Argument addressing code: triple object functor node, offset in + + /** Argument addressing code: triple object functor node, offset in * low nibble, only usable after a successful TestFunctorName. */ public static final byte ADDRFunctorNode = 0x40; - + /** * Constructor. * @param instructions the set of byte-coded instructions and argument pointers. @@ -81,7 +81,7 @@ public class RETEClauseFilter implements RETESourceNode { this.instructions = instructions; this.args = args; } - + /** * Create a filter node from a rule clause. * Clause complexity is limited to less than 50 args in a Functor. @@ -89,17 +89,17 @@ public class RETEClauseFilter implements RETESourceNode { * @param envLength the size of binding environment that should be created on successful matches * @param varList a list to which all clause variables will be appended */ - public static RETEClauseFilter compile(TriplePattern clause, int envLength, List<Node> varList) { + public static RETEClauseFilter compile(TriplePattern clause, int envLength, List<Node> varList) { byte[] instructions = new byte[300]; byte[] bindInstructions = new byte[100]; ArrayList<Object> args = new ArrayList<>(); - int pc = 0; + int pc = 0; int bpc = 0; - + // Pass 0 - prepare env creation statement bindInstructions[bpc++] = CREATEToken; bindInstructions[bpc++] = (byte)envLength; - + // Pass 1 - check literal values Node n = clause.getSubject(); if ( !n.isVariable() ) { @@ -162,16 +162,16 @@ public class RETEClauseFilter implements RETESourceNode { varList.add(n); } bindInstructions[bpc++] = END; - + // Pass 4 - Pack instructions byte[] packed = new byte[pc + bpc]; System.arraycopy(instructions, 0, packed, 0, pc); System.arraycopy(bindInstructions, 0, packed, pc, bpc); Object[] packedArgs = args.toArray(); - + return new RETEClauseFilter(packed, packedArgs); } - + /** * Set the continuation node for this node. */ @@ -186,23 +186,32 @@ public class RETEClauseFilter implements RETESourceNode { * @param isAdd true if the triple is being added to the working set. */ public void fire(Triple triple, boolean isAdd) { - + Functor lastFunctor = null; // bound by TESTFunctorName BindingVector env = null; // bound by CREATEToken Node n = null; // Temp workspace - + for (int pc = 0; pc < instructions.length; ) { switch(instructions[pc++]) { - - case TESTValue: - // Check triple entry (arg1) against literal value (arg2) - if (! getTripleValue(triple, instructions[pc++], lastFunctor) - .sameValueAs(args[instructions[pc++]])) return; - break; - + + case TESTValue: + // Check triple entry (arg1) against literal value (arg2) + Node arg1 = getTripleValue(triple, instructions[pc++], lastFunctor); + Object obj2 = args[instructions[pc++]]; + if ( ! ( obj2 instanceof Node arg2) ) + return; + if ( ! arg1.sameValueAs(arg2) ) + return; + break; + // Was: + // when sameValueAs took an Object as argument (a non-node was then false). + // if (! getTripleValue(triple, instructions[pc++], lastFunctor) + // .sameValueAs(args[instructions[pc++]])) return; + // break; + case TESTFunctorName: // Check literal value is a functor of name arg1. - // Side effect: leaves a loop variable pointing to functor + // Side effect: leaves a loop variable pointing to functor // for possible later functor argument accesses n = triple.getObject(); if ( !n.isLiteral() ) return; @@ -210,26 +219,25 @@ public class RETEClauseFilter implements RETESourceNode { lastFunctor = (Functor)n.getLiteralValue(); if ( !lastFunctor.getName().equals(args[instructions[pc++]]) ) return; break; - + case CREATEToken: // Create a result environment of length arg1 env = new BindingVector(new Node[instructions[pc++]]); break; - + case BIND: // Bind a node (arg1) to a place in the rules token (arg2) n = getTripleValue(triple, instructions[pc++], lastFunctor); if ( !env.bind(instructions[pc++], n) ) return; break; - + case END: // Success, fire the continuation continuation.fire(env, isAdd); } } - } - + /** * Helpful function. Return the node from the argument triple * corresponding to the byte code address. @@ -247,7 +255,7 @@ public class RETEClauseFilter implements RETESourceNode { } return null; } - + /** * Clone this node in the network. * @param netCopy a map from RETENode to cloned instance @@ -263,5 +271,5 @@ public class RETEClauseFilter implements RETESourceNode { } return clone; } - + } diff --git a/jena-core/src/test/java/org/apache/jena/graph/test/TestNode.java b/jena-core/src/test/java/org/apache/jena/graph/test/TestNode.java index ed7271d26e..4f682d5737 100644 --- a/jena-core/src/test/java/org/apache/jena/graph/test/TestNode.java +++ b/jena-core/src/test/java/org/apache/jena/graph/test/TestNode.java @@ -628,11 +628,17 @@ public class TestNode extends GraphTestBase { assertFalse("", NodeCreateUtils.create("_X").sameTermAs(NodeCreateUtils.create("_Y"))); assertTrue(NodeCreateUtils.create("10").sameTermAs(NodeCreateUtils.create("10"))); assertFalse("", NodeCreateUtils.create("10").sameTermAs(NodeCreateUtils.create("11"))); + // Jena6. nulls no longer allowed. - assertFalse("", NodeCreateUtils.create("S").sameTermAs(null)); - assertFalse("", NodeCreateUtils.create("_X").sameTermAs(null)); - assertFalse("", NodeCreateUtils.create("10").sameTermAs(null)); - assertFalse("", Node.ANY.sameTermAs(null)); + try { + NodeCreateUtils.create("S").sameTermAs(null); + fail("Expected NullPointerException"); + } catch (NullPointerException ex) {} + +// assertFalse("", NodeCreateUtils.create("S").sameTermAs(null)); +// assertFalse("", NodeCreateUtils.create("_X").sameTermAs(null)); +// assertFalse("", NodeCreateUtils.create("10").sameTermAs(null)); +// assertFalse("", Node.ANY.sameTermAs(null)); } public void testDataSameValue() {
