001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.oauth2.sdk.util;
019
020
021import java.net.URI;
022import java.net.URISyntaxException;
023
024
025/**
026 * URI operations.
027 */
028public final class URIUtils {
029
030
031        /**
032         * Gets the base part (schema, host, port and path) of the specified
033         * URI.
034         *
035         * @param uri The URI. May be {@code null}.
036         *
037         * @return The base part of the URI, {@code null} if the original URI
038         *         is {@code null} or doesn't specify a protocol.
039         */
040        public static URI getBaseURI(final URI uri) {
041
042                if (uri == null)
043                        return null;
044
045                try {
046                        return new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), null, null);
047
048                } catch (URISyntaxException e) {
049
050                        return null;
051                }
052        }
053        
054        
055        /**
056         * Prepends the specified path component to a URI. The prepended and
057         * any existing path component are always joined with a single slash
058         * ('/') between them
059         *
060         * @param uri           The URI, {@code null} if not specified.
061         * @param pathComponent The path component to prepend, {@code null} if
062         *                      not specified.
063         *
064         * @return The URI with prepended path component, {@code null} if the
065         *         original URI wasn't specified.
066         */
067        public static URI prependPath(final URI uri, final String pathComponent) {
068                
069                if (uri == null) {
070                        return null;
071                }
072                
073                if (StringUtils.isBlank(pathComponent)) {
074                        return uri;
075                }
076                
077                String origPath = uri.getPath();
078                if (origPath == null || origPath.isEmpty() || origPath.equals("/")) {
079                        origPath = null;
080                }
081                String joinedPath = joinPathComponents(pathComponent, origPath);
082                joinedPath = prependLeadingSlashIfMissing(joinedPath);
083                
084                try {
085                        return new URI(
086                                uri.getScheme(), null, uri.getHost(), uri.getPort(),
087                                joinedPath,
088                                uri.getQuery(), uri.getFragment());
089                } catch (URISyntaxException e) {
090                        // should never happen when starting from legal URI
091                        return null;
092                }
093        }
094        
095        
096        /**
097         * Prepends a leading slash `/` if missing to the specified string.
098         *
099         * @param s The string, {@code null} if not specified.
100         *
101         * @return The string with leading slash, {@code null} if not
102         *         originally specified.
103         */
104        public static String prependLeadingSlashIfMissing(String s) {
105                if (s == null) {
106                        return null;
107                }
108                if (s.startsWith("/")) {
109                        return s;
110                }
111                return "/" + s;
112        }
113        
114        
115        /**
116         * Strips any leading slashes '/' if present from the specified string.
117         *
118         * @param s The string, {@code null} if not specified.
119         *
120         * @return The string with no leading slash, {@code null} if not
121         *         originally specified.
122         */
123        public static String stripLeadingSlashIfPresent(final String s) {
124                if (StringUtils.isBlank(s)) {
125                        return s;
126                }
127                if (s.startsWith("/")) {
128                        String tmp = s;
129                        while (tmp.startsWith("/")) {
130                                tmp = tmp.substring(1);
131                        }
132                        return tmp;
133                }
134                return s;
135        }
136        
137        
138        /**
139         * Joins two path components. If the two path components are not
140         * {@code null} or empty they are joined so that there is only a single
141         * slash ('/') between them.
142         *
143         * @param c1 The first path component, {@code null} if not specified.
144         * @param c2 The second path component, {@code null} if not specified.
145         *
146         * @return The joined path components, {@code null} if both are not
147         *         specified, or if one is {@code null} the other unmodified.
148         */
149        public static String joinPathComponents(final String c1, final String c2) {
150                
151                if (c1 == null && c2 == null) {
152                        return null;
153                }
154                
155                if (c1 == null || c1.isEmpty()) {
156                        return c2;
157                }
158                
159                if (c2 == null || c2.isEmpty()) {
160                        return c1;
161                }
162                
163                if (c1.endsWith("/") && ! c2.startsWith("/")) {
164                        return c1 + c2;
165                }
166                if (! c1.endsWith("/") && c2.startsWith("/")) {
167                        return c1 + c2;
168                }
169                if (c1.endsWith("/") && c2.startsWith("/")) {
170                        return c1 + stripLeadingSlashIfPresent(c2);
171                }
172                return c1 + "/" + c2;
173        }
174        
175        
176        /**
177         * Strips the query string from the specified URI.
178         *
179         * @param uri The URI. May be {@code null}.'
180         *
181         * @return The URI with stripped query string, {@code null} if the
182         *         original URI is {@code null} or doesn't specify a protocol.
183         */
184        public static URI stripQueryString(final URI uri) {
185                
186                if (uri == null)
187                        return null;
188                
189                try {
190                        return new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), null, uri.getFragment());
191                        
192                } catch (URISyntaxException e) {
193                        return null;
194                }
195        }
196        
197        
198        /**
199         * Removes the trailing slash ("/") from the specified URI, if present.
200         *
201         * @param uri The URI. May be {@code null}.
202         *
203         * @return The URI with no trailing slash, {@code null} if the original
204         *         URI is {@code null}.
205         */
206        public static URI removeTrailingSlash(final URI uri) {
207                
208                if (uri == null)
209                        return null;
210                
211                String uriString = uri.toString();
212                
213                if (uriString.charAt(uriString.length() - 1 ) == '/') {
214                        return URI.create(uriString.substring(0, uriString.length() - 1));
215                }
216                
217                return uri;
218        }
219
220
221        /**
222         * Prevents public instantiation.
223         */
224        private URIUtils() {}
225}