Skip to content

Commit 14e3f03

Browse files
committed
GH-3618: Node.sameTermAs(Node) and Node.sameValueAs(Node)
1 parent 84de561 commit 14e3f03

File tree

5 files changed

+74
-55
lines changed

5 files changed

+74
-55
lines changed

jena-core/src/main/java/org/apache/jena/graph/Node.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.io.IOException;
2222
import java.io.ObjectStreamException;
2323
import java.io.Serializable;
24+
import java.util.Objects;
2425
import java.util.function.Function;
2526

2627
import org.apache.jena.datatypes.RDFDatatype ;
@@ -250,22 +251,26 @@ public boolean hasURI( String uri )
250251
* <p>
251252
* The {@link Node} argument must not be null.
252253
*/
253-
public boolean sameTermAs(Object node) {
254+
public boolean sameTermAs(Node node) {
255+
Objects.requireNonNull(node);
254256
return equals(node);
255257
}
256258

257259
/**
258-
* Test that two nodes are semantically equivalent.
259-
* In some cases this may be the same as equals, in others
260-
* equals is stricter. For example, two xsd:int literals with
261-
* the same value but different lexical form are semantically
262-
* equivalent but distinguished by the java equals function.
263-
* <p>Default implementation is to use {@link #equals}
264-
* subclasses should override this.</p>
260+
* Test that two nodes represent the same value.
265261
* <p>
262+
* In some cases this may be the same as {@link #sameTermAs}. For example, two
263+
* xsd:int literals with the same value but different lexical form are
264+
* semantically equivalent but distinguished by the java equals function.
265+
* </p>
266+
* <p>
267+
* The default implementation is to use {@link #equals}; subclasses should
268+
* override this.
269+
* </p>
266270
* The {@link Node} argument must not be null.
267271
*/
268-
public boolean sameValueAs(Object node) {
272+
public boolean sameValueAs(Node node) {
273+
Objects.requireNonNull(node);
269274
return equals(node);
270275
}
271276

jena-core/src/main/java/org/apache/jena/graph/Node_Literal.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public boolean equals(Object obj) {
126126
}
127127

128128
@Override
129-
public boolean sameValueAs(Object other) {
129+
public boolean sameValueAs(Node other) {
130130
if ( other instanceof Node_Literal otherLiteral )
131131
return label.sameValueAs( otherLiteral.getLiteral() );
132132
return false;

jena-core/src/main/java/org/apache/jena/reasoner/rulesys/Node_RuleVariable.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ public int hashCode() {
174174
* Test that two nodes are semantically equivalent.
175175
*/
176176
@Override
177-
public boolean sameValueAs(Object other) {
177+
public boolean sameValueAs(Node other) {
178178
return other instanceof Node_RuleVariable;
179179
}
180180

jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/RETEClauseFilter.java

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -31,47 +31,47 @@
3131
* and bindings are implemented using a simple byte-coded interpreter.
3232
*/
3333
public class RETEClauseFilter implements RETESourceNode {
34-
34+
3535
/** Contains the set of byte-coded instructions and argument pointers */
3636
protected byte[] instructions;
37-
37+
3838
/** Contains the object arguments referenced from the instructions array */
3939
protected Object[] args;
40-
40+
4141
/** The network node to receive any created tokens */
4242
protected RETESinkNode continuation;
43-
43+
4444
/** Instruction code: Check triple entry (arg1) against literal value (arg2). */
4545
public static final byte TESTValue = 0x01;
46-
46+
4747
/** Instruction code: Check literal value is a functor of name arg1 */
4848
public static final byte TESTFunctorName = 0x02;
49-
49+
5050
/** Instruction code: Cross match two triple entries (arg1, arg2) */
5151
public static final byte TESTIntraMatch = 0x03;
52-
52+
5353
/** Instruction code: Create a result environment of length arg1. */
5454
public static final byte CREATEToken = 0x04;
55-
55+
5656
/** Instruction code: Bind a node (arg1) to a place in the rules token (arg2). */
5757
public static final byte BIND = 0x05;
58-
58+
5959
/** Instruction code: Final entry - dispatch to the network. */
6060
public static final byte END = 0x06;
61-
61+
6262
/** Argument addressing code: triple subject */
6363
public static final byte ADDRSubject = 0x10;
64-
64+
6565
/** Argument addressing code: triple predicate */
6666
public static final byte ADDRPredicate = 0x20;
67-
67+
6868
/** Argument addressing code: triple object as a whole */
6969
public static final byte ADDRObject = 0x30;
70-
71-
/** Argument addressing code: triple object functor node, offset in
70+
71+
/** Argument addressing code: triple object functor node, offset in
7272
* low nibble, only usable after a successful TestFunctorName. */
7373
public static final byte ADDRFunctorNode = 0x40;
74-
74+
7575
/**
7676
* Constructor.
7777
* @param instructions the set of byte-coded instructions and argument pointers.
@@ -81,25 +81,25 @@ public RETEClauseFilter(byte[] instructions, Object[] args) {
8181
this.instructions = instructions;
8282
this.args = args;
8383
}
84-
84+
8585
/**
8686
* Create a filter node from a rule clause.
8787
* Clause complexity is limited to less than 50 args in a Functor.
8888
* @param clause the rule clause
8989
* @param envLength the size of binding environment that should be created on successful matches
9090
* @param varList a list to which all clause variables will be appended
9191
*/
92-
public static RETEClauseFilter compile(TriplePattern clause, int envLength, List<Node> varList) {
92+
public static RETEClauseFilter compile(TriplePattern clause, int envLength, List<Node> varList) {
9393
byte[] instructions = new byte[300];
9494
byte[] bindInstructions = new byte[100];
9595
ArrayList<Object> args = new ArrayList<>();
96-
int pc = 0;
96+
int pc = 0;
9797
int bpc = 0;
98-
98+
9999
// Pass 0 - prepare env creation statement
100100
bindInstructions[bpc++] = CREATEToken;
101101
bindInstructions[bpc++] = (byte)envLength;
102-
102+
103103
// Pass 1 - check literal values
104104
Node n = clause.getSubject();
105105
if ( !n.isVariable() ) {
@@ -162,16 +162,16 @@ public static RETEClauseFilter compile(TriplePattern clause, int envLength, List
162162
varList.add(n);
163163
}
164164
bindInstructions[bpc++] = END;
165-
165+
166166
// Pass 4 - Pack instructions
167167
byte[] packed = new byte[pc + bpc];
168168
System.arraycopy(instructions, 0, packed, 0, pc);
169169
System.arraycopy(bindInstructions, 0, packed, pc, bpc);
170170
Object[] packedArgs = args.toArray();
171-
171+
172172
return new RETEClauseFilter(packed, packedArgs);
173173
}
174-
174+
175175
/**
176176
* Set the continuation node for this node.
177177
*/
@@ -186,50 +186,58 @@ public void setContinuation(RETESinkNode continuation) {
186186
* @param isAdd true if the triple is being added to the working set.
187187
*/
188188
public void fire(Triple triple, boolean isAdd) {
189-
189+
190190
Functor lastFunctor = null; // bound by TESTFunctorName
191191
BindingVector env = null; // bound by CREATEToken
192192
Node n = null; // Temp workspace
193-
193+
194194
for (int pc = 0; pc < instructions.length; ) {
195195
switch(instructions[pc++]) {
196-
197-
case TESTValue:
198-
// Check triple entry (arg1) against literal value (arg2)
199-
if (! getTripleValue(triple, instructions[pc++], lastFunctor)
200-
.sameValueAs(args[instructions[pc++]])) return;
201-
break;
202-
196+
197+
case TESTValue:
198+
// Check triple entry (arg1) against literal value (arg2)
199+
Node arg1 = getTripleValue(triple, instructions[pc++], lastFunctor);
200+
Object obj2 = args[instructions[pc++]];
201+
if ( ! ( obj2 instanceof Node arg2) )
202+
return;
203+
if ( ! arg1.sameValueAs(arg2) )
204+
return;
205+
break;
206+
// Was:
207+
// when sameValueAs took an Object as argument (a non-node was then false).
208+
// if (! getTripleValue(triple, instructions[pc++], lastFunctor)
209+
// .sameValueAs(args[instructions[pc++]])) return;
210+
// break;
211+
203212
case TESTFunctorName:
204213
// Check literal value is a functor of name arg1.
205-
// Side effect: leaves a loop variable pointing to functor
214+
// Side effect: leaves a loop variable pointing to functor
206215
// for possible later functor argument accesses
207216
n = triple.getObject();
208217
if ( !n.isLiteral() ) return;
209218
if ( n.getLiteralDatatype() != FunctorDatatype.theFunctorDatatype) return;
210219
lastFunctor = (Functor)n.getLiteralValue();
211220
if ( !lastFunctor.getName().equals(args[instructions[pc++]]) ) return;
212221
break;
213-
222+
214223
case CREATEToken:
215224
// Create a result environment of length arg1
216225
env = new BindingVector(new Node[instructions[pc++]]);
217226
break;
218-
227+
219228
case BIND:
220229
// Bind a node (arg1) to a place in the rules token (arg2)
221230
n = getTripleValue(triple, instructions[pc++], lastFunctor);
222231
if ( !env.bind(instructions[pc++], n) ) return;
223232
break;
224-
233+
225234
case END:
226235
// Success, fire the continuation
227236
continuation.fire(env, isAdd);
228237
}
229238
}
230-
231239
}
232-
240+
233241
/**
234242
* Helpful function. Return the node from the argument triple
235243
* corresponding to the byte code address.
@@ -247,7 +255,7 @@ private Node getTripleValue(Triple triple, byte address, Functor lastFunctor) {
247255
}
248256
return null;
249257
}
250-
258+
251259
/**
252260
* Clone this node in the network.
253261
* @param netCopy a map from RETENode to cloned instance
@@ -263,5 +271,5 @@ public RETENode clone(Map<RETENode, RETENode> netCopy, RETERuleContext context)
263271
}
264272
return clone;
265273
}
266-
274+
267275
}

jena-core/src/test/java/org/apache/jena/graph/test/TestNode.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -628,11 +628,17 @@ public void testSimpleMatches() {
628628
assertFalse("", NodeCreateUtils.create("_X").sameTermAs(NodeCreateUtils.create("_Y")));
629629
assertTrue(NodeCreateUtils.create("10").sameTermAs(NodeCreateUtils.create("10")));
630630
assertFalse("", NodeCreateUtils.create("10").sameTermAs(NodeCreateUtils.create("11")));
631+
// Jena6. nulls no longer allowed.
631632

632-
assertFalse("", NodeCreateUtils.create("S").sameTermAs(null));
633-
assertFalse("", NodeCreateUtils.create("_X").sameTermAs(null));
634-
assertFalse("", NodeCreateUtils.create("10").sameTermAs(null));
635-
assertFalse("", Node.ANY.sameTermAs(null));
633+
try {
634+
NodeCreateUtils.create("S").sameTermAs(null);
635+
fail("Expected NullPointerException");
636+
} catch (NullPointerException ex) {}
637+
638+
// assertFalse("", NodeCreateUtils.create("S").sameTermAs(null));
639+
// assertFalse("", NodeCreateUtils.create("_X").sameTermAs(null));
640+
// assertFalse("", NodeCreateUtils.create("10").sameTermAs(null));
641+
// assertFalse("", Node.ANY.sameTermAs(null));
636642
}
637643

638644
public void testDataSameValue() {

0 commit comments

Comments
 (0)