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