001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2014 ForgeRock AS.
015 */
016
017package org.forgerock.openig.util;
018
019import static org.forgerock.openig.util.URIUtil.*;
020
021import java.net.URI;
022import java.net.URISyntaxException;
023
024/**
025 * A MutableUri is a modifiable {@link URI} substitute.
026 * Unlike URIs, which are immutable, a MutableUri can have its fields updated independently.
027 * That makes it easier if you just want to change a element of an Uri.
028 *
029 * @see URI
030 */
031public final class MutableUri implements Comparable<MutableUri> {
032
033    /**
034     * Factory method for avoiding typing {@code new MutableUri("http://...")}.
035     * @param uri URL encoded URI
036     * @return a new MutableUri instance
037     * @throws URISyntaxException if the given Uri is not well-formed
038     */
039    public static MutableUri uri(String uri) throws URISyntaxException {
040        return new MutableUri(uri);
041    }
042
043    /**
044     * The real URI, hidden by this class. Recreated each time a field is updated.
045     */
046    private URI uri;
047
048    /**
049     * Builds a new MutableUri using the given URI.
050     * @param uri URI
051     */
052    public MutableUri(final URI uri) {
053        this.uri = uri;
054    }
055
056    /**
057     * Builds a new MutableUri with deep copy.
058     * @param mutableUri URI
059     */
060    public MutableUri(final MutableUri mutableUri) {
061        this.uri = mutableUri.asURI();
062    }
063
064    /**
065     * Builds a new MutableUri using the given URL encoded String URI.
066     * @param uri URL encoded URI
067     * @throws URISyntaxException if the given Uri is not well-formed
068     */
069    public MutableUri(final String uri) throws URISyntaxException {
070        this.uri = new URI(uri);
071    }
072
073    /**
074     * Builds a new MutableUri using the given fields values (decoded values).
075     * @param scheme Scheme name
076     * @param userInfo User name and authorization information
077     * @param host Host name
078     * @param port Port number
079     * @param path Path
080     * @param query Query
081     * @param fragment Fragment
082     * @throws URISyntaxException if the produced URI is not well-formed
083     */
084    public MutableUri(String scheme,
085                      String userInfo,
086                      String host,
087                      int port,
088                      String path,
089                      String query,
090                      String fragment)
091            throws URISyntaxException {
092        uri = new URI(scheme, userInfo, host, port, path, query, fragment);
093    }
094
095    /**
096     * Returns the equivalent {@link URI} instance.
097     * @return the equivalent {@link URI} instance.
098     */
099    public URI asURI() {
100        return uri;
101    }
102
103    /**
104     * Returns the scheme name.
105     * @return the scheme name.
106     */
107    public String getScheme() {
108        return uri.getScheme();
109    }
110
111    /**
112     * Update the scheme of this MutableUri.
113     * @param scheme new scheme name
114     * @throws URISyntaxException if the new equivalent URI is invalid
115     */
116    public void setScheme(final String scheme) throws URISyntaxException {
117        this.uri = new URI(scheme,
118                           uri.getUserInfo(),
119                           uri.getHost(),
120                           uri.getPort(),
121                           uri.getPath(),
122                           uri.getQuery(),
123                           uri.getFragment());
124    }
125
126    /**
127     * Returns the user info element.
128     * @return the user info element.
129     */
130    public String getUserInfo() {
131        return uri.getUserInfo();
132    }
133
134    /**
135     * Returns the raw (encoded) user info element.
136     * @return the raw user info element.
137     */
138    public String getRawUserInfo() {
139        return uri.getRawUserInfo();
140    }
141
142    /**
143     * Update the user info (not encoded) of this MutableUri.
144     * @param userInfo new user info element (not encoded)
145     * @throws URISyntaxException if the new equivalent URI is invalid
146     */
147    public void setUserInfo(final String userInfo) throws URISyntaxException {
148        this.uri = new URI(uri.getScheme(),
149                           userInfo,
150                           uri.getHost(),
151                           uri.getPort(),
152                           uri.getPath(),
153                           uri.getQuery(),
154                           uri.getFragment());
155    }
156
157    /**
158     * Update the user info (encoded) of this MutableUri.
159     * @param rawUserInfo new user info element (encoded)
160     * @throws URISyntaxException if the new equivalent URI is invalid
161     */
162    public void setRawUserInfo(String rawUserInfo) throws URISyntaxException {
163        uri = create(uri.getScheme(),
164                     rawUserInfo,
165                     uri.getHost(),
166                     uri.getPort(),
167                     uri.getRawPath(),
168                     uri.getRawQuery(),
169                     uri.getRawFragment());
170    }
171
172    /**
173     * Returns the host element.
174     * @return the host element.
175     */
176    public String getHost() {
177        return uri.getHost();
178    }
179
180    /**
181     * Update the host name of this MutableUri.
182     * @param host new host element
183     * @throws URISyntaxException if the new equivalent URI is invalid
184     */
185    public void setHost(final String host) throws URISyntaxException {
186        this.uri = new URI(uri.getScheme(),
187                           uri.getUserInfo(),
188                           host,
189                           uri.getPort(),
190                           uri.getPath(),
191                           uri.getQuery(),
192                           uri.getFragment());
193    }
194
195    /**
196     * Returns the port element.
197     * @return the port element.
198     */
199    public int getPort() {
200        return uri.getPort();
201    }
202
203    /**
204     * Update the port of this MutableUri.
205     * @param port new port number
206     * @throws URISyntaxException if the new equivalent URI is invalid
207     */
208    public void setPort(final int port) throws URISyntaxException {
209        uri = new URI(uri.getScheme(),
210                      uri.getUserInfo(),
211                      uri.getHost(),
212                      port,
213                      uri.getPath(),
214                      uri.getQuery(),
215                      uri.getFragment());
216    }
217
218    /**
219     * Returns the path element.
220     * @return the path element.
221     */
222    public String getPath() {
223        return uri.getPath();
224    }
225
226    /**
227     * Returns the raw (encoded) path element.
228     * @return the raw path element.
229     */
230    public String getRawPath() {
231        return uri.getRawPath();
232    }
233
234    /**
235     * Update the path (not encoded) of this MutableUri.
236     * @param path new path element (not encoded)
237     * @throws URISyntaxException if the new equivalent URI is invalid
238     */
239    public void setPath(final String path) throws URISyntaxException {
240        this.uri = new URI(uri.getScheme(),
241                           uri.getUserInfo(),
242                           uri.getHost(),
243                           uri.getPort(),
244                           path,
245                           uri.getQuery(),
246                           uri.getFragment());
247
248    }
249
250    /**
251     * Update the pah (encoded) of this MutableUri.
252     * @param rawPath new path element (encoded)
253     * @throws URISyntaxException if the new equivalent URI is invalid
254     */
255    public void setRawPath(String rawPath) throws URISyntaxException {
256        uri = create(uri.getScheme(),
257                     uri.getRawUserInfo(),
258                     uri.getHost(),
259                     uri.getPort(),
260                     rawPath,
261                     uri.getRawQuery(),
262                     uri.getRawFragment());
263    }
264
265    /**
266     * Returns the path element.
267     * @return the path element.
268     */
269    public String getQuery() {
270        return uri.getQuery();
271    }
272
273    /**
274     * Returns the raw (encoded) query element.
275     * @return the raw query element.
276     */
277    public String getRawQuery() {
278        return uri.getRawQuery();
279    }
280
281    /**
282     * Update the query string (not encoded) of this MutableUri.
283     * @param query new query string element (not encoded)
284     * @throws URISyntaxException if the new equivalent URI is invalid
285     */
286    public void setQuery(final String query) throws URISyntaxException {
287        this.uri = new URI(uri.getScheme(),
288                           uri.getUserInfo(),
289                           uri.getHost(),
290                           uri.getPort(),
291                           uri.getPath(),
292                           query,
293                           uri.getFragment());
294    }
295
296    /**
297     * Update the query (encoded) of this MutableUri.
298     * @param rawQuery new query element (encoded)
299     * @throws URISyntaxException if the new equivalent URI is invalid
300     */
301    public void setRawQuery(String rawQuery) throws URISyntaxException {
302        uri = create(uri.getScheme(),
303                     uri.getRawUserInfo(),
304                     uri.getHost(),
305                     uri.getPort(),
306                     uri.getRawPath(),
307                     rawQuery,
308                     uri.getRawFragment());
309    }
310
311    /**
312     * Returns the fragment element.
313     * @return the fragment element.
314     */
315    public String getFragment() {
316        return uri.getFragment();
317    }
318
319    /**
320     * Returns the raw (encoded) fragment element.
321     * @return the raw fragment element.
322     */
323    public String getRawFragment() {
324        return uri.getRawFragment();
325    }
326
327    /**
328     * Update the fragment (not encoded) of this MutableUri.
329     * @param fragment new fragment element (not encoded)
330     * @throws URISyntaxException if the new equivalent URI is invalid
331     */
332    public void setFragment(final String fragment) throws URISyntaxException {
333        this.uri = new URI(uri.getScheme(),
334                           uri.getUserInfo(),
335                           uri.getHost(),
336                           uri.getPort(),
337                           uri.getPath(),
338                           uri.getQuery(),
339                           fragment);
340    }
341
342    /**
343     * Update the fragment (encoded) of this MutableUri.
344     * @param rawFragment new framgent element (encoded)
345     * @throws URISyntaxException if the new equivalent URI is invalid
346     */
347    public void setRawFragment(String rawFragment) throws URISyntaxException {
348        uri = create(uri.getScheme(),
349                     uri.getRawUserInfo(),
350                     uri.getHost(),
351                     uri.getPort(),
352                     uri.getRawPath(),
353                     uri.getRawQuery(),
354                     rawFragment);
355    }
356
357    /**
358     * Returns the authority compound element.
359     * @return the authority compound element.
360     */
361    public String getAuthority() {
362        return uri.getAuthority();
363    }
364
365    /**
366     * Returns the raw (encoded) authority compound element.
367     * @return the authority compound element.
368     */
369    public String getRawAuthority() {
370        return uri.getRawAuthority();
371    }
372
373    /**
374     * Changes the base scheme, host and port of this MutableUri to that specified in a base URI,
375     * or leaves them unchanged if the base URI is {@code null}. This implementation only
376     * uses scheme, host and port. The remaining components of the URI remain intact.
377     *
378     * @param base the URI to base the other URI on.
379     * @return this (rebased) instance
380     */
381    public MutableUri rebase(MutableUri base) {
382        if (base == null) {
383            return this;
384        }
385        String scheme = base.getScheme();
386        String host = base.getHost();
387        int port = base.getPort();
388        if (scheme == null || host == null) {
389            return this;
390        }
391        try {
392            setScheme(scheme);
393            setHost(host);
394            setPort(port);
395        } catch (URISyntaxException e) {
396            throw new IllegalStateException(e);
397        }
398        return this;
399    }
400
401    /**
402     * Changes the base scheme, host and port of this MutableUri to that specified in a base URI,
403     * or leaves them unchanged if the base URI is {@code null}. This implementation only
404     * uses scheme, host and port. The remaining components of the URI remain intact.
405     *
406     * @param base the URI to base the other URI on.
407     * @return this (rebased) instance
408     */
409    public MutableUri rebase(URI base) {
410        return rebase(new MutableUri(base));
411    }
412
413    @Override
414    public int compareTo(final MutableUri o) {
415        return asURI().compareTo(o.asURI());
416    }
417
418    /**
419     * Relativizes the given URI against this URI.
420     * @param uri the uri to relativizes against this instance
421     * @return this instance (mutated)
422     * @see URI#relativize(URI)
423     */
424    public MutableUri relativize(final MutableUri uri) {
425        this.uri = this.uri.relativize(uri.asURI());
426        return this;
427    }
428
429    /**
430     * Resolves the given URI against this URI.
431     * @param uri the uri to resolve against this instance
432     * @return this instance (mutated)
433     * @see URI#resolve(URI)
434     */
435    public MutableUri resolve(final MutableUri uri) {
436        this.uri = this.uri.resolve(uri.asURI());
437        return this;
438    }
439
440    @Override
441    public String toString() {
442        return uri.toString();
443    }
444
445    /**
446     * Returns the content of this URI as a US-ASCII string.
447     * @return the content of this URI as a US-ASCII string.
448     */
449    public String toASCIIString() {
450        return uri.toASCIIString();
451    }
452
453    @Override
454    public boolean equals(final Object o) {
455        if (this == o) {
456            return true;
457        }
458        if (o == null || !(o instanceof MutableUri)) {
459            return false;
460        }
461        MutableUri that = (MutableUri) o;
462        return uri.equals(that.uri);
463
464    }
465
466    @Override
467    public int hashCode() {
468        return uri.hashCode();
469    }
470}