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