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 @Override 497 public String asStringValue() { 498 return getValue(); 499 } 500 501 /** 502 * Set the value 503 * <p> 504 * <p> 505 * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally 506 * represented in hex), a uuid, an oid, or any other combination of lowercase 507 * letters, numerals, "-" and ".", with a length limit of 36 characters. 508 * </p> 509 * <p> 510 * regex: [a-z0-9\-\.]{1,36} 511 * </p> 512 */ 513 @Override 514 public void setValueAsString(String theValue) { 515 setValue(theValue); 516 } 517 518 @Override 519 public String getVersionIdPart() { 520 return myUnqualifiedVersionId; 521 } 522 523 public Long getVersionIdPartAsLong() { 524 if (!hasVersionIdPart()) { 525 return null; 526 } else { 527 return Long.parseLong(getVersionIdPart()); 528 } 529 } 530 531 /** 532 * Returns true if this ID has a base url 533 * 534 * @see #getBaseUrl() 535 */ 536 public boolean hasBaseUrl() { 537 return isNotBlank(myBaseUrl); 538 } 539 540 @Override 541 public boolean hasIdPart() { 542 return isNotBlank(getIdPart()); 543 } 544 545 @Override 546 public boolean hasResourceType() { 547 return isNotBlank(myResourceType); 548 } 549 550 @Override 551 public boolean hasVersionIdPart() { 552 return isNotBlank(getVersionIdPart()); 553 } 554 555 @Override 556 public int hashCode() { 557 HashCodeBuilder b = new HashCodeBuilder(); 558 b.append(getValueAsString()); 559 return b.toHashCode(); 560 } 561 562 /** 563 * Returns <code>true</code> if this ID contains an absolute URL (in other 564 * words, a URL starting with "http://" or "https://" 565 */ 566 @Override 567 public boolean isAbsolute() { 568 if (StringUtils.isBlank(getValue())) { 569 return false; 570 } 571 return isUrlAbsolute(getValue()); 572 } 573 574 @Override 575 public boolean isEmpty() { 576 return isBlank(getValue()); 577 } 578 579 @Override 580 public boolean isIdPartValid() { 581 String id = getIdPart(); 582 if (StringUtils.isBlank(id)) { 583 return false; 584 } 585 if (id.length() > 64) { 586 return false; 587 } 588 for (int i = 0; i < id.length(); i++) { 589 char nextChar = id.charAt(i); 590 if (nextChar >= 'a' && nextChar <= 'z') { 591 continue; 592 } 593 if (nextChar >= 'A' && nextChar <= 'Z') { 594 continue; 595 } 596 if (nextChar >= '0' && nextChar <= '9') { 597 continue; 598 } 599 if (nextChar == '-' || nextChar == '.') { 600 continue; 601 } 602 return false; 603 } 604 return true; 605 } 606 607 /** 608 * Returns <code>true</code> if the unqualified ID is a valid {@link Long} 609 * value (in other words, it consists only of digits) 610 */ 611 @Override 612 public boolean isIdPartValidLong() { 613 return isValidLong(getIdPart()); 614 } 615 616 /** 617 * Returns <code>true</code> if the ID is a local reference (in other words, 618 * it begins with the '#' character) 619 */ 620 @Override 621 public boolean isLocal() { 622 return defaultString(myUnqualifiedId).startsWith("#"); 623 } 624 625 public boolean isUrn() { 626 return defaultString(myUnqualifiedId).startsWith(URN_PREFIX); 627 } 628 629 @Override 630 public boolean isVersionIdPartValidLong() { 631 return isValidLong(getVersionIdPart()); 632 } 633 634 @Override 635 public IIdType setParts(String theBaseUrl, String theResourceType, String theIdPart, String theVersionIdPart) { 636 if (isNotBlank(theVersionIdPart)) { 637 Validate.notBlank(theResourceType, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated"); 638 Validate.notBlank(theIdPart, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated"); 639 } 640 if (isNotBlank(theBaseUrl) && isNotBlank(theIdPart)) { 641 Validate.notBlank(theResourceType, "If theBaseUrl is populated and theIdPart is populated, theResourceType must be populated"); 642 } 643 644 setValue(null); 645 646 myBaseUrl = theBaseUrl; 647 myResourceType = theResourceType; 648 myUnqualifiedId = theIdPart; 649 myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionIdPart, null); 650 myHaveComponentParts = true; 651 652 return this; 653 } 654 655 @Override 656 public String toString() { 657 return getValue(); 658 } 659 660 /** 661 * Returns a new IdType containing this IdType's values but with no server 662 * base URL if one is present in this IdType. For example, if this IdType 663 * contains the ID "http://foo/Patient/1", this method will return a new 664 * IdType containing ID "Patient/1". 665 */ 666 @Override 667 public IdType toUnqualified() { 668 if (isLocal() || isUrn()) { 669 return new IdType(getValueAsString()); 670 } 671 return new IdType(getResourceType(), getIdPart(), getVersionIdPart()); 672 } 673 674 @Override 675 public IdType toUnqualifiedVersionless() { 676 if (isLocal() || isUrn()) { 677 return new IdType(getValueAsString()); 678 } 679 return new IdType(getResourceType(), getIdPart()); 680 } 681 682 @Override 683 public IdType toVersionless() { 684 if (isLocal() || isUrn()) { 685 return new IdType(getValueAsString()); 686 } 687 return new IdType(getBaseUrl(), getResourceType(), getIdPart(), null); 688 } 689 690 @Override 691 public IdType withResourceType(String theResourceName) { 692 if (isLocal() || isUrn()) { 693 return new IdType(getValueAsString()); 694 } 695 return new IdType(theResourceName, getIdPart(), getVersionIdPart()); 696 } 697 698 /** 699 * Returns a view of this ID as a fully qualified URL, given a server base and 700 * resource name (which will only be used if the ID does not already contain 701 * those respective parts). Essentially, because IdType can contain either a 702 * complete URL or a partial one (or even jut a simple ID), this method may be 703 * used to translate into a complete URL. 704 * 705 * @param theServerBase The server base (e.g. "http://example.com/fhir") 706 * @param theResourceType The resource name (e.g. "Patient") 707 * @return A fully qualified URL for this ID (e.g. 708 * "http://example.com/fhir/Patient/1") 709 */ 710 @Override 711 public IdType withServerBase(String theServerBase, String theResourceType) { 712 if (isLocal() || isUrn()) { 713 return new IdType(getValueAsString()); 714 } 715 return new IdType(theServerBase, theResourceType, getIdPart(), getVersionIdPart()); 716 } 717 718 /** 719 * Creates a new instance of this ID which is identical, but refers to the 720 * specific version of this resource ID noted by theVersion. 721 * 722 * @param theVersion The actual version string, e.g. "1". If theVersion is blank or null, returns the same as {@link #toVersionless()}} 723 * @return A new instance of IdType which is identical, but refers to the 724 * specific version of this resource ID noted by theVersion. 725 */ 726 @Override 727 public IdType withVersion(String theVersion) { 728 if (isBlank(theVersion)) { 729 return toVersionless(); 730 } 731 732 if (isLocal() || isUrn()) { 733 return new IdType(getValueAsString()); 734 } 735 736 String existingValue = getValue(); 737 738 int i = existingValue.indexOf("_history"); 739 String value; 740 if (i > 1) { 741 value = existingValue.substring(0, i - 1); 742 } else { 743 value = existingValue; 744 } 745 746 return new IdType(value + '/' + "_history" + '/' + theVersion); 747 } 748 749 private static boolean isUrlAbsolute(String theValue) { 750 String value = theValue.toLowerCase(); 751 return value.startsWith("http://") || value.startsWith("https://"); 752 } 753 754 private static boolean isValidLong(String id) { 755 if (StringUtils.isBlank(id)) { 756 return false; 757 } 758 for (int i = 0; i < id.length(); i++) { 759 if (Character.isDigit(id.charAt(i)) == false) { 760 return false; 761 } 762 } 763 return true; 764 } 765 766 /** 767 * Construct a new ID with with form "urn:uuid:[UUID]" where [UUID] is a new, 768 * randomly created UUID generated by {@link UUID#randomUUID()} 769 */ 770 public static IdType newRandomUuid() { 771 return new IdType("urn:uuid:" + UUID.randomUUID().toString()); 772 } 773 774 /** 775 * Retrieves the ID from the given resource instance 776 */ 777 public static IdType of(IBaseResource theResouce) { 778 if (theResouce == null) { 779 throw new NullPointerException("theResource can not be null"); 780 } else { 781 IIdType retVal = theResouce.getIdElement(); 782 if (retVal == null) { 783 return null; 784 } else if (retVal instanceof IdType) { 785 return (IdType) retVal; 786 } else { 787 return new IdType(retVal.getValue()); 788 } 789 } 790 } 791 792 private static String toPlainStringWithNpeThrowIfNeeded(BigDecimal theIdPart) { 793 if (theIdPart == null) { 794 throw new NullPointerException("BigDecimal ID can not be null"); 795 } 796 return theIdPart.toPlainString(); 797 } 798 799 private static String toPlainStringWithNpeThrowIfNeeded(Long theIdPart) { 800 if (theIdPart == null) { 801 throw new NullPointerException("Long ID can not be null"); 802 } 803 return theIdPart.toString(); 804 } 805 806}