
io.net.uri.builder.URITemplate Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of uri-builder Show documentation
Show all versions of uri-builder Show documentation
Small library that helps build URI easier in Java, encoding path, query parameters, etc.
The newest version!
package io.net.uri.builder;
import org.jetbrains.annotations.NotNull;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Helper for building URI (simplified Micronaut copycat of UriBuilder)
*
* @author Anton Kurako (GoodforGod)
* @since 21.08.2021
*/
class URITemplate implements Comparable {
private static final String UTF8_ENCODING = StandardCharsets.UTF_8.name();
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 = "(" + STRING_PATTERN_HOST_IPV6 + "|" + STRING_PATTERN_HOST_IPV4 + ")";
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 = "(.*)";
private static final char QUERY_OPERATOR = '?';
private static final char SLASH_OPERATOR = '/';
private static final char HASH_OPERATOR = '#';
private static final char EXPAND_MODIFIER = '*';
private static final char OPERATOR_NONE = '0';
private static final char VAR_START = '{';
private static final char VAR_END = '}';
private static final char AND_OPERATOR = '&';
private static final String SLASH_STRING = "/";
private static final char DOT_OPERATOR = '.';
// Regex patterns that matches URIs. See RFC 3986, appendix B
static final Pattern PATTERN_SCHEME = Pattern.compile("^" + STRING_PATTERN_SCHEME + "//.*");
static final Pattern PATTERN_FULL_PATH = Pattern.compile("^([^#?]*)(\\?([^#]*))?(#(.*))?$");
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 + ")?");
protected final String templateString;
final List segments = new ArrayList<>();
/**
* Construct a new URI template for the given template.
*
* @param templateString The template string
*/
public URITemplate(CharSequence templateString) {
this(templateString, new Object[0]);
}
/**
* Construct a new URI template for the given template.
*
* @param templateString The template string
* @param parserArguments The parsed arguments
*/
@SuppressWarnings("MagicNumber")
protected URITemplate(CharSequence templateString, Object... parserArguments) {
if (templateString == null) {
throw new IllegalArgumentException("Argument [templateString] should not be null");
}
String templateAsString = templateString.toString();
if (templateAsString.endsWith(SLASH_STRING)) {
int len = templateAsString.length();
if (len > 1) {
templateAsString = templateAsString.substring(0, len - 1);
}
}
if (PATTERN_SCHEME.matcher(templateAsString).matches()) {
Matcher matcher = PATTERN_FULL_URI.matcher(templateAsString);
if (matcher.find()) {
this.templateString = templateAsString;
String scheme = matcher.group(2);
if (scheme != null) {
createParser(scheme + "://", parserArguments).parse(segments);
}
String userInfo = matcher.group(5);
String host = matcher.group(6);
String port = matcher.group(8);
String path = matcher.group(9);
String query = matcher.group(11);
String fragment = matcher.group(13);
if (userInfo != null) {
createParser(userInfo, parserArguments).parse(segments);
}
if (host != null) {
createParser(host, parserArguments).parse(segments);
}
if (port != null) {
createParser(':' + port, parserArguments).parse(segments);
}
if (path != null) {
if (fragment != null) {
createParser(path + HASH_OPERATOR + fragment).parse(segments);
} else {
createParser(path, parserArguments).parse(segments);
}
}
if (query != null) {
createParser(query, parserArguments).parse(segments);
}
} else {
throw new IllegalArgumentException("Invalid URI template: " + templateString);
}
} else {
this.templateString = templateAsString;
createParser(this.templateString, parserArguments).parse(segments);
}
}
/**
* @param templateString The template
* @param segments The list of segments
*/
protected URITemplate(String templateString, List segments) {
this.templateString = templateString;
this.segments.addAll(segments);
}
@Override
public String toString() {
return toString(pathSegment -> true);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
URITemplate that = (URITemplate) o;
return templateString.equals(that.templateString);
}
@Override
public int hashCode() {
return templateString.hashCode();
}
@Override
public int compareTo(URITemplate o) {
if (this == o) {
return 0;
}
Integer thisVariableCount = 0;
Integer thatVariableCount = 0;
Integer thisRawLength = 0;
Integer thatRawLength = 0;
for (PathSegment segment : this.segments) {
if (segment.isVariable()) {
if (!segment.isQuerySegment()) {
thisVariableCount++;
}
} else {
thisRawLength += segment.length();
}
}
for (PathSegment segment : o.segments) {
if (segment.isVariable()) {
if (!segment.isQuerySegment()) {
thatVariableCount++;
}
} else {
thatRawLength += segment.length();
}
}
// using that.compareTo because more raw length should have higher precedence
int rawCompare = thatRawLength.compareTo(thisRawLength);
if (rawCompare == 0) {
return thisVariableCount.compareTo(thatVariableCount);
} else {
return rawCompare;
}
}
/**
* Nests another URI template with this template.
*
* @param uriTemplate The URI template. If it does not begin with forward slash
* it will automatically be appended with forward slash
* @return The new URI template
*/
public URITemplate nest(CharSequence uriTemplate) {
return nest(uriTemplate, new Object[0]);
}
/**
* Nests another URI template with this template.
*
* @param uriTemplate The URI template. If it does not begin with forward
* slash it will automatically be appended with forward
* slash
* @param parserArguments The parsed arguments
* @return The new URI template
*/
protected URITemplate nest(CharSequence uriTemplate, Object... parserArguments) {
if (uriTemplate == null)
return this;
int len = uriTemplate.length();
if (len == 0)
return this;
List newSegments = buildNestedSegments(uriTemplate, len, parserArguments);
return newUriTemplate(uriTemplate, newSegments);
}
/**
* Expand the string with the given parameters.
*
* @param parameters The parameters
* @return The expanded URI
*/
public String expand(@NotNull Map parameters) {
final Map paramsAsStrings = parameters.entrySet().stream()
.filter(e -> e.getValue() != null)
.collect(Collectors.toMap(Map.Entry::getKey, e -> String.valueOf(e.getValue())));
StringBuilder builder = new StringBuilder(templateString.length());
boolean anyPreviousHasContent = false;
boolean anyPreviousHasOperator = false;
boolean queryParameter = false;
for (PathSegment segment : segments) {
String result = segment.expand(paramsAsStrings, anyPreviousHasContent, anyPreviousHasOperator);
if (result == null) {
continue;
}
if (segment instanceof UriTemplateParser.VariablePathSegment) {
UriTemplateParser.VariablePathSegment varPathSegment = (UriTemplateParser.VariablePathSegment) segment;
if (varPathSegment.isQuerySegment && !queryParameter) {
// reset anyPrevious* when we reach query parameters
queryParameter = true;
anyPreviousHasContent = false;
anyPreviousHasOperator = false;
}
final char operator = varPathSegment.getOperator();
if (operator != OPERATOR_NONE && result.contains(String.valueOf(operator))) {
anyPreviousHasOperator = true;
}
anyPreviousHasContent = anyPreviousHasContent || result.length() > 0;
}
builder.append(result);
}
return builder.toString();
}
/**
* Create a new {@link URITemplate} for the given URI.
*
* @param uri The URI
* @return The template
*/
public static URITemplate of(String uri) {
return new URITemplate(uri);
}
/**
* @param uriTemplate The URI template
* @param newSegments The new segments
* @return The new {@link URITemplate}
*/
protected URITemplate newUriTemplate(CharSequence uriTemplate, List newSegments) {
return new URITemplate(normalizeNested(this.templateString, uriTemplate), newSegments);
}
/**
* Normalize a nested URI.
*
* @param uri The URI
* @param nested The nested URI
* @return The new URI
*/
protected String normalizeNested(String uri, CharSequence nested) {
if (nested == null || nested.length() < 1) {
return uri;
}
String nestedStr = nested.toString();
char firstNested = nestedStr.charAt(0);
int len = nestedStr.length();
if (len == 1 && firstNested == SLASH_OPERATOR) {
return uri;
}
switch (firstNested) {
case VAR_START:
if (len > 1) {
switch (nested.charAt(1)) {
case SLASH_OPERATOR:
case HASH_OPERATOR:
case QUERY_OPERATOR:
case AND_OPERATOR:
if (uri.endsWith(SLASH_STRING)) {
return uri.substring(0, uri.length() - 1) + nestedStr;
} else {
return uri + nestedStr;
}
default:
if (!uri.endsWith(SLASH_STRING)) {
return uri + SLASH_STRING + nestedStr;
} else {
return uri + nestedStr;
}
}
} else {
return uri;
}
case SLASH_OPERATOR:
if (uri.endsWith(SLASH_STRING)) {
return uri + nestedStr.substring(1);
} else {
return uri + nestedStr;
}
default:
if (uri.endsWith(SLASH_STRING)) {
return uri + nestedStr;
} else {
return uri + SLASH_STRING + nestedStr;
}
}
}
/**
* @param uriTemplate The URI template
* @param len The lenght
* @param parserArguments The parsed arguments
* @return A list of path segments
*/
protected List buildNestedSegments(CharSequence uriTemplate, int len, Object... parserArguments) {
List newSegments = new ArrayList<>();
List querySegments = new ArrayList<>();
for (PathSegment segment : segments) {
if (!segment.isQuerySegment()) {
newSegments.add(segment);
} else {
querySegments.add(segment);
}
}
String templateString = uriTemplate.toString();
if (shouldPrependSlash(templateString, len)) {
templateString = SLASH_OPERATOR + templateString;
} else if (!segments.isEmpty() && templateString.startsWith(SLASH_STRING)) {
if (len == 1 && uriTemplate.charAt(0) == SLASH_OPERATOR) {
templateString = "";
} else {
PathSegment last = segments.get(segments.size() - 1);
if (last instanceof UriTemplateParser.RawPathSegment) {
String v = ((UriTemplateParser.RawPathSegment) last).value;
if (v.endsWith(SLASH_STRING)) {
templateString = templateString.substring(1);
} else {
templateString = normalizeNested(SLASH_STRING, templateString.substring(1));
}
}
}
}
createParser(templateString, parserArguments).parse(newSegments);
newSegments.addAll(querySegments);
return newSegments;
}
/**
* Creates a parser.
*
* @param templateString The template
* @param parserArguments The parsed arguments
* @return The created parser
*/
protected UriTemplateParser createParser(String templateString, Object... parserArguments) {
return new UriTemplateParser(templateString);
}
/**
* Returns the template as a string filtering the segments with the provided
* filter.
*
* @param filter The filter to test segments
* @return The template as a string
*/
protected String toString(Predicate filter) {
StringBuilder builder = new StringBuilder(templateString.length());
UriTemplateParser.VariablePathSegment previousVariable = null;
for (PathSegment segment : segments) {
if (!filter.test(segment)) {
continue;
}
boolean isVar = segment instanceof UriTemplateParser.VariablePathSegment;
if (previousVariable != null && isVar) {
UriTemplateParser.VariablePathSegment varSeg = (UriTemplateParser.VariablePathSegment) segment;
if (varSeg.operator == previousVariable.operator && varSeg.modifierChar != EXPAND_MODIFIER) {
builder.append(varSeg.delimiter);
} else {
builder.append(VAR_END);
builder.append(VAR_START);
char op = varSeg.operator;
if (OPERATOR_NONE != op) {
builder.append(op);
}
}
builder.append(segment);
previousVariable = varSeg;
} else {
if (isVar) {
previousVariable = (UriTemplateParser.VariablePathSegment) segment;
builder.append(VAR_START);
char op = previousVariable.operator;
if (OPERATOR_NONE != op) {
builder.append(op);
}
} else {
if (previousVariable != null) {
builder.append(VAR_END);
previousVariable = null;
}
}
builder.append(segment.toString());
}
}
if (previousVariable != null) {
builder.append(VAR_END);
}
return builder.toString();
}
private boolean shouldPrependSlash(String templateString, int len) {
String parentString = this.templateString;
int parentLen = parentString.length();
return (parentLen > 0 && parentString.charAt(parentLen - 1) != SLASH_OPERATOR) &&
templateString.charAt(0) != SLASH_OPERATOR &&
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 templateString.charAt(0) != SLASH_OPERATOR;
}
/**
* Represents an expandable path segment.
*/
protected interface PathSegment extends CharSequence {
/**
* @return Whether this segment is part of the query string
*/
default boolean isQuerySegment() {
return false;
}
/**
* If this path segment represents a variable returns the underlying variable
* name.
*
* @return The variable name if present
*/
default Optional getVariable() {
return Optional.empty();
}
/**
* @return True if this is a variable segment
*/
default boolean isVariable() {
return getVariable().isPresent();
}
/**
* Expands the query segment.
*
* @param parameters The parameters
* @param previousHasContent Whether there was previous content
* @param anyPreviousHasOperator Whether an operator is present
* @return The expanded string
*/
String expand(Map parameters, boolean previousHasContent, boolean anyPreviousHasOperator);
}
/**
* An URI template parser.
*/
protected static class UriTemplateParser {
private static final int STATE_TEXT = 0; // raw text
private static final int STATE_VAR_START = 1; // the start of a URI variable ie. {
private static final int STATE_VAR_CONTENT = 2; // within a URI variable. ie. {var}
private static final int STATE_VAR_NEXT = 11; // within the next variable in a URI variable declaration ie. {var, var2}
private static final int STATE_VAR_MODIFIER = 12; // within a variable modifier ie. {var:1}
private static final int STATE_VAR_NEXT_MODIFIER = 13; // within a variable modifier of a next variable ie. {var, var2:1}
String templateText;
private int state = STATE_TEXT;
private char operator = OPERATOR_NONE; // zero means no operator
private char modifier = OPERATOR_NONE; // zero means no modifier
private String varDelimiter;
private boolean isQuerySegment = false;
/**
* @param templateText The template
*/
UriTemplateParser(String templateText) {
this.templateText = templateText;
}
/**
* Parse a list of segments.
*
* @param segments The list of segments
*/
protected void parse(List segments) {
char[] chars = templateText.toCharArray();
StringBuilder buff = new StringBuilder();
StringBuilder modBuff = new StringBuilder();
int varCount = 0;
for (char c : chars) {
switch (state) {
case STATE_TEXT:
if (c == VAR_START) {
if (buff.length() > 0) {
String val = buff.toString();
addRawContentSegment(segments, val, isQuerySegment);
}
buff.delete(0, buff.length());
state = STATE_VAR_START;
} else {
if (c == QUERY_OPERATOR || c == HASH_OPERATOR) {
isQuerySegment = true;
}
buff.append(c);
}
continue;
case STATE_VAR_MODIFIER:
case STATE_VAR_NEXT_MODIFIER:
if (c == ' ') {
continue;
}
case STATE_VAR_NEXT:
case STATE_VAR_CONTENT:
switch (c) {
case ':':
case EXPAND_MODIFIER: // arrived to expansion modifier
if (state == STATE_VAR_MODIFIER || state == STATE_VAR_NEXT_MODIFIER) {
modBuff.append(c);
continue;
}
modifier = c;
state = state == STATE_VAR_NEXT ? STATE_VAR_NEXT_MODIFIER : STATE_VAR_MODIFIER;
continue;
case ',': // arrived to new variable
state = STATE_VAR_NEXT;
case VAR_END: // arrived to variable end
if (buff.length() > 0) {
String val = buff.toString();
final String prefix;
final String delimiter;
final boolean encode;
final boolean repeatPrefix;
switch (operator) {
case '+':
encode = false;
prefix = null;
delimiter = ",";
repeatPrefix = varCount < 1;
break;
case HASH_OPERATOR:
encode = false;
repeatPrefix = varCount < 1;
prefix = String.valueOf(operator);
delimiter = ",";
break;
case DOT_OPERATOR:
case SLASH_OPERATOR:
encode = true;
repeatPrefix = varCount < 1;
prefix = String.valueOf(operator);
delimiter = modifier == EXPAND_MODIFIER ? prefix : ",";
break;
case ';':
encode = true;
repeatPrefix = true;
prefix = operator + val + '=';
delimiter = modifier == EXPAND_MODIFIER ? prefix : ",";
break;
case QUERY_OPERATOR:
case AND_OPERATOR:
encode = true;
repeatPrefix = true;
prefix = varCount < 1 ? operator + val + '=' : val + "=";
delimiter = modifier == EXPAND_MODIFIER ? AND_OPERATOR + val + '=' : ",";
break;
default:
repeatPrefix = varCount < 1;
encode = true;
prefix = null;
delimiter = ",";
}
String modifierStr = modBuff.toString();
char modifierChar = modifier;
String previous = state == STATE_VAR_NEXT || state == STATE_VAR_NEXT_MODIFIER ? this.varDelimiter
: null;
addVariableSegment(segments, val, prefix, delimiter, encode, repeatPrefix, modifierStr, modifierChar,
operator, previous, isQuerySegment);
}
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;
varCount++;
} else {
varCount = 0;
}
state = hasAnotherVar ? STATE_VAR_NEXT : STATE_TEXT;
modBuff.delete(0, modBuff.length());
buff.delete(0, buff.length());
modifier = OPERATOR_NONE;
if (!hasAnotherVar) {
operator = OPERATOR_NONE;
}
continue;
default:
switch (modifier) {
case EXPAND_MODIFIER:
throw new IllegalStateException(
"Expansion modifier * must be immediately followed by a closing brace '}'");
case ':':
modBuff.append(c);
continue;
default:
buff.append(c);
continue;
}
}
case STATE_VAR_START:
switch (c) {
case ' ':
continue;
case ';':
case QUERY_OPERATOR:
case AND_OPERATOR:
case HASH_OPERATOR:
isQuerySegment = true;
case '+':
case DOT_OPERATOR:
case SLASH_OPERATOR:
operator = c;
state = STATE_VAR_CONTENT;
continue;
default:
state = STATE_VAR_CONTENT;
buff.append(c);
}
default:
// no-op
}
}
if (state == STATE_TEXT && buff.length() > 0) {
String val = buff.toString();
addRawContentSegment(segments, val, isQuerySegment);
}
}
/**
* Adds a raw content segment.
*
* @param segments The segments
* @param value The value
* @param isQuerySegment Whether is a query segment
*/
protected void addRawContentSegment(List segments, String value, boolean isQuerySegment) {
segments.add(new RawPathSegment(isQuerySegment, value));
}
/**
* Adds a new variable segment.
*
* @param segments The segments to augment
* @param variable The variable
* @param prefix The prefix to use when expanding the variable
* @param delimiter The delimiter to use when expanding the variable
* @param encode Whether to URL encode the variable
* @param repeatPrefix Whether to repeat the prefix for each expanded
* variable
* @param modifierStr The modifier string
* @param modifierChar The modifier as char
* @param operator The currently active operator
* @param previousDelimiter The delimiter to use if a variable appeared before
* this variable
* @param isQuerySegment Whether is a query segment
*/
protected void addVariableSegment(List segments,
String variable,
String prefix,
String delimiter,
boolean encode,
boolean repeatPrefix,
String modifierStr,
char modifierChar,
char operator,
String previousDelimiter, boolean isQuerySegment) {
segments.add(new VariablePathSegment(isQuerySegment, variable, prefix, delimiter, encode, modifierChar, operator, modifierStr,
previousDelimiter, repeatPrefix));
}
/**
* 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;
}
@Override
public String expand(Map parameters, boolean previousHasContent, boolean anyPreviousHasOperator) {
return value;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RawPathSegment that = (RawPathSegment) o;
if (isQuerySegment != that.isQuerySegment) {
return false;
}
return Objects.equals(value, that.value);
}
@Override
public int hashCode() {
int result = (isQuerySegment ? 1 : 0);
result = 31 * result + (value != null ? value.hashCode() : 0);
return result;
}
@Override
public int length() {
return value.length();
}
@Override
public char charAt(int index) {
return value.charAt(index);
}
@Override
public CharSequence subSequence(int start, int end) {
return value.subSequence(start, end);
}
@Override
public String toString() {
return value;
}
}
/**
* Variable path segment implementation.
*/
private static 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;
}
@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();
}
@Override
public char charAt(int index) {
return toString().charAt(index);
}
@Override
public CharSequence subSequence(int start, int end) {
return toString().subSequence(start, end);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(variable);
if (modifierChar != OPERATOR_NONE) {
builder.append(modifierChar);
if (null != modifierStr) {
builder.append(modifierStr);
}
}
return builder.toString();
}
private String escape(String v) {
return v.replace("%", "%25").replaceAll("\\s", "%20");
}
@Override
public String expand(Map parameters, boolean previousHasContent, boolean anyPreviousHasOperator) {
String found = parameters.get(variable);
if (found != null) {
String prefixToUse = prefix;
if (operator == QUERY_OPERATOR && !anyPreviousHasOperator && prefix != null
&& !prefix.startsWith(String.valueOf(operator))) {
prefixToUse = operator + prefix;
}
boolean isQuery = operator == QUERY_OPERATOR;
String str = found;
str = applyModifier(modifierStr, modifierChar, str, str.length());
String result = encode ? encode(str, isQuery) : escape(str);
int len = result.length();
StringBuilder finalResult = new StringBuilder(previousHasContent && previousDelimiter != null ? previousDelimiter : "");
if (len == 0) {
switch (operator) {
case SLASH_OPERATOR:
break;
case ';':
if (prefixToUse != null && prefixToUse.endsWith("=")) {
finalResult.append(prefixToUse, 0, prefixToUse.length() - 1).append(result);
break;
}
default:
if (prefixToUse != null) {
finalResult.append(prefixToUse).append(result);
} else {
finalResult.append(result);
}
}
} else if (prefixToUse != null && repeatPrefix) {
finalResult.append(prefixToUse).append(result);
} else {
finalResult.append(result);
}
return finalResult.toString();
} else {
return (operator == SLASH_OPERATOR)
? null
: "";
}
}
private String applyModifier(String modifierStr, char modifierChar, String result, int len) {
if (modifierChar == ':' && modifierStr.length() > 0 && Character.isDigit(modifierStr.charAt(0))) {
try {
int subResult = Integer.parseInt(modifierStr.trim(), 10);
if (subResult < len) {
result = result.substring(0, subResult);
}
} catch (NumberFormatException e) {
result = ":" + modifierStr;
}
}
return result;
}
private String encode(String str, boolean query) {
try {
final String encoded = URLEncoder.encode(str, UTF8_ENCODING);
return (query)
? encoded
: encoded.replace("+", "%20");
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException(e);
}
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy