diff --git a/http/src/main/java/io/micronaut/http/uri/DefaultUriBuilder.java b/http/src/main/java/io/micronaut/http/uri/DefaultUriBuilder.java index 839f1017be..8d9a0accbc 100644 --- a/http/src/main/java/io/micronaut/http/uri/DefaultUriBuilder.java +++ b/http/src/main/java/io/micronaut/http/uri/DefaultUriBuilder.java @@ -15,6 +15,8 @@ */ package io.micronaut.http.uri; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.annotation.Nullable; import io.micronaut.core.convert.value.MutableConvertibleMultiValues; import io.micronaut.core.convert.value.MutableConvertibleMultiValuesMap; import io.micronaut.core.util.ArrayUtils; @@ -22,14 +24,14 @@ import io.micronaut.core.util.StringUtils; import io.micronaut.http.exceptions.UriSyntaxException; -import io.micronaut.core.annotation.NonNull; -import io.micronaut.core.annotation.Nullable; -import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -57,7 +59,7 @@ class DefaultUriBuilder implements UriBuilder { private static final Pattern PATTERN_FULL_URI = Pattern.compile( "^(" + STRING_PATTERN_SCHEME + ")?" + "(//(" + STRING_PATTERN_USER_INFO + "@)?" + STRING_PATTERN_HOST + "(:" + STRING_PATTERN_PORT + ")?" + ")?" + STRING_PATTERN_PATH + "(\\?" + STRING_PATTERN_QUERY + ")?" + "(#" + STRING_PATTERN_REMAINING + ")?"); - + private String authority; private final MutableConvertibleMultiValues queryParams; private String scheme; @@ -120,7 +122,7 @@ class DefaultUriBuilder implements UriBuilder { this.host = host; } if (port != null) { - this.port = Integer.valueOf(port); + this.port = Integer.parseInt(port); } if (path != null) { @@ -345,7 +347,7 @@ private String reconstructAsString(Map values) { StringBuilder path = this.path; if (StringUtils.isNotEmpty(path)) { - if (builder.length() > 0 && path.charAt(0) != '/') { + if (!builder.isEmpty() && path.charAt(0) != '/') { builder.append('/'); } String pathStr = path.toString(); @@ -413,10 +415,6 @@ private String expandOrEncode(String value, Map values) } private String encode(String userInfo) { - try { - return URLEncoder.encode(userInfo, StandardCharsets.UTF_8.name()); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("No available charset: " + e.getMessage()); - } + return URLEncoder.encode(userInfo, StandardCharsets.UTF_8); } } diff --git a/http/src/main/java/io/micronaut/http/uri/UriTemplate.java b/http/src/main/java/io/micronaut/http/uri/UriTemplate.java index 713903f08e..c40c01be63 100644 --- a/http/src/main/java/io/micronaut/http/uri/UriTemplate.java +++ b/http/src/main/java/io/micronaut/http/uri/UriTemplate.java @@ -20,8 +20,8 @@ import io.micronaut.core.util.ObjectUtils; import io.micronaut.core.util.StringUtils; -import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -50,9 +50,9 @@ public class UriTemplate implements Comparable { private static final String STRING_PATTERN_SCHEME = "([^:/?#]+):"; private static final String STRING_PATTERN_USER_INFO = "([^@\\[/?#]*)"; private static final String STRING_PATTERN_HOST_IPV4 = "[^\\[{/?#:]*"; - private static final String STRING_PATTERN_HOST_IPV6 = "\\[[\\p{XDigit}\\:\\.]*[%\\p{Alnum}]*\\]"; + private static final String STRING_PATTERN_HOST_IPV6 = "\\[[\\p{XDigit}:.]*[%\\p{Alnum}]*]"; private static final String STRING_PATTERN_HOST = "(" + STRING_PATTERN_HOST_IPV6 + "|" + STRING_PATTERN_HOST_IPV4 + ")"; - private static final String STRING_PATTERN_PORT = "(\\d*(?:\\{[^/]+?\\})?)"; + private static final String STRING_PATTERN_PORT = "(\\d*(?:\\{[^/]+?})?)"; private static final String STRING_PATTERN_PATH = "([^#]*)"; private static final String STRING_PATTERN_QUERY = "([^#]*)"; private static final String STRING_PATTERN_REMAINING = "(.*)"; @@ -186,10 +186,10 @@ public long getRawSegmentCount() { */ public int getRawSegmentLength() { return segments.stream() - .filter(segment -> !segment.isVariable()) - .map(CharSequence::length) - .reduce(Integer::sum) - .orElse(0); + .filter(segment -> !segment.isVariable()) + .map(CharSequence::length) + .reduce(Integer::sum) + .orElse(0); } /** @@ -219,17 +219,17 @@ public String expand(Map parameters) { continue; } if (segment instanceof UriTemplateParser.VariablePathSegment varPathSegment) { - if (varPathSegment.isQuerySegment && ! queryParameter) { + if (varPathSegment.isQuerySegment && !queryParameter) { // reset anyPrevious* when we reach query parameters queryParameter = true; anyPreviousHasContent = false; anyPreviousHasOperator = false; } - final char operator = varPathSegment.getOperator(); + final char operator = varPathSegment.operator(); if (operator != OPERATOR_NONE && result.contains(String.valueOf(operator))) { anyPreviousHasOperator = true; } - anyPreviousHasContent = anyPreviousHasContent || result.length() > 0; + anyPreviousHasContent = anyPreviousHasContent || !result.isEmpty(); } builder.append(result); } @@ -280,12 +280,12 @@ public int compareTo(UriTemplate o) { return 0; } - Integer thisVariableCount = 0; - Integer thatVariableCount = 0; - Integer thisRawLength = 0; - Integer thatRawLength = 0; + int thisVariableCount = 0; + int thatVariableCount = 0; + int thisRawLength = 0; + int thatRawLength = 0; - for (PathSegment segment: this.segments) { + for (PathSegment segment : this.segments) { if (segment.isVariable()) { if (!segment.isQuerySegment()) { thisVariableCount++; @@ -295,7 +295,7 @@ public int compareTo(UriTemplate o) { } } - for (PathSegment segment: o.segments) { + for (PathSegment segment : o.segments) { if (segment.isVariable()) { if (!segment.isQuerySegment()) { thatVariableCount++; @@ -306,9 +306,9 @@ public int compareTo(UriTemplate o) { } //using that.compareTo because more raw length should have higher precedence - int rawCompare = thatRawLength.compareTo(thisRawLength); + int rawCompare = Integer.compare(thatRawLength, thisRawLength); if (rawCompare == 0) { - return thisVariableCount.compareTo(thatVariableCount); + return Integer.compare(thisVariableCount, thatVariableCount); } else { return rawCompare; } @@ -356,7 +356,8 @@ protected UriTemplate newUriTemplate(CharSequence uriTemplate, List /** * Normalize a nested URI. - * @param uri The URI + * + * @param uri The URI * @param nested The nested URI * @return The new URI */ @@ -490,7 +491,7 @@ protected String toString(Predicate filter) { builder.append(op); } } - builder.append(segment.toString()); + builder.append(segment); previousVariable = varSeg; } else { if (isVar) { @@ -500,13 +501,13 @@ protected String toString(Predicate filter) { if (OPERATOR_NONE != op) { builder.append(op); } - builder.append(segment.toString()); + builder.append(segment); } else { if (previousVariable != null) { builder.append(VAR_END); previousVariable = null; } - builder.append(segment.toString()); + builder.append(segment); } } } @@ -521,21 +522,17 @@ private boolean shouldPrependSlash(String templateString, int len) { int parentLen = parentString.length(); return (parentLen > 0 && parentString.charAt(parentLen - 1) != SLASH_OPERATOR) && templateString.charAt(0) != SLASH_OPERATOR && - isAdditionalPathVar(templateString, len); + isAdditionalPathVar(templateString, len); } private boolean isAdditionalPathVar(String templateString, int len) { if (len > 1) { boolean isVar = templateString.charAt(0) == VAR_START; if (isVar) { - switch (templateString.charAt(1)) { - case SLASH_OPERATOR: - case QUERY_OPERATOR: - case HASH_OPERATOR: - return false; - default: - return true; - } + return switch (templateString.charAt(1)) { + case SLASH_OPERATOR, QUERY_OPERATOR, HASH_OPERATOR -> false; + default -> true; + }; } } return templateString.charAt(0) != SLASH_OPERATOR; @@ -572,8 +569,8 @@ default boolean isVariable() { /** * Expands the query segment. * - * @param parameters The parameters - * @param previousHasContent Whether there was previous content + * @param parameters The parameters + * @param previousHasContent Whether there was previous content * @param anyPreviousHasOperator Whether an operator is present * @return The expanded string */ @@ -618,20 +615,19 @@ protected void parse(List segments) { switch (state) { case STATE_TEXT: if (c == VAR_START) { - if (buff.length() > 0) { + if (!buff.isEmpty()) { String val = buff.toString(); addRawContentSegment(segments, val, isQuerySegment); } buff.delete(0, buff.length()); state = STATE_VAR_START; - continue; } else { if (c == QUERY_OPERATOR || c == HASH_OPERATOR) { isQuerySegment = true; } buff.append(c); - continue; } + continue; case STATE_VAR_MODIFIER: case STATE_VAR_NEXT_MODIFIER: if (c == ' ') { @@ -654,8 +650,7 @@ protected void parse(List segments) { state = STATE_VAR_NEXT; // fall through case VAR_END: // arrived to variable end - - if (buff.length() > 0) { + if (!buff.isEmpty()) { String val = buff.toString(); final String prefix; final String delimiter; @@ -707,23 +702,13 @@ protected void parse(List segments) { } boolean hasAnotherVar = state == STATE_VAR_NEXT && c != VAR_END; if (hasAnotherVar) { - String delimiter; - switch (operator) { - case ';': - delimiter = null; - break; - case QUERY_OPERATOR: - case AND_OPERATOR: - delimiter = "&"; - break; - case DOT_OPERATOR: - case SLASH_OPERATOR: - delimiter = String.valueOf(operator); - break; - default: - delimiter = ","; - } - varDelimiter = delimiter; + varDelimiter = switch (operator) { + case ';' -> null; + case QUERY_OPERATOR, AND_OPERATOR -> "&"; + case DOT_OPERATOR, SLASH_OPERATOR -> + String.valueOf(operator); + default -> ","; + }; varCount++; } else { varCount = 0; @@ -775,7 +760,7 @@ protected void parse(List segments) { } } - if (state == STATE_TEXT && buff.length() > 0) { + if (state == STATE_TEXT && !buff.isEmpty()) { String val = buff.toString(); addRawContentSegment(segments, val, isQuerySegment); } @@ -823,19 +808,7 @@ protected void addVariableSegment(List segments, /** * Raw path segment implementation. */ - private static class RawPathSegment implements PathSegment { - private final boolean isQuerySegment; - private final String value; - - public RawPathSegment(boolean isQuerySegment, String value) { - this.isQuerySegment = isQuerySegment; - this.value = value; - } - - @Override - public boolean isQuerySegment() { - return isQuerySegment; - } + private record RawPathSegment(boolean isQuerySegment, String value) implements PathSegment { @Override public String expand(Map parameters, boolean previousHasContent, boolean anyPreviousHasOperator) { @@ -888,46 +861,17 @@ public String toString() { /** * Variable path segment implementation. */ - private class VariablePathSegment implements PathSegment { - - private final boolean isQuerySegment; - private final String variable; - private final String prefix; - private final String delimiter; - private final boolean encode; - private final char modifierChar; - private final char operator; - private final String modifierStr; - private final String previousDelimiter; - private final boolean repeatPrefix; - - public VariablePathSegment(boolean isQuerySegment, String variable, String prefix, String delimiter, boolean encode, char modifierChar, char operator, String modifierStr, String previousDelimiter, boolean repeatPrefix) { - this.isQuerySegment = isQuerySegment; - this.variable = variable; - this.prefix = prefix; - this.delimiter = delimiter; - this.encode = encode; - this.modifierChar = modifierChar; - this.operator = operator; - this.modifierStr = modifierStr; - this.previousDelimiter = previousDelimiter; - this.repeatPrefix = repeatPrefix; - } + private record VariablePathSegment(boolean isQuerySegment, String variable, String prefix, + String delimiter, boolean encode, char modifierChar, + char operator, String modifierStr, + String previousDelimiter, + boolean repeatPrefix) implements PathSegment { @Override public Optional getVariable() { return Optional.of(variable); } - public char getOperator() { - return this.operator; - } - - @Override - public boolean isQuerySegment() { - return isQuerySegment; - } - @Override public int length() { return toString().length(); @@ -964,9 +908,9 @@ private String escape(String v) { public String expand(Map parameters, boolean previousHasContent, boolean anyPreviousHasOperator) { Object found = parameters.get(variable); boolean isOptional = found instanceof Optional; - if (found != null && !(isOptional && !((Optional) found).isPresent())) { + if (found != null && !(isOptional && ((Optional) found).isEmpty())) { if (isOptional) { - found = ((Optional) found).get(); + found = ((Optional) found).get(); } String prefixToUse = prefix; if (operator == QUERY_OPERATOR && !anyPreviousHasOperator && prefix != null && !prefix.startsWith(String.valueOf(operator))) { @@ -983,39 +927,35 @@ public String expand(Map parameters, boolean previousHasContent, found = expandPOJO(found); // Turn POJO into a Map } - if (found instanceof Iterable iterable) { - Iterable iter = iterable; - if (iter instanceof Collection collection && collection.isEmpty()) { + if (found instanceof Iterable iterable) { + if (iterable instanceof Collection collection && collection.isEmpty()) { return ""; } StringJoiner joiner = new StringJoiner(delimiter); - for (Object o : iter) { + for (Object o : iterable) { if (o != null) { String v = o.toString(); joiner.add(encode ? encode(v, isQuery) : escape(v)); } } result = joiner.toString(); - } else if (found instanceof Map map) { + } else if (found instanceof Map map) { if (map.isEmpty()) { return ""; } final StringJoiner joiner; if (modifierChar == EXPAND_MODIFIER) { - - switch (operator) { - case AND_OPERATOR: - case QUERY_OPERATOR: + joiner = switch (operator) { + case AND_OPERATOR, QUERY_OPERATOR -> { prefixToUse = String.valueOf(anyPreviousHasOperator ? AND_OPERATOR : operator); - joiner = new StringJoiner(String.valueOf(AND_OPERATOR)); - break; - case ';': + yield new StringJoiner(String.valueOf(AND_OPERATOR)); + } + case ';' -> { prefixToUse = String.valueOf(operator); - joiner = new StringJoiner(String.valueOf(prefixToUse)); - break; - default: - joiner = new StringJoiner(delimiter); - } + yield new StringJoiner(prefixToUse); + } + default -> new StringJoiner(delimiter); + }; } else { joiner = new StringJoiner(delimiter); } @@ -1025,8 +965,8 @@ public String expand(Map parameters, boolean previousHasContent, return; } String ks = key.toString(); - Iterable values = (some instanceof Iterable i) ? i : Collections.singletonList(some); - for (Object value: values) { + Iterable values = (some instanceof Iterable i) ? i : Collections.singletonList(some); + for (Object value : values) { if (value == null) { continue; } @@ -1061,7 +1001,7 @@ public String expand(Map parameters, boolean previousHasContent, break; case ';': if (prefixToUse != null && prefixToUse.endsWith("=")) { - finalResult.append(prefixToUse.substring(0, prefixToUse.length() - 1)).append(result); + finalResult.append(prefixToUse, 0, prefixToUse.length() - 1).append(result); break; } // fall through @@ -1079,19 +1019,16 @@ public String expand(Map parameters, boolean previousHasContent, } return finalResult.toString(); } else { - switch (operator) { - case SLASH_OPERATOR: - return null; - default: - return ""; + if (operator == SLASH_OPERATOR) { + return null; } + return ""; } - } private String applyModifier(String modifierStr, char modifierChar, String result, int len) { - if (modifierChar == ':' && modifierStr.length() > 0 && Character.isDigit(modifierStr.charAt(0))) { + if (modifierChar == ':' && !modifierStr.isEmpty() && Character.isDigit(modifierStr.charAt(0))) { try { int subResult = Integer.parseInt(modifierStr.trim(), 10); if (subResult < len) { @@ -1105,16 +1042,8 @@ private String applyModifier(String modifierStr, char modifierChar, String resul } private String encode(String str, boolean query) { - try { - String encoded = URLEncoder.encode(str, "UTF-8"); - if (query) { - return encoded; - } else { - return encoded.replace("+", "%20"); - } - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("No available encoding", e); - } + String encoded = URLEncoder.encode(str, StandardCharsets.UTF_8); + return query ? encoded : encoded.replace("+", "%20"); } private Object expandPOJO(Object found) {