diff --git a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/ast/ASTServiceGraphPattern.java b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/ast/ASTServiceGraphPattern.java index b30eda87f36..1f7e38567dd 100644 --- a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/ast/ASTServiceGraphPattern.java +++ b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/ast/ASTServiceGraphPattern.java @@ -57,11 +57,8 @@ public boolean isSilent() { public String getPatternString() { if (patternString == null) { - ASTOperationContainer parentContainer = (ASTOperationContainer) getParentContainer(this); - - String sourceString = parentContainer.getSourceString(); - // snip away line until begin token line position + String sourceString = getSourceString(); String substring = sourceString; for (int i = 1; i < getBeginTokenLinePos(); i++) { substring = substring.substring(substring.indexOf('\n') + 1); @@ -84,6 +81,27 @@ public String getPatternString() { return patternString; } + private String getSourceString() { + Node theParent = getParentContainer(this); + String sourceString = null; + if (theParent instanceof ASTOperationContainer) { + sourceString = ((ASTOperationContainer) theParent).getSourceString(); + } else if (theParent instanceof ASTUpdateSequence) { + sourceString = ((ASTUpdateSequence) theParent).getSourceString(); + } + + while (sourceString == null && theParent != null) { + theParent = theParent.jjtGetParent(); + if (theParent == null) { + break; + } + if (theParent instanceof ASTUpdateSequence) { + sourceString = ((ASTUpdateSequence) theParent).getSourceString(); + } + } + return sourceString; + } + private Node getParentContainer(Node node) { if (node instanceof ASTOperationContainer || node == null) { return node; diff --git a/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/TestServiceUpdateExprBuilder.java b/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/TestServiceUpdateExprBuilder.java new file mode 100644 index 00000000000..b6b55594855 --- /dev/null +++ b/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/TestServiceUpdateExprBuilder.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2020 Eclipse RDF4J contributors. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + *******************************************************************************/ +package org.eclipse.rdf4j.query.parser.sparql; + +import org.eclipse.rdf4j.query.algebra.Service; +import org.eclipse.rdf4j.query.parser.ParsedUpdate; +import org.junit.Test; + +public class TestServiceUpdateExprBuilder { + + /** + * The test reproduces a {@link NullPointerException} that is thrown when parsing an update with several update + * expressions and one of these contain {@link Service} operator The NPE is thrown because the sourceString is set + * to the outermost operation container + */ + @Test + public void testServiceWithMultipleUpdateExpr() { + SPARQLParser parser = new SPARQLParser(); + String updateStr = "PREFIX family: \n" + + "DROP ALL ;\n" + + "INSERT {\n" + + " family:Alice family:knows family:Bob .\n" + + "}\n" + + "WHERE {\n" + + " SERVICE {\n" + + " family:Alice family:knows family:Bob .\n" + + " }\n" + + "}"; + // should not throw NPE, but prior to 3.1.3 it does + parser.parseUpdate(updateStr, null); + } +}