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