001 /*
002 * The contents of this file are subject to the terms
003 * of the Common Development and Distribution License
004 * (the "License"). You may not use this file except
005 * in compliance with the License.
006 *
007 * You can obtain a copy of the license at
008 * http://www.opensource.org/licenses/cddl1.php
009 * See the License for the specific language governing
010 * permissions and limitations under the License.
011 */
012
013 /*
014 * UriBuilder.java
015 *
016 * Created on July 18, 2007, 11:53 AM
017 *
018 */
019
020 package javax.ws.rs.core;
021
022 import java.lang.reflect.Method;
023 import java.net.URI;
024 import java.util.Map;
025 import javax.ws.rs.ext.RuntimeDelegate;
026
027 /**
028 * URI template aware utility class for building URIs from their components. See
029 * {@link javax.ws.rs.Path#value} for an explanation of URI templates.
030 *
031 * <p>Builder methods perform contextual encoding of characters not permitted in
032 * the corresponding URI component following the rules of the
033 * <a href="http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1">application/x-www-form-urlencoded</a>
034 * media type for query parameters and
035 * <a href="http://ietf.org/rfc/rfc3986.txt">RFC 3986</a> for all other
036 * components. Note that only characters not permitted in a particular component
037 * are subject to encoding so, e.g., a path supplied to one of the {@code path}
038 * methods may contain matrix parameters or multiple path segments since the
039 * separators are legal characters and will not be encoded. Percent encoded
040 * values are also recognized where allowed and will not be double encoded.</p>
041 *
042 * <p>URI templates are allowed in most components of a URI but their value is
043 * restricted to a particular component. E.g.
044 * <blockquote><code>UriBuilder.fromPath("{arg1}").build("foo#bar");</code></blockquote>
045 * would result in encoding of the '#' such that the resulting URI is
046 * "foo%23bar". To create a URI "foo#bar" use
047 * <blockquote><code>UriBuilder.fromPath("{arg1}").fragment("{arg2}").build("foo", "bar")</code></blockquote>
048 * instead. URI template names and delimiters are never encoded but their
049 * values are encoded when a URI is built.
050 * Template parameter regular expressions are ignored when building a URI, i.e.
051 * no validation is performed.
052 *
053 * @see java.net.URI
054 * @see javax.ws.rs.Path
055 */
056 public abstract class UriBuilder {
057
058 /**
059 * Protected constructor, use one of the static <code>from<i>XXX</i></code>
060 * methods to obtain an instance.
061 */
062 protected UriBuilder() {}
063
064 /**
065 * Creates a new instance of UriBuilder.
066 * @return a new instance of UriBuilder
067 */
068 protected static UriBuilder newInstance() {
069 UriBuilder b = RuntimeDelegate.getInstance().createUriBuilder();
070 return b;
071 }
072
073 /**
074 * Create a new instance initialized from an existing URI.
075 * @param uri a URI that will be used to initialize the UriBuilder.
076 * @return a new UriBuilder
077 * @throws IllegalArgumentException if uri is null
078 */
079 public static UriBuilder fromUri(URI uri) throws IllegalArgumentException {
080 UriBuilder b = newInstance();
081 b.uri(uri);
082 return b;
083 }
084
085 /**
086 * Create a new instance initialized from an existing URI.
087 * @param uri a URI that will be used to initialize the UriBuilder, may not
088 * contain URI parameters.
089 * @return a new UriBuilder
090 * @throws IllegalArgumentException if uri is not a valid URI or is null
091 */
092 public static UriBuilder fromUri(String uri) throws IllegalArgumentException {
093 URI u;
094 try {
095 u = URI.create(uri);
096 } catch (NullPointerException ex) {
097 throw new IllegalArgumentException(ex.getMessage(), ex);
098 }
099 return fromUri(u);
100 }
101
102 /**
103 * Create a new instance representing a relative URI initialized from a
104 * URI path.
105 * @param path a URI path that will be used to initialize the UriBuilder,
106 * may contain URI template parameters.
107 * @return a new UriBuilder
108 * @throws IllegalArgumentException if path is null
109 */
110 public static UriBuilder fromPath(String path) throws IllegalArgumentException {
111 UriBuilder b = newInstance();
112 b.path(path);
113 return b;
114 }
115
116 /**
117 * Create a new instance representing a relative URI initialized from a
118 * root resource class.
119 *
120 * @param resource a root resource whose {@link javax.ws.rs.Path} value will
121 * be used to initialize the UriBuilder.
122 * @return a new UriBuilder
123 * @throws IllegalArgumentException if resource is not annotated with
124 * {@link javax.ws.rs.Path} or resource is null.
125 */
126 public static UriBuilder fromResource(Class<?> resource) throws IllegalArgumentException {
127 UriBuilder b = newInstance();
128 b.path(resource);
129 return b;
130 }
131
132 /**
133 * Create a copy of the UriBuilder preserving its state. This is a more
134 * efficient means of creating a copy than constructing a new UriBuilder
135 * from a URI returned by the {@link #build} method.
136 * @return a copy of the UriBuilder
137 */
138 @Override
139 public abstract UriBuilder clone();
140
141 /**
142 * Copies the non-null components of the supplied URI to the UriBuilder replacing
143 * any existing values for those components.
144 * @param uri the URI to copy components from
145 * @return the updated UriBuilder
146 * @throws IllegalArgumentException if uri is null
147 */
148 public abstract UriBuilder uri(URI uri) throws IllegalArgumentException;
149
150 /**
151 * Set the URI scheme.
152 * @param scheme the URI scheme, may contain URI template parameters.
153 * A null value will unset the URI scheme.
154 * @return the updated UriBuilder
155 * @throws IllegalArgumentException if scheme is invalid
156 */
157 public abstract UriBuilder scheme(String scheme) throws IllegalArgumentException;
158
159 /**
160 * Set the URI scheme-specific-part (see {@link java.net.URI}). This
161 * method will overwrite any existing
162 * values for authority, user-info, host, port and path.
163 * @param ssp the URI scheme-specific-part, may contain URI template parameters
164 * @return the updated UriBuilder
165 * @throws IllegalArgumentException if ssp cannot be parsed or is null
166 */
167 public abstract UriBuilder schemeSpecificPart(String ssp) throws IllegalArgumentException;
168
169 /**
170 * Set the URI user-info.
171 * @param ui the URI user-info, may contain URI template parameters.
172 * A null value will unset userInfo component of the URI.
173 * @return the updated UriBuilder
174 */
175 public abstract UriBuilder userInfo(String ui);
176
177 /**
178 * Set the URI host.
179 * @return the updated UriBuilder
180 * @param host the URI host, may contain URI template parameters.
181 * A null value will unset the host component of the URI.
182 * @throws IllegalArgumentException if host is invalid.
183 */
184 public abstract UriBuilder host(String host) throws IllegalArgumentException;
185
186 /**
187 * Set the URI port.
188 * @param port the URI port, a value of -1 will unset an explicit port.
189 * @return the updated UriBuilder
190 * @throws IllegalArgumentException if port is invalid
191 */
192 public abstract UriBuilder port(int port) throws IllegalArgumentException;
193
194 /**
195 * Set the URI path. This method will overwrite
196 * any existing path and associated matrix parameters.
197 * Existing '/' characters are preserved thus a single value can
198 * represent multiple URI path segments.
199 * @param path the path, may contain URI template parameters.
200 * A null value will unset the path component of the URI.
201 * @return the updated UriBuilder
202 */
203 public abstract UriBuilder replacePath(String path);
204
205 /**
206 * Append path to the existing path.
207 * When constructing the final path, a '/' separator will be inserted
208 * between the existing path and the supplied path if necessary.
209 * Existing '/' characters are preserved thus a single value can
210 * represent multiple URI path segments.
211 * @param path the path, may contain URI template parameters
212 * @return the updated UriBuilder
213 * @throws IllegalArgumentException if path is null
214 */
215 public abstract UriBuilder path(String path) throws IllegalArgumentException;
216
217 /**
218 * Append the path from a Path-annotated class to the
219 * existing path.
220 * When constructing the final path, a '/' separator will be inserted
221 * between the existing path and the supplied path if necessary.
222 *
223 * @param resource a resource whose {@link javax.ws.rs.Path} value will be
224 * used to obtain the path to append.
225 * @return the updated UriBuilder
226 * @throws IllegalArgumentException if resource is null, or
227 * if resource is not annotated with {@link javax.ws.rs.Path}
228 */
229 public abstract UriBuilder path(Class resource) throws IllegalArgumentException;
230
231 /**
232 * Append the path from a Path-annotated method to the
233 * existing path.
234 * When constructing the final path, a '/' separator will be inserted
235 * between the existing path and the supplied path if necessary.
236 * This method is a convenience shortcut to <code>path(Method)</code>, it
237 * can only be used in cases where there is a single method with the
238 * specified name that is annotated with {@link javax.ws.rs.Path}.
239 *
240 * @param resource the resource containing the method
241 * @param method the name of the method whose {@link javax.ws.rs.Path} value will be
242 * used to obtain the path to append
243 * @return the updated UriBuilder
244 * @throws IllegalArgumentException if resource or method is null,
245 * or there is more than or less than one variant of the method annotated with
246 * {@link javax.ws.rs.Path}
247 */
248 public abstract UriBuilder path(Class resource, String method) throws IllegalArgumentException;
249
250 /**
251 * Append the path from a {@link javax.ws.rs.Path}-annotated method to the
252 * existing path.
253 * When constructing the final path, a '/' separator will be inserted
254 * between the existing path and the supplied path if necessary.
255 *
256 * @param method a method whose {@link javax.ws.rs.Path} value will be
257 * used to obtain the path to append to the existing path
258 * @return the updated UriBuilder
259 * @throws IllegalArgumentException if method is null or is
260 * not annotated with a {@link javax.ws.rs.Path}
261 */
262 public abstract UriBuilder path(Method method) throws IllegalArgumentException;
263
264 /**
265 * Append path segments to the existing path.
266 * When constructing the final path, a '/' separator will be inserted
267 * between the existing path and the first path segment if necessary and
268 * each supplied segment will also be separated by '/'.
269 * Existing '/' characters are encoded thus a single value can
270 * only represent a single URI path segment.
271 * @param segments the path segment values, each may contain URI template
272 * parameters
273 * @return the updated UriBuilder
274 * @throws IllegalArgumentException if segments or any element of segments
275 * is null
276 */
277 public abstract UriBuilder segment(String... segments) throws IllegalArgumentException;
278
279 /**
280 * Set the matrix parameters of the current final segment of the current URI path.
281 * This method will overwrite any existing matrix parameters on the current final
282 * segment of the current URI path. Note that the matrix parameters
283 * are tied to a particular path segment; subsequent addition of path segments
284 * will not affect their position in the URI path.
285 * @param matrix the matrix parameters, may contain URI template parameters.
286 * A null value will remove all matrix parameters of the current final segment
287 * of the current URI path.
288 * @return the updated UriBuilder
289 * @throws IllegalArgumentException if matrix cannot be parsed
290 * @see <a href="http://www.w3.org/DesignIssues/MatrixURIs.html">Matrix URIs</a>
291 */
292 public abstract UriBuilder replaceMatrix(String matrix) throws IllegalArgumentException;
293
294 /**
295 * Append a matrix parameter to the existing set of matrix parameters of
296 * the current final segment of the URI path. If multiple values are supplied
297 * the parameter will be added once per value. Note that the matrix parameters
298 * are tied to a particular path segment; subsequent addition of path segments
299 * will not affect their position in the URI path.
300 * @param name the matrix parameter name, may contain URI template parameters
301 * @param values the matrix parameter value(s), each object will be converted
302 * to a {@code String} using its {@code toString()} method. Stringified
303 * values may contain URI template parameters.
304 * @return the updated UriBuilder
305 * @throws IllegalArgumentException if name or values is null
306 * @see <a href="http://www.w3.org/DesignIssues/MatrixURIs.html">Matrix URIs</a>
307 */
308 public abstract UriBuilder matrixParam(String name, Object... values) throws IllegalArgumentException;
309
310 /**
311 * Replace the existing value(s) of a matrix parameter on
312 * the current final segment of the URI path. If multiple values are supplied
313 * the parameter will be added once per value. Note that the matrix parameters
314 * are tied to a particular path segment; subsequent addition of path segments
315 * will not affect their position in the URI path.
316 * @param name the matrix parameter name, may contain URI template parameters
317 * @param values the matrix parameter value(s), each object will be converted
318 * to a {@code String} using its {@code toString()} method. Stringified
319 * values may contain URI template parameters. If {@code values} is empty
320 * or null then all current values of the parameter are removed.
321 * @return the updated UriBuilder
322 * @throws IllegalArgumentException if name is null.
323 * @see <a href="http://www.w3.org/DesignIssues/MatrixURIs.html">Matrix URIs</a>
324 */
325 public abstract UriBuilder replaceMatrixParam(String name, Object... values) throws IllegalArgumentException;
326
327 /**
328 * Set the URI query string. This method will overwrite any existing query
329 * parameters.
330 * @param query the URI query string, may contain URI template parameters.
331 * A null value will remove all query parameters.
332 * @return the updated UriBuilder
333 * @throws IllegalArgumentException if query cannot be parsed
334 */
335 public abstract UriBuilder replaceQuery(String query) throws IllegalArgumentException;
336
337 /**
338 * Append a query parameter to the existing set of query parameters. If
339 * multiple values are supplied the parameter will be added once per value.
340 * @param name the query parameter name, may contain URI template parameters
341 * @param values the query parameter value(s), each object will be converted
342 * to a {@code String} using its {@code toString()} method. Stringified
343 * values may contain URI template parameters.
344 * @return the updated UriBuilder
345 * @throws IllegalArgumentException if name or values is null
346 */
347 public abstract UriBuilder queryParam(String name, Object... values) throws IllegalArgumentException;
348
349 /**
350 * Replace the existing value(s) of a query parameter. If
351 * multiple values are supplied the parameter will be added once per value.
352 * @param name the query parameter name, may contain URI template parameters
353 * @param values the query parameter value(s), each object will be converted
354 * to a {@code String} using its {@code toString()} method. Stringified
355 * values may contain URI template parameters. If {@code values} is empty
356 * or null then all current values of the parameter are removed.
357 * @return the updated UriBuilder
358 * @throws IllegalArgumentException if name is null
359 */
360 public abstract UriBuilder replaceQueryParam(String name, Object... values) throws IllegalArgumentException;
361
362 /**
363 * Set the URI fragment.
364 * @param fragment the URI fragment, may contain URI template parameters.
365 * A null value will remove any existing fragment.
366 * @return the updated UriBuilder
367 */
368 public abstract UriBuilder fragment(String fragment);
369
370 /**
371 * Build a URI, any URI template parameters will be replaced by the value in
372 * the supplied map. Values are converted to <code>String</code> using
373 * their <code>toString</code> method and are then encoded to match the
374 * rules of the URI component to which they pertain. All '%' characters
375 * in the stringified values will be encoded.
376 * The state of the builder is unaffected; this method may be called
377 * multiple times on the same builder instance.
378 * @param values a map of URI template parameter names and values
379 * @return the URI built from the UriBuilder
380 * @throws IllegalArgumentException if there are any URI template parameters
381 * without a supplied value, or if a template parameter value is null.
382 * @throws UriBuilderException if a URI cannot be constructed based on the
383 * current state of the builder.
384 */
385 public abstract URI buildFromMap(Map<String, ? extends Object> values)
386 throws IllegalArgumentException, UriBuilderException;
387
388 /**
389 * Build a URI, any URI template parameters will be replaced by the value in
390 * the supplied map. Values are converted to <code>String</code> using
391 * their <code>toString</code> method and are then encoded to match the
392 * rules of the URI component to which they pertain. All % characters in
393 * the stringified values that are not followed by two hexadecimal numbers
394 * will be encoded.
395 * The state of the builder is unaffected; this method may be called
396 * multiple times on the same builder instance.
397 * @param values a map of URI template parameter names and values
398 * @return the URI built from the UriBuilder
399 * @throws IllegalArgumentException if there are any URI template parameters
400 * without a supplied value, or if a template parameter value is null.
401 * @throws UriBuilderException if a URI cannot be constructed based on the
402 * current state of the builder.
403 */
404 public abstract URI buildFromEncodedMap(Map<String, ? extends Object> values)
405 throws IllegalArgumentException, UriBuilderException;
406
407 /**
408 * Build a URI, using the supplied values in order to replace any URI
409 * template parameters. Values are converted to <code>String</code> using
410 * their <code>toString</code> method and are then encoded to match the
411 * rules of the URI component to which they pertain. All '%' characters
412 * in the stringified values will be encoded.
413 * The state of the builder is unaffected; this method may be called
414 * multiple times on the same builder instance.
415 * <p>All instances of the same template parameter
416 * will be replaced by the same value that corresponds to the position of the
417 * first instance of the template parameter. e.g. the template "{a}/{b}/{a}"
418 * with values {"x", "y", "z"} will result in the the URI "x/y/x", <i>not</i>
419 * "x/y/z".
420 * @param values a list of URI template parameter values
421 * @return the URI built from the UriBuilder
422 * @throws IllegalArgumentException if there are any URI template parameters
423 * without a supplied value, or if a value is null.
424 * @throws UriBuilderException if a URI cannot be constructed based on the
425 * current state of the builder.
426 */
427 public abstract URI build(Object... values)
428 throws IllegalArgumentException, UriBuilderException;
429
430 /**
431 * Build a URI.
432 * Any URI templates parameters will be replaced with the supplied values in
433 * order. Values are converted to <code>String</code> using
434 * their <code>toString</code> method and are then encoded to match the
435 * rules of the URI component to which they pertain. All % characters in
436 * the stringified values that are not followed by two hexadecimal numbers
437 * will be encoded.
438 * The state of the builder is unaffected; this method may be called
439 * multiple times on the same builder instance.
440 * <p>All instances of the same template parameter
441 * will be replaced by the same value that corresponds to the position of the
442 * first instance of the template parameter. e.g. the template "{a}/{b}/{a}"
443 * with values {"x", "y", "z"} will result in the the URI "x/y/x", <i>not</i>
444 * "x/y/z".
445 * @param values a list of URI template parameter values
446 * @return the URI built from the UriBuilder
447 * @throws IllegalArgumentException if there are any URI template parameters
448 * without a supplied value, or if a value is null.
449 * @throws UriBuilderException if a URI cannot be constructed based on the
450 * current state of the builder.
451 */
452 public abstract URI buildFromEncoded(Object... values)
453 throws IllegalArgumentException, UriBuilderException;
454 }