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() {

Reply via email to