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