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