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    }