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