001package org.hl7.fhir.dstu3.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*/ 033import static org.apache.commons.lang3.StringUtils.*; 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> 050 * 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.) 051 * </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 * Note that the 64 character 072 * limit applies only to the ID portion ("123" in the examples above). 073 * </p> 074 * <p> 075 * In most situations, you only need to populate the resource's ID (e.g. 076 * <code>123</code>) in resources you are constructing and the encoder will 077 * infer the rest from the context in which the object is being used. On the 078 * other hand, the parser will always try to populate the complete absolute 079 * identity on objects it creates as a convenience. 080 * </p> 081 * <p> 082 * Regex for ID: [a-z0-9\-\.]{1,64} 083 * </p> 084 */ 085@DatatypeDef(name = "id", profileOf=StringType.class) 086public final class IdType extends UriType implements IPrimitiveType<String>, IIdType { 087 public static final String URN_PREFIX = "urn:"; 088 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 theResource) { 226 if (theResource == null) { 227 throw new NullPointerException("theResource can not be null"); 228 } else { 229 theResource.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 return StringUtils.equals(getValueAsString(), ((IdType)theArg0).getValueAsString()); 253 } 254 255 /** 256 * Returns true if this IdType matches the given IdType in terms of resource 257 * type and ID, but ignores the URL base 258 */ 259 @SuppressWarnings("deprecation") 260 public boolean equalsIgnoreBase(IdType theId) { 261 if (theId == null) { 262 return false; 263 } 264 if (theId.isEmpty()) { 265 return isEmpty(); 266 } 267 return ObjectUtils.equals(getResourceType(), theId.getResourceType()) 268 && ObjectUtils.equals(getIdPart(), theId.getIdPart()) 269 && ObjectUtils.equals(getVersionIdPart(), theId.getVersionIdPart()); 270 } 271 272 /** 273 * Returns the portion of this resource ID which corresponds to the server 274 * base URL. For example given the resource ID 275 * <code>http://example.com/fhir/Patient/123</code> the base URL would be 276 * <code>http://example.com/fhir</code>. 277 * <p> 278 * This method may return null if the ID contains no base (e.g. "Patient/123") 279 * </p> 280 */ 281 @Override 282 public String getBaseUrl() { 283 return myBaseUrl; 284 } 285 286 /** 287 * Returns only the logical ID part of this ID. For example, given the ID 288 * "http://example,.com/fhir/Patient/123/_history/456", this method would 289 * return "123". 290 */ 291 @Override 292 public String getIdPart() { 293 return myUnqualifiedId; 294 } 295 296 /** 297 * Returns the unqualified portion of this ID as a big decimal, or 298 * <code>null</code> if the value is null 299 * 300 * @throws NumberFormatException 301 * If the value is not a valid BigDecimal 302 */ 303 public BigDecimal getIdPartAsBigDecimal() { 304 String val = getIdPart(); 305 if (isBlank(val)) { 306 return null; 307 } 308 return new BigDecimal(val); 309 } 310 311 /** 312 * Returns the unqualified portion of this ID as a {@link Long}, or 313 * <code>null</code> if the value is null 314 * 315 * @throws NumberFormatException 316 * If the value is not a valid Long 317 */ 318 @Override 319 public Long getIdPartAsLong() { 320 String val = getIdPart(); 321 if (isBlank(val)) { 322 return null; 323 } 324 return Long.parseLong(val); 325 } 326 327 @Override 328 public String getResourceType() { 329 return myResourceType; 330 } 331 332 /** 333 * Returns the value of this ID. Note that this value may be a fully qualified 334 * URL, a relative/partial URL, or a simple ID. Use {@link #getIdPart()} to 335 * get just the ID portion. 336 * 337 * @see #getIdPart() 338 */ 339 @Override 340 public String getValue() { 341 String retVal = super.getValue(); 342 if (retVal == null && myHaveComponentParts) { 343 344 if (isLocal() || isUrn()) { 345 return myUnqualifiedId; 346 } 347 348 StringBuilder b = new StringBuilder(); 349 if (isNotBlank(myBaseUrl)) { 350 b.append(myBaseUrl); 351 if (myBaseUrl.charAt(myBaseUrl.length() - 1) != '/') { 352 b.append('/'); 353 } 354 } 355 356 if (isNotBlank(myResourceType)) { 357 b.append(myResourceType); 358 } 359 360 if (b.length() > 0 && isNotBlank(myUnqualifiedId)) { 361 b.append('/'); 362 } 363 364 if (isNotBlank(myUnqualifiedId)) { 365 b.append(myUnqualifiedId); 366 } else if (isNotBlank(myUnqualifiedVersionId)) { 367 b.append('/'); 368 } 369 370 if (isNotBlank(myUnqualifiedVersionId)) { 371 b.append('/'); 372 b.append("_history"); 373 b.append('/'); 374 b.append(myUnqualifiedVersionId); 375 } 376 retVal = b.toString(); 377 super.setValue(retVal); 378 } 379 return retVal; 380 } 381 382 @Override 383 public String getValueAsString() { 384 return getValue(); 385 } 386 387 @Override 388 public String getVersionIdPart() { 389 return myUnqualifiedVersionId; 390 } 391 392 public Long getVersionIdPartAsLong() { 393 if (!hasVersionIdPart()) { 394 return null; 395 } else { 396 return Long.parseLong(getVersionIdPart()); 397 } 398 } 399 400 /** 401 * Returns true if this ID has a base url 402 * 403 * @see #getBaseUrl() 404 */ 405 public boolean hasBaseUrl() { 406 return isNotBlank(myBaseUrl); 407 } 408 409 @Override 410 public int hashCode() { 411 HashCodeBuilder b = new HashCodeBuilder(); 412 b.append(getValueAsString()); 413 return b.toHashCode(); 414 } 415 416 @Override 417 public boolean hasIdPart() { 418 return isNotBlank(getIdPart()); 419 } 420 421 @Override 422 public boolean hasResourceType() { 423 return isNotBlank(myResourceType); 424 } 425 426 @Override 427 public boolean hasVersionIdPart() { 428 return isNotBlank(getVersionIdPart()); 429 } 430 431 /** 432 * Returns <code>true</code> if this ID contains an absolute URL (in other 433 * words, a URL starting with "http://" or "https://" 434 */ 435 @Override 436 public boolean isAbsolute() { 437 if (StringUtils.isBlank(getValue())) { 438 return false; 439 } 440 return isUrlAbsolute(getValue()); 441 } 442 443 @Override 444 public boolean isEmpty() { 445 return super.isEmpty() && isBlank(getValue()); 446 } 447 448 @Override 449 public boolean isIdPartValid() { 450 String id = getIdPart(); 451 if (StringUtils.isBlank(id)) { 452 return false; 453 } 454 if (id.length() > 64) { 455 return false; 456 } 457 for (int i = 0; i < id.length(); i++) { 458 char nextChar = id.charAt(i); 459 if (nextChar >= 'a' && nextChar <= 'z') { 460 continue; 461 } 462 if (nextChar >= 'A' && nextChar <= 'Z') { 463 continue; 464 } 465 if (nextChar >= '0' && nextChar <= '9') { 466 continue; 467 } 468 if (nextChar == '-' || nextChar == '.') { 469 continue; 470 } 471 return false; 472 } 473 return true; 474 } 475 476 /** 477 * Returns <code>true</code> if the unqualified ID is a valid {@link Long} 478 * value (in other words, it consists only of digits) 479 */ 480 @Override 481 public boolean isIdPartValidLong() { 482 return isValidLong(getIdPart()); 483 } 484 485 /** 486 * Returns <code>true</code> if the ID is a local reference (in other words, 487 * it begins with the '#' character) 488 */ 489 @Override 490 public boolean isLocal() { 491 return defaultString(myUnqualifiedId).startsWith("#"); 492 } 493 494 public boolean isUrn() { 495 return defaultString(myUnqualifiedId).startsWith(URN_PREFIX); 496 } 497 498 @Override 499 public boolean isVersionIdPartValidLong() { 500 return isValidLong(getVersionIdPart()); 501 } 502 503 /** 504 * Set the value 505 * 506 * <p> 507 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally 508 * represented in hex), a uuid, an oid, or any other combination of lowercase 509 * letters, numerals, "-" and ".", with a length limit of 36 characters. 510 * </p> 511 * <p> 512 * regex: [a-z0-9\-\.]{1,36} 513 * </p> 514 */ 515 @Override 516 public IdType setValue(String theValue) { 517 // TODO: add validation 518 super.setValue(theValue); 519 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_PREFIX)) { 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 The actual version string, e.g. "1". If theVersion is blank or null, returns the same as {@link #toVersionless()}} 676 * @return A new instance of IdType which is identical, but refers to the 677 * specific version of this resource ID noted by theVersion. 678 */ 679 @Override 680 public IdType withVersion(String theVersion) { 681 if (isBlank(theVersion)) { 682 return toVersionless(); 683 } 684 685 if (isLocal() || isUrn()) { 686 return new IdType(getValueAsString()); 687 } 688 689 String existingValue = getValue(); 690 691 int i = existingValue.indexOf("_history"); 692 String value; 693 if (i > 1) { 694 value = existingValue.substring(0, i - 1); 695 } else { 696 value = existingValue; 697 } 698 699 return new IdType(value + '/' + "_history" + '/' + theVersion); 700 } 701 702 private static boolean isUrlAbsolute(String theValue) { 703 String value = theValue.toLowerCase(); 704 return value.startsWith("http://") || value.startsWith("https://"); 705 } 706 707 private static boolean isValidLong(String id) { 708 if (StringUtils.isBlank(id)) { 709 return false; 710 } 711 for (int i = 0; i < id.length(); i++) { 712 if (Character.isDigit(id.charAt(i)) == false) { 713 return false; 714 } 715 } 716 return true; 717 } 718 719 /** 720 * Construct a new ID with with form "urn:uuid:[UUID]" where [UUID] is a new, 721 * randomly created UUID generated by {@link UUID#randomUUID()} 722 */ 723 public static IdType newRandomUuid() { 724 return new IdType("urn:uuid:" + UUID.randomUUID().toString()); 725 } 726 727 /** 728 * Retrieves the ID from the given resource instance 729 */ 730 public static IdType of(IBaseResource theResouce) { 731 if (theResouce == null) { 732 throw new NullPointerException("theResource can not be null"); 733 } else { 734 IIdType retVal = theResouce.getIdElement(); 735 if (retVal == null) { 736 return null; 737 } else if (retVal instanceof IdType) { 738 return (IdType) retVal; 739 } else { 740 return new IdType(retVal.getValue()); 741 } 742 } 743 } 744 745 private static String toPlainStringWithNpeThrowIfNeeded(BigDecimal theIdPart) { 746 if (theIdPart == null) { 747 throw new NullPointerException("BigDecimal ID can not be null"); 748 } 749 return theIdPart.toPlainString(); 750 } 751 752 private static String toPlainStringWithNpeThrowIfNeeded(Long theIdPart) { 753 if (theIdPart == null) { 754 throw new NullPointerException("Long ID can not be null"); 755 } 756 return theIdPart.toString(); 757 } 758 759 public String fhirType() { 760 return "id"; 761 } 762 763 @Override 764 public IIdType setParts(String theBaseUrl, String theResourceType, String theIdPart, String theVersionIdPart) { 765 if (isNotBlank(theVersionIdPart)) { 766 Validate.notBlank(theResourceType, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated"); 767 Validate.notBlank(theIdPart, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated"); 768 } 769 if (isNotBlank(theBaseUrl) && isNotBlank(theIdPart)) { 770 Validate.notBlank(theResourceType, "If theBaseUrl is populated and theIdPart is populated, theResourceType must be populated"); 771 } 772 773 setValue(null); 774 775 myBaseUrl = theBaseUrl; 776 myResourceType = theResourceType; 777 myUnqualifiedId = theIdPart; 778 myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionIdPart, null); 779 myHaveComponentParts = true; 780 781 return this; 782 } 783 784}