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