/** * Copyright 2005-2014 Restlet * * The contents of this file are subject to the terms of one of the following * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can * select the license that you prefer but you may not use this file except in * compliance with one of these Licenses. * * You can obtain a copy of the Apache 2.0 license at * http://www.opensource.org/licenses/apache-2.0 * * You can obtain a copy of the EPL 1.0 license at * http://www.opensource.org/licenses/eclipse-1.0 * * See the Licenses for the specific language governing permissions and * limitations under the Licenses. * * Alternatively, you can obtain a royalty free commercial license with less * limitations, transferable or non-transferable, directly at * http://restlet.com/products/restlet-framework * * Restlet is a registered trademark of Restlet S.A.S. */ package org.restlet.ext.jaxrs; import static org.restlet.ext.jaxrs.internal.util.Util.append; import static org.restlet.ext.jaxrs.internal.util.Util.getPathTemplateWithoutRegExps; import static org.restlet.ext.jaxrs.internal.util.Util.indexBeginMatrixOfLastSegment; import static org.restlet.ext.jaxrs.internal.util.Util.notEndsWith; import static org.restlet.ext.jaxrs.internal.util.Util.notStartsWith; import java.io.IOException; import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.ws.rs.Path; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilderException; import org.restlet.ext.jaxrs.internal.exceptions.IllegalPathException; import org.restlet.ext.jaxrs.internal.exceptions.IllegalPathOnClassException; import org.restlet.ext.jaxrs.internal.exceptions.IllegalPathOnMethodException; import org.restlet.ext.jaxrs.internal.exceptions.MissingAnnotationException; import org.restlet.ext.jaxrs.internal.todo.NotYetImplementedException; import org.restlet.ext.jaxrs.internal.util.EncodeOrCheck; import org.restlet.routing.Template; import org.restlet.util.Resolver; /** * Abstract implementation of interface {@link UriBuilder}. Not intended for * public use. * * @author Stephan Koops * @see UriBuilder */ public abstract class AbstractUriBuilder extends UriBuilder { private class ArrayVariableResolver extends Resolver<String> { private final boolean encoding; private int i = 0; private final Map<String, String> retrievedValues = new HashMap<String, String>(); private final Object[] values; ArrayVariableResolver(Object[] values, boolean encoding) { this.values = values; this.encoding = encoding; } @Override public String resolve(String variableName) { String varValue = this.retrievedValues.get(variableName); if (varValue == null) { if (this.i >= this.values.length) { throw new IllegalArgumentException( "The value given array contains not enough elements (contains " + this.values.length + ", but need at least " + (this.i + 1) + ")"); } final Object value = this.values[this.i]; if (value == null) { throw new IllegalArgumentException( "The given array contains null value at position (" + this.i + ")"); } varValue = value.toString(); varValue = EncodeOrCheck.all(varValue, this.encoding); this.i++; this.retrievedValues.put(variableName, varValue); } return varValue; } } /** * {@link String} or {@link StringBuilder}. It is flexible to avoid * unnecessary converting. */ private CharSequence fragment; /** * no converting or appending necessary. */ private String host; /** * {@link String} or {@link StringBuilder}. It is flexible to avoid * unnecessary converting. */ private CharSequence path; private String port = null; /** * {@link String} or {@link StringBuilder}. It is flexible to avoid * unnecessary converting. */ private CharSequence query; /** * no converting or appending necessary. */ private String scheme; /** * {@link String} or {@link StringBuilder}. It is flexible to avoid * unnecessary converting. */ private CharSequence userInfo; protected AbstractUriBuilder() { } /** * adds a valid (encoded or checked) path segment. The path may start with * "/" or not, but must not be null. * * @param path * @param newPathSegment */ private void addValidPathSegment(CharSequence newPathSegment) { final StringBuilder path = getPath(); if ((this.host != null) && notEndsWith(path, '/') && notStartsWith(newPathSegment, '/')) { path.append('/'); } path.append(newPathSegment); } /** * adds a valid (encoded or checked) path segment. The elements may start * with "/" or not, but must not be null. * * @param newPathSegments */ private void addValidPathSegments(List<CharSequence> newPathSegments) { for (final CharSequence newPathSegment : newPathSegments) { addValidPathSegment(newPathSegment); } } /** * Build a URI, using the supplied values in order to replace any URI * template parameters. Values are converted to <code>String</code> using * their <code>toString</code> method and are then encoded to match the * rules of the URI component to which they pertain. All '%' characters in * the stringified values will be encoded. The state of the builder is * unaffected; this method may be called multiple times on the same builder * instance. * <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". * * @param values * a list of URI template parameter values * @return the URI built from the UriBuilder * @throws IllegalArgumentException * if there are any URI template parameters without a supplied * value, or if a value is null. * @throws UriBuilderException * if a URI cannot be constructed based on the current state of * the builder. * @see javax.ws.rs.core.UriBuilder#build(java.lang.Object[]) */ @Override public URI build(Object... values) throws IllegalArgumentException, UriBuilderException { final Template template = new Template(toStringWithCheck(false)); return buildUri(template .format(new ArrayVariableResolver(values, true))); } /** * @see javax.ws.rs.core.UriBuilder#buildFromEncoded(java.lang.Object[]) */ @Override public URI buildFromEncoded(Object... values) throws IllegalArgumentException, UriBuilderException { final Template template = new Template(toStringWithCheck(false)); return buildUri(template .format(new ArrayVariableResolver(values, false))); } /** * @see javax.ws.rs.core.UriBuilder#buildFromEncodedMap(java.util.Map) */ @Override public URI buildFromEncodedMap(Map<String, ? extends Object> values) throws IllegalArgumentException, UriBuilderException { return this.buildFromMap(values, false); } /** * @see javax.ws.rs.core.UriBuilder#buildFromMap(java.util.Map) */ @Override public URI buildFromMap(Map<String, ? extends Object> values) throws IllegalArgumentException, UriBuilderException { return this.buildFromMap(values, true); } /** * Build a URI, any URI template parameters will be replaced by the value in * the supplied map. The <code>build</code> method does not change the state * of the <code>UriBuilder</code> and it may be called multiple times on the * same builder instance. * * @param values * a map of URI template parameter names and values * @param encode * true, if the value should be encoded, or false if not. * @return the URI built from the UriBuilder * @throws IllegalArgumentException * if automatic encoding is disabled and a supplied value * contains illegal characters, or if there are any URI template * parameters without a supplied value * @throws UriBuilderException * if a URI cannot be constructed based on the current state of * the builder. * @see javax.ws.rs.core.UriBuilder#build(java.util.Map) */ private URI buildFromMap(final Map<String, ? extends Object> values, final boolean encode) throws IllegalArgumentException, UriBuilderException { final Template template = new Template(toStringWithCheck(false)); return buildUri(template.format(new Resolver<String>() { @Override public String resolve(String variableName) { final Object varValue = values.get(variableName); if (varValue == null) { throw new IllegalArgumentException( "The value Map must contain a value for all given Templet variables. The value for variable " + variableName + " is missing"); } return EncodeOrCheck.all(varValue.toString(), encode); } })); } /** * @param refAsString * @return * @throws UriBuilderException */ private URI buildUri(String refAsString) throws UriBuilderException { try { return new URI(refAsString); } catch (URISyntaxException e) { throw new UriBuilderException( "Could not build the URI from String " + refAsString, e); } } /** * @param uriBuilder */ protected void copyInto(AbstractUriBuilder uriBuilder) { if (this.fragment != null) { uriBuilder.fragment = this.fragment; this.fragment = this.fragment.toString(); } uriBuilder.host = this.host; uriBuilder.port = this.port; uriBuilder.scheme = this.scheme; if (this.userInfo != null) { uriBuilder.userInfo = this.userInfo; this.userInfo = this.userInfo.toString(); } if (this.path != null) { uriBuilder.path = this.path; this.path = this.path.toString(); } if (this.query != null) { uriBuilder.query = this.query; this.query = this.query.toString(); // copy this.query to new query, because typically the clone will // be changed and not the orignal. } } /** * Set the URI fragment using an unencoded value. * * @param fragment * the URI fragment, may contain URI template parameters * @return the updated UriBuilder * @throws IllegalArgumentException * if fragment is null, or if automatic encoding is disabled and * fragment contains illegal characters * @see javax.ws.rs.core.UriBuilder#fragment(java.lang.String) */ @Override public UriBuilder fragment(String fragment) throws IllegalArgumentException { if (fragment == null) { this.fragment = null; } else { this.fragment = EncodeOrCheck.fragment(fragment); } return this; } /** * @return the extension of the current state, with a "." at beginning. Is * intended to be overriden in subclass ExtendedUriBuilder. Must not * return null, "" or ".". */ protected String getExtension() { return null; } /** * @return The path as StringBuilder. Ensures, that the returned * StringBuilder is available in the instance variable. */ private StringBuilder getPath() { StringBuilder path; if (this.path instanceof StringBuilder) { return (StringBuilder) this.path; } if (this.path == null) { path = new StringBuilder(); this.path = path; } else { path = new StringBuilder(this.path); this.path = path; } return path; } /** * Set the URI host. * * @return the updated UriBuilder * @param host * the URI host, may contain URI template parameters * @throws IllegalArgumentException * if host is invalid or is null * @see javax.ws.rs.core.UriBuilder#host(java.lang.String) */ @Override public UriBuilder host(String host) throws IllegalArgumentException { if (host == null) { this.host = null; } else { this.host = EncodeOrCheck.host(host); } return this; } /** * Append a matrix parameter to the existing set of matrix parameters of the * current final segment of the URI path. If multiple values are supplied * the parameter will be added once per value. Note that the matrix * parameters are tied to a particular path segment; subsequent addition of * path segments will not affect their position in the URI path. * * @param name * the matrix parameter name, may contain URI template parameters * @param values * the matrix parameter value(s), each object will be converted * to a {@code String} using its {@code toString()} method. * Stringified values may contain URI template parameters. * @return the updated UriBuilder * @throws IllegalArgumentException * if name or value is null, or if automatic encoding is * disabled and the name or any stringified value contains * illegal characters * @see <a href="http://www.w3.org/DesignIssues/MatrixURIs.html">Matrix * URIs</a> * @see javax.ws.rs.core.UriBuilder#matrixParam(String, Object...) */ @Override public UriBuilder matrixParam(String name, Object... values) throws IllegalArgumentException { if (values == null) { throw new IllegalArgumentException("The values must not be null"); } CharSequence ncs; ncs = EncodeOrCheck.nameOrValue(name, true, "matrix parameter name"); final List<String> valuesStr = new ArrayList<String>(); for (final Object value : values) { final String vcs = EncodeOrCheck.nameOrValue(value, true, "matrix parameter value"); valuesStr.add(vcs); } final StringBuilder path = getPath(); for (final String vcs : valuesStr) { path.append(';'); path.append(ncs); path.append('='); path.append(vcs); } return this; } /** * Appends the given path to the current path.S * * @param pathToAppend * @return the current UriBuilder * @throws IllegalArgumentException */ protected UriBuilder path(CharSequence pathToAppend) throws IllegalArgumentException { if (pathToAppend == null) throw new IllegalArgumentException( "the path to append must not be null"); CharSequence validPathSegment = EncodeOrCheck.pathSegmentsWithMatrix( pathToAppend, true); addValidPathSegment(validPathSegment); return this; } /** * Append path segments from a Path-annotated class to the existing list of * segments. When constructing the final path, each segment will be * separated by '/' if necessary. The value of the encode property of the * Path annotation will be used when processing the value of the * @<!---->{@link Path} but it will not be used to modify the state of * automatic encoding for the builder. * * @param rootResource * a resource whose @Path value will be used to obtain the * path segment. * @return the updated UriBuilder * @throws IllegalArgumentException * if resource is null, or if resource.encode is false and * resource.value contains illegal characters, or if resource is * not annotated with UrPath * @see javax.ws.rs.core.UriBuilder#path(java.lang.Class) */ @Override @SuppressWarnings("rawtypes") public UriBuilder path(Class rootResource) throws IllegalArgumentException { if (rootResource == null) { throw new IllegalArgumentException( "The root resource class must not be null"); } String newPathSegment; try { newPathSegment = getPathTemplateWithoutRegExps(rootResource); } catch (IllegalPathOnClassException e) { throw e.getCause(); } catch (MissingAnnotationException e) { throw new IllegalArgumentException("The class " + rootResource.getName() + " is not a valid root resource class. " + "A root resource class must be annotated with @Path"); } addValidPathSegment(newPathSegment); return this; } /** * Append path segments from a Path-annotated method to the existing list of * segments. When constructing the final path, each segment will be * separated by '/' if necessary. This method is a convenience shortcut to * <code>path(Method)</code>, it can only be used in cases where there is a * single method with the specified name that is annotated with @<!----> * {@link Path}. * * @param rootResource * the root resource class containing the method. * @param methodName * the name of the method whose @{@link Path} value will be * used to obtain the path segment. * @return the updated UriBuilder * @throws IllegalArgumentException * if resource or method is null, or if the specified method * does not exist, or there is more than or less than one * variant of the method annotated with UriPath * @see javax.ws.rs.core.UriBuilder#path(java.lang.Class, java.lang.String) */ @SuppressWarnings("rawtypes") @Override public UriBuilder path(Class rootResource, String methodName) throws IllegalArgumentException { if (methodName == null) { throw new IllegalArgumentException( "The method name must not be null"); } String resMethodPath = null; for (final Method method : rootResource.getMethods()) { if (!method.getName().equals(methodName)) { continue; } String path; try { path = getPathTemplateWithoutRegExps(method); } catch (IllegalPathOnMethodException e) { throw e.getCause(); } catch (MissingAnnotationException e) { throw new IllegalArgumentException(e); } if (path == null) { continue; } if ((resMethodPath != null) && !resMethodPath.equals(path)) { throw new IllegalArgumentException("The class " + rootResource + " has more than one methods with the name " + methodName + " annotated with @Path"); } resMethodPath = path; } if (resMethodPath == null) { throw new IllegalArgumentException("The class " + rootResource + " has no method with the name " + methodName + " annotated with @Path"); } addValidPathSegment(resMethodPath); return this; } /** * Append the path from a {@link javax.ws.rs.Path}-annotated method to the * existing path. When constructing the final path, a '/' separator will be * inserted between the existing path and the supplied path if necessary. * * @param method * a method whose {@link javax.ws.rs.Path} value will be used to * obtain the path to append to the existing path * @return the updated UriBuilder * @throws IllegalArgumentException * if any element of methods is null or is not annotated with a * {@link javax.ws.rs.Path} * @see javax.ws.rs.core.UriBuilder#path(java.lang.reflect.Method) */ @Override public UriBuilder path(Method method) throws IllegalArgumentException { if (method == null) { return this; } String validSegment; try { validSegment = getPathTemplateWithoutRegExps(method); } catch (MissingAnnotationException e) { throw new IllegalArgumentException(e); } catch (IllegalPathException e) { throw e.getCause(); } addValidPathSegment(validSegment); return this; } /** * Append path to the existing path. When constructing the final path, a '/' * separator will be inserted between the existing path and the supplied * path if necessary. Existing '/' characters are preserved thus a single * value can represent multiple URI path segments. * * @param pathToAppend * the path to append to the current path, may contain URI * template parameters * @return the updated UriBuilder * @throws IllegalArgumentException * if path is null * @see javax.ws.rs.core.UriBuilder#path(java.lang.String) */ @Override public UriBuilder path(String pathToAppend) throws IllegalArgumentException { path((CharSequence) pathToAppend); return this; } /** * append the given path segments to the current path * * @param segments * @return */ private List<CharSequence> pathSegmentsWithMatrix(String[] segments) { if (segments == null) { return new ArrayList<CharSequence>(0); } final int length = segments.length; final List<CharSequence> r = new ArrayList<CharSequence>(length); if (length == 0) { return r; } final String s = segments[0]; if ((s == null) || (s.length() == 0)) { return r; } r.add(EncodeOrCheck.pathSegmentWithMatrix(s, true)); for (int i = 1; i < length; i++) { r.add(EncodeOrCheck.pathSegmentWithMatrix(segments[i], true)); } return r; } /** * Set the URI port. * * @param port * the URI port, a value of -1 will unset an explicit port. * @return the updated UriBuilder * @throws IllegalArgumentException * if port is invalid * @see javax.ws.rs.core.UriBuilder#port(int) */ @Override public UriBuilder port(int port) throws IllegalArgumentException { if (port == -1) { this.port = null; } else if (port > 65535 || port < -1) { throw new IllegalArgumentException( "The port must between zero and 65535 or -1 to unset the explizit port"); } else { this.port = String.valueOf(port); } return this; } /** * Set the URI port. Only integers or a variable template is allowed * * @param port * the URI port (null will unset an explicit port) or a template * variable. * @return the updated UriBuilder * @throws IllegalArgumentException * if given value is invalid * @see #port(int) */ public UriBuilder port(String port) throws IllegalArgumentException { if (port == null) { this.port = null; } else if (port.startsWith("{") && port.endsWith("}")) { this.port = port; } else { this.port(Integer.parseInt(port)); } return this; } /** * Append a query parameter to the existing set of query parameters. If * multiple values are supplied the parameter will be added once per value. * * @param name * the query parameter name, may contain URI template parameters * @param values * the query parameter value(s), each object will be converted to * a {@code String} using its {@code toString()} method. * Stringified values may contain URI template parameters. * @return the updated UriBuilder * @throws IllegalArgumentException * if name or value is null, or if automatic encoding is * disabled and name or value contains illegal characters * @see javax.ws.rs.core.UriBuilder#queryParam(String, Object...) */ @Override public UriBuilder queryParam(String name, Object... values) throws IllegalArgumentException { if (values == null) { throw new IllegalArgumentException("The values must not be null"); } CharSequence ncs; ncs = EncodeOrCheck.nameOrValue(name, true, "query parameter name"); final List<String> valuesStr = new ArrayList<String>(); for (final Object value : values) { final String vcs = EncodeOrCheck.nameOrValue(value, true, "query parameter value"); valuesStr.add(vcs); } final Iterator<String> valueIter = valuesStr.iterator(); StringBuilder query; if (this.query == null) { query = new StringBuilder(); this.query = query; } else if (this.query instanceof StringBuilder) { query = (StringBuilder) this.query; query.append('&'); } else { query = new StringBuilder(this.query.toString()); query.append('&'); } query.append(ncs); query.append('='); query.append(valueIter.next()); while (valueIter.hasNext()) { query.append('&'); query.append(ncs); query.append('='); query.append(valueIter.next()); } return this; } /** * Set the matrix parameters of the current final segment of the current URI * path. This method will overwrite any existing matrix parameters on the * current final segment of the current URI path. Note that the matrix * parameters are tied to a particular path segment; subsequent addition of * path segments will not affect their position in the URI path. * * @param matrix * the matrix parameters, may contain URI template parameters. A * null value will remove all matrix parameters of the current * final segment of the current URI path. * @return the updated UriBuilder * @throws IllegalArgumentException * if matrix cannot be parsed, or if automatic encoding is * disabled and any matrix parameter name or value contains * illegal characters * @see javax.ws.rs.core.UriBuilder#replaceMatrix(String) */ @Override public UriBuilder replaceMatrix(String matrix) throws IllegalArgumentException { CharSequence mpcs = null; if (matrix != null) { mpcs = EncodeOrCheck.fullMatrix(matrix); } final StringBuilder path = getPath(); // remove matrix params from last path segment final int beginLastPs = path.lastIndexOf("/"); if (beginLastPs >= 0) { final int beginMp = path.indexOf(";", beginLastPs); if (beginMp >= 0) { path.delete(beginMp, path.length()); } } // add new matrix parameters if (mpcs == null) { return this; } if ((mpcs.length() == 0) || (mpcs.charAt(0) != ';')) { path.append(";"); } path.append(mpcs); return this; } /** * @see javax.ws.rs.core.UriBuilder#replaceMatrixParam(java.lang.String, * java.lang.Object[]) */ @Override public UriBuilder replaceMatrixParam(String name, Object... values) throws IllegalArgumentException { // TODO replace matrix parameters throw new NotYetImplementedException( "Sorry, the relacement of matrix parameters is not supported yet"); } /** * Replaces the current path with the given path. This method checks, if * there is an extension in, if needed by this class. * * @param newPath * the new path. */ protected UriBuilder replacePath(CharSequence newPath) throws IllegalArgumentException { // TODO check for extension in subclass if (newPath == null || (newPath.length() > 0 && newPath.charAt(0) == '/')) { this.path = newPath; } else { this.path = null; path(newPath); } return this; } /** * Set the URI path. This method will overwrite any existing path and * associated matrix parameters. Existing '/' characters are preserved thus * a single value can represent multiple URI path segments. * * @param newPath * the path to replace the old path with, may contain URI * template parameters. A null value will unset the path * component of the URI. * @return the updated UriBuilder * @see javax.ws.rs.core.UriBuilder#replacePath(java.lang.String) */ @Override public UriBuilder replacePath(String newPath) throws IllegalArgumentException { return replacePath((CharSequence) newPath); } /** * Set the URI query string. This method will overwrite any existing query * parameters. * * @param query * the URI query string, may contain URI template parameters. A * null value will remove all query parameters. * @return the updated UriBuilder * @throws IllegalArgumentException * if query cannot be parsed * @see javax.ws.rs.core.UriBuilder#replaceQuery(java.lang.String) */ @Override public UriBuilder replaceQuery(String query) throws IllegalArgumentException { if ((query == null) || (query.length() == 0)) { this.query = null; } else { this.query = EncodeOrCheck.fullQuery(query, true); } return this; } /** * @see javax.ws.rs.core.UriBuilder#replaceQueryParam(String, Object[]) */ @Override public UriBuilder replaceQueryParam(String name, Object... values) throws IllegalArgumentException { throw new NotYetImplementedException( "Sorry, the relacement of query parameters is not supported yet"); } /** * Set the URI scheme. * * @param scheme * the URI scheme, may contain URI template parameters * @return the updated UriBuilder * @throws IllegalArgumentException * if scheme is invalid or is null * @see javax.ws.rs.core.UriBuilder#scheme(java.lang.String) */ @Override public UriBuilder scheme(String scheme) throws IllegalArgumentException { this.scheme = EncodeOrCheck.scheme(scheme); return this; } /** * Set the URI scheme-specific-part (see {@link java.net.URI}). This method * will overwrite any existing values for authority, user-info, host, port * and path. * * @param ssp * the URI scheme-specific-part, may contain URI template * parameters * @return the updated UriBuilder * @throws IllegalArgumentException * if ssp cannot be parsed or is null * @see javax.ws.rs.core.UriBuilder#schemeSpecificPart(java.lang.String) */ @Override public UriBuilder schemeSpecificPart(String ssp) throws IllegalArgumentException { if (ssp == null) { throw new IllegalArgumentException( "The scheme specific part must not be null"); } if (ssp.startsWith("//")) { ssp = ssp.substring(2); } // split schemeSpecificPart final int firstSlashPos = ssp.indexOf('/'); String authority; CharSequence path; CharSequence query; CharSequence fragment; if (firstSlashPos >= 0) { authority = ssp.substring(0, firstSlashPos); final String pathQueryFragment = ssp.substring(firstSlashPos); String pathQuery; final int firstCrosshatchPos = pathQueryFragment.indexOf('#'); if (firstCrosshatchPos >= 0) { pathQuery = pathQueryFragment.substring(0, firstCrosshatchPos); fragment = pathQueryFragment.substring(firstCrosshatchPos + 1); } else { pathQuery = pathQueryFragment; fragment = null; } final int firstQmPos = pathQuery.indexOf('?'); if (firstQmPos >= 0) { path = pathQuery.substring(0, firstQmPos); query = pathQuery.substring(firstQmPos + 1); } else { path = pathQuery; query = null; } } else { authority = ssp; path = null; query = null; fragment = null; } CharSequence userInfo; String host; String port; final int atSignPos = authority.lastIndexOf('@'); if (atSignPos >= 0) { userInfo = authority.substring(0, atSignPos); authority = authority.substring(atSignPos + 1); } else { userInfo = null; } final int colonPos = authority.lastIndexOf(':'); if (colonPos >= 0) { host = authority.substring(0, colonPos); port = authority.substring(colonPos + 1); } else { host = authority; port = null; } // check / convert values if (userInfo != null) { userInfo = EncodeOrCheck.userInfo(userInfo, true); } if (host != null) { host = EncodeOrCheck.host(host); } if (path != null) { path = EncodeOrCheck.pathSegmentsWithMatrix(path, true); } if (query != null) { query = EncodeOrCheck.fullQuery(query, true); } if (fragment != null) { fragment = EncodeOrCheck.fragment(fragment); } // check and set max. one: the port port(port); // set checked / converted this.userInfo = userInfo; this.host = host; this.replacePath(path); this.query = query; this.fragment = fragment; return this; } /** * Append path segments to the existing path. When constructing the final * path, a '/' separator will be inserted between the existing path and the * first path segment if necessary and each supplied segment will also be * separated by '/'. Existing '/' characters are encoded thus a single value * can only represent a single URI path segment. * * @param segments * the path segment values, each may contain URI template * parameters * @return the updated UriBuilder * @throws IllegalArgumentException * if segments or any element of segments is null * @see javax.ws.rs.core.UriBuilder#segment(java.lang.String[]) */ @Override public UriBuilder segment(String... segments) throws IllegalArgumentException { if (segments == null) { throw new IllegalArgumentException("The segments must not be null"); } // first check preconditions final List<CharSequence> newPathSegments = pathSegmentsWithMatrix(segments); // than add addValidPathSegments(newPathSegments); return this; } /** * Returns the actual URI as String. * * @return the actual URI as String. * @see #toStringWithCheck(boolean) */ @Override public String toString() { return this.toString(false); } /** * Returns the actual URI as String. * * @param convertBraces * if true, all braces are converted, if false then not. * * @return the actual URI as {@link String}. Never returns null. * @see #toStringWithCheck(boolean) */ private String toString(boolean convertBraces) { try { final StringBuilder stb = new StringBuilder(); if (this.scheme != null) { append(stb, this.scheme, convertBraces); stb.append("://"); } if (this.userInfo != null) { append(stb, this.userInfo, convertBraces); stb.append('@'); } if (this.host != null) { append(stb, this.host, convertBraces); } if (this.port != null) { stb.append(':'); stb.append(this.port); } String extension = getExtension(); CharSequence thisPath = this.path; if (thisPath != null || extension != null) { if (stb.length() > 0) { if (notStartsWith(thisPath, '/') || ((thisPath == null) && (extension != null))) { stb.append('/'); } } if (extension == null) { append(stb, thisPath, convertBraces); } else { int begLastMatrix = indexBeginMatrixOfLastSegment(thisPath); if (begLastMatrix >= 0) { append(stb, thisPath, convertBraces, 0, begLastMatrix); append(stb, extension, convertBraces); append(stb, thisPath, convertBraces, begLastMatrix); } else { // no matrix parameters found append(stb, thisPath, convertBraces); append(stb, extension, convertBraces); } } } if (this.query != null) { stb.append('?'); append(stb, this.query, convertBraces); } if (this.fragment != null) { stb.append('#'); append(stb, this.fragment, convertBraces); } return stb.toString(); } catch (IOException e) { throw new RuntimeException( "Could not write the UriBuilder to a String; but this Exception could not occur normally", e); } } /** * Returns the actual URI as String. Check for valid scheme and host before * * @param convertBraces * if true, all braces are converted, if false then not. * * @return the actual URI as String. * @see #toString() */ private String toStringWithCheck(boolean convertBraces) { if (this.host == null) { if (this.port != null) { throw new UriBuilderException( "You must set a host, if you set a port"); } if ((this.userInfo != null) && (this.userInfo.length() >= 0)) { throw new UriBuilderException( "You must set a host, if you set a userInfo"); } } return toString(convertBraces); } /** * Copies the non-null components of the supplied URI to the UriBuilder * replacing any existing values for those components. * * @param uri * the URI to copy components from * @return the updated UriBuilder * @throws IllegalArgumentException * if uri is null * @see javax.ws.rs.core.UriBuilder#uri(java.net.URI) */ @Override public UriBuilder uri(URI uri) throws IllegalArgumentException { if (uri == null) { throw new IllegalArgumentException("The URI must not be null"); } if (uri.getScheme() != null) { this.scheme = uri.getScheme(); } if (uri.getHost() != null) { this.host = uri.getHost(); } this.port(uri.getPort()); if (uri.getRawUserInfo() != null) { this.userInfo = uri.getRawUserInfo(); } if (uri.getRawPath() != null) { this.replacePath(uri.getRawPath()); } if (uri.getRawQuery() != null) { this.query = uri.getRawQuery(); } if (uri.getRawFragment() != null) { this.fragment = uri.getRawFragment(); } return this; } /** * Set the URI user-info. * * @param userInfo * the URI user-info, may contain URI template parameters * @return the updated UriBuilder * @throws IllegalArgumentException * if automatic encoding is disabled and the userInfo contains * illegal characters, or if the userInfo is null. * @see javax.ws.rs.core.UriBuilder#userInfo(java.lang.String) */ @Override public UriBuilder userInfo(String userInfo) throws IllegalArgumentException { if (userInfo == null) { this.userInfo = null; } else { this.userInfo = EncodeOrCheck.userInfo(userInfo, true); } return this; } }