diff --git a/gradle.properties b/gradle.properties index cce7480..9d7acb6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ group = org.xbib name = net -version = 2.0.0 +version = 2.0.1 # test junit.version = 5.5.1 diff --git a/net-url/src/main/java/org/xbib/net/URL.java b/net-url/src/main/java/org/xbib/net/URL.java index 4a069af..1ae655d 100755 --- a/net-url/src/main/java/org/xbib/net/URL.java +++ b/net-url/src/main/java/org/xbib/net/URL.java @@ -11,6 +11,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; +import java.nio.charset.CodingErrorAction; import java.nio.charset.MalformedInputException; import java.nio.charset.StandardCharsets; import java.nio.charset.UnmappableCharacterException; @@ -239,7 +240,11 @@ public class URL implements Comparable { } public static Parser parser() { - return new Parser(); + return new Parser(StandardCharsets.UTF_8, CodingErrorAction.REPORT); + } + + public static Parser parser(Charset charset, CodingErrorAction codingErrorAction) { + return new Parser(charset, codingErrorAction); } public static Resolver base(String base) { @@ -262,42 +267,71 @@ public class URL implements Comparable { } public static URL from(String input) { - return from(input, true); + return from(input, StandardCharsets.UTF_8, CodingErrorAction.REPORT, true, false); } public static URL create(String input) { - return from(input, false); + return from(input, StandardCharsets.UTF_8, CodingErrorAction.REPORT, false, false); } - public static URL from(String input, boolean resolve) { + public static URL create(String input, boolean disableException) { + return from(input, StandardCharsets.UTF_8, CodingErrorAction.REPORT, false, disableException); + } + + public static URL from(String input, + Charset charset, CodingErrorAction codingErrorAction, + boolean resolve, boolean disableException) { try { - return parser().parse(input, resolve); + return parser(charset, codingErrorAction).parse(input, resolve); } catch (URLSyntaxException | MalformedInputException | UnmappableCharacterException e) { - throw new IllegalArgumentException(e); + if (disableException) { + logger.log(Level.WARNING, e.getMessage(), e); + return null; + } else { + throw new IllegalArgumentException(e); + } } } public URL resolve(String spec) { - return from(this, spec); + return from(this, spec, false); } - public static URL from(URL base, String spec) { + public URL resolve(String spec, boolean disableException) { + return from(this, spec, disableException); + } + + public static URL from(URL base, String spec, boolean disableException) { try { return new Resolver(base).resolve(spec); } catch (URLSyntaxException | MalformedInputException | UnmappableCharacterException e) { - throw new IllegalArgumentException(e); + if (disableException) { + logger.log(Level.WARNING, e.getMessage(), e); + return null; + } else { + throw new IllegalArgumentException(e); + } } } public URL resolve(URL spec) { - return from(this, spec); + return from(this, spec, false); } - public static URL from(URL base, URL spec) { + public URL resolve(URL spec, boolean disableException) { + return from(this, spec, disableException); + } + + public static URL from(URL base, URL spec, boolean disableException) { try { return new Resolver(base).resolve(spec); } catch (URLSyntaxException e) { - throw new IllegalArgumentException(e); + if (disableException) { + logger.log(Level.WARNING, e.getMessage(), e); + return null; + } else { + throw new IllegalArgumentException(e); + } } } @@ -319,11 +353,20 @@ public class URL implements Comparable { } public static QueryParameters parseQueryString(String query) { + return parseQueryString(query, false); + } + + public static QueryParameters parseQueryString(String query, boolean disableException) { Objects.requireNonNull(query); try { return URL.parser().parse(query.charAt(0) == QUESTION_CHAR ? query : QUESTION_CHAR + query).getQueryParams(); } catch (URLSyntaxException | MalformedInputException | UnmappableCharacterException e) { - throw new IllegalArgumentException(e); + if (disableException) { + logger.log(Level.WARNING, e.getMessage(), e); + return null; + } else { + throw new IllegalArgumentException(e); + } } } @@ -774,13 +817,13 @@ public class URL implements Comparable { /** * The URL builder class is required for building an URL. It uses fluent API methods - * and pre-processes paralameter accordingly. + * and pre-processes parameter accordingly. */ public static class Builder { private PercentEncoder regNameEncoder; - private final PercentDecoder percentDecoder; + private PercentDecoder percentDecoder; private final QueryParameters queryParams; @@ -788,6 +831,8 @@ public class URL implements Comparable { private Charset charset; + private CodingErrorAction codingErrorAction; + private String scheme; private String schemeSpecificPart; @@ -809,10 +854,9 @@ public class URL implements Comparable { private boolean fatalResolveErrorsEnabled; private Builder() { - charset(StandardCharsets.UTF_8); - this.percentDecoder = new PercentDecoder(); this.queryParams = new QueryParameters(); this.pathSegments = new ArrayList<>(); + charset(StandardCharsets.UTF_8, CodingErrorAction.REPLACE); } /** @@ -820,9 +864,12 @@ public class URL implements Comparable { * @param charset the chaarcter set * @return this builder */ - public Builder charset(Charset charset) { + public Builder charset(Charset charset, CodingErrorAction codingErrorAction) { this.charset = charset; + this.codingErrorAction = codingErrorAction; this.regNameEncoder = PercentEncoders.getRegNameEncoder(charset); + this.percentDecoder = new PercentDecoder(charset.newDecoder() + .onUnmappableCharacter(codingErrorAction)); return this; } @@ -916,7 +963,7 @@ public class URL implements Comparable { public Builder path(String path) { try { - parser().parsePathWithQueryAndFragment(this, path); + parser(charset, codingErrorAction).parsePathWithQueryAndFragment(this, path); } catch (CharacterCodingException e) { throw new IllegalArgumentException(e); } @@ -1044,8 +1091,9 @@ public class URL implements Comparable { private final Builder builder; - private Parser() { + private Parser(Charset charset, CodingErrorAction codingErrorAction) { builder = new Builder(); + builder.charset(charset, codingErrorAction); } public URL parse(String input) @@ -1267,6 +1315,7 @@ public class URL implements Comparable { if (relative.isEmpty()) { return base; } + // TODO(jprante) parser(charset, codingErrorAction) URL url = parser().parse(relative); return resolve(url); } diff --git a/net-url/src/test/java/org/xbib/net/URLParserTest.java b/net-url/src/test/java/org/xbib/net/URLParserTest.java index 59e8684..52d1868 100644 --- a/net-url/src/test/java/org/xbib/net/URLParserTest.java +++ b/net-url/src/test/java/org/xbib/net/URLParserTest.java @@ -3,6 +3,8 @@ package org.xbib.net; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.StandardCharsets; import java.util.Iterator; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -420,6 +422,18 @@ class URLParserTest { assertEquals("foo:bar", url.getPassword()); } + @Test + void testCharset() throws Exception { + // default parser uses UTF-8 + Assertions.assertThrows(URLSyntaxException.class, () -> { + String string = "http%3A%2F%2Flibrary.fes.de%2Flibrary%2Fjournals%2Fde-part%2Fdas-rote-bl%E4ttla%2Findex.html"; + URL url = URL.parser().parse(string); + }); + String string = "http%3A%2F%2Flibrary.fes.de%2Flibrary%2Fjournals%2Fde-part%2Fdas-rote-bl%E4ttla%2Findex.html"; + URL url = URL.parser(StandardCharsets.ISO_8859_1, CodingErrorAction.REPLACE).parse(string); + assertEquals("http://library.fes.de/library/journals/de-part/das-rote-blättla/index.html", url.toString()); + } + private void assertUrlCompatibility(String url) throws Exception { String s = URL.from(url).toExternalForm(); assertEquals(s, URL.from(s).toExternalForm());