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