001package org.hl7.fhir.dstu3.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*/ 031import static org.apache.commons.lang3.StringUtils.*; 032 033import java.math.BigDecimal; 034import java.util.UUID; 035 036import org.apache.commons.lang3.*; 037import org.apache.commons.lang3.builder.HashCodeBuilder; 038import org.hl7.fhir.instance.model.api.*; 039 040import ca.uhn.fhir.model.api.annotation.DatatypeDef; 041 042/** 043 * This class represents the logical identity for a resource, or as much of that 044 * identity is known. In FHIR, every resource must have a "logical ID" which is 045 * defined by the FHIR specification as: 046 * <p> 047 * <code> 048 * Any combination of upper or lower case ASCII letters ('A'..'Z', and 'a'..'z', numerals ('0'..'9'), '-' and '.', with a length limit of 64 characters. (This might be an integer, an un-prefixed OID, UUID or any other identifier pattern that meets these constraints.) 049 * </code> 050 * </p> 051 * <p> 052 * This class contains that logical ID, and can optionally also contain a 053 * relative or absolute URL representing the resource identity. For example, the 054 * following are all valid values for IdType, and all might represent the same 055 * resource: 056 * </p> 057 * <ul> 058 * <li><code>123</code> (just a resource's ID)</li> 059 * <li><code>Patient/123</code> (a relative identity)</li> 060 * <li><code>http://example.com/Patient/123 (an absolute identity)</code></li> 061 * <li> 062 * <code>http://example.com/Patient/123/_history/1 (an absolute identity with a version id)</code> 063 * </li> 064 * <li> 065 * <code>Patient/123/_history/1 (a relative identity with a version id)</code> 066 * </li> 067 * </ul> 068 * <p> 069 * Note that the 64 character 070 * limit applies only to the ID portion ("123" in the examples above). 071 * </p> 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,64} 081 * </p> 082 */ 083@DatatypeDef(name = "id", profileOf=StringType.class) 084public final class IdType extends UriType implements IPrimitiveType<String>, IIdType { 085 public static final String URN_PREFIX = "urn:"; 086 087 /** 088 * This is the maximum length for the ID 089 */ 090 public static final int MAX_LENGTH = 64; // maximum length 091 092 private static final long serialVersionUID = 2L; 093 private String myBaseUrl; 094 private boolean myHaveComponentParts; 095 private String myResourceType; 096 private String myUnqualifiedId; 097 private String myUnqualifiedVersionId; 098 099 /** 100 * Create a new empty ID 101 */ 102 public IdType() { 103 super(); 104 } 105 106 /** 107 * Create a new ID, using a BigDecimal input. Uses 108 * {@link BigDecimal#toPlainString()} to generate the string representation. 109 */ 110 public IdType(BigDecimal thePid) { 111 if (thePid != null) { 112 setValue(toPlainStringWithNpeThrowIfNeeded(thePid)); 113 } else { 114 setValue(null); 115 } 116 } 117 118 /** 119 * Create a new ID using a long 120 */ 121 public IdType(long theId) { 122 setValue(Long.toString(theId)); 123 } 124 125 /** 126 * Create a new ID using a string. This String may contain a simple ID (e.g. 127 * "1234") or it may contain a complete URL 128 * (http://example.com/fhir/Patient/1234). 129 * 130 * <p> 131 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally 132 * represented in hex), a uuid, an oid, or any other combination of lowercase 133 * letters, numerals, "-" and ".", with a length limit of 36 characters. 134 * </p> 135 * <p> 136 * regex: [a-z0-9\-\.]{1,36} 137 * </p> 138 */ 139 public IdType(String theValue) { 140 setValue(theValue); 141 } 142 143 /** 144 * Constructor 145 * 146 * @param theResourceType 147 * The resource type (e.g. "Patient") 148 * @param theIdPart 149 * The ID (e.g. "123") 150 */ 151 public IdType(String theResourceType, BigDecimal theIdPart) { 152 this(theResourceType, toPlainStringWithNpeThrowIfNeeded(theIdPart)); 153 } 154 155 /** 156 * Constructor 157 * 158 * @param theResourceType 159 * The resource type (e.g. "Patient") 160 * @param theIdPart 161 * The ID (e.g. "123") 162 */ 163 public IdType(String theResourceType, Long theIdPart) { 164 this(theResourceType, toPlainStringWithNpeThrowIfNeeded(theIdPart)); 165 } 166 167 /** 168 * Constructor 169 * 170 * @param theResourceType 171 * The resource type (e.g. "Patient") 172 * @param theId 173 * The ID (e.g. "123") 174 */ 175 public IdType(String theResourceType, String theId) { 176 this(theResourceType, theId, null); 177 } 178 179 /** 180 * Constructor 181 * 182 * @param theResourceType 183 * The resource type (e.g. "Patient") 184 * @param theId 185 * The ID (e.g. "123") 186 * @param theVersionId 187 * The version ID ("e.g. "456") 188 */ 189 public IdType(String theResourceType, String theId, String theVersionId) { 190 this(null, theResourceType, theId, theVersionId); 191 } 192 193 /** 194 * Constructor 195 * 196 * @param theBaseUrl 197 * The server base URL (e.g. "http://example.com/fhir") 198 * @param theResourceType 199 * The resource type (e.g. "Patient") 200 * @param theId 201 * The ID (e.g. "123") 202 * @param theVersionId 203 * The version ID ("e.g. "456") 204 */ 205 public IdType(String theBaseUrl, String theResourceType, String theId, String theVersionId) { 206 myBaseUrl = theBaseUrl; 207 myResourceType = theResourceType; 208 myUnqualifiedId = theId; 209 myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionId, null); 210 myHaveComponentParts = true; 211 if (isBlank(myBaseUrl) && isBlank(myResourceType) && isBlank(myUnqualifiedId) && isBlank(myUnqualifiedVersionId)) { 212 myHaveComponentParts = false; 213 } 214 } 215 216 /** 217 * Creates an ID based on a given URL 218 */ 219 public IdType(UriType theUrl) { 220 setValue(theUrl.getValueAsString()); 221 } 222 223 public void applyTo(IBaseResource theResouce) { 224 if (theResouce == null) { 225 throw new NullPointerException("theResource can not be null"); 226 } else { 227 theResouce.setId(new IdType(getValue())); 228 } 229 } 230 231 /** 232 * @deprecated Use {@link #getIdPartAsBigDecimal()} instead (this method was 233 * deprocated because its name is ambiguous) 234 */ 235 @Deprecated 236 public BigDecimal asBigDecimal() { 237 return getIdPartAsBigDecimal(); 238 } 239 240 @Override 241 public IdType copy() { 242 return new IdType(getValue()); 243 } 244 245 @Override 246 public boolean equals(Object theArg0) { 247 if (!(theArg0 instanceof IdType)) { 248 return false; 249 } 250 return StringUtils.equals(getValueAsString(), ((IdType)theArg0).getValueAsString()); 251 } 252 253 /** 254 * Returns true if this IdType matches the given IdType in terms of resource 255 * type and ID, but ignores the URL base 256 */ 257 @SuppressWarnings("deprecation") 258 public boolean equalsIgnoreBase(IdType theId) { 259 if (theId == null) { 260 return false; 261 } 262 if (theId.isEmpty()) { 263 return isEmpty(); 264 } 265 return ObjectUtils.equals(getResourceType(), theId.getResourceType()) 266 && ObjectUtils.equals(getIdPart(), theId.getIdPart()) 267 && ObjectUtils.equals(getVersionIdPart(), theId.getVersionIdPart()); 268 } 269 270 /** 271 * Returns the portion of this resource ID which corresponds to the server 272 * base URL. For example given the resource ID 273 * <code>http://example.com/fhir/Patient/123</code> the base URL would be 274 * <code>http://example.com/fhir</code>. 275 * <p> 276 * This method may return null if the ID contains no base (e.g. "Patient/123") 277 * </p> 278 */ 279 @Override 280 public String getBaseUrl() { 281 return myBaseUrl; 282 } 283 284 /** 285 * Returns only the logical ID part of this ID. For example, given the ID 286 * "http://example,.com/fhir/Patient/123/_history/456", this method would 287 * return "123". 288 */ 289 @Override 290 public String getIdPart() { 291 return myUnqualifiedId; 292 } 293 294 /** 295 * Returns the unqualified portion of this ID as a big decimal, or 296 * <code>null</code> if the value is null 297 * 298 * @throws NumberFormatException 299 * If the value is not a valid BigDecimal 300 */ 301 public BigDecimal getIdPartAsBigDecimal() { 302 String val = getIdPart(); 303 if (isBlank(val)) { 304 return null; 305 } 306 return new BigDecimal(val); 307 } 308 309 /** 310 * Returns the unqualified portion of this ID as a {@link Long}, or 311 * <code>null</code> if the value is null 312 * 313 * @throws NumberFormatException 314 * If the value is not a valid Long 315 */ 316 @Override 317 public Long getIdPartAsLong() { 318 String val = getIdPart(); 319 if (isBlank(val)) { 320 return null; 321 } 322 return Long.parseLong(val); 323 } 324 325 @Override 326 public String getResourceType() { 327 return myResourceType; 328 } 329 330 /** 331 * Returns the value of this ID. Note that this value may be a fully qualified 332 * URL, a relative/partial URL, or a simple ID. Use {@link #getIdPart()} to 333 * get just the ID portion. 334 * 335 * @see #getIdPart() 336 */ 337 @Override 338 public String getValue() { 339 String retVal = super.getValue(); 340 if (retVal == null && myHaveComponentParts) { 341 342 if (isLocal() || isUrn()) { 343 return myUnqualifiedId; 344 } 345 346 StringBuilder b = new StringBuilder(); 347 if (isNotBlank(myBaseUrl)) { 348 b.append(myBaseUrl); 349 if (myBaseUrl.charAt(myBaseUrl.length() - 1) != '/') { 350 b.append('/'); 351 } 352 } 353 354 if (isNotBlank(myResourceType)) { 355 b.append(myResourceType); 356 } 357 358 if (b.length() > 0 && isNotBlank(myUnqualifiedId)) { 359 b.append('/'); 360 } 361 362 if (isNotBlank(myUnqualifiedId)) { 363 b.append(myUnqualifiedId); 364 } else if (isNotBlank(myUnqualifiedVersionId)) { 365 b.append('/'); 366 } 367 368 if (isNotBlank(myUnqualifiedVersionId)) { 369 b.append('/'); 370 b.append("_history"); 371 b.append('/'); 372 b.append(myUnqualifiedVersionId); 373 } 374 retVal = b.toString(); 375 super.setValue(retVal); 376 } 377 return retVal; 378 } 379 380 @Override 381 public String getValueAsString() { 382 return getValue(); 383 } 384 385 @Override 386 public String getVersionIdPart() { 387 return myUnqualifiedVersionId; 388 } 389 390 public Long getVersionIdPartAsLong() { 391 if (!hasVersionIdPart()) { 392 return null; 393 } else { 394 return Long.parseLong(getVersionIdPart()); 395 } 396 } 397 398 /** 399 * Returns true if this ID has a base url 400 * 401 * @see #getBaseUrl() 402 */ 403 public boolean hasBaseUrl() { 404 return isNotBlank(myBaseUrl); 405 } 406 407 @Override 408 public int hashCode() { 409 HashCodeBuilder b = new HashCodeBuilder(); 410 b.append(getValueAsString()); 411 return b.toHashCode(); 412 } 413 414 @Override 415 public boolean hasIdPart() { 416 return isNotBlank(getIdPart()); 417 } 418 419 @Override 420 public boolean hasResourceType() { 421 return isNotBlank(myResourceType); 422 } 423 424 @Override 425 public boolean hasVersionIdPart() { 426 return isNotBlank(getVersionIdPart()); 427 } 428 429 /** 430 * Returns <code>true</code> if this ID contains an absolute URL (in other 431 * words, a URL starting with "http://" or "https://" 432 */ 433 @Override 434 public boolean isAbsolute() { 435 if (StringUtils.isBlank(getValue())) { 436 return false; 437 } 438 return isUrlAbsolute(getValue()); 439 } 440 441 @Override 442 public boolean isEmpty() { 443 return super.isEmpty() && isBlank(getValue()); 444 } 445 446 @Override 447 public boolean isIdPartValid() { 448 String id = getIdPart(); 449 if (StringUtils.isBlank(id)) { 450 return false; 451 } 452 if (id.length() > 64) { 453 return false; 454 } 455 for (int i = 0; i < id.length(); i++) { 456 char nextChar = id.charAt(i); 457 if (nextChar >= 'a' && nextChar <= 'z') { 458 continue; 459 } 460 if (nextChar >= 'A' && nextChar <= 'Z') { 461 continue; 462 } 463 if (nextChar >= '0' && nextChar <= '9') { 464 continue; 465 } 466 if (nextChar == '-' || nextChar == '.') { 467 continue; 468 } 469 return false; 470 } 471 return true; 472 } 473 474 /** 475 * Returns <code>true</code> if the unqualified ID is a valid {@link Long} 476 * value (in other words, it consists only of digits) 477 */ 478 @Override 479 public boolean isIdPartValidLong() { 480 return isValidLong(getIdPart()); 481 } 482 483 /** 484 * Returns <code>true</code> if the ID is a local reference (in other words, 485 * it begins with the '#' character) 486 */ 487 @Override 488 public boolean isLocal() { 489 return defaultString(myUnqualifiedId).startsWith("#"); 490 } 491 492 public boolean isUrn() { 493 return defaultString(myUnqualifiedId).startsWith(URN_PREFIX); 494 } 495 496 @Override 497 public boolean isVersionIdPartValidLong() { 498 return isValidLong(getVersionIdPart()); 499 } 500 501 /** 502 * Set the value 503 * 504 * <p> 505 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally 506 * represented in hex), a uuid, an oid, or any other combination of lowercase 507 * letters, numerals, "-" and ".", with a length limit of 36 characters. 508 * </p> 509 * <p> 510 * regex: [a-z0-9\-\.]{1,36} 511 * </p> 512 */ 513 @Override 514 public IdType setValue(String theValue) { 515 // TODO: add validation 516 super.setValue(theValue); 517 myHaveComponentParts = false; 518 519 if (StringUtils.isBlank(theValue)) { 520 myBaseUrl = null; 521 super.setValue(null); 522 myUnqualifiedId = null; 523 myUnqualifiedVersionId = null; 524 myResourceType = null; 525 } else if (theValue.charAt(0) == '#' && theValue.length() > 1) { 526 super.setValue(theValue); 527 myBaseUrl = null; 528 myUnqualifiedId = theValue; 529 myUnqualifiedVersionId = null; 530 myResourceType = null; 531 myHaveComponentParts = true; 532 } else if (theValue.startsWith(URN_PREFIX)) { 533 myBaseUrl = null; 534 myUnqualifiedId = theValue; 535 myUnqualifiedVersionId = null; 536 myResourceType = null; 537 myHaveComponentParts = true; 538 } else { 539 int vidIndex = theValue.indexOf("/_history/"); 540 int idIndex; 541 if (vidIndex != -1) { 542 myUnqualifiedVersionId = theValue.substring(vidIndex + "/_history/".length()); 543 idIndex = theValue.lastIndexOf('/', vidIndex - 1); 544 myUnqualifiedId = theValue.substring(idIndex + 1, vidIndex); 545 } else { 546 idIndex = theValue.lastIndexOf('/'); 547 myUnqualifiedId = theValue.substring(idIndex + 1); 548 myUnqualifiedVersionId = null; 549 } 550 551 myBaseUrl = null; 552 if (idIndex <= 0) { 553 myResourceType = null; 554 } else { 555 int typeIndex = theValue.lastIndexOf('/', idIndex - 1); 556 if (typeIndex == -1) { 557 myResourceType = theValue.substring(0, idIndex); 558 } else { 559 if (typeIndex > 0 && '/' == theValue.charAt(typeIndex - 1)) { 560 typeIndex = theValue.indexOf('/', typeIndex + 1); 561 } 562 if (typeIndex >= idIndex) { 563 // e.g. http://example.org/foo 564 // 'foo' was the id but we're making that the resource type. Nullify the id part because we don't have an id. 565 // Also set null value to the super.setValue() and enable myHaveComponentParts so it forces getValue() to properly 566 // recreate the url 567 myResourceType = myUnqualifiedId; 568 myUnqualifiedId = null; 569 super.setValue(null); 570 myHaveComponentParts = true; 571 } else { 572 myResourceType = theValue.substring(typeIndex + 1, idIndex); 573 } 574 575 if (typeIndex > 4) { 576 myBaseUrl = theValue.substring(0, typeIndex); 577 } 578 579 } 580 } 581 582 } 583 return this; 584 } 585 586 /** 587 * Set the value 588 * 589 * <p> 590 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally 591 * represented in hex), a uuid, an oid, or any other combination of lowercase 592 * letters, numerals, "-" and ".", with a length limit of 36 characters. 593 * </p> 594 * <p> 595 * regex: [a-z0-9\-\.]{1,36} 596 * </p> 597 */ 598 @Override 599 public void setValueAsString(String theValue) { 600 setValue(theValue); 601 } 602 603 @Override 604 public String toString() { 605 return getValue(); 606 } 607 608 /** 609 * Returns a new IdType containing this IdType's values but with no server 610 * base URL if one is present in this IdType. For example, if this IdType 611 * contains the ID "http://foo/Patient/1", this method will return a new 612 * IdType containing ID "Patient/1". 613 */ 614 @Override 615 public IdType toUnqualified() { 616 if (isLocal() || isUrn()) { 617 return new IdType(getValueAsString()); 618 } 619 return new IdType(getResourceType(), getIdPart(), getVersionIdPart()); 620 } 621 622 @Override 623 public IdType toUnqualifiedVersionless() { 624 if (isLocal() || isUrn()) { 625 return new IdType(getValueAsString()); 626 } 627 return new IdType(getResourceType(), getIdPart()); 628 } 629 630 @Override 631 public IdType toVersionless() { 632 if (isLocal() || isUrn()) { 633 return new IdType(getValueAsString()); 634 } 635 return new IdType(getBaseUrl(), getResourceType(), getIdPart(), null); 636 } 637 638 @Override 639 public IdType withResourceType(String theResourceName) { 640 if (isLocal() || isUrn()) { 641 return new IdType(getValueAsString()); 642 } 643 return new IdType(theResourceName, getIdPart(), getVersionIdPart()); 644 } 645 646 /** 647 * Returns a view of this ID as a fully qualified URL, given a server base and 648 * resource name (which will only be used if the ID does not already contain 649 * those respective parts). Essentially, because IdType can contain either a 650 * complete URL or a partial one (or even jut a simple ID), this method may be 651 * used to translate into a complete URL. 652 * 653 * @param theServerBase 654 * The server base (e.g. "http://example.com/fhir") 655 * @param theResourceType 656 * The resource name (e.g. "Patient") 657 * @return A fully qualified URL for this ID (e.g. 658 * "http://example.com/fhir/Patient/1") 659 */ 660 @Override 661 public IdType withServerBase(String theServerBase, String theResourceType) { 662 if (isLocal() || isUrn()) { 663 return new IdType(getValueAsString()); 664 } 665 return new IdType(theServerBase, theResourceType, getIdPart(), getVersionIdPart()); 666 } 667 668 /** 669 * Creates a new instance of this ID which is identical, but refers to the 670 * specific version of this resource ID noted by theVersion. 671 * 672 * @param theVersion 673 * The actual version string, e.g. "1" 674 * @return A new instance of IdType which is identical, but refers to the 675 * specific version of this resource ID noted by theVersion. 676 */ 677 @Override 678 public IdType withVersion(String theVersion) { 679 Validate.notBlank(theVersion, "Version may not be null or empty"); 680 681 if (isLocal() || isUrn()) { 682 return new IdType(getValueAsString()); 683 } 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 780}