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