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