/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.everrest.core.impl.uri;
import javax.ws.rs.Path;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriBuilderException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.everrest.core.impl.uri.UriComponent.FRAGMENT;
import static org.everrest.core.impl.uri.UriComponent.HOST;
import static org.everrest.core.impl.uri.UriComponent.MATRIX_PARAM;
import static org.everrest.core.impl.uri.UriComponent.PATH;
import static org.everrest.core.impl.uri.UriComponent.PATH_SEGMENT;
import static org.everrest.core.impl.uri.UriComponent.PORT;
import static org.everrest.core.impl.uri.UriComponent.QUERY;
import static org.everrest.core.impl.uri.UriComponent.QUERY_STRING;
import static org.everrest.core.impl.uri.UriComponent.SCHEME;
import static org.everrest.core.impl.uri.UriComponent.SSP;
import static org.everrest.core.impl.uri.UriComponent.USER_INFO;
import static org.everrest.core.impl.uri.UriComponent.encode;
import static org.everrest.core.impl.uri.UriComponent.recognizeEncode;
import static org.everrest.core.impl.uri.UriComponent.validateUriComponent;
import static org.everrest.core.util.StringUtils.charAtIs;
import static org.everrest.core.util.StringUtils.scan;
public class UriBuilderImpl extends UriBuilder {
private String schema;
private String authority;
private String userInfo;
private String host;
private String port;
private StringBuilder path = new StringBuilder();
private StringBuilder query = new StringBuilder();
private String fragment;
private String ssp;
private MultivaluedMap<String, Object> matrixParameters;
public UriBuilderImpl() {
}
@Override
public URI buildFromMap(Map<String, ?> values) {
return buildFromMap(values, true);
}
@Override
public URI buildFromMap(Map<String, ?> values, boolean encodeSlashInPath) {
checkArgument(values != null, "Null values aren't allowed");
String uri;
encodeFragment();
if (ssp != null) {
uri = createUriFromSspWithValues(values, true);
} else {
preparePath();
uri = createUriWithValues(values, true, encodeSlashInPath);
}
try {
return new URI(uri);
} catch (URISyntaxException e) {
throw new UriBuilderException(e);
}
}
@Override
public URI buildFromEncodedMap(Map<String, ?> values) {
checkArgument(values != null, "Null values aren't allowed");
String uri;
encodeFragment();
if (ssp != null) {
uri = createUriFromSspWithValues(values, false);
} else {
preparePath();
uri = createUriWithValues(values, false, false);
}
try {
return new URI(uri);
} catch (URISyntaxException e) {
throw new UriBuilderException(e);
}
}
@Override
public URI build(Object... values) {
return build(values, true);
}
@Override
public URI build(Object[] values, boolean encodeSlashInPath) {
checkArgument(values != null, "Null values aren't allowed");
String uri;
encodeFragment();
if (ssp != null) {
uri = createUriFromSspWithValues(values, true);
} else {
preparePath();
uri = createUriWithValues(values, true, encodeSlashInPath);
}
try {
return new URI(uri);
} catch (URISyntaxException e) {
throw new UriBuilderException(e);
}
}
@Override
public URI buildFromEncoded(Object... values) {
checkArgument(values != null, "Null values aren't allowed");
String uri;
encodeFragment();
if (ssp != null) {
uri = createUriFromSspWithValues(values, false);
} else {
preparePath();
uri = createUriWithValues(values, false, false);
}
try {
return new URI(uri);
} catch (URISyntaxException e) {
throw new UriBuilderException(e);
}
}
private String createUriWithValues(Object[] values, boolean encode, boolean encodeSlashInPath) {
return createUriWithValues(schema, authority, userInfo, host, port, path.toString(), query.toString(), fragment, values, encode,
encodeSlashInPath, false);
}
private String createUriWithValues(Map<String, ?> values, boolean encode, boolean encodeSlashInPath) {
return createUriWithValues(schema, authority, userInfo, host, port, path.toString(), query.toString(), fragment, values, encode,
encodeSlashInPath, false);
}
private String createUriFromSspWithValues(Map<String, ?> values, boolean encode) {
StringBuilder uri = new StringBuilder();
appendUriPart(uri, ssp, SSP, values, encode, false);
uri.append(':');
appendUriPart(uri, ssp, SSP, values, encode, false);
if (!isNullOrEmpty(fragment)) {
uri.append('#');
appendUriPart(uri, ssp, SSP, values, encode, false);
}
return uri.toString();
}
private String createUriFromSspWithValues(Object[] values, boolean encode) {
StringBuilder uri = new StringBuilder();
Map<String, String> m = new HashMap<>();
int offset = 0;
offset = appendUriPart(uri, schema, SCHEME, values, offset, m, encode, false);
uri.append(':');
offset = appendUriPart(uri, ssp, SSP, values, offset, m, encode, false);
if (!isNullOrEmpty(fragment)) {
uri.append('#');
appendUriPart(uri, fragment, FRAGMENT, values, offset, m, encode, false);
}
return uri.toString();
}
@Override
public String toTemplate() {
return createUriWithValues(schema,
authority,
userInfo,
host,
port,
path.toString(),
query.toString(),
fragment,
new Object[0],
false,
false,
true);
}
private void preparePath() {
appendMatrixParametersToPath();
if (path.length() == 0 && !isNullOrEmpty(host) && (query.length() > 0 || !isNullOrEmpty(fragment))) {
path.append('/');
}
}
private void encodeFragment() {
if (!isNullOrEmpty(fragment)) {
fragment = recognizeEncode(fragment, FRAGMENT, true);
}
}
@Override
public UriBuilder clone() {
return new UriBuilderImpl(this);
}
/**
* For {@link #clone()} method.
*
* @param cloned
* current UriBuilder.
*/
protected UriBuilderImpl(UriBuilderImpl cloned) {
this.schema = cloned.schema;
this.ssp = cloned.ssp;
this.authority = cloned.authority;
this.userInfo = cloned.userInfo;
this.host = cloned.host;
this.port = cloned.port;
this.path = new StringBuilder(cloned.path);
if (cloned.matrixParameters != null) {
this.matrixParameters = new MultivaluedHashMap<String, Object>(cloned.matrixParameters);
}
this.query = new StringBuilder(cloned.query);
this.fragment = cloned.fragment;
}
@Override
public UriBuilder fragment(String fragment) {
this.fragment = fragment == null ? null : encode(fragment, FRAGMENT, true);
return this;
}
@Override
public UriBuilder resolveTemplate(String name, Object value) {
return resolveTemplate(name, value, false);
}
@Override
public UriBuilder resolveTemplate(String name, Object value, boolean encodeSlashInPath) {
checkArgument(name != null, "Null name of parameter isn't allowed");
checkArgument(value != null, "Null value of parameter isn't allowed");
Map<String, Object> params = new HashMap<>(4);
params.put(name, value);
return resolveTemplates(params, encodeSlashInPath);
}
@Override
public UriBuilder resolveTemplateFromEncoded(String name, Object value) {
return resolveTemplate(name, value, false);
}
@Override
public UriBuilder resolveTemplates(Map<String, Object> templateValues) {
return resolveTemplates(templateValues, false);
}
@Override
public UriBuilder resolveTemplates(Map<String, Object> templateValues, boolean encodeSlashInPath) throws IllegalArgumentException {
checkArgument(templateValues != null, "Null map isn't allowed");
checkArgument(!templateValues.containsKey(null), "Null names in map aren't allowed");
checkArgument(!templateValues.containsValue(null), "Null values in map aren't allowed");
parseTemplate(
createUriWithValues(schema,
authority,
userInfo,
host,
port,
path.toString(),
query.toString(),
fragment,
templateValues,
false,
encodeSlashInPath,
true)
);
return this;
}
@Override
public UriBuilder resolveTemplatesFromEncoded(Map<String, Object> templateValues) {
return resolveTemplates(templateValues, false);
}
private void parseTemplate(String uriTemplate) {
UriParser uriParser = new UriParser(uriTemplate);
uriParser.parse();
if (uriParser.getScheme() != null) {
this.schema = uriParser.getScheme();
}
if (uriParser.isOpaque() && uriParser.getSchemeSpecificPart() != null) {
this.ssp = uriParser.getSchemeSpecificPart();
}
if (uriParser.getAuthority() != null) {
this.authority = uriParser.getAuthority();
}
if (uriParser.getUserInfo() != null) {
this.userInfo = uriParser.getUserInfo();
}
if (uriParser.getHost() != null) {
this.host = uriParser.getHost();
}
if (uriParser.getPort() != null) {
this.port = uriParser.getPort();
}
if (!isNullOrEmpty(uriParser.getPath())) {
this.path.setLength(0);
path(uriParser.getPath());
}
if (uriParser.getQuery() != null) {
this.query.setLength(0);
this.query.append(uriParser.getQuery());
}
if (uriParser.getFragment() != null) {
this.fragment = uriParser.getFragment();
}
}
@Override
public UriBuilder host(String host) {
if (host == null) {
this.host = null;
this.authority = null;
} else if (host.isEmpty()) {
throw new IllegalArgumentException("Invalid host ''");
} else {
this.host = recognizeEncode(host, HOST, true);
this.authority = null;
}
return this;
}
@Override
public UriBuilder matrixParam(String name, Object... values) {
checkArgument(name != null, "Null name isn't allowed");
checkArgument(values != null, "Null values aren't allowed");
matrixParam(false, name, values);
return this;
}
private void matrixParam(boolean replace, String name, Object... values) {
if (matrixParameters == null) {
if (values == null || values.length == 0) {
return;
}
matrixParameters = new MultivaluedHashMap<>();
}
name = recognizeEncode(name, MATRIX_PARAM, true);
if (replace) {
matrixParameters.remove(name);
}
if (values != null && values.length > 0) {
checkArgument(Arrays.stream(values).allMatch(o -> o != null), "Null value isn't allowed");
matrixParameters.addAll(name, values);
}
}
@Override
public UriBuilder path(String addingPath) {
checkArgument(addingPath != null, "Null path isn't allowed");
if (!addingPath.isEmpty()) {
appendMatrixParametersToPath();
int startOfMatrixParamsInAddingPath = findStartOfMatrixParams(addingPath);
String addingPathMatrixParams = null;
boolean needAddTrailingSlash = false;
if (startOfMatrixParamsInAddingPath != -1) {
needAddTrailingSlash = charAtIs(addingPath, addingPath.length() - 1, '/');
addingPathMatrixParams = addingPath.substring(startOfMatrixParamsInAddingPath + 1);
addingPath = addingPath.substring(0, startOfMatrixParamsInAddingPath);
}
boolean currentPathHasTrailingSlash = charAtIs(path, path.length() - 1, '/');
boolean addingPathHasLeadingSlash = charAtIs(addingPath, 0, '/');
addingPath = recognizeEncode(addingPath, PATH, true);
if (!isNullOrEmpty(addingPathMatrixParams)) {
parseMatrixParams(addingPathMatrixParams);
}
if (currentPathHasTrailingSlash && addingPathHasLeadingSlash) {
if (addingPath.length() > 1) {
path.append(addingPath, 1, addingPath.length());
}
} else if (path.length() > 0 && !currentPathHasTrailingSlash && !addingPathHasLeadingSlash) {
path.append('/');
path.append(addingPath);
} else {
path.append(addingPath);
}
if (needAddTrailingSlash) {
path.append('/');
}
}
return this;
}
private void appendMatrixParametersToPath() {
if (matrixParameters != null) {
boolean pathEndsWithSlash = path.length() > 1 && charAtIs(path, path.length() - 1, '/');
if (pathEndsWithSlash) {
path.setLength(path.length() - 1);
}
for (Map.Entry<String, List<Object>> entry : matrixParameters.entrySet()) {
List<Object> values = entry.getValue();
if (!(values == null || values.isEmpty())) {
String name = recognizeEncode(entry.getKey(), MATRIX_PARAM, true);
for (Object value : values) {
path.append(';');
path.append(recognizeEncode(name, MATRIX_PARAM, true));
path.append('=');
path.append(recognizeEncode(String.valueOf(value), MATRIX_PARAM, true));
}
}
}
matrixParameters.clear();
if (pathEndsWithSlash) {
path.append('/');
}
}
}
@SuppressWarnings({"unchecked"})
@Override
public UriBuilder path(Class resource) {
checkArgument(resource != null, "Null resource class isn't allowed");
Annotation pathAnnotation = resource.getAnnotation(Path.class);
checkArgument(pathAnnotation != null, "Class is not annotated with javax.ws.rs.Path");
return path(((Path)pathAnnotation).value());
}
@Override
public UriBuilder path(Method method) {
checkArgument(method != null, "Null method isn't allowed");
Path pathAnnotation = method.getAnnotation(Path.class);
return pathAnnotation == null ? this : path(pathAnnotation.value());
}
@Override
public UriBuilder path(Class resource, String method) {
checkArgument(resource != null, "Null resource class isn't allowed");
checkArgument(method != null, "Null method name isn't allowed");
Method[] methods = resource.getMethods();
Method matched = null;
for (int i = 0, length = methods.length; i < length; i++) {
Method m = methods[i];
if (m.getName().equals(method)) {
if (matched != null && !(matched.isSynthetic() || m.isSynthetic())) {
throw new IllegalArgumentException(String.format("More then one method with name %s found", method));
} else {
matched = m;
}
}
}
if (matched == null) {
throw new IllegalArgumentException(String.format("Method %s not found at resource class %s", method, resource.getName()));
}
path(matched);
return this;
}
@Override
public UriBuilder port(int port) {
checkArgument(port == -1 || (port >= 0 && port <= 65535), "Invalid port %d", port);
if (port == -1) {
this.port = null;
} else {
this.port = Integer.toString(port);
}
return this;
}
@Override
public UriBuilder queryParam(String name, Object... values) {
checkArgument(name != null, "Null name isn't allowed");
checkArgument(values != null, "Null values aren't allowed");
for (int i = 0, length = values.length; i < length; i++) {
Object o = values[i];
checkArgument(o != null, "Null value isn't allowed");
if (query.length() > 0) {
query.append('&');
}
query.append(recognizeEncode(name, QUERY, true));
query.append('=');
query.append(recognizeEncode(o.toString(), QUERY, true));
}
return this;
}
@Override
public UriBuilder replaceMatrixParam(String name, Object... values) {
checkArgument(name != null, "Null name isn't allowed");
matrixParam(true, name, values);
return this;
}
@Override
public UriBuilder replaceMatrix(String matrix) {
if (matrixParameters != null) {
matrixParameters.clear();
}
parseMatrixParams(matrix);
return this;
}
private void parseMatrixParams(String matrixString) {
if (!isNullOrEmpty(matrixString)) {
int p = 0;
final int length = matrixString.length();
while (p < length) {
if (charAtIs(matrixString, p, '=')) {
throw new UriBuilderException("Matrix parameter name is empty");
}
int n = scan(matrixString, p, ';', length);
if (n > p) {
final String pair = matrixString.substring(p, n);
final int eq = scan(pair, 0, '=', pair.length());
if (eq == pair.length() || eq == (pair.length() - 1)) {
matrixParam(false, pair);
} else {
matrixParam(false, pair.substring(0, eq), pair.substring(eq + 1));
}
}
p = n + 1;
}
}
}
private int findStartOfMatrixParams(String path) {
final int pathLength = path.length();
int semicolonPos = -1;
if (pathLength > 0) {
int startLookUpFrom;
if (charAtIs(path, pathLength - 1, '/')) {
startLookUpFrom = pathLength - 2;
} else {
startLookUpFrom = pathLength - 1;
}
for (int i = startLookUpFrom; i >= 0 && !charAtIs(path, i, '/'); i--) {
if (charAtIs(path, i, ';')) {
semicolonPos = i;
}
}
}
return semicolonPos;
}
@Override
public UriBuilder replacePath(String path) {
this.path.setLength(0);
if (!isNullOrEmpty(path)) {
path(path);
}
return this;
}
@Override
public UriBuilder replaceQueryParam(String name, Object... values) {
checkArgument(name != null, "Null name isn't allowed");
if (query.length() > 0) {
int p = 0;
final String queryString = query.toString();
final int queryStringLength = queryString.length();
query.setLength(0);
name = recognizeEncode(name, QUERY, true);
while (p < queryStringLength) {
int n = scan(queryString, p, '&', queryStringLength);
// Do nothing for sequence such as '&&'.
if (n > p) {
String pair = queryString.substring(p, n);
int eq = scan(pair, 0, '=', pair.length());
String pairName = eq == pair.length()
? pair : pair.substring(0, eq);
if (!name.equals(pairName)) {
if (query.length() > 0) {
query.append('&');
}
query.append(pair);
}
}
p = n + 1;
}
}
if (values != null && values.length > 0) {
queryParam(name, values);
}
return this;
}
@Override
public UriBuilder replaceQuery(String queryString) {
query.setLength(0);
if (!isNullOrEmpty(queryString)) {
validateQueryString(queryString);
query.append(recognizeEncode(queryString, QUERY_STRING, true));
}
return this;
}
private void validateQueryString(String queryString) {
int p = 0;
while (p < queryString.length()) {
if (charAtIs(queryString, p, '=')) {
// something like a=x&=y
throw new IllegalArgumentException(String.format("Invalid query string at %d. Query parameter name is empty.", p));
}
int n = scan(queryString, p, '&', queryString.length());
p = n + 1;
}
}
@Override
public UriBuilder scheme(String schema) {
this.schema = schema != null ? validateUriComponent(schema, SCHEME, true) : null;
return this;
}
@Override
public UriBuilder schemeSpecificPart(String ssp) {
checkArgument(ssp != null, "Null scheme specific part isn't allowed");
this.ssp = recognizeEncode(ssp, SSP, true);
authority = null;
userInfo = null;
host = null;
port = null;
path.setLength(0);
return this;
}
@Override
public UriBuilder segment(String... segments) {
checkArgument(segments != null, "Null path segments aren't allowed");
for (String segment : segments) {
checkArgument(segment != null, "Null path segment isn't allowed");
path(recognizeEncode(segment, PATH_SEGMENT, true));
}
return this;
}
@Override
public UriBuilder uri(URI uri) {
checkArgument(uri != null, "Null URI isn't allowed");
if (uri.getScheme() != null) {
schema = uri.getScheme();
}
if (uri.isOpaque()) {
ssp = uri.getRawSchemeSpecificPart();
} else {
if (uri.getRawUserInfo() == null && uri.getHost() == null && uri.getPort() == -1) {
if (uri.getRawAuthority() != null) {
authority = uri.getRawAuthority();
}
} else {
if (uri.getRawUserInfo() != null) {
userInfo = uri.getRawUserInfo();
}
if (uri.getHost() != null) {
host = uri.getHost();
}
if (uri.getPort() != -1) {
port = Integer.toString(uri.getPort());
}
}
if (!isNullOrEmpty(uri.getRawPath())) {
path.setLength(0);
path.append(uri.getRawPath());
}
if (!isNullOrEmpty(uri.getRawQuery())) {
query.setLength(0);
query.append(uri.getRawQuery());
}
if (uri.getRawFragment() != null) {
fragment = uri.getRawFragment();
}
}
return this;
}
@Override
public UriBuilder uri(String uriTemplate) {
checkArgument(uriTemplate != null, "Null URI template isn't allowed");
parseTemplate(uriTemplate);
return this;
}
@Override
public UriBuilder userInfo(String userInfo) {
this.userInfo = userInfo != null ? recognizeEncode(userInfo, USER_INFO, true) : null;
return this;
}
/**
* @param sb
* the StringBuilder for appending URI part
* @param str
* URI part
* @param component
* the URI component
* @param values
* values map
* @param encode
* if true then encode value before add it in URI, otherwise value must be validate to legal characters
* @param asTemplate
* if true ignore absence value for any URI parameters
*/
private void appendUriPart(StringBuilder sb, String str, int component, Map<String, ?> values, boolean encode, boolean asTemplate) {
int p = 0;
int n = 0;
int length = str.length();
while (p < length) {
p = findStartOfUriTemplate(str, n);
if (p > n) {
sb.append(str, n, p);
}
if (charAtIs(str, p, '{')) {
n = findEndOfUriTemplate(str, p);
if (charAtIs(str, n, '}')) {
String paramName = extractNameOfUriTemplate(str, p, n);
Object paramValue = values.get(paramName);
if (paramValue == null) {
if (!asTemplate) {
throw new IllegalArgumentException(String.format("Not found corresponding value for parameter %s", paramName));
}
sb.append('{').append(paramName).append('}');
} else {
sb.append(encode
? encode(paramValue.toString(), component, asTemplate)
: recognizeEncode(paramValue.toString(), component, asTemplate)
);
}
}
}
n++;
}
}
/**
* @param sb
* the StringBuilder for appending URI part
* @param str
* URI part
* @param component
* the URI component
* @param sourceValues
* the source array of values
* @param offset
* the offset in array
* @param values
* values map, keep parameter/value pair which have been already found. From java docs:
* <p>
* All instances of the same template parameter will be replaced by the same value that corresponds to the
* position of the first instance of the template parameter. e.g. the template "{a}/{b}/{a}" with values
* {"x", "y", "z"} will result in the the URI "x/y/x", <i>not</i> "x/y/z".
* </p>
* @param encode
* if true then encode value before add it in URI, otherwise value must be validate to legal characters
* @param asTemplate
* if true ignore absence value for any URI parameters
* @return offset
*/
private int appendUriPart(StringBuilder sb, String str, int component, Object[] sourceValues, int offset, Map<String, String> values,
boolean encode, boolean asTemplate) {
int p = 0;
int n = 0;
int length = str.length();
while (p < length) {
p = findStartOfUriTemplate(str, n);
if (p > n) {
sb.append(str, n, p);
}
if (charAtIs(str, p, '{')) {
n = findEndOfUriTemplate(str, p);
if (charAtIs(str, n, '}')) {
String paramName = extractNameOfUriTemplate(str, p, n);
String processedParamValue = values.get(paramName);
if (processedParamValue != null) {
sb.append(processedParamValue);
} else {
Object newParamValue = null;
if (offset < sourceValues.length) {
newParamValue = sourceValues[offset++];
}
if (newParamValue != null) {
processedParamValue = encode
? encode(newParamValue.toString(), component, asTemplate)
: recognizeEncode(newParamValue.toString(), component, asTemplate);
values.put(paramName, processedParamValue);
sb.append(processedParamValue);
} else {
if (!asTemplate) {
throw new IllegalArgumentException(String.format("Not found corresponding value for parameter %s", paramName));
}
sb.append('{').append(paramName).append('}');
}
}
}
}
n++;
}
return offset;
}
private int findStartOfUriTemplate(String str, int startFrom) {
return scan(str, startFrom, '{', str.length());
}
private int findEndOfUriTemplate(String str, int startFrom) {
return scan(str, startFrom, '}', str.length());
}
private String extractNameOfUriTemplate(String str, int startOfTemplate, int endOfTemplate) {
int templateRegexSeparator = scan(str, startOfTemplate, ':', endOfTemplate);
String name;
if (templateRegexSeparator < endOfTemplate) {
name = str.substring(startOfTemplate + 1, templateRegexSeparator);
} else {
name = str.substring(startOfTemplate + 1, endOfTemplate);
}
return name.trim();
}
/**
* Create URI from URI part. Each URI part can contains templates.
*
* @param schema
* the schema URI part
* @param userInfo
* the user info URI part
* @param host
* the host name URI part
* @param port
* the port number URI part
* @param path
* the path URI part
* @param query
* the query string URI part
* @param fragment
* the fragment URI part
* @param values
* the values which must be used instead templates parameters
* @param encode
* if true then encode value before add it in URI, otherwise value must be validate to legal characters
* @param asTemplate
* if true ignore absence value for any URI parameters
* @return the URI string
*/
private String createUriWithValues(String schema,
String authority,
String userInfo,
String host,
String port,
String path,
String query,
String fragment,
Map<String, ?> values,
boolean encode,
boolean encodeSlashInPath,
boolean asTemplate) {
StringBuilder sb = new StringBuilder();
if (schema != null) {
appendUriPart(sb, schema, SCHEME, values, false, asTemplate);
sb.append(':');
}
if (authority != null || userInfo != null || host != null || port != null) {
sb.append('/');
sb.append('/');
if (userInfo == null && host == null && port == null) {
sb.append(authority);
} else {
if (!isNullOrEmpty(userInfo)) {
appendUriPart(sb, userInfo, USER_INFO, values, encode, asTemplate);
sb.append('@');
}
if (host != null) {
appendUriPart(sb, host, HOST, values, encode, asTemplate);
}
if (port != null) {
sb.append(':');
appendUriPart(sb, port, PORT, values, encode, asTemplate);
}
}
}
if (!isNullOrEmpty(path)) {
if (sb.length() > 0 && !charAtIs(path, 0, '/')) {
sb.append('/');
}
appendUriPart(sb, path, encodeSlashInPath ? PATH_SEGMENT : PATH, values, encode, asTemplate);
}
if (!isNullOrEmpty(query)) {
sb.append('?');
appendUriPart(sb, query, QUERY, values, encode, asTemplate);
}
if (!isNullOrEmpty(fragment)) {
sb.append('#');
appendUriPart(sb, fragment, FRAGMENT, values, encode, asTemplate);
}
return sb.toString();
}
/**
* Create URI from URI part. Each URI part can contains templates.
*
* @param schema
* the schema URI part
* @param authority
* the authority
* @param userInfo
* the user info URI part
* @param host
* the host name URI part
* @param port
* the port number URI part
* @param path
* the path URI part
* @param query
* the query string URI part
* @param fragment
* the fragment URI part
* @param values
* the values which must be used instead templates parameters
* @param encode
* if true then encode value before add it in URI, otherwise value must be validate to legal characters
* @param asTemplate
* if true ignore absence value for any URI parameters
* @return the URI string
*/
private String createUriWithValues(String schema,
String authority,
String userInfo,
String host,
String port,
String path,
String query,
String fragment,
Object[] values,
boolean encode,
boolean encodeSlashInPath,
boolean asTemplate) {
Map<String, String> m = new HashMap<>();
StringBuilder sb = new StringBuilder();
int p = 0;
if (schema != null) {
p = appendUriPart(sb, schema, SCHEME, values, p, m, false, asTemplate);
sb.append(':');
}
if (authority != null || !isNullOrEmpty(userInfo) || host != null || port != null) {
sb.append('/');
sb.append('/');
if (!isNullOrEmpty(userInfo) || host != null || port != null) {
if (!isNullOrEmpty(userInfo)) {
p = appendUriPart(sb, userInfo, USER_INFO, values, p, m, encode, asTemplate);
sb.append('@');
}
if (host != null) {
p = appendUriPart(sb, host, HOST, values, p, m, encode, asTemplate);
}
if (port != null) {
sb.append(':');
p = appendUriPart(sb, port, PORT, values, p, m, encode, asTemplate);
}
} else {
sb.append(authority);
}
}
if (!isNullOrEmpty(path)) {
if (host != null && sb.length() > 0 && !charAtIs(path, 0, '/')) {
sb.append('/');
}
p = appendUriPart(sb, path, encodeSlashInPath ? PATH_SEGMENT : PATH, values, p, m, encode, asTemplate);
}
if (!isNullOrEmpty(query)) {
sb.append('?');
p = appendUriPart(sb, query, QUERY, values, p, m, encode, asTemplate);
}
if (!isNullOrEmpty(fragment)) {
sb.append('#');
appendUriPart(sb, fragment, FRAGMENT, values, p, m, encode, asTemplate);
}
return sb.toString();
}
}