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 2010–2011 ApexIdentity Inc.
015 * Portions Copyright 2011-2014 ForgeRock AS.
016 */
017
018package org.forgerock.openig.util;
019
020import java.net.URI;
021import java.net.URISyntaxException;
022
023import org.forgerock.openig.http.Form;
024
025/**
026 * Utility class for performing operations on universal resource identifiers.
027 */
028public final class URIUtil {
029
030    /** Static methods only. */
031    private URIUtil() {
032    }
033
034    /**
035     * Returns a hierarchical URI constructed from the given components. Differs from the URI
036     * constructor by accepting raw versions of userInfo, path, query and fragment components.
037     *
038     * @param scheme the scheme component of the URI or {@code null} if none.
039     * @param rawUserInfo the raw user-information component of the URI or {@code null} if none.
040     * @param host the host component of the URI or {@code null} if none.
041     * @param port the port number of the URI or {@code -1} if none.
042     * @param rawPath the raw path component of the URI or {@code null} if none.
043     * @param rawQuery the raw query component of the URI or {@code null} if none.
044     * @param rawFragment the raw fragment component of the URI or {@code null} if none.
045     * @return the URI constructed from the given components.
046     * @throws URISyntaxException if the resulting URI would be malformed per RFC 2396.
047     */
048    public static URI create(String scheme, String rawUserInfo, String host, int port,
049                             String rawPath, String rawQuery, String rawFragment) throws URISyntaxException {
050        StringBuilder sb = new StringBuilder();
051        if (scheme != null) {
052            sb.append(scheme).append(':');
053        }
054        if (host != null) {
055            sb.append("//");
056        }
057        if (rawUserInfo != null) {
058            sb.append(rawUserInfo).append('@');
059        }
060        if (host != null) {
061            sb.append(host);
062            if (port != -1) {
063                sb.append(':').append(Integer.toString(port));
064            }
065        }
066        if (rawPath != null) {
067            sb.append(rawPath);
068        }
069        if (rawQuery != null) {
070            sb.append('?').append(rawQuery);
071        }
072        if (rawFragment != null) {
073            sb.append("#").append(rawFragment);
074        }
075        return new URI(sb.toString());
076    }
077
078    /**
079     * Changes the base scheme, host and port of a request to that specified in a base URI,
080     * or leaves them unchanged if the base URI is {@code null}. This implementation only
081     * uses scheme, host and port. The remaining components of the URI remain intact.
082     *
083     * @param uri the URI whose base is to be changed.
084     * @param base the URI to base the other URI on.
085     * @return the the URI with the new established base.
086     */
087    public static URI rebase(URI uri, URI base)  {
088        if (base == null) {
089            return uri;
090        }
091        String scheme = base.getScheme();
092        String host = base.getHost();
093        int port = base.getPort();
094        if (scheme == null || host == null) {
095            return uri;
096        }
097        try {
098            return create(scheme, uri.getRawUserInfo(), host, port, uri.getRawPath(),
099                    uri.getRawQuery(), uri.getRawFragment());
100        } catch (URISyntaxException e) {
101            throw new IllegalStateException(e);
102        }
103    }
104
105    /**
106     * Returns a new URI having the provided query parameters. The scheme,
107     * authority, path, and fragment remain unchanged.
108     *
109     * @param uri
110     *            the URI whose query is to be changed.
111     * @param query
112     *            the form containing the query parameters.
113     * @return a new URI having the provided query parameters. The scheme,
114     *         authority, path, and fragment remain unchanged.
115     */
116    public static URI withQuery(final URI uri, final Form query) {
117        try {
118            return create(uri.getScheme(), uri.getRawUserInfo(), uri.getHost(), uri.getPort(), uri
119                    .getRawPath(), query.toString(), uri.getRawFragment());
120        } catch (final URISyntaxException e) {
121            throw new IllegalStateException(e);
122        }
123    }
124
125    /**
126     * Returns a new URI having the same scheme, authority and path, but no
127     * query nor fragment.
128     *
129     * @param uri
130     *            the URI whose query and fragments are to be removed.
131     * @return a new URI having the same scheme, authority and path, but no
132     *         query nor fragment.
133     */
134    public static URI withoutQueryAndFragment(final URI uri) {
135        try {
136            return new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), null, null);
137        } catch (final URISyntaxException e) {
138            throw new IllegalStateException(e);
139        }
140    }
141}