diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/util/URIUtil.java b/core/model/src/main/java/org/eclipse/rdf4j/model/util/URIUtil.java index e3495f23d43..8fb9789c0df 100644 --- a/core/model/src/main/java/org/eclipse/rdf4j/model/util/URIUtil.java +++ b/core/model/src/main/java/org/eclipse/rdf4j/model/util/URIUtil.java @@ -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; } @@ -341,7 +354,7 @@ private static boolean isNameStartChar(int codePoint) { * @return true if the supplied code point represents a valid name char, false otherwise. */ private static boolean isNameChar(int codePoint) { - return isPN_CHARS(codePoint) || codePoint == '.' || codePoint == ':' | codePoint == '\\' || codePoint == '%'; + return isPN_CHARS(codePoint) || codePoint == '.' || codePoint == ':'; } /** diff --git a/core/model/src/test/java/org/eclipse/rdf4j/model/util/URIUtilTest.java b/core/model/src/test/java/org/eclipse/rdf4j/model/util/URIUtilTest.java index 767ef29ae09..f8ade38b236 100644 --- a/core/model/src/test/java/org/eclipse/rdf4j/model/util/URIUtilTest.java +++ b/core/model/src/test/java/org/eclipse/rdf4j/model/util/URIUtilTest.java @@ -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")); } } diff --git a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/ast/SyntaxTreeBuilderTokenManager.java b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/ast/SyntaxTreeBuilderTokenManager.java index 7844df6e4b5..4e48ce7b458 100644 --- a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/ast/SyntaxTreeBuilderTokenManager.java +++ b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/ast/SyntaxTreeBuilderTokenManager.java @@ -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; @@ -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) { diff --git a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/ast/sparql.jjt b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/ast/sparql.jjt index 1c1fc5eaf65..83f93871829 100644 --- a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/ast/sparql.jjt +++ b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/ast/sparql.jjt @@ -345,7 +345,7 @@ TOKEN: | <#PN_LOCAL: ( | ":" | | ) ( ( | "." | ":" | )* ( | ":" | ) )?> | <#PLX: | > | <#PERCENT: "%" > -| <#PN_LOCAL_ESC: "\\" [ "_", "~", ".", "-", "!", "$", "&", "\"", "(", ")", "*", "+", ",", ";", "=", "/", "?", "#", "@", "%" ]> +| <#PN_LOCAL_ESC: "\\" [ "_", "~", ".", "-", "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=", "/", "?", "#", "@", "%" ]> | <#VARNAME: ( | ) ()*> | | >"> diff --git a/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/SPARQLParserTest.java b/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/SPARQLParserTest.java index 81912e2b320..fa963633783 100644 --- a/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/SPARQLParserTest.java +++ b/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/SPARQLParserTest.java @@ -976,6 +976,32 @@ public void testGroupByProjectionHandling_function() { verifySerializable(parsedQuery.getTupleExpr()); } + @Test + public void testApostrophe() { + String query = "PREFIX rdf: \n" + + "select * where { \n" + + " rdf:Test\\'s ?p1 ?o1\n" + + "}"; + + HashSet customPrefixes = new HashSet<>(); + SPARQLParser parser = new SPARQLParser(customPrefixes); + + parser.parseQuery(query, null); + } + + @Test + public void testApostropheInsertData() { + String query = "PREFIX rdf: \n" + + "INSERT DATA { \n" + + " rdf:Test\\'s a .\n" + + "}"; + + HashSet customPrefixes = new HashSet<>(); + SPARQLParser parser = new SPARQLParser(customPrefixes); + + parser.parseUpdate(query, null); + } + private AggregateFunctionFactory buildDummyFactory() { return new AggregateFunctionFactory() { @Override diff --git a/core/rio/trig/src/test/resources/testcases/trig/trig-syntax-minimal-whitespace-01.trig b/core/rio/trig/src/test/resources/testcases/trig/trig-syntax-minimal-whitespace-01.trig index f53451f8759..b7f3f441d14 100644 --- a/core/rio/trig/src/test/resources/testcases/trig/trig-syntax-minimal-whitespace-01.trig +++ b/core/rio/trig/src/test/resources/testcases/trig/trig-syntax-minimal-whitespace-01.trig @@ -19,3 +19,5 @@ d:. _:s{:s :p :o .:s :p"Alice".:s :p _:o.} :{: : :}{: : :}:{: : :} :{():()}{():[]}:{[]:[]} +{b:a\'s a :Foo} + diff --git a/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleUtil.java b/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleUtil.java index b7b9985449f..9ce214265e3 100644 --- a/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleUtil.java +++ b/core/rio/turtle/src/main/java/org/eclipse/rdf4j/rio/turtle/TurtleUtil.java @@ -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; @@ -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) { diff --git a/core/rio/turtle/src/test/java/org/eclipse/rdf4j/rio/turtle/TurtleUtilTest.java b/core/rio/turtle/src/test/java/org/eclipse/rdf4j/rio/turtle/TurtleUtilTest.java index d884ca41496..279bf5b5e77 100644 --- a/core/rio/turtle/src/test/java/org/eclipse/rdf4j/rio/turtle/TurtleUtilTest.java +++ b/core/rio/turtle/src/test/java/org/eclipse/rdf4j/rio/turtle/TurtleUtilTest.java @@ -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")); } /** diff --git a/core/rio/turtle/src/test/resources/test-newlines.ttl b/core/rio/turtle/src/test/resources/test-newlines.ttl index 677f2d33622..86b88ace9e5 100644 --- a/core/rio/turtle/src/test/resources/test-newlines.ttl +++ b/core/rio/turtle/src/test/resources/test-newlines.ttl @@ -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 . diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 4f552816923..69bda428af6 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -9,32 +9,34 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "@playwright/test": "^1.39.0", - "@types/node": "^20.8.7" + "@playwright/test": "^1.49.0", + "@types/node": "^22.10.1" } }, "node_modules/@playwright/test": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz", - "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==", + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0.tgz", + "integrity": "sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "playwright": "1.39.0" + "playwright": "1.49.0" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@types/node": { - "version": "20.8.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz", - "integrity": "sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==", + "version": "22.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", + "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.25.1" + "undici-types": "~6.20.0" } }, "node_modules/fsevents": { @@ -43,6 +45,7 @@ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -52,59 +55,62 @@ } }, "node_modules/playwright": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", - "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0.tgz", + "integrity": "sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.39.0" + "playwright-core": "1.49.0" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" }, "optionalDependencies": { "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", - "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0.tgz", + "integrity": "sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==", "dev": true, + "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/undici-types": { - "version": "5.25.3", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", - "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", - "dev": true + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" } }, "dependencies": { "@playwright/test": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz", - "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==", + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0.tgz", + "integrity": "sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw==", "dev": true, "requires": { - "playwright": "1.39.0" + "playwright": "1.49.0" } }, "@types/node": { - "version": "20.8.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz", - "integrity": "sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==", + "version": "22.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", + "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", "dev": true, "requires": { - "undici-types": "~5.25.1" + "undici-types": "~6.20.0" } }, "fsevents": { @@ -115,25 +121,25 @@ "optional": true }, "playwright": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", - "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0.tgz", + "integrity": "sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A==", "dev": true, "requires": { "fsevents": "2.3.2", - "playwright-core": "1.39.0" + "playwright-core": "1.49.0" } }, "playwright-core": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", - "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0.tgz", + "integrity": "sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==", "dev": true }, "undici-types": { - "version": "5.25.3", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", - "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true } } diff --git a/e2e/package.json b/e2e/package.json index 5d2cea305d4..0665994aef6 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -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" } } diff --git a/e2e/run.sh b/e2e/run.sh index 8fb93afce7e..f5a1b35f54e 100755 --- a/e2e/run.sh +++ b/e2e/run.sh @@ -12,9 +12,11 @@ set -e +npm install + for APP_SERVER in tomcat jetty; do export APP_SERVER - + cd .. cd docker ./run.sh @@ -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