org.codehaus.httpcache4j.util.URIBuilder Maven / Gradle / Ivy
/*
* Copyright (c) 2009. The Codehaus. All Rights Reserved.
*
* Licensed 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.codehaus.httpcache4j.util;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.codehaus.httpcache4j.Parameter;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.Collator;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
/**
* Immutable URI builder.
* Paths in this URI builder will be UTF-8 {@link org.codehaus.httpcache4j.util.URIEncoder URIEncoded}.
* Query Parameters needs to be URIEncoded before they are added.
* All methods return a NEW instance of the URI builder, meaning you can create a ROOT uri builder and use it
* to your heart's content, as the instance will never change.
*
* @author Erlend Hamnaberg
* @version $Revision: $
*/
public final class URIBuilder {
public static AtomicReference schemeDefaults = new AtomicReference(new URISchemeDefaults());
private final String scheme;
private final String host;
private final int port;
private final List path;
private final String fragment;
private final Map> parameters;
private final boolean wasPathAbsolute;
private final boolean endsWithSlash;
private URIBuilder(String scheme, String host, int port, List path, String fragment, Map> parameters, boolean wasPathAbsolute, boolean endsWithSlash) {
this.scheme = scheme;
this.host = host;
this.port = port;
this.path = path;
this.fragment = fragment;
this.parameters = parameters;
this.wasPathAbsolute = wasPathAbsolute;
this.endsWithSlash = endsWithSlash;
}
/**
* This is the scheme to use. Usually 'http' or 'https'.
* @param scheme the scheme
* @return a new URIBuilder with the new scheme set.
*/
@Deprecated
public URIBuilder scheme(String scheme) {
return withScheme(scheme);
}
@Deprecated
public URIBuilder host(String host) {
return withHost(host);
}
public URIBuilder withHost(String host) {
return new URIBuilder(scheme, host, port, path, fragment, parameters, wasPathAbsolute, endsWithSlash);
}
/**
* Sets the port. This is not required to set if you are using default ports for 'http' or 'https'
* @param port the port to set
* @return a new URIBuilder with the port set
*/
@Deprecated
public URIBuilder port(int port) {
return withPort(port);
}
/**
* Sets the port. This is not required to set if you are using default ports for 'http' or 'https'
* @param port the port to set
* @return a new URIBuilder with the port set
*/
public URIBuilder withPort(int port) {
Optional defaultPort = schemeDefaults.get().getPort(scheme);
if (defaultPort.isPresent() && (port == defaultPort.get())) {
port = -1;
}
return new URIBuilder(scheme, host, port, path, fragment, parameters, wasPathAbsolute, endsWithSlash);
}
/**
* This is the scheme to use. Usually 'http' or 'https'.
* @param scheme the scheme
* @return a new URIBuilder with the new scheme set.
*/
public URIBuilder withScheme(String scheme) {
return new URIBuilder(scheme, host, port, path, fragment, parameters, wasPathAbsolute, endsWithSlash);
}
/**
* Adds a raw path to the URI.
* @param path a path which may contain '/'
* @return a new URI builder which contains the added path.
*/
public URIBuilder addRawPath(String path) {
boolean pathAbsolute = wasPathAbsolute || this.path.isEmpty() && path.startsWith("/");
boolean endsWithSlash = this.endsWithSlash || this.path.isEmpty() && path.endsWith("/");
List appendedPath = toPathParts(path);
ImmutableList.Builder currentPath = ImmutableList.builder();
currentPath.addAll(this.path);
currentPath.addAll(appendedPath);
return new URIBuilder(scheme, host, port, currentPath.build(), fragment, parameters, pathAbsolute, endsWithSlash);
}
/**
* Appends the path part to the URI.
* We do not expect the path separator '/' to appear here, as each element will be URLEncoded.
* If the '/' character do appear it will be URLEncoded with the rest of the path.
*
* @param path path elements.
* @return a new URI builder which contains the new path.
*/
public URIBuilder addPath(List path) {
List appendedPath = Lists.transform(path, stringToPath);
ImmutableList.Builder currentPath = ImmutableList.builder();
currentPath.addAll(this.path);
currentPath.addAll(appendedPath);
return new URIBuilder(scheme, host, port, currentPath.build(), fragment, parameters, wasPathAbsolute, endsWithSlash);
}
/**
* @see #addPath(java.util.List)
*
* @param path path elements
* @return a new URI builder which contains the new path.
*/
public URIBuilder addPath(String... path) {
return addPath(Arrays.asList(path));
}
/**
* @see #path(java.util.List)
*
* @param path path elements.
* @return a new URI builder which contains the new path.
*/
@Deprecated
public URIBuilder path(String... path) {
return withPath(Arrays.asList(path));
}
/**
* @see #path(java.util.List)
*
* @param path path elements.
* @return a new URI builder which contains the new path.
*/
public URIBuilder withPath(String... path) {
return withPath(Arrays.asList(path));
}
/**
* Sets the path of the uri.
* We do not expect the path separator '/' to appear here, as each element will be URLEncoded.
* If the '/' character do appear it will be URLEncoded with the rest of the path.
*
* @param pathList path elements.
* @return a new URI builder which contains the new path.
*/
@Deprecated
public URIBuilder path(List pathList) {
return withPath(pathList);
}
/**
* Sets the path of the uri.
* We do not expect the path separator '/' to appear here, as each element will be URLEncoded.
* If the '/' character do appear it will be URLEncoded with the rest of the path.
*
* @param pathList path elements.
* @return a new URI builder which contains the new path.
*/
public URIBuilder withPath(List pathList) {
List paths = Lists.transform(pathList, stringToPath);
return pathInternal(paths, false, false);
}
/**
* @see #path(java.util.List)
*
* @param path path elements.
* @return a new URI builder which contains the new path.
*/
@Deprecated
public URIBuilder rawPath(String path) {
return withRawPath(path);
}
/**
* @see #path(java.util.List)
*
* @param path path elements.
* @return a new URI builder which contains the new path.
*/
public URIBuilder withRawPath(String path) {
boolean pathAbsoulute = path.startsWith("/");
boolean endsWithSlash = path.endsWith("/");
List parts = toPathParts(path);
return pathInternal(parts, pathAbsoulute, endsWithSlash);
}
private URIBuilder pathInternal(List pathList, boolean pathAbsolute, boolean endsWithSlash) {
return new URIBuilder(scheme, host, port, ImmutableList.copyOf(pathList), fragment, parameters, pathAbsolute, endsWithSlash);
}
@Deprecated
public URIBuilder fragment(String fragment) {
return withFragment(fragment);
}
public URIBuilder withFragment(String fragment) {
return new URIBuilder(scheme, host, port, path, fragment, parameters, wasPathAbsolute, endsWithSlash);
}
/**
* Creates a new URIBuilder with no parameters, but all other values retained.
* @return new URIBuilder with no parameters.
*/
public URIBuilder noParameters() {
return withParameters(Collections.emptyList());
}
/**
* Sets a list of parameters. This will clear out all previously set parameters in the new instance.
* @param parameters the list of parameters
* @return new URIBuilder with parameters.
* @deprecated use #withParameters instead
*/
@Deprecated
public URIBuilder parameters(List parameters) {
return withParameters(parameters);
}
/**
* Sets a list of parameters. This will clear out all previously set parameters in the new instance.
* @param parameters the list of parameters
* @return new URIBuilder with parameters.
*/
public URIBuilder withParameters(List parameters) {
Map> paraMap = new LinkedHashMap>();
for (Parameter parameter : parameters) {
addToQueryMap(paraMap, parameter.getName(), parameter.getValue());
}
return withParameters(paraMap);
}
public URIBuilder withParameters(Map> params) {
Map> paraMap = new LinkedHashMap>();
paraMap.putAll(params);
return new URIBuilder(scheme, host, port, path, fragment, Collections.unmodifiableMap(paraMap), wasPathAbsolute, endsWithSlash);
}
/**
* Adds a new Parameter to the collection of parameters
* @param name the parameter name
* @param value the parameter value
* @return a new instance of the URIBuilder
*/
public URIBuilder addParameter(String name, String value) {
return addParameter(new Parameter(name, value));
}
/**
* Adds a new Parameter to the collection of parameters
* @param parameter the parameter
* @return a new instance of the URIBuilder
*/
public URIBuilder addParameter(Parameter parameter) {
return addParameters(Arrays.asList(parameter));
}
/**
* Adds Parameters to the collection of parameters
* @return a new instance of the URIBuilder
*/
public URIBuilder addParameters(List parameters) {
Map> paraMap = new LinkedHashMap>(this.parameters);
for (Parameter parameter : parameters) {
addToQueryMap(paraMap, parameter.getName(), parameter.getValue());
}
return new URIBuilder(scheme, host, port, path, fragment, Collections.unmodifiableMap(paraMap), wasPathAbsolute, endsWithSlash);
}
public URIBuilder addParameters(Map> params) {
Map> paraMap = new LinkedHashMap>(this.parameters);
paraMap.putAll(params);
return new URIBuilder(scheme, host, port, path, fragment, Collections.unmodifiableMap(paraMap), wasPathAbsolute, endsWithSlash);
}
public URIBuilder removeParameters(String name) {
Map> map = new HashMap>(this.parameters);
map.remove(name);
return new URIBuilder(scheme, host, port, path, fragment, Collections.unmodifiableMap(map), wasPathAbsolute, endsWithSlash);
}
public URIBuilder replaceParameter(String name, String value) {
Map> map = new HashMap>(this.parameters);
map.remove(name);
addToQueryMap(map, name, value);
return new URIBuilder(scheme, host, port, path, fragment, Collections.unmodifiableMap(map), wasPathAbsolute, endsWithSlash);
}
private String toPath(boolean encodepath) {
if (path.isEmpty()) {
return null;
}
StringBuilder builder = new StringBuilder();
for (Path pathElement : path) {
if (builder.length() > 0) {
builder.append("/");
}
builder.append(encodepath ? pathElement.getEncodedValue() : pathElement.getValue());
}
if ((wasPathAbsolute || host != null) && builder.length() > 1) {
if (!"/".equals(builder.substring(0, 1))) {
builder.insert(0, "/");
}
}
if (endsWithSlash) {
builder.append("/");
}
return builder.toString();
}
public URI toURI() {
try {
return new URI(scheme, null, host, port, toPath(true), toQuery(false), fragment);
} catch (URISyntaxException e) {
throw new IllegalStateException(e);
}
}
public URI toNormalizedURI(boolean encodePath) {
try {
return new URI(scheme, null, host, port, toPath(encodePath), toQuery(true), fragment).normalize();
} catch (URISyntaxException e) {
throw new IllegalStateException(e);
}
}
public URI toNormalizedURI() {
return toNormalizedURI(false);
}
/**
* @return true if the scheme and host parts are not set.
*/
public boolean isRelative() {
return (scheme == null && host == null);
}
public URI toAbsoluteURI() {
if (isRelative()) {
try {
String path = toPath(true);
if (!path.startsWith("/")) {
path = "/" + path;
}
return new URI(null, null, null, -1, path, toQuery(false), fragment);
} catch (URISyntaxException e) {
throw new IllegalStateException(e);
}
}
return toURI();
}
private String toQuery(boolean sort) {
StringBuilder builder = new StringBuilder();
List params = getParametersAsList();
if (sort) {
Collections.sort(params, new Comparator() {
@Override
public int compare(Parameter o1, Parameter o2) {
return Collator.getInstance(Locale.getDefault()).compare(o1.getName(), o2.getName());
}
});
}
for (Parameter parameter : params) {
if (builder.length() > 0) {
builder.append("&");
}
builder.append(parameter);
}
if (builder.length() == 0) {
return null;
}
return builder.toString();
}
private List getParametersAsList() {
List list = new ArrayList();
for (Map.Entry> entry : parameters.entrySet()) {
for (String value : entry.getValue()) {
list.add(new Parameter(entry.getKey(), value));
}
}
return list;
}
public List getParametersByName(final String name) {
List params = parameters.get(name);
if (params == null) {
return Collections.emptyList();
}
return Lists.transform(params, new Function() {
public Parameter apply(String s) {
return new Parameter(name, s);
}
});
}
public String getFirstParameterValueByName(final String name) {
List list = getParametersByName(name);
if (!list.isEmpty()) {
return list.get(0).getValue();
}
return null;
}
/**
* Constructs a new URIBuilder from the given URI
* @param uri the uri to use
* @return a new URIBuilder which has the information from the URI.
*/
public static URIBuilder fromURI(URI uri) {
boolean pathAbsoluteness = uri.getPath().startsWith("/");
boolean endsWithSlash = uri.getPath().endsWith("/");
return new URIBuilder(uri.getScheme(), uri.getHost(), uri.getPort(), toPathParts(uri.getPath()), uri.getFragment(), toQueryMap(uri.getQuery()), pathAbsoluteness, endsWithSlash);
}
/**
* Creates an empty URIBuilder.
* @return an empty URIBuilder which result of {@link #toURI()} ()} will return "".
*/
public static URIBuilder empty() {
return new URIBuilder(null, null, -1, Collections.emptyList(), null, Collections.>emptyMap(), false, false);
}
public String getScheme() {
return scheme;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public List getPath() {
return Lists.transform(path, pathToString);
}
public List getEncodedPath() {
return Lists.transform(path, encodedPathToString);
}
public String getCurrentPath() {
return toPath(false);
}
public String getFragment() {
return fragment;
}
public List getParameters() {
return Collections.unmodifiableList(getParametersAsList());
}
public static Map> toQueryMap(String query) {
Map> map = new LinkedHashMap>();
if (query != null) {
Iterable parts = Splitter.on("&").omitEmptyStrings().trimResults().split(query);
for (String part : parts) {
String[] equalParts = part.split("=");
String name = null;
String value = null;
if (equalParts.length == 1) {
name = equalParts[0];
}
else if (equalParts.length == 2) {
name = equalParts[0];
value = equalParts[1];
}
if (name != null) {
addToQueryMap(map, URIDecoder.decodeUTF8(name), URIDecoder.decodeUTF8(value));
}
}
}
return Collections.unmodifiableMap(map);
}
private static void addToQueryMap(Map> map, String name, String value) {
List list = map.get(name);
if (list == null) {
list = new ArrayList();
}
if (value != null) {
list.add(value);
}
map.put(name, list);
}
private static List toPathParts(String path) {
if (path == null) {
return Collections.emptyList();
}
if (!path.contains("/")) {
return Collections.singletonList(new Path(path));
}
else {
if (path.startsWith("/")) {
path = path.substring(1);
}
List stringList = Arrays.asList(path.split("/"));
return ImmutableList.copyOf(Lists.transform(stringList, stringToPath));
}
}
private static Function stringToPath = new Function() {
public Path apply(String from) {
return new Path(from);
}
};
private static Function pathToString = new Function() {
public String apply(Path from) {
return from.getValue();
}
};
private static Function encodedPathToString = new Function() {
public String apply(Path from) {
return from.getEncodedValue();
}
};
private static class Path {
private final String value;
private Path(String value) {
this.value = URIDecoder.decodeUTF8(value);
}
String getEncodedValue() {
return URIEncoder.encodeUTF8(value);
}
String getValue() {
return value;
}
@Override
public String toString() {
return value;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy