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