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 IdType ret = new IdType(getValue()); 263 copyValues(ret); 264 return ret; 265 } 266 267 @Override 268 public boolean equals(Object theArg0) { 269 if (!(theArg0 instanceof IdType)) { 270 return false; 271 } 272 return StringUtils.equals(getValueAsString(), ((IdType) theArg0).getValueAsString()); 273 } 274 275 /** 276 * Returns true if this IdType matches the given IdType in terms of resource 277 * type and ID, but ignores the URL base 278 */ 279 @SuppressWarnings("deprecation") 280 public boolean equalsIgnoreBase(IdType theId) { 281 if (theId == null) { 282 return false; 283 } 284 if (theId.isEmpty()) { 285 return isEmpty(); 286 } 287 return ObjectUtils.equals(getResourceType(), theId.getResourceType()) 288 && ObjectUtils.equals(getIdPart(), theId.getIdPart()) 289 && ObjectUtils.equals(getVersionIdPart(), theId.getVersionIdPart()); 290 } 291 292 public String fhirType() { 293 return "id"; 294 } 295 296 /** 297 * Returns the portion of this resource ID which corresponds to the server 298 * base URL. For example given the resource ID 299 * <code>http://example.com/fhir/Patient/123</code> the base URL would be 300 * <code>http://example.com/fhir</code>. 301 * <p> 302 * This method may return null if the ID contains no base (e.g. "Patient/123") 303 * </p> 304 */ 305 @Override 306 public String getBaseUrl() { 307 return myBaseUrl; 308 } 309 310 /** 311 * Returns only the logical ID part of this ID. For example, given the ID 312 * "http://example,.com/fhir/Patient/123/_history/456", this method would 313 * return "123". 314 */ 315 @Override 316 public String getIdPart() { 317 return myUnqualifiedId; 318 } 319 320 /** 321 * Returns the unqualified portion of this ID as a big decimal, or 322 * <code>null</code> if the value is null 323 * 324 * @throws NumberFormatException If the value is not a valid BigDecimal 325 */ 326 public BigDecimal getIdPartAsBigDecimal() { 327 String val = getIdPart(); 328 if (isBlank(val)) { 329 return null; 330 } 331 return new BigDecimal(val); 332 } 333 334 /** 335 * Returns the unqualified portion of this ID as a {@link Long}, or 336 * <code>null</code> if the value is null 337 * 338 * @throws NumberFormatException If the value is not a valid Long 339 */ 340 @Override 341 public Long getIdPartAsLong() { 342 String val = getIdPart(); 343 if (isBlank(val)) { 344 return null; 345 } 346 return Long.parseLong(val); 347 } 348 349 @Override 350 public String getResourceType() { 351 return myResourceType; 352 } 353 354 /** 355 * Returns the value of this ID. Note that this value may be a fully qualified 356 * URL, a relative/partial URL, or a simple ID. Use {@link #getIdPart()} to 357 * get just the ID portion. 358 * 359 * @see #getIdPart() 360 */ 361 @Override 362 public String getValue() { 363 String retVal = super.getValue(); 364 if (retVal == null && myHaveComponentParts) { 365 366 if (isLocal() || isUrn()) { 367 return myUnqualifiedId; 368 } 369 370 StringBuilder b = new StringBuilder(); 371 if (isNotBlank(myBaseUrl)) { 372 b.append(myBaseUrl); 373 if (myBaseUrl.charAt(myBaseUrl.length() - 1) != '/') { 374 b.append('/'); 375 } 376 } 377 378 if (isNotBlank(myResourceType)) { 379 b.append(myResourceType); 380 } 381 382 if (b.length() > 0 && isNotBlank(myUnqualifiedId)) { 383 b.append('/'); 384 } 385 386 if (isNotBlank(myUnqualifiedId)) { 387 b.append(myUnqualifiedId); 388 } else if (isNotBlank(myUnqualifiedVersionId)) { 389 b.append('/'); 390 } 391 392 if (isNotBlank(myUnqualifiedVersionId)) { 393 b.append('/'); 394 b.append("_history"); 395 b.append('/'); 396 b.append(myUnqualifiedVersionId); 397 } 398 retVal = b.toString(); 399 super.setValue(retVal); 400 } 401 return retVal; 402 } 403 404 /** 405 * Set the value 406 * <p> 407 * <p> 408 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally 409 * represented in hex), a uuid, an oid, or any other combination of lowercase 410 * letters, numerals, "-" and ".", with a length limit of 36 characters. 411 * </p> 412 * <p> 413 * regex: [a-z0-9\-\.]{1,36} 414 * </p> 415 */ 416 @Override 417 public IdType setValue(String theValue) { 418 // TODO: add validation 419 super.setValue(theValue); 420 myHaveComponentParts = false; 421 422 if (StringUtils.isBlank(theValue)) { 423 myBaseUrl = null; 424 super.setValue(null); 425 myUnqualifiedId = null; 426 myUnqualifiedVersionId = null; 427 myResourceType = null; 428 } else if (theValue.charAt(0) == '#' && theValue.length() > 1) { 429 super.setValue(theValue); 430 myBaseUrl = null; 431 myUnqualifiedId = theValue; 432 myUnqualifiedVersionId = null; 433 myResourceType = null; 434 myHaveComponentParts = true; 435 } else if (theValue.startsWith(URN_PREFIX)) { 436 myBaseUrl = null; 437 myUnqualifiedId = theValue; 438 myUnqualifiedVersionId = null; 439 myResourceType = null; 440 myHaveComponentParts = true; 441 } else { 442 int vidIndex = theValue.indexOf("/_history/"); 443 int idIndex; 444 if (vidIndex != -1) { 445 myUnqualifiedVersionId = theValue.substring(vidIndex + "/_history/".length()); 446 idIndex = theValue.lastIndexOf('/', vidIndex - 1); 447 myUnqualifiedId = theValue.substring(idIndex + 1, vidIndex); 448 } else { 449 idIndex = theValue.lastIndexOf('/'); 450 myUnqualifiedId = theValue.substring(idIndex + 1); 451 myUnqualifiedVersionId = null; 452 } 453 454 myBaseUrl = null; 455 if (idIndex <= 0) { 456 myResourceType = null; 457 } else { 458 int typeIndex = theValue.lastIndexOf('/', idIndex - 1); 459 if (typeIndex == -1) { 460 myResourceType = theValue.substring(0, idIndex); 461 } else { 462 if (typeIndex > 0 && '/' == theValue.charAt(typeIndex - 1)) { 463 typeIndex = theValue.indexOf('/', typeIndex + 1); 464 } 465 if (typeIndex >= idIndex) { 466 // e.g. http://example.org/foo 467 // 'foo' was the id but we're making that the resource type. Nullify the id part because we don't have an id. 468 // Also set null value to the super.setValue() and enable myHaveComponentParts so it forces getValue() to properly 469 // recreate the url 470 myResourceType = myUnqualifiedId; 471 myUnqualifiedId = null; 472 super.setValue(null); 473 myHaveComponentParts = true; 474 } else { 475 myResourceType = theValue.substring(typeIndex + 1, idIndex); 476 } 477 478 if (typeIndex > 4) { 479 myBaseUrl = theValue.substring(0, typeIndex); 480 } 481 482 } 483 } 484 485 } 486 return this; 487 } 488 @Override 489 public String getValueAsString() { 490 return getValue(); 491 } 492 493 /** 494 * Set the value 495 * <p> 496 * <p> 497 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally 498 * represented in hex), a uuid, an oid, or any other combination of lowercase 499 * letters, numerals, "-" and ".", with a length limit of 36 characters. 500 * </p> 501 * <p> 502 * regex: [a-z0-9\-\.]{1,36} 503 * </p> 504 */ 505 @Override 506 public void setValueAsString(String theValue) { 507 setValue(theValue); 508 } 509 510 @Override 511 public String getVersionIdPart() { 512 return myUnqualifiedVersionId; 513 } 514 515 public Long getVersionIdPartAsLong() { 516 if (!hasVersionIdPart()) { 517 return null; 518 } else { 519 return Long.parseLong(getVersionIdPart()); 520 } 521 } 522 523 /** 524 * Returns true if this ID has a base url 525 * 526 * @see #getBaseUrl() 527 */ 528 public boolean hasBaseUrl() { 529 return isNotBlank(myBaseUrl); 530 } 531 532 @Override 533 public boolean hasIdPart() { 534 return isNotBlank(getIdPart()); 535 } 536 537 @Override 538 public boolean hasResourceType() { 539 return isNotBlank(myResourceType); 540 } 541 542 @Override 543 public boolean hasVersionIdPart() { 544 return isNotBlank(getVersionIdPart()); 545 } 546 547 @Override 548 public int hashCode() { 549 HashCodeBuilder b = new HashCodeBuilder(); 550 b.append(getValueAsString()); 551 return b.toHashCode(); 552 } 553 554 /** 555 * Returns <code>true</code> if this ID contains an absolute URL (in other 556 * words, a URL starting with "http://" or "https://" 557 */ 558 @Override 559 public boolean isAbsolute() { 560 if (StringUtils.isBlank(getValue())) { 561 return false; 562 } 563 return isUrlAbsolute(getValue()); 564 } 565 566 @Override 567 public boolean isEmpty() { 568 return isBlank(getValue()); 569 } 570 571 @Override 572 public boolean isIdPartValid() { 573 String id = getIdPart(); 574 if (StringUtils.isBlank(id)) { 575 return false; 576 } 577 if (id.length() > 64) { 578 return false; 579 } 580 for (int i = 0; i < id.length(); i++) { 581 char nextChar = id.charAt(i); 582 if (nextChar >= 'a' && nextChar <= 'z') { 583 continue; 584 } 585 if (nextChar >= 'A' && nextChar <= 'Z') { 586 continue; 587 } 588 if (nextChar >= '0' && nextChar <= '9') { 589 continue; 590 } 591 if (nextChar == '-' || nextChar == '.') { 592 continue; 593 } 594 return false; 595 } 596 return true; 597 } 598 599 /** 600 * Returns <code>true</code> if the unqualified ID is a valid {@link Long} 601 * value (in other words, it consists only of digits) 602 */ 603 @Override 604 public boolean isIdPartValidLong() { 605 return isValidLong(getIdPart()); 606 } 607 608 /** 609 * Returns <code>true</code> if the ID is a local reference (in other words, 610 * it begins with the '#' character) 611 */ 612 @Override 613 public boolean isLocal() { 614 return defaultString(myUnqualifiedId).startsWith("#"); 615 } 616 617 public boolean isUrn() { 618 return defaultString(myUnqualifiedId).startsWith(URN_PREFIX); 619 } 620 621 @Override 622 public boolean isVersionIdPartValidLong() { 623 return isValidLong(getVersionIdPart()); 624 } 625 626 @Override 627 public IIdType setParts(String theBaseUrl, String theResourceType, String theIdPart, String theVersionIdPart) { 628 if (isNotBlank(theVersionIdPart)) { 629 Validate.notBlank(theResourceType, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated"); 630 Validate.notBlank(theIdPart, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated"); 631 } 632 if (isNotBlank(theBaseUrl) && isNotBlank(theIdPart)) { 633 Validate.notBlank(theResourceType, "If theBaseUrl is populated and theIdPart is populated, theResourceType must be populated"); 634 } 635 636 setValue(null); 637 638 myBaseUrl = theBaseUrl; 639 myResourceType = theResourceType; 640 myUnqualifiedId = theIdPart; 641 myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionIdPart, null); 642 myHaveComponentParts = true; 643 644 return this; 645 } 646 647 @Override 648 public String toString() { 649 return getValue(); 650 } 651 652 /** 653 * Returns a new IdType containing this IdType's values but with no server 654 * base URL if one is present in this IdType. For example, if this IdType 655 * contains the ID "http://foo/Patient/1", this method will return a new 656 * IdType containing ID "Patient/1". 657 */ 658 @Override 659 public IdType toUnqualified() { 660 if (isLocal() || isUrn()) { 661 return new IdType(getValueAsString()); 662 } 663 return new IdType(getResourceType(), getIdPart(), getVersionIdPart()); 664 } 665 666 @Override 667 public IdType toUnqualifiedVersionless() { 668 if (isLocal() || isUrn()) { 669 return new IdType(getValueAsString()); 670 } 671 return new IdType(getResourceType(), getIdPart()); 672 } 673 674 @Override 675 public IdType toVersionless() { 676 if (isLocal() || isUrn()) { 677 return new IdType(getValueAsString()); 678 } 679 return new IdType(getBaseUrl(), getResourceType(), getIdPart(), null); 680 } 681 682 @Override 683 public IdType withResourceType(String theResourceName) { 684 if (isLocal() || isUrn()) { 685 return new IdType(getValueAsString()); 686 } 687 return new IdType(theResourceName, getIdPart(), getVersionIdPart()); 688 } 689 690 /** 691 * Returns a view of this ID as a fully qualified URL, given a server base and 692 * resource name (which will only be used if the ID does not already contain 693 * those respective parts). Essentially, because IdType can contain either a 694 * complete URL or a partial one (or even jut a simple ID), this method may be 695 * used to translate into a complete URL. 696 * 697 * @param theServerBase The server base (e.g. "http://example.com/fhir") 698 * @param theResourceType The resource name (e.g. "Patient") 699 * @return A fully qualified URL for this ID (e.g. 700 * "http://example.com/fhir/Patient/1") 701 */ 702 @Override 703 public IdType withServerBase(String theServerBase, String theResourceType) { 704 if (isLocal() || isUrn()) { 705 return new IdType(getValueAsString()); 706 } 707 return new IdType(theServerBase, theResourceType, getIdPart(), getVersionIdPart()); 708 } 709 710 /** 711 * Creates a new instance of this ID which is identical, but refers to the 712 * specific version of this resource ID noted by theVersion. 713 * 714 * @param theVersion The actual version string, e.g. "1". If theVersion is blank or null, returns the same as {@link #toVersionless()}} 715 * @return A new instance of IdType which is identical, but refers to the 716 * specific version of this resource ID noted by theVersion. 717 */ 718 @Override 719 public IdType withVersion(String theVersion) { 720 if (isBlank(theVersion)) { 721 return toVersionless(); 722 } 723 724 if (isLocal() || isUrn()) { 725 return new IdType(getValueAsString()); 726 } 727 728 String existingValue = getValue(); 729 730 int i = existingValue.indexOf("_history"); 731 String value; 732 if (i > 1) { 733 value = existingValue.substring(0, i - 1); 734 } else { 735 value = existingValue; 736 } 737 738 return new IdType(value + '/' + "_history" + '/' + theVersion); 739 } 740 741 private static boolean isUrlAbsolute(String theValue) { 742 String value = theValue.toLowerCase(); 743 return value.startsWith("http://") || value.startsWith("https://"); 744 } 745 746 private static boolean isValidLong(String id) { 747 if (StringUtils.isBlank(id)) { 748 return false; 749 } 750 for (int i = 0; i < id.length(); i++) { 751 if (Character.isDigit(id.charAt(i)) == false) { 752 return false; 753 } 754 } 755 return true; 756 } 757 758 /** 759 * Construct a new ID with with form "urn:uuid:[UUID]" where [UUID] is a new, 760 * randomly created UUID generated by {@link UUID#randomUUID()} 761 */ 762 public static IdType newRandomUuid() { 763 return new IdType("urn:uuid:" + UUID.randomUUID().toString()); 764 } 765 766 /** 767 * Retrieves the ID from the given resource instance 768 */ 769 public static IdType of(IBaseResource theResouce) { 770 if (theResouce == null) { 771 throw new NullPointerException("theResource can not be null"); 772 } else { 773 IIdType retVal = theResouce.getIdElement(); 774 if (retVal == null) { 775 return null; 776 } else if (retVal instanceof IdType) { 777 return (IdType) retVal; 778 } else { 779 return new IdType(retVal.getValue()); 780 } 781 } 782 } 783 784 private static String toPlainStringWithNpeThrowIfNeeded(BigDecimal theIdPart) { 785 if (theIdPart == null) { 786 throw new NullPointerException("BigDecimal ID can not be null"); 787 } 788 return theIdPart.toPlainString(); 789 } 790 791 private static String toPlainStringWithNpeThrowIfNeeded(Long theIdPart) { 792 if (theIdPart == null) { 793 throw new NullPointerException("Long ID can not be null"); 794 } 795 return theIdPart.toString(); 796 } 797 798}