Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-5215 Support apostrophe in local name for SPARQL 1.1 and Turtle/Trig/etc. #5216

Merged
merged 5 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,19 @@ public static boolean isValidLocalName(String name) {

for (int i = 1; i < name.length(); i++) {
if (!isNameChar(name.charAt(i))) {

// PLX
if (name.charAt(i) == '%') {
continue;
} else if (name.charAt(i) == '\\') {
if (i + 1 < name.length() && isPN_LOCAL_ESC(name.substring(i, i + 2))) {
i++;
continue;
} else {
return false;
}
}

return false;
}

Expand Down Expand Up @@ -341,7 +354,7 @@ private static boolean isNameStartChar(int codePoint) {
* @return <code>true</code> if the supplied code point represents a valid name char, <code>false</code> otherwise.
*/
private static boolean isNameChar(int codePoint) {
return isPN_CHARS(codePoint) || codePoint == '.' || codePoint == ':' | codePoint == '\\' || codePoint == '%';
return isPN_CHARS(codePoint) || codePoint == '.' || codePoint == ':';
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,6 @@ public void isValidLocalName() {
assertFalse(URIUtil.isValidLocalName("foo\tbar"));
assertFalse(URIUtil.isValidLocalName("foo\nbar"));
assertFalse(URIUtil.isValidLocalName("*foobar"));
assertTrue(URIUtil.isValidLocalName("fo\\'obar"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2296,7 +2296,7 @@ private int jjMoveNfa_0_curCharLessThan64(int startsAt, int i, int kind) {
}
break;
case 82:
if ((0xa800ff7e00000000L & l) != 0L) {
if ((0xa800fffa00000000L & l) != 0L) {
jjCheckNAddStates(44, 47);
}
break;
Expand Down Expand Up @@ -2326,12 +2326,12 @@ private int jjMoveNfa_0_curCharLessThan64(int startsAt, int i, int kind) {
}
break;
case 88:
if ((0xa800ff7e00000000L & l) != 0L && kind > 146) {
if ((0xa800fffa00000000L & l) != 0L && kind > 146) {
kind = 146;
}
break;
case 90:
if ((0xa800ff7e00000000L & l) == 0L) {
if ((0xa800fffa00000000L & l) == 0L) {
break;
}
if (kind > 146) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ TOKEN:
| <#PN_LOCAL: (<PN_CHARS_U> | ":" | <NUM> | <PLX> ) ( (<PN_CHARS> | "." | ":" | <PLX>)* ( <PN_CHARS> | ":" | <PLX>) )?>
| <#PLX: <PERCENT> | <PN_LOCAL_ESC>>
| <#PERCENT: "%" <HEX> <HEX>>
| <#PN_LOCAL_ESC: "\\" [ "_", "~", ".", "-", "!", "$", "&", "\"", "(", ")", "*", "+", ",", ";", "=", "/", "?", "#", "@", "%" ]>
| <#PN_LOCAL_ESC: "\\" [ "_", "~", ".", "-", "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=", "/", "?", "#", "@", "%" ]>
| <#VARNAME: (<PN_CHARS_U> | <NUM> ) (<VAR_CHAR>)*>
| <TRIPLE_OPEN: "<<">
| <TRIPLE_CLOSE: ">>">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,32 @@ public void testGroupByProjectionHandling_function() {
verifySerializable(parsedQuery.getTupleExpr());
}

@Test
public void testApostrophe() {
String query = "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n" +
"select * where { \n" +
" rdf:Test\\'s ?p1 ?o1\n" +
"}";

HashSet<Namespace> customPrefixes = new HashSet<>();
SPARQLParser parser = new SPARQLParser(customPrefixes);

parser.parseQuery(query, null);
}

@Test
public void testApostropheInsertData() {
String query = "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n" +
"INSERT DATA { \n" +
" rdf:Test\\'s a <http://example.com/Test> .\n" +
"}";

HashSet<Namespace> customPrefixes = new HashSet<>();
SPARQLParser parser = new SPARQLParser(customPrefixes);

parser.parseUpdate(query, null);
}

private AggregateFunctionFactory buildDummyFactory() {
return new AggregateFunctionFactory() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ d:<http://example/d/>.
_:s{:s :p :o .:s :p"Alice".:s :p _:o.}
:{: : :}{: : :}:{: : :}
:{():()}{():[]}:{[]:[]}
{b:a\'s a :Foo}

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.Arrays;

import org.eclipse.rdf4j.common.text.ASCIIUtil;
import org.eclipse.rdf4j.model.util.URIUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -366,41 +367,7 @@ public static boolean isPN_LOCAL_ESC(String name) {
}

public static boolean isPN_LOCAL(String name) {
// Empty names are legal
if (name.isEmpty()) {
return true;
}

if (!isPN_CHARS_U(name.charAt(0)) && name.charAt(0) != ':' && !ASCIIUtil.isNumber(name.charAt(0))
&& !isPLX_START(name)) {
logger.debug("PN_LOCAL was not valid (start characters invalid) i=" + 0 + " nextchar="
+ name.charAt(0) + " name=" + name);
return false;
}

if (!isNameStartChar(name.charAt(0))) {
logger.debug("name was not valid (start character invalid) i=" + 0 + " nextchar=" + name.charAt(0)
+ " name=" + name);
return false;
}

for (int i = 1; i < name.length(); i++) {
if (!isNameChar(name.charAt(i))) {
logger.debug("name was not valid (intermediate character invalid) i=" + i + " nextchar="
+ name.charAt(i) + " name=" + name);
return false;
}

// Check if the percent encoding was less than two characters from the
// end of the prefix, in which case it is invalid
if (name.charAt(i) == '%' && (name.length() - i) < 3) {
logger.debug("name was not valid (short percent escape) i=" + i + " nextchar=" + name.charAt(i)
+ " name=" + name);
return false;
}
}

return true;
return URIUtil.isValidLocalName(name);
}

// public static boolean isLegalName(String name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ public final void testIsPN_LOCAL() {
assertFalse(TurtleUtil.isPN_LOCAL("foo\tbar"));
assertFalse(TurtleUtil.isPN_LOCAL("foo\nbar"));
assertFalse(TurtleUtil.isPN_LOCAL("*foobar"));
assertTrue(TurtleUtil.isPN_LOCAL("foo\\'bar"));
}

/**
Expand Down
3 changes: 2 additions & 1 deletion core/rio/turtle/src/test/resources/test-newlines.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
ex:a a ex:Foo .
ex:b a ex:Foo .
ex:b ex:value '''^M'''.
ex:c
ex:c
:foo ex:Foo .
rdf:Test\'s a ex:Foo .
ex:a ex:value 1.0 .
ex:a ex:value 3.0 .
90 changes: 48 additions & 42 deletions e2e/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"author": "",
"license": "ISC",
"devDependencies": {
"@playwright/test": "^1.39.0",
"@types/node": "^20.8.7"
"@playwright/test": "^1.49.0",
"@types/node": "^22.10.1"
}
}
10 changes: 6 additions & 4 deletions e2e/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@

set -e

npm install

for APP_SERVER in tomcat jetty; do
export APP_SERVER

cd ..
cd docker
./run.sh
Expand All @@ -38,16 +40,16 @@ for APP_SERVER in tomcat jetty; do

cd ..
cd docker
./shutdown.sh
./shutdown.sh

# test for error code
if [ $status_npx -ne 0 ] ; then
echo "Error in E2E test for $APP_SERVER"
exit $status_npx
fi

echo "E2E test for $APP_SERVER OK"

# don't redo the whole build process just for making another docker image
export SKIP_BUILD="skip"
done
Expand Down
Loading