Skip to content
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 @@ -16,8 +16,6 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
Expand Down Expand Up @@ -203,14 +201,34 @@ private static boolean forbiddenIriCharacter(int c) {
}

private void validateIri(String text) {
try {
URI uri = new URI(text);
if (!uri.isAbsolute()) {
throw error("N-Quads requires an absolute IRI");
if (!isAbsoluteIri(text)) {
throw error("N-Quads requires an absolute IRI");
}
}

private static boolean isAbsoluteIri(String text) {
if (text == null || text.isEmpty() || !isAsciiAlpha(text.charAt(0))) {
return false;
}
int i = 1;
int length = text.length();
while (i < length) {
char c = text.charAt(i);
if (isSchemeChar(c)) {
i++;
} else {
break;
}
} catch (URISyntaxException e) {
throw new ParsingException(location("Invalid absolute IRI: " + e.getReason()), e);
}
return i < length && text.charAt(i) == ':';
}

private static boolean isAsciiAlpha(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}

private static boolean isSchemeChar(char c) {
return isAsciiAlpha(c) || (c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.';
}

private BNode blankNode() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
Expand Down Expand Up @@ -182,14 +180,34 @@ private static boolean forbiddenIriCharacter(int c) {
}

private void validateIri(String text) {
try {
URI uri = new URI(text);
if (!uri.isAbsolute()) {
throw error("N-Triples requires an absolute IRI");
if (!isAbsoluteIri(text)) {
throw error("N-Triples requires an absolute IRI");
}
}

private static boolean isAbsoluteIri(String text) {
if (text == null || text.isEmpty() || !isAsciiAlpha(text.charAt(0))) {
return false;
}
int i = 1;
int length = text.length();
while (i < length) {
char c = text.charAt(i);
if (isSchemeChar(c)) {
i++;
} else {
break;
}
} catch (URISyntaxException e) {
throw new ParsingException(location("Invalid absolute IRI: " + e.getReason()), e);
}
return i < length && text.charAt(i) == ':';
}

private static boolean isAsciiAlpha(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}

private static boolean isSchemeChar(char c) {
return isAsciiAlpha(c) || (c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.';
}

private BNode blankNode() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.io.StringReader;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;

/**
Expand Down Expand Up @@ -195,4 +196,36 @@ void testParseUnicodeEscapeIRIUxInGraph() throws ParsingException {
parser.parse(new StringReader(nquad));
verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI, expectedGraphIRI);
}

@Test
@DisplayName("Test parsing a quad with non-ASCII Unicode characters in IRI (W3C test060 NBSP and math symbols)")
void testParseNonAsciiUnicodeInIRI() throws ParsingException {
String nquad = "<urn:ex:s:003> <urn:ex:020> <urn:ex:\\u00a0> <urn:ex:\\u221e> .";
IRI expectedSubject = mock(IRI.class);
IRI expectedPredicate = mock(IRI.class);
IRI expectedObject = mock(IRI.class);
IRI expectedGraph = mock(IRI.class);

lenient().when(mockValueFactory.createIRI(eq("urn:ex:s:003"))).thenReturn(expectedSubject);
lenient().when(mockValueFactory.createIRI(eq("urn:ex:020"))).thenReturn(expectedPredicate);
lenient().when(mockValueFactory.createIRI(eq("urn:ex:\u00a0"))).thenReturn(expectedObject);
lenient().when(mockValueFactory.createIRI(eq("urn:ex:\u221e"))).thenReturn(expectedGraph);

parser.parse(new StringReader(nquad));
verify(mockModel).add(expectedSubject, expectedPredicate, expectedObject, expectedGraph);
}

@Test
@DisplayName("Test parsing rejects relative IRIs in N-Quads")
void testParseRejectsRelativeIRI() {
String nquad = "<relative/iri> <http://example.org/p> <http://example.org/o> .";
assertThrows(ParsingException.class, () -> parser.parse(new StringReader(nquad)));
}

@Test
@DisplayName("Test parsing rejects IRIs without valid scheme")
void testParseRejectsInvalidScheme() {
String nquad = "<:invalidScheme> <http://example.org/p> <http://example.org/o> .";
assertThrows(ParsingException.class, () -> parser.parse(new StringReader(nquad)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.io.StringReader;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;

/**
Expand Down Expand Up @@ -210,4 +211,34 @@ void testParseUnicodeEscapeIRIU() throws ParsingException {
parser.parse(new StringReader(ntriple));
verify(mockModel).add(expectedSubjectIRI, mockPredicateIRI, mockObjectIRI);
}

@Test
@DisplayName("Test parsing a triple with non-ASCII Unicode characters in IRI (W3C test060 NBSP and math symbols)")
void testParseNonAsciiUnicodeInIRI() throws ParsingException {
String ntriple = "<urn:ex:s:003> <urn:ex:020> <urn:ex:\\u00a0> .";
IRI expectedSubject = mock(IRI.class);
IRI expectedPredicate = mock(IRI.class);
IRI expectedObject = mock(IRI.class);

lenient().when(mockValueFactory.createIRI(eq("urn:ex:s:003"))).thenReturn(expectedSubject);
lenient().when(mockValueFactory.createIRI(eq("urn:ex:020"))).thenReturn(expectedPredicate);
lenient().when(mockValueFactory.createIRI(eq("urn:ex:\u00a0"))).thenReturn(expectedObject);

parser.parse(new StringReader(ntriple));
verify(mockModel).add(expectedSubject, expectedPredicate, expectedObject);
}

@Test
@DisplayName("Test parsing rejects relative IRIs in N-Triples")
void testParseRejectsRelativeIRI() {
String ntriple = "<relative/iri> <http://example.org/p> <http://example.org/o> .";
assertThrows(ParsingException.class, () -> parser.parse(new StringReader(ntriple)));
}

@Test
@DisplayName("Test parsing rejects IRIs without valid scheme in N-Triples")
void testParseRejectsInvalidScheme() {
String ntriple = "<:invalidScheme> <http://example.org/p> <http://example.org/o> .";
assertThrows(ParsingException.class, () -> parser.parse(new StringReader(ntriple)));
}
}
Loading