001package org.hl7.fhir.dstu2.model; 002 003 004 005/* 006 Copyright (c) 2011+, HL7, Inc. 007 All rights reserved. 008 009 Redistribution and use in source and binary forms, with or without modification, 010 are permitted provided that the following conditions are met: 011 012 * Redistributions of source code must retain the above copyright notice, this 013 list of conditions and the following disclaimer. 014 * Redistributions in binary form must reproduce the above copyright notice, 015 this list of conditions and the following disclaimer in the documentation 016 and/or other materials provided with the distribution. 017 * Neither the name of HL7 nor the names of its contributors may be used to 018 endorse or promote products derived from this software without specific 019 prior written permission. 020 021 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 022 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 023 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 024 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 025 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 026 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 027 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 028 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 029 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 030 POSSIBILITY OF SUCH DAMAGE. 031 032*/ 033 034import static org.apache.commons.lang3.StringUtils.isBlank; 035import static org.apache.commons.lang3.StringUtils.isNotBlank; 036 037import java.math.BigDecimal; 038import java.util.UUID; 039 040import org.apache.commons.lang3.*; 041import org.apache.commons.lang3.builder.HashCodeBuilder; 042import org.hl7.fhir.instance.model.api.*; 043 044import ca.uhn.fhir.model.api.annotation.DatatypeDef; 045 046/** 047 * This class represents the logical identity for a resource, or as much of that 048 * identity is known. In FHIR, every resource must have a "logical ID" which is 049 * defined by the FHIR specification as: 050 * <p> 051 * <code>A whole number in the range 0 to 2^64-1 (optionally represented in hex), 052 * a uuid, an oid, or any other combination of lowercase letters, numerals, "-" 053 * and ".", with a length limit of 36 characters</code> 054 * </p> 055 * <p> 056 * This class contains that logical ID, and can optionally also contain a 057 * relative or absolute URL representing the resource identity. For example, the 058 * following are all valid values for IdType, and all might represent the same 059 * resource: 060 * </p> 061 * <ul> 062 * <li><code>123</code> (just a resource's ID)</li> 063 * <li><code>Patient/123</code> (a relative identity)</li> 064 * <li><code>http://example.com/Patient/123 (an absolute identity)</code></li> 065 * <li> 066 * <code>http://example.com/Patient/123/_history/1 (an absolute identity with a version id)</code> 067 * </li> 068 * <li> 069 * <code>Patient/123/_history/1 (a relative identity with a version id)</code> 070 * </li> 071 * </ul> 072 * <p> 073 * In most situations, you only need to populate the resource's ID (e.g. 074 * <code>123</code>) in resources you are constructing and the encoder will 075 * infer the rest from the context in which the object is being used. On the 076 * other hand, the parser will always try to populate the complete absolute 077 * identity on objects it creates as a convenience. 078 * </p> 079 * <p> 080 * Regex for ID: [a-z0-9\-\.]{1,36} 081 * </p> 082 */ 083@DatatypeDef(name = "id", profileOf=StringType.class) 084public final class IdType extends UriType implements IPrimitiveType<String>, IIdType { 085 /** 086 * This is the maximum length for the ID 087 */ 088 public static final int MAX_LENGTH = 64; // maximum length 089 090 private static final long serialVersionUID = 2L; 091 private String myBaseUrl; 092 private boolean myHaveComponentParts; 093 private String myResourceType; 094 private String myUnqualifiedId; 095 private String myUnqualifiedVersionId; 096 097 /** 098 * Create a new empty ID 099 */ 100 public IdType() { 101 super(); 102 } 103 104 /** 105 * Create a new ID, using a BigDecimal input. Uses 106 * {@link BigDecimal#toPlainString()} to generate the string representation. 107 */ 108 public IdType(BigDecimal thePid) { 109 if (thePid != null) { 110 setValue(toPlainStringWithNpeThrowIfNeeded(thePid)); 111 } else { 112 setValue(null); 113 } 114 } 115 116 /** 117 * Create a new ID using a long 118 */ 119 public IdType(long theId) { 120 setValue(Long.toString(theId)); 121 } 122 123 /** 124 * Create a new ID using a string. This String may contain a simple ID (e.g. 125 * "1234") or it may contain a complete URL 126 * (http://example.com/fhir/Patient/1234). 127 * 128 * <p> 129 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally 130 * represented in hex), a uuid, an oid, or any other combination of lowercase 131 * letters, numerals, "-" and ".", with a length limit of 36 characters. 132 * </p> 133 * <p> 134 * regex: [a-z0-9\-\.]{1,36} 135 * </p> 136 */ 137 public IdType(String theValue) { 138 setValue(theValue); 139 } 140 141 /** 142 * Constructor 143 * 144 * @param theResourceType 145 * The resource type (e.g. "Patient") 146 * @param theIdPart 147 * The ID (e.g. "123") 148 */ 149 public IdType(String theResourceType, BigDecimal theIdPart) { 150 this(theResourceType, toPlainStringWithNpeThrowIfNeeded(theIdPart)); 151 } 152 153 /** 154 * Constructor 155 * 156 * @param theResourceType 157 * The resource type (e.g. "Patient") 158 * @param theIdPart 159 * The ID (e.g. "123") 160 */ 161 public IdType(String theResourceType, Long theIdPart) { 162 this(theResourceType, toPlainStringWithNpeThrowIfNeeded(theIdPart)); 163 } 164 165 /** 166 * Constructor 167 * 168 * @param theResourceType 169 * The resource type (e.g. "Patient") 170 * @param theId 171 * The ID (e.g. "123") 172 */ 173 public IdType(String theResourceType, String theId) { 174 this(theResourceType, theId, null); 175 } 176 177 /** 178 * Constructor 179 * 180 * @param theResourceType 181 * The resource type (e.g. "Patient") 182 * @param theId 183 * The ID (e.g. "123") 184 * @param theVersionId 185 * The version ID ("e.g. "456") 186 */ 187 public IdType(String theResourceType, String theId, String theVersionId) { 188 this(null, theResourceType, theId, theVersionId); 189 } 190 191 /** 192 * Constructor 193 * 194 * @param theBaseUrl 195 * The server base URL (e.g. "http://example.com/fhir") 196 * @param theResourceType 197 * The resource type (e.g. "Patient") 198 * @param theId 199 * The ID (e.g. "123") 200 * @param theVersionId 201 * The version ID ("e.g. "456") 202 */ 203 public IdType(String theBaseUrl, String theResourceType, String theId, String theVersionId) { 204 myBaseUrl = theBaseUrl; 205 myResourceType = theResourceType; 206 myUnqualifiedId = theId; 207 myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionId, null); 208 myHaveComponentParts = true; 209 if (isBlank(myBaseUrl) && isBlank(myResourceType) && isBlank(myUnqualifiedId) && isBlank(myUnqualifiedVersionId)) { 210 myHaveComponentParts = false; 211 } 212 } 213 214 /** 215 * Creates an ID based on a given URL 216 */ 217 public IdType(UriType theUrl) { 218 setValue(theUrl.getValueAsString()); 219 } 220 221 public void applyTo(IBaseResource theResouce) { 222 if (theResouce == null) { 223 throw new NullPointerException("theResource can not be null"); 224 } else { 225 theResouce.setId(new IdType(getValue())); 226 } 227 } 228 229 /** 230 * @deprecated Use {@link #getIdPartAsBigDecimal()} instead (this method was 231 * deprocated because its name is ambiguous) 232 */ 233 @Deprecated 234 public BigDecimal asBigDecimal() { 235 return getIdPartAsBigDecimal(); 236 } 237 238 @Override 239 public IdType copy() { 240 return new IdType(getValue()); 241 } 242 243 private String determineLocalPrefix(String theValue) { 244 if (theValue == null || theValue.isEmpty()) { 245 return null; 246 } 247 if (theValue.startsWith("#")) { 248 return "#"; 249 } 250 int lastPrefix = -1; 251 for (int i = 0; i < theValue.length(); i++) { 252 char nextChar = theValue.charAt(i); 253 if (nextChar == ':') { 254 lastPrefix = i; 255 } else if (!Character.isLetter(nextChar) || !Character.isLowerCase(nextChar)) { 256 break; 257 } 258 } 259 if (lastPrefix != -1) { 260 String candidate = theValue.substring(0, lastPrefix + 1); 261 if (candidate.startsWith("cid:") || candidate.startsWith("urn:")) { 262 return candidate; 263 } else { 264 return null; 265 } 266 } else { 267 return null; 268 } 269 } 270 271 @Override 272 public boolean equals(Object theArg0) { 273 if (!(theArg0 instanceof IdType)) { 274 return false; 275 } 276 IdType id = (IdType) theArg0; 277 return StringUtils.equals(getValueAsString(), id.getValueAsString()); 278 } 279 280 /** 281 * Returns true if this IdType matches the given IdType in terms of resource 282 * type and ID, but ignores the URL base 283 */ 284 @SuppressWarnings("deprecation") 285 public boolean equalsIgnoreBase(IdType theId) { 286 if (theId == null) { 287 return false; 288 } 289 if (theId.isEmpty()) { 290 return isEmpty(); 291 } 292 return ObjectUtils.equals(getResourceType(), theId.getResourceType()) 293 && ObjectUtils.equals(getIdPart(), theId.getIdPart()) 294 && ObjectUtils.equals(getVersionIdPart(), theId.getVersionIdPart()); 295 } 296 297 /** 298 * Returns the portion of this resource ID which corresponds to the server 299 * base URL. For example given the resource ID 300 * <code>http://example.com/fhir/Patient/123</code> the base URL would be 301 * <code>http://example.com/fhir</code>. 302 * <p> 303 * This method may return null if the ID contains no base (e.g. "Patient/123") 304 * </p> 305 */ 306 @Override 307 public String getBaseUrl() { 308 return myBaseUrl; 309 } 310 311 /** 312 * Returns only the logical ID part of this ID. For example, given the ID 313 * "http://example,.com/fhir/Patient/123/_history/456", this method would 314 * return "123". 315 */ 316 @Override 317 public String getIdPart() { 318 return myUnqualifiedId; 319 } 320 321 /** 322 * Returns the unqualified portion of this ID as a big decimal, or 323 * <code>null</code> if the value is null 324 * 325 * @throws NumberFormatException 326 * If the value is not a valid BigDecimal 327 */ 328 public BigDecimal getIdPartAsBigDecimal() { 329 String val = getIdPart(); 330 if (isBlank(val)) { 331 return null; 332 } 333 return new BigDecimal(val); 334 } 335 336 /** 337 * Returns the unqualified portion of this ID as a {@link Long}, or 338 * <code>null</code> if the value is null 339 * 340 * @throws NumberFormatException 341 * If the value is not a valid Long 342 */ 343 @Override 344 public Long getIdPartAsLong() { 345 String val = getIdPart(); 346 if (isBlank(val)) { 347 return null; 348 } 349 return Long.parseLong(val); 350 } 351 352 @Override 353 public String getResourceType() { 354 return myResourceType; 355 } 356 357 /** 358 * Returns the value of this ID. Note that this value may be a fully qualified 359 * URL, a relative/partial URL, or a simple ID. Use {@link #getIdPart()} to 360 * get just the ID portion. 361 * 362 * @see #getIdPart() 363 */ 364 @Override 365 public String getValue() { 366 String retVal = super.getValue(); 367 if (retVal == null && myHaveComponentParts) { 368 369 if (determineLocalPrefix(myBaseUrl) != null && myResourceType == null && myUnqualifiedVersionId == null) { 370 return myBaseUrl + myUnqualifiedId; 371 } 372 373 StringBuilder b = new StringBuilder(); 374 if (isNotBlank(myBaseUrl)) { 375 b.append(myBaseUrl); 376 if (myBaseUrl.charAt(myBaseUrl.length() - 1) != '/') { 377 b.append('/'); 378 } 379 } 380 381 if (isNotBlank(myResourceType)) { 382 b.append(myResourceType); 383 } 384 385 if (b.length() > 0 && isNotBlank(myUnqualifiedId)) { 386 b.append('/'); 387 } 388 389 if (isNotBlank(myUnqualifiedId)) { 390 b.append(myUnqualifiedId); 391 } else if (isNotBlank(myUnqualifiedVersionId)) { 392 b.append('/'); 393 } 394 395 if (isNotBlank(myUnqualifiedVersionId)) { 396 b.append('/'); 397 b.append("_history"); 398 b.append('/'); 399 b.append(myUnqualifiedVersionId); 400 } 401 retVal = b.toString(); 402 super.setValue(retVal); 403 } 404 return retVal; 405 } 406 407 @Override 408 public String getValueAsString() { 409 return getValue(); 410 } 411 412 @Override 413 public String getVersionIdPart() { 414 return myUnqualifiedVersionId; 415 } 416 417 public Long getVersionIdPartAsLong() { 418 if (!hasVersionIdPart()) { 419 return null; 420 } else { 421 return Long.parseLong(getVersionIdPart()); 422 } 423 } 424 425 /** 426 * Returns true if this ID has a base url 427 * 428 * @see #getBaseUrl() 429 */ 430 public boolean hasBaseUrl() { 431 return isNotBlank(myBaseUrl); 432 } 433 434 @Override 435 public int hashCode() { 436 HashCodeBuilder b = new HashCodeBuilder(); 437 b.append(getValueAsString()); 438 return b.toHashCode(); 439 } 440 441 @Override 442 public boolean hasIdPart() { 443 return isNotBlank(getIdPart()); 444 } 445 446 @Override 447 public boolean hasResourceType() { 448 return isNotBlank(myResourceType); 449 } 450 451 @Override 452 public boolean hasVersionIdPart() { 453 return isNotBlank(getVersionIdPart()); 454 } 455 456 /** 457 * Returns <code>true</code> if this ID contains an absolute URL (in other 458 * words, a URL starting with "http://" or "https://" 459 */ 460 @Override 461 public boolean isAbsolute() { 462 if (StringUtils.isBlank(getValue())) { 463 return false; 464 } 465 return isUrlAbsolute(getValue()); 466 } 467 468 @Override 469 public boolean isEmpty() { 470 return isBlank(getValue()); 471 } 472 473 @Override 474 public boolean isIdPartValid() { 475 String id = getIdPart(); 476 if (StringUtils.isBlank(id)) { 477 return false; 478 } 479 if (id.length() > 64) { 480 return false; 481 } 482 for (int i = 0; i < id.length(); i++) { 483 char nextChar = id.charAt(i); 484 if (nextChar >= 'a' && nextChar <= 'z') { 485 continue; 486 } 487 if (nextChar >= 'A' && nextChar <= 'Z') { 488 continue; 489 } 490 if (nextChar >= '0' && nextChar <= '9') { 491 continue; 492 } 493 if (nextChar == '-' || nextChar == '.') { 494 continue; 495 } 496 return false; 497 } 498 return true; 499 } 500 501 /** 502 * Returns <code>true</code> if the unqualified ID is a valid {@link Long} 503 * value (in other words, it consists only of digits) 504 */ 505 @Override 506 public boolean isIdPartValidLong() { 507 return isValidLong(getIdPart()); 508 } 509 510 /** 511 * Returns <code>true</code> if the ID is a local reference (in other words, 512 * it begins with the '#' character) 513 */ 514 @Override 515 public boolean isLocal() { 516 return "#".equals(myBaseUrl); 517 } 518 519 @Override 520 public boolean isVersionIdPartValidLong() { 521 return isValidLong(getVersionIdPart()); 522 } 523 524 /** 525 * Set the value 526 * 527 * <p> 528 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally 529 * represented in hex), a uuid, an oid, or any other combination of lowercase 530 * letters, numerals, "-" and ".", with a length limit of 36 characters. 531 * </p> 532 * <p> 533 * regex: [a-z0-9\-\.]{1,36} 534 * </p> 535 */ 536 @Override 537 public IdType setValue(String theValue) { 538 // TODO: add validation 539 super.setValue(theValue); 540 myHaveComponentParts = false; 541 542 String localPrefix = determineLocalPrefix(theValue); 543 544 if (StringUtils.isBlank(theValue)) { 545 myBaseUrl = null; 546 super.setValue(null); 547 myUnqualifiedId = null; 548 myUnqualifiedVersionId = null; 549 myResourceType = null; 550 } else if (theValue.charAt(0) == '#' && theValue.length() > 1) { 551 super.setValue(theValue); 552 myBaseUrl = "#"; 553 myUnqualifiedId = theValue.substring(1); 554 myUnqualifiedVersionId = null; 555 myResourceType = null; 556 myHaveComponentParts = true; 557 } else if (localPrefix != null) { 558 myBaseUrl = localPrefix; 559 myUnqualifiedId = theValue.substring(localPrefix.length()); 560 } else { 561 int vidIndex = theValue.indexOf("/_history/"); 562 int idIndex; 563 if (vidIndex != -1) { 564 myUnqualifiedVersionId = theValue.substring(vidIndex + "/_history/".length()); 565 idIndex = theValue.lastIndexOf('/', vidIndex - 1); 566 myUnqualifiedId = theValue.substring(idIndex + 1, vidIndex); 567 } else { 568 idIndex = theValue.lastIndexOf('/'); 569 myUnqualifiedId = theValue.substring(idIndex + 1); 570 myUnqualifiedVersionId = null; 571 } 572 573 myBaseUrl = null; 574 if (idIndex <= 0) { 575 myResourceType = null; 576 } else { 577 int typeIndex = theValue.lastIndexOf('/', idIndex - 1); 578 if (typeIndex == -1) { 579 myResourceType = theValue.substring(0, idIndex); 580 } else { 581 if (typeIndex > 0 && '/' == theValue.charAt(typeIndex - 1)) { 582 typeIndex = theValue.indexOf('/', typeIndex + 1); 583 } 584 if (typeIndex >= idIndex) { 585 // e.g. http://example.org/foo 586 // 'foo' was the id but we're making that the resource type. Nullify the id part because we don't have an id. 587 // Also set null value to the super.setValue() and enable myHaveComponentParts so it forces getValue() to properly 588 // recreate the url 589 myResourceType = myUnqualifiedId; 590 myUnqualifiedId = null; 591 super.setValue(null); 592 myHaveComponentParts = true; 593 } else { 594 myResourceType = theValue.substring(typeIndex + 1, idIndex); 595 } 596 597 if (typeIndex > 4) { 598 myBaseUrl = theValue.substring(0, typeIndex); 599 } 600 601 } 602 } 603 604 } 605 return this; 606 } 607 608 /** 609 * Set the value 610 * 611 * <p> 612 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally 613 * represented in hex), a uuid, an oid, or any other combination of lowercase 614 * letters, numerals, "-" and ".", with a length limit of 36 characters. 615 * </p> 616 * <p> 617 * regex: [a-z0-9\-\.]{1,36} 618 * </p> 619 */ 620 @Override 621 public void setValueAsString(String theValue) { 622 setValue(theValue); 623 } 624 625 @Override 626 public String toString() { 627 return getValue(); 628 } 629 630 /** 631 * Returns a new IdType containing this IdType's values but with no server 632 * base URL if one is present in this IdType. For example, if this IdType 633 * contains the ID "http://foo/Patient/1", this method will return a new 634 * IdType containing ID "Patient/1". 635 */ 636 @Override 637 public IdType toUnqualified() { 638 return new IdType(getResourceType(), getIdPart(), getVersionIdPart()); 639 } 640 641 @Override 642 public IdType toUnqualifiedVersionless() { 643 return new IdType(getResourceType(), getIdPart()); 644 } 645 646 @Override 647 public IdType toVersionless() { 648 return new IdType(getBaseUrl(), getResourceType(), getIdPart(), null); 649 } 650 651 @Override 652 public IdType withResourceType(String theResourceName) { 653 return new IdType(theResourceName, getIdPart(), getVersionIdPart()); 654 } 655 656 /** 657 * Returns a view of this ID as a fully qualified URL, given a server base and 658 * resource name (which will only be used if the ID does not already contain 659 * those respective parts). Essentially, because IdType can contain either a 660 * complete URL or a partial one (or even jut a simple ID), this method may be 661 * used to translate into a complete URL. 662 * 663 * @param theServerBase 664 * The server base (e.g. "http://example.com/fhir") 665 * @param theResourceType 666 * The resource name (e.g. "Patient") 667 * @return A fully qualified URL for this ID (e.g. 668 * "http://example.com/fhir/Patient/1") 669 */ 670 @Override 671 public IdType withServerBase(String theServerBase, String theResourceType) { 672 return new IdType(theServerBase, theResourceType, getIdPart(), getVersionIdPart()); 673 } 674 675 /** 676 * Creates a new instance of this ID which is identical, but refers to the 677 * specific version of this resource ID noted by theVersion. 678 * 679 * @param theVersion 680 * The actual version string, e.g. "1" 681 * @return A new instance of IdType which is identical, but refers to the 682 * specific version of this resource ID noted by theVersion. 683 */ 684 public IdType withVersion(String theVersion) { 685 Validate.notBlank(theVersion, "Version may not be null or empty"); 686 687 String existingValue = getValue(); 688 689 int i = existingValue.indexOf("_history"); 690 String value; 691 if (i > 1) { 692 value = existingValue.substring(0, i - 1); 693 } else { 694 value = existingValue; 695 } 696 697 return new IdType(value + '/' + "_history" + '/' + theVersion); 698 } 699 700 private static boolean isUrlAbsolute(String theValue) { 701 String value = theValue.toLowerCase(); 702 return value.startsWith("http://") || value.startsWith("https://"); 703 } 704 705 private static boolean isValidLong(String id) { 706 if (StringUtils.isBlank(id)) { 707 return false; 708 } 709 for (int i = 0; i < id.length(); i++) { 710 if (Character.isDigit(id.charAt(i)) == false) { 711 return false; 712 } 713 } 714 return true; 715 } 716 717 /** 718 * Construct a new ID with with form "urn:uuid:[UUID]" where [UUID] is a new, 719 * randomly created UUID generated by {@link UUID#randomUUID()} 720 */ 721 public static IdType newRandomUuid() { 722 return new IdType("urn:uuid:" + UUID.randomUUID().toString()); 723 } 724 725 /** 726 * Retrieves the ID from the given resource instance 727 */ 728 public static IdType of(IBaseResource theResouce) { 729 if (theResouce == null) { 730 throw new NullPointerException("theResource can not be null"); 731 } else { 732 IIdType retVal = theResouce.getIdElement(); 733 if (retVal == null) { 734 return null; 735 } else if (retVal instanceof IdType) { 736 return (IdType) retVal; 737 } else { 738 return new IdType(retVal.getValue()); 739 } 740 } 741 } 742 743 private static String toPlainStringWithNpeThrowIfNeeded(BigDecimal theIdPart) { 744 if (theIdPart == null) { 745 throw new NullPointerException("BigDecimal ID can not be null"); 746 } 747 return theIdPart.toPlainString(); 748 } 749 750 private static String toPlainStringWithNpeThrowIfNeeded(Long theIdPart) { 751 if (theIdPart == null) { 752 throw new NullPointerException("Long ID can not be null"); 753 } 754 return theIdPart.toString(); 755 } 756 757 public String fhirType() { 758 return "id"; 759 } 760 761 @Override 762 public IIdType setParts(String theBaseUrl, String theResourceType, String theIdPart, String theVersionIdPart) { 763 if (isNotBlank(theVersionIdPart)) { 764 Validate.notBlank(theResourceType, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated"); 765 Validate.notBlank(theIdPart, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated"); 766 } 767 if (isNotBlank(theBaseUrl) && isNotBlank(theIdPart)) { 768 Validate.notBlank(theResourceType, "If theBaseUrl is populated and theIdPart is populated, theResourceType must be populated"); 769 } 770 771 setValue(null); 772 773 myBaseUrl = theBaseUrl; 774 myResourceType = theResourceType; 775 myUnqualifiedId = theIdPart; 776 myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionIdPart, null); 777 myHaveComponentParts = true; 778 779 return this; 780 } 781}