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