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}