org.apache.camel.util.URISupport Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.util;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern;
import static org.apache.camel.util.CamelURIParser.URI_ALREADY_NORMALIZED;
/**
* URI utilities.
*
* IMPORTANT: This class is only intended for Camel internal, Camel components, and other Camel features. If you need a
* general purpose URI/URL utility class then do not use this class. This class is implemented in a certain way to work
* and support how Camel internally parses endpoint URIs.
*/
public final class URISupport {
public static final String RAW_TOKEN_PREFIX = "RAW";
public static final char[] RAW_TOKEN_START = { '(', '{' };
public static final char[] RAW_TOKEN_END = { ')', '}' };
// Java 17 text blocks have new lines with optional white space
private static final String TEXT_BLOCK_MARKER = System.lineSeparator();
private static final Pattern TEXT_BLOCK_PATTERN = Pattern.compile("\n\\s*");
// Match any key-value pair in the URI query string whose key contains
// "passphrase" or "password" or secret key (case-insensitive).
// First capture group is the key, second is the value.
@SuppressWarnings("RegExpUnnecessaryNonCapturingGroup")
private static final Pattern ALL_SECRETS = Pattern.compile(
"([?&][^=]*(?:" + SensitiveUtils.getSensitivePattern() + ")[^=]*)=(RAW(([{][^}]*[}])|([(][^)]*[)]))|[^&]*)",
Pattern.CASE_INSENSITIVE);
// Match the user password in the URI as second capture group
// (applies to URI with authority component and userinfo token in the form
// "user:password").
private static final Pattern USERINFO_PASSWORD = Pattern.compile("(.*://.*?:)(.*)(@)");
// Match the user password in the URI path as second capture group
// (applies to URI path with authority component and userinfo token in the
// form "user:password").
private static final Pattern PATH_USERINFO_PASSWORD = Pattern.compile("(.*?:)(.*)(@)");
private static final Charset CHARSET = StandardCharsets.UTF_8;
private static final String EMPTY_QUERY_STRING = "";
private URISupport() {
// Helper class
}
/**
* Removes detected sensitive information (such as passwords) from the URI and returns the result.
*
* @param uri The uri to sanitize.
* @return Returns null if the uri is null, otherwise the URI with the passphrase, password or secretKey
* sanitized.
* @see #ALL_SECRETS and #USERINFO_PASSWORD for the matched pattern
*/
public static String sanitizeUri(String uri) {
// use xxxxx as replacement as that works well with JMX also
String sanitized = uri;
if (uri != null) {
sanitized = ALL_SECRETS.matcher(sanitized).replaceAll("$1=xxxxxx");
sanitized = USERINFO_PASSWORD.matcher(sanitized).replaceFirst("$1xxxxxx$3");
}
return sanitized;
}
public static String textBlockToSingleLine(String uri) {
// text blocks
if (uri != null && uri.contains(TEXT_BLOCK_MARKER)) {
uri = TEXT_BLOCK_PATTERN.matcher(uri).replaceAll("");
uri = uri.trim();
}
return uri;
}
/**
* Removes detected sensitive information (such as passwords) from the path part of an URI (that is, the
* part without the query parameters or component prefix) and returns the result.
*
* @param path the URI path to sanitize
* @return null if the path is null, otherwise the sanitized path
*/
public static String sanitizePath(String path) {
String sanitized = path;
if (path != null) {
sanitized = PATH_USERINFO_PASSWORD.matcher(sanitized).replaceFirst("$1xxxxxx$3");
}
return sanitized;
}
/**
* Extracts the scheme specific path from the URI that is used as the remainder option when creating endpoints.
*
* @param u the URI
* @param useRaw whether to force using raw values
* @return the remainder path
*/
public static String extractRemainderPath(URI u, boolean useRaw) {
String path = useRaw ? u.getRawSchemeSpecificPart() : u.getSchemeSpecificPart();
// lets trim off any query arguments
if (path.startsWith("//")) {
path = path.substring(2);
}
return StringHelper.before(path, "?", path);
}
/**
* Extracts the query part of the given uri
*
* @param uri the uri
* @return the query parameters or null if the uri has no query
*/
public static String extractQuery(String uri) {
if (uri == null) {
return null;
}
return StringHelper.after(uri, "?");
}
/**
* Strips the query parameters from the uri
*
* @param uri the uri
* @return the uri without the query parameter
*/
public static String stripQuery(String uri) {
return StringHelper.before(uri, "?", uri);
}
/**
* Parses the query part of the uri (eg the parameters).
*
* The URI parameters will by default be URI encoded. However you can define a parameter values with the syntax:
* key=RAW(value) which tells Camel to not encode the value, and use the value as is (eg key=value) and the
* value has not been encoded.
*
* @param uri the uri
* @return the parameters, or an empty map if no parameters (eg never null)
* @throws URISyntaxException is thrown if uri has invalid syntax.
* @see #RAW_TOKEN_PREFIX
* @see #RAW_TOKEN_START
* @see #RAW_TOKEN_END
*/
public static Map parseQuery(String uri) throws URISyntaxException {
return parseQuery(uri, false);
}
/**
* Parses the query part of the uri (eg the parameters).
*
* The URI parameters will by default be URI encoded. However you can define a parameter values with the syntax:
* key=RAW(value) which tells Camel to not encode the value, and use the value as is (eg key=value) and the
* value has not been encoded.
*
* @param uri the uri
* @param useRaw whether to force using raw values
* @return the parameters, or an empty map if no parameters (eg never null)
* @throws URISyntaxException is thrown if uri has invalid syntax.
* @see #RAW_TOKEN_PREFIX
* @see #RAW_TOKEN_START
* @see #RAW_TOKEN_END
*/
public static Map parseQuery(String uri, boolean useRaw) throws URISyntaxException {
return parseQuery(uri, useRaw, false);
}
/**
* Parses the query part of the uri (eg the parameters).
*
* The URI parameters will by default be URI encoded. However you can define a parameter values with the syntax:
* key=RAW(value) which tells Camel to not encode the value, and use the value as is (eg key=value) and the
* value has not been encoded.
*
* @param uri the uri
* @param useRaw whether to force using raw values
* @param lenient whether to parse lenient and ignore trailing & markers which has no key or value which
* can happen when using HTTP components
* @return the parameters, or an empty map if no parameters (eg never null)
* @throws URISyntaxException is thrown if uri has invalid syntax.
* @see #RAW_TOKEN_PREFIX
* @see #RAW_TOKEN_START
* @see #RAW_TOKEN_END
*/
public static Map parseQuery(String uri, boolean useRaw, boolean lenient) throws URISyntaxException {
if (uri == null || uri.isEmpty()) {
// return an empty map
return Collections.emptyMap();
}
// must check for trailing & as the uri.split("&") will ignore those
if (!lenient && uri.endsWith("&")) {
throw new URISyntaxException(
uri, "Invalid uri syntax: Trailing & marker found. " + "Check the uri and remove the trailing & marker.");
}
URIScanner scanner = new URIScanner();
return scanner.parseQuery(uri, useRaw);
}
/**
* Scans RAW tokens in the string and returns the list of pair indexes which tell where a RAW token starts and ends
* in the string.
*
* This is a companion method with {@link #isRaw(int, List)} and the returned value is supposed to be used as the
* parameter of that method.
*
* @param str the string to scan RAW tokens
* @return the list of pair indexes which represent the start and end positions of a RAW token
* @see #isRaw(int, List)
* @see #RAW_TOKEN_PREFIX
* @see #RAW_TOKEN_START
* @see #RAW_TOKEN_END
*/
public static List> scanRaw(String str) {
return URIScanner.scanRaw(str);
}
/**
* Tests if the index is within any pair of the start and end indexes which represent the start and end positions of
* a RAW token.
*
* This is a companion method with {@link #scanRaw(String)} and is supposed to consume the returned value of that
* method as the second parameter pairs.
*
* @param index the index to be tested
* @param pairs the list of pair indexes which represent the start and end positions of a RAW token
* @return true if the index is within any pair of the indexes, false otherwise
* @see #scanRaw(String)
* @see #RAW_TOKEN_PREFIX
* @see #RAW_TOKEN_START
* @see #RAW_TOKEN_END
*/
public static boolean isRaw(int index, List> pairs) {
if (pairs == null || pairs.isEmpty()) {
return false;
}
for (Pair pair : pairs) {
if (index < pair.getLeft()) {
return false;
}
if (index <= pair.getRight()) {
return true;
}
}
return false;
}
/**
* Parses the query parameters of the uri (eg the query part).
*
* @param uri the uri
* @return the parameters, or an empty map if no parameters (eg never null)
* @throws URISyntaxException is thrown if uri has invalid syntax.
*/
public static Map parseParameters(URI uri) throws URISyntaxException {
String query = prepareQuery(uri);
if (query == null) {
// empty an empty map
return new LinkedHashMap<>(0);
}
return parseQuery(query);
}
public static String prepareQuery(URI uri) {
String query = uri.getQuery();
if (query == null) {
String schemeSpecificPart = uri.getSchemeSpecificPart();
query = StringHelper.after(schemeSpecificPart, "?");
} else if (query.indexOf('?') == 0) {
// skip leading query
query = query.substring(1);
}
return query;
}
/**
* Traverses the given parameters, and resolve any parameter values which uses the RAW token syntax:
* key=RAW(value). This method will then remove the RAW tokens, and replace the content of the value, with
* just the value.
*
* @param parameters the uri parameters
* @see #parseQuery(String)
* @see #RAW_TOKEN_PREFIX
* @see #RAW_TOKEN_START
* @see #RAW_TOKEN_END
*/
public static void resolveRawParameterValues(Map parameters) {
resolveRawParameterValues(parameters, null);
}
/**
* Traverses the given parameters, and resolve any parameter values which uses the RAW token syntax:
* key=RAW(value). This method will then remove the RAW tokens, and replace the content of the value, with
* just the value.
*
* @param parameters the uri parameters
* @param onReplace optional function executed when replace the raw value
* @see #parseQuery(String)
* @see #RAW_TOKEN_PREFIX
* @see #RAW_TOKEN_START
* @see #RAW_TOKEN_END
*/
public static void resolveRawParameterValues(Map parameters, Function onReplace) {
for (Map.Entry entry : parameters.entrySet()) {
if (entry.getValue() == null) {
continue;
}
// if the value is a list then we need to iterate
Object value = entry.getValue();
if (value instanceof List list) {
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
if (obj == null) {
continue;
}
String str = obj.toString();
String raw = URIScanner.resolveRaw(str);
if (raw != null) {
// update the string in the list
// do not encode RAW parameters unless it has %
// need to reverse: replace % with %25 to avoid losing "%" when decoding
String s = raw.replace("%25", "%");
if (onReplace != null) {
s = onReplace.apply(s);
}
list.set(i, s);
}
}
} else {
String str = entry.getValue().toString();
String raw = URIScanner.resolveRaw(str);
if (raw != null) {
// do not encode RAW parameters unless it has %
// need to reverse: replace % with %25 to avoid losing "%" when decoding
String s = raw.replace("%25", "%");
if (onReplace != null) {
s = onReplace.apply(s);
}
entry.setValue(s);
}
}
}
}
/**
* Creates a URI with the given query
*
* @param uri the uri
* @param query the query to append to the uri
* @return uri with the query appended
* @throws URISyntaxException is thrown if uri has invalid syntax.
*/
public static URI createURIWithQuery(URI uri, String query) throws URISyntaxException {
ObjectHelper.notNull(uri, "uri");
// assemble string as new uri and replace parameters with the query
// instead
String s = uri.toString();
String before = StringHelper.before(s, "?");
if (before == null) {
before = StringHelper.before(s, "#");
}
if (before != null) {
s = before;
}
if (query != null) {
s = s + "?" + query;
}
if (!s.contains("#") && uri.getFragment() != null) {
s = s + "#" + uri.getFragment();
}
return new URI(s);
}
/**
* Strips the prefix from the value.
*
* Returns the value as-is if not starting with the prefix.
*
* @param value the value
* @param prefix the prefix to remove from value
* @return the value without the prefix
*/
public static String stripPrefix(String value, String prefix) {
if (value == null || prefix == null) {
return value;
}
if (value.startsWith(prefix)) {
return value.substring(prefix.length());
}
return value;
}
/**
* Strips the suffix from the value.
*
* Returns the value as-is if not ending with the prefix.
*
* @param value the value
* @param suffix the suffix to remove from value
* @return the value without the suffix
*/
public static String stripSuffix(final String value, final String suffix) {
if (value == null || suffix == null) {
return value;
}
if (value.endsWith(suffix)) {
return value.substring(0, value.length() - suffix.length());
}
return value;
}
/**
* Assembles a query from the given map.
*
* @param options the map with the options (eg key/value pairs)
* @return a query string with key1=value&key2=value2&..., or an empty string if there is no
* options.
*/
public static String createQueryString(Map options) {
final Set keySet = options.keySet();
return createQueryString(keySet.toArray(new String[0]), options, true);
}
/**
* Assembles a query from the given map.
*
* @param options the map with the options (eg key/value pairs)
* @param encode whether to URL encode the query string
* @return a query string with key1=value&key2=value2&..., or an empty string if there is no
* options.
*/
public static String createQueryString(Map options, boolean encode) {
return createQueryString(options.keySet(), options, encode);
}
private static String createQueryString(String[] sortedKeys, Map options, boolean encode) {
if (options.isEmpty()) {
return EMPTY_QUERY_STRING;
}
StringBuilder rc = new StringBuilder(128);
boolean first = true;
for (String key : sortedKeys) {
if (first) {
first = false;
} else {
rc.append("&");
}
Object value = options.get(key);
// the value may be a list since the same key has multiple
// values
if (value instanceof List) {
List list = (List) value;
for (Iterator it = list.iterator(); it.hasNext();) {
String s = it.next();
appendQueryStringParameter(key, s, rc, encode);
// append & separator if there is more in the list
// to append
if (it.hasNext()) {
rc.append("&");
}
}
} else {
// use the value as a String
String s = value != null ? value.toString() : null;
appendQueryStringParameter(key, s, rc, encode);
}
}
return rc.toString();
}
/**
* Assembles a query from the given map.
*
* @param options the map with the options (eg key/value pairs)
* @param ampersand to use & for Java code, and & for XML
* @return a query string with key1=value&key2=value2&..., or an empty string if there
* is no options.
* @throws URISyntaxException is thrown if uri has invalid syntax.
*/
@Deprecated(since = "4.1.0")
public static String createQueryString(Map options, String ampersand, boolean encode) {
if (!options.isEmpty()) {
StringBuilder rc = new StringBuilder();
boolean first = true;
for (String key : options.keySet()) {
if (first) {
first = false;
} else {
rc.append(ampersand);
}
Object value = options.get(key);
// use the value as a String
String s = value != null ? value.toString() : null;
appendQueryStringParameter(key, s, rc, encode);
}
return rc.toString();
} else {
return "";
}
}
@Deprecated(since = "4.0.0")
public static String createQueryString(Collection sortedKeys, Map options, boolean encode) {
return createQueryString(sortedKeys.toArray(new String[0]), options, encode);
}
private static void appendQueryStringParameter(String key, String value, StringBuilder rc, boolean encode) {
if (encode) {
String encoded = URLEncoder.encode(key, CHARSET);
rc.append(encoded);
} else {
rc.append(key);
}
if (value == null) {
return;
}
// only append if value is not null
rc.append("=");
String raw = URIScanner.resolveRaw(value);
if (raw != null) {
// do not encode RAW parameters unless it has %
// need to replace % with %25 to avoid losing "%" when decoding
final String s = URIScanner.replacePercent(value);
rc.append(s);
} else {
if (encode) {
String encoded = URLEncoder.encode(value, CHARSET);
rc.append(encoded);
} else {
rc.append(value);
}
}
}
/**
* Creates a URI from the original URI and the remaining parameters
*
* Used by various Camel components
*/
public static URI createRemainingURI(URI originalURI, Map params) throws URISyntaxException {
String s = createQueryString(params);
if (s.isEmpty()) {
s = null;
}
return createURIWithQuery(originalURI, s);
}
/**
* Appends the given parameters to the given URI.
*
* It keeps the original parameters and if a new parameter is already defined in {@code originalURI}, it will be
* replaced by its value in {@code newParameters}.
*
* @param originalURI the original URI
* @param newParameters the parameters to add
* @return the URI with all the parameters
* @throws URISyntaxException is thrown if the uri syntax is invalid
* @throws UnsupportedEncodingException is thrown if encoding error
*/
public static String appendParametersToURI(String originalURI, Map newParameters)
throws URISyntaxException {
URI uri = new URI(normalizeUri(originalURI));
Map parameters = parseParameters(uri);
parameters.putAll(newParameters);
return createRemainingURI(uri, parameters).toString();
}
/**
* Normalizes the uri by reordering the parameters so they are sorted and thus we can use the uris for endpoint
* matching.
*
* The URI parameters will by default be URI encoded. However you can define a parameter values with the syntax:
* key=RAW(value) which tells Camel to not encode the value, and use the value as is (eg key=value) and the
* value has not been encoded.
*
* @param uri the uri
* @return the normalized uri
* @throws URISyntaxException in thrown if the uri syntax is invalid
*
* @see #RAW_TOKEN_PREFIX
* @see #RAW_TOKEN_START
* @see #RAW_TOKEN_END
*/
public static String normalizeUri(String uri) throws URISyntaxException {
// try to parse using the simpler and faster Camel URI parser
String[] parts = CamelURIParser.fastParseUri(uri);
if (parts != null) {
// we optimized specially if an empty array is returned
if (parts == URI_ALREADY_NORMALIZED) {
return uri;
}
// use the faster and more simple normalizer
return doFastNormalizeUri(parts);
} else {
// use the legacy normalizer as the uri is complex and may have unsafe URL characters
return doComplexNormalizeUri(uri);
}
}
/**
* Normalizes the URI so unsafe characters are encoded
*
* @param uri the input uri
* @return as URI instance
* @throws URISyntaxException is thrown if syntax error in the input uri
*/
public static URI normalizeUriAsURI(String uri) throws URISyntaxException {
// java 17 text blocks to single line uri
uri = URISupport.textBlockToSingleLine(uri);
return new URI(UnsafeUriCharactersEncoder.encode(uri, true));
}
/**
* The complex (and Camel 2.x) compatible URI normalizer when the URI is more complex such as having percent encoded
* values, or other unsafe URL characters, or have authority user/password, etc.
*/
private static String doComplexNormalizeUri(String uri) throws URISyntaxException {
// java 17 text blocks to single line uri
uri = URISupport.textBlockToSingleLine(uri);
URI u = new URI(UnsafeUriCharactersEncoder.encode(uri, true));
String scheme = u.getScheme();
String path = u.getSchemeSpecificPart();
// not possible to normalize
if (scheme == null || path == null) {
return uri;
}
// find start and end position in path as we only check the context-path and not the query parameters
int start = path.startsWith("//") ? 2 : 0;
int end = path.indexOf('?');
if (start == 0 && end == 0 || start == 2 && end == 2) {
// special when there is no context path
path = "";
} else {
if (start != 0 && end == -1) {
path = path.substring(start);
} else if (end != -1) {
path = path.substring(start, end);
}
if (scheme.startsWith("http")) {
path = UnsafeUriCharactersEncoder.encodeHttpURI(path);
} else {
path = UnsafeUriCharactersEncoder.encode(path);
}
}
// okay if we have user info in the path and they use @ in username or password,
// then we need to encode them (but leave the last @ sign before the hostname)
// this is needed as Camel end users may not encode their user info properly,
// but expect this to work out of the box with Camel, and hence we need to
// fix it for them
int idxPath = path.indexOf('/');
if (StringHelper.countChar(path, '@', idxPath) > 1) {
String userInfoPath = idxPath > 0 ? path.substring(0, idxPath) : path;
int max = userInfoPath.lastIndexOf('@');
String before = userInfoPath.substring(0, max);
// after must be from original path
String after = path.substring(max);
// replace the @ with %40
before = before.replace("@", "%40");
path = before + after;
}
// in case there are parameters we should reorder them
String query = prepareQuery(u);
if (query == null) {
// no parameters then just return
return buildUri(scheme, path, null);
} else {
Map parameters = URISupport.parseQuery(query, false, false);
if (parameters.size() == 1) {
// only 1 parameter need to create new query string
query = URISupport.createQueryString(parameters);
} else {
// reorder parameters a..z
final Set keySet = parameters.keySet();
final String[] parametersArray = keySet.toArray(new String[0]);
Arrays.sort(parametersArray);
// build uri object with sorted parameters
query = URISupport.createQueryString(parametersArray, parameters, true);
}
return buildUri(scheme, path, query);
}
}
/**
* The fast parser for normalizing Camel endpoint URIs when the URI is not complex and can be parsed in a much more
* efficient way.
*/
private static String doFastNormalizeUri(String[] parts) throws URISyntaxException {
String scheme = parts[0];
String path = parts[1];
String query = parts[2];
// in case there are parameters we should reorder them
if (query == null) {
// no parameters then just return
return buildUri(scheme, path, null);
} else {
return buildReorderingParameters(scheme, path, query);
}
}
private static String buildReorderingParameters(String scheme, String path, String query) throws URISyntaxException {
Map parameters = null;
if (query.indexOf('&') != -1) {
// only parse if there are parameters
parameters = URISupport.parseQuery(query, false, false);
}
if (parameters != null && parameters.size() != 1) {
final Set entries = parameters.keySet();
// reorder parameters a..z
// optimize and only build new query if the keys was resorted
boolean sort = false;
String prev = null;
for (String key : entries) {
if (prev != null) {
int comp = key.compareTo(prev);
if (comp < 0) {
sort = true;
break;
}
}
prev = key;
}
if (sort) {
final String[] array = entries.toArray(new String[0]);
Arrays.sort(array);
query = URISupport.createQueryString(array, parameters, true);
}
}
return buildUri(scheme, path, query);
}
private static String buildUri(String scheme, String path, String query) {
// must include :// to do a correct URI all components can work with
int len = scheme.length() + 3 + path.length();
if (query != null) {
len += 1 + query.length();
StringBuilder sb = new StringBuilder(len);
sb.append(scheme).append("://").append(path).append('?').append(query);
return sb.toString();
} else {
StringBuilder sb = new StringBuilder(len);
sb.append(scheme).append("://").append(path);
return sb.toString();
}
}
public static Map extractProperties(Map properties, String optionPrefix) {
Map rc = new LinkedHashMap<>(properties.size());
for (Iterator> it = properties.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = it.next();
String name = entry.getKey();
if (name.startsWith(optionPrefix)) {
Object value = properties.get(name);
name = name.substring(optionPrefix.length());
rc.put(name, value);
it.remove();
}
}
return rc;
}
private static String makeUri(String uriWithoutQuery, String query) {
int len = uriWithoutQuery.length();
if (query != null) {
len += 1 + query.length();
StringBuilder sb = new StringBuilder(len);
sb.append(uriWithoutQuery).append('?').append(query);
return sb.toString();
} else {
StringBuilder sb = new StringBuilder(len);
sb.append(uriWithoutQuery);
return sb.toString();
}
}
public static String getDecodeQuery(final String uri) {
try {
URI u = new URI(uri);
String query = URISupport.prepareQuery(u);
String uriWithoutQuery = URISupport.stripQuery(uri);
if (query == null) {
return uriWithoutQuery;
} else {
Map parameters = URISupport.parseQuery(query, false, false);
if (parameters.size() == 1) {
// only 1 parameter need to create new query string
query = URISupport.createQueryString(parameters);
} else {
// reorder parameters a..z
final Set keySet = parameters.keySet();
final String[] parametersArray = keySet.toArray(new String[0]);
Arrays.sort(parametersArray);
// build uri object with sorted parameters
query = URISupport.createQueryString(parametersArray, parameters, true);
}
return makeUri(uriWithoutQuery, query);
}
} catch (URISyntaxException ex) {
return null;
}
}
public static String pathAndQueryOf(final URI uri) {
final String path = uri.getPath();
String pathAndQuery = path;
if (ObjectHelper.isEmpty(path)) {
pathAndQuery = "/";
}
final String query = uri.getQuery();
if (ObjectHelper.isNotEmpty(query)) {
pathAndQuery += "?" + query;
}
return pathAndQuery;
}
public static String joinPaths(final String... paths) {
if (paths == null || paths.length == 0) {
return "";
}
final StringBuilder joined = new StringBuilder(paths.length * 64);
boolean addedLast = false;
for (int i = paths.length - 1; i >= 0; i--) {
String path = paths[i];
if (ObjectHelper.isNotEmpty(path)) {
if (addedLast) {
path = stripSuffix(path, "/");
}
addedLast = true;
if (path.charAt(0) == '/') {
joined.insert(0, path);
} else {
if (i > 0) {
joined.insert(0, '/').insert(1, path);
} else {
joined.insert(0, path);
}
}
}
}
return joined.toString();
}
public static String buildMultiValueQuery(String key, Iterable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy