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  /**
240   * @deprecated Use {@link #getIdPartAsBigDecimal()} instead (this method was
241   *             deprocated because its name is ambiguous)
242   */
243  @Deprecated
244  public BigDecimal asBigDecimal() {
245    return getIdPartAsBigDecimal();
246  }
247
248  @Override
249  public IdType copy() {
250    return new IdType(getValue());
251  }
252
253  @Override
254  public boolean equals(Object theArg0) {
255    if (!(theArg0 instanceof IdType)) {
256      return false;
257    }
258    return StringUtils.equals(getValueAsString(), ((IdType)theArg0).getValueAsString());
259  }
260
261  /**
262   * Returns true if this IdType matches the given IdType in terms of resource
263   * type and ID, but ignores the URL base
264   */
265  @SuppressWarnings("deprecation")
266  public boolean equalsIgnoreBase(IdType theId) {
267    if (theId == null) {
268      return false;
269    }
270    if (theId.isEmpty()) {
271      return isEmpty();
272    }
273    return ObjectUtils.equals(getResourceType(), theId.getResourceType())
274      && ObjectUtils.equals(getIdPart(), theId.getIdPart())
275      && ObjectUtils.equals(getVersionIdPart(), theId.getVersionIdPart());
276  }
277
278  /**
279   * Returns the portion of this resource ID which corresponds to the server
280   * base URL. For example given the resource ID
281   * <code>http://example.com/fhir/Patient/123</code> the base URL would be
282   * <code>http://example.com/fhir</code>.
283   * <p>
284   * This method may return null if the ID contains no base (e.g. "Patient/123")
285   * </p>
286   */
287  @Override
288  public String getBaseUrl() {
289    return myBaseUrl;
290  }
291
292  /**
293   * Returns only the logical ID part of this ID. For example, given the ID
294   * "http://example,.com/fhir/Patient/123/_history/456", this method would
295   * return "123".
296   */
297  @Override
298  public String getIdPart() {
299    return myUnqualifiedId;
300  }
301
302  /**
303   * Returns the unqualified portion of this ID as a big decimal, or
304   * <code>null</code> if the value is null
305   *
306   * @throws NumberFormatException
307   *           If the value is not a valid BigDecimal
308   */
309  public BigDecimal getIdPartAsBigDecimal() {
310    String val = getIdPart();
311    if (isBlank(val)) {
312      return null;
313    }
314    return new BigDecimal(val);
315  }
316
317  /**
318   * Returns the unqualified portion of this ID as a {@link Long}, or
319   * <code>null</code> if the value is null
320   *
321   * @throws NumberFormatException
322   *           If the value is not a valid Long
323   */
324  @Override
325  public Long getIdPartAsLong() {
326    String val = getIdPart();
327    if (isBlank(val)) {
328      return null;
329    }
330    return Long.parseLong(val);
331  }
332
333  @Override
334  public String getResourceType() {
335    return myResourceType;
336  }
337
338  /**
339   * Returns the value of this ID. Note that this value may be a fully qualified
340   * URL, a relative/partial URL, or a simple ID. Use {@link #getIdPart()} to
341   * get just the ID portion.
342   *
343   * @see #getIdPart()
344   */
345  @Override
346  public String getValue() {
347    String retVal = super.getValue();
348    if (retVal == null && myHaveComponentParts) {
349
350      if (isLocal() || isUrn()) {
351        return myUnqualifiedId;
352      }
353
354      StringBuilder b = new StringBuilder();
355      if (isNotBlank(myBaseUrl)) {
356        b.append(myBaseUrl);
357        if (myBaseUrl.charAt(myBaseUrl.length() - 1) != '/') {
358          b.append('/');
359        }
360      }
361
362      if (isNotBlank(myResourceType)) {
363        b.append(myResourceType);
364      }
365
366      if (b.length() > 0 && isNotBlank(myUnqualifiedId)) {
367        b.append('/');
368      }
369
370      if (isNotBlank(myUnqualifiedId)) {
371        b.append(myUnqualifiedId);
372      } else if (isNotBlank(myUnqualifiedVersionId)) {
373        b.append('/');
374      }
375
376      if (isNotBlank(myUnqualifiedVersionId)) {
377        b.append('/');
378        b.append("_history");
379        b.append('/');
380        b.append(myUnqualifiedVersionId);
381      }
382      retVal = b.toString();
383      super.setValue(retVal);
384    }
385    return retVal;
386  }
387
388  @Override
389  public String getValueAsString() {
390    return getValue();
391  }
392
393  @Override
394  public String asStringValue() {
395    return getValue();
396  }
397
398  @Override
399  public String getVersionIdPart() {
400    return myUnqualifiedVersionId;
401  }
402
403  public Long getVersionIdPartAsLong() {
404    if (!hasVersionIdPart()) {
405      return null;
406    } else {
407      return Long.parseLong(getVersionIdPart());
408    }
409  }
410
411  /**
412   * Returns true if this ID has a base url
413   *
414   * @see #getBaseUrl()
415   */
416  public boolean hasBaseUrl() {
417    return isNotBlank(myBaseUrl);
418  }
419
420  @Override
421  public int hashCode() {
422    HashCodeBuilder b = new HashCodeBuilder();
423    b.append(getValueAsString());
424    return b.toHashCode();
425  }
426
427  @Override
428  public boolean hasIdPart() {
429    return isNotBlank(getIdPart());
430  }
431
432  @Override
433  public boolean hasResourceType() {
434    return isNotBlank(myResourceType);
435  }
436
437  @Override
438  public boolean hasVersionIdPart() {
439    return isNotBlank(getVersionIdPart());
440  }
441
442  /**
443   * Returns <code>true</code> if this ID contains an absolute URL (in other
444   * words, a URL starting with "http://" or "https://"
445   */
446  @Override
447  public boolean isAbsolute() {
448    if (StringUtils.isBlank(getValue())) {
449      return false;
450    }
451    return isUrlAbsolute(getValue());
452  }
453
454  @Override
455  public boolean isEmpty() {
456    return super.isEmpty() && isBlank(getValue());
457  }
458
459  @Override
460  public boolean isIdPartValid() {
461    String id = getIdPart();
462    if (StringUtils.isBlank(id)) {
463      return false;
464    }
465    if (id.length() > 64) {
466      return false;
467    }
468    for (int i = 0; i < id.length(); i++) {
469      char nextChar = id.charAt(i);
470      if (nextChar >= 'a' && nextChar <= 'z') {
471        continue;
472      }
473      if (nextChar >= 'A' && nextChar <= 'Z') {
474        continue;
475      }
476      if (nextChar >= '0' && nextChar <= '9') {
477        continue;
478      }
479      if (nextChar == '-' || nextChar == '.') {
480        continue;
481      }
482      return false;
483    }
484    return true;
485  }
486
487  /**
488   * Returns <code>true</code> if the unqualified ID is a valid {@link Long}
489   * value (in other words, it consists only of digits)
490   */
491  @Override
492  public boolean isIdPartValidLong() {
493    return isValidLong(getIdPart());
494  }
495
496  /**
497   * Returns <code>true</code> if the ID is a local reference (in other words,
498   * it begins with the '#' character)
499   */
500  @Override
501  public boolean isLocal() {
502    return defaultString(myUnqualifiedId).startsWith("#");
503  }
504
505  public boolean isUrn() {
506    return defaultString(myUnqualifiedId).startsWith(URN_PREFIX);
507  }
508
509  @Override
510  public boolean isVersionIdPartValidLong() {
511    return isValidLong(getVersionIdPart());
512  }
513
514  /**
515   * Set the value
516   *
517   * <p>
518   * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally
519   * represented in hex), a uuid, an oid, or any other combination of lowercase
520   * letters, numerals, "-" and ".", with a length limit of 36 characters.
521   * </p>
522   * <p>
523   * regex: [a-z0-9\-\.]{1,36}
524   * </p>
525   */
526  @Override
527  public IdType setValue(String theValue) {
528    // TODO: add validation
529    super.setValue(theValue);
530
531    myHaveComponentParts = false;
532
533    if (StringUtils.isBlank(theValue)) {
534      myBaseUrl = null;
535      super.setValue(null);
536      myUnqualifiedId = null;
537      myUnqualifiedVersionId = null;
538      myResourceType = null;
539    } else if (theValue.charAt(0) == '#' && theValue.length() > 1) {
540      super.setValue(theValue);
541      myBaseUrl = null;
542      myUnqualifiedId = theValue;
543      myUnqualifiedVersionId = null;
544      myResourceType = null;
545      myHaveComponentParts = true;
546    } else if (theValue.startsWith(URN_PREFIX)) {
547      myBaseUrl = null;
548      myUnqualifiedId = theValue;
549      myUnqualifiedVersionId = null;
550      myResourceType = null;
551      myHaveComponentParts = true;
552    } else {
553      int vidIndex = theValue.indexOf("/_history/");
554      int idIndex;
555      if (vidIndex != -1) {
556        myUnqualifiedVersionId = theValue.substring(vidIndex + "/_history/".length());
557        idIndex = theValue.lastIndexOf('/', vidIndex - 1);
558        myUnqualifiedId = theValue.substring(idIndex + 1, vidIndex);
559      } else {
560        idIndex = theValue.lastIndexOf('/');
561        myUnqualifiedId = theValue.substring(idIndex + 1);
562        myUnqualifiedVersionId = null;
563      }
564
565      myBaseUrl = null;
566      if (idIndex <= 0) {
567        myResourceType = null;
568      } else {
569        int typeIndex = theValue.lastIndexOf('/', idIndex - 1);
570        if (typeIndex == -1) {
571          myResourceType = theValue.substring(0, idIndex);
572        } else {
573          if (typeIndex > 0 && '/' == theValue.charAt(typeIndex - 1)) {
574            typeIndex = theValue.indexOf('/', typeIndex + 1);
575          }
576          if (typeIndex >= idIndex) {
577            // e.g. http://example.org/foo
578            // 'foo' was the id but we're making that the resource type. Nullify the id part because we don't have an id.
579            // Also set null value to the super.setValue() and enable myHaveComponentParts so it forces getValue() to properly
580            // recreate the url
581            myResourceType = myUnqualifiedId;
582            myUnqualifiedId = null;
583            super.setValue(null);
584            myHaveComponentParts = true;
585          } else {
586            myResourceType = theValue.substring(typeIndex + 1, idIndex);
587          }
588
589          if (typeIndex > 4) {
590            myBaseUrl = theValue.substring(0, typeIndex);
591          }
592
593        }
594      }
595
596    }
597    return this;
598  }
599
600  /**
601   * Set the value
602   *
603   * <p>
604   * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally
605   * represented in hex), a uuid, an oid, or any other combination of lowercase
606   * letters, numerals, "-" and ".", with a length limit of 36 characters.
607   * </p>
608   * <p>
609   * regex: [a-z0-9\-\.]{1,36}
610   * </p>
611   */
612  @Override
613  public void setValueAsString(String theValue) {
614    setValue(theValue);
615  }
616
617  @Override
618  public String toString() {
619    return getValue();
620  }
621
622  /**
623   * Returns a new IdType containing this IdType's values but with no server
624   * base URL if one is present in this IdType. For example, if this IdType
625   * contains the ID "http://foo/Patient/1", this method will return a new
626   * IdType containing ID "Patient/1".
627   */
628  @Override
629  public IdType toUnqualified() {
630    if (isLocal() || isUrn()) {
631      return new IdType(getValueAsString());
632    }
633    return new IdType(getResourceType(), getIdPart(), getVersionIdPart());
634  }
635
636  @Override
637  public IdType toUnqualifiedVersionless() {
638    if (isLocal() || isUrn()) {
639      return new IdType(getValueAsString());
640    }
641    return new IdType(getResourceType(), getIdPart());
642  }
643
644  @Override
645  public IdType toVersionless() {
646    if (isLocal() || isUrn()) {
647      return new IdType(getValueAsString());
648    }
649    return new IdType(getBaseUrl(), getResourceType(), getIdPart(), null);
650  }
651
652  @Override
653  public IdType withResourceType(String theResourceName) {
654    if (isLocal() || isUrn()) {
655      return new IdType(getValueAsString());
656    }
657    return new IdType(theResourceName, getIdPart(), getVersionIdPart());
658  }
659
660  /**
661   * Returns a view of this ID as a fully qualified URL, given a server base and
662   * resource name (which will only be used if the ID does not already contain
663   * those respective parts). Essentially, because IdType can contain either a
664   * complete URL or a partial one (or even jut a simple ID), this method may be
665   * used to translate into a complete URL.
666   *
667   * @param theServerBase
668   *          The server base (e.g. "http://example.com/fhir")
669   * @param theResourceType
670   *          The resource name (e.g. "Patient")
671   * @return A fully qualified URL for this ID (e.g.
672   *         "http://example.com/fhir/Patient/1")
673   */
674  @Override
675  public IdType withServerBase(String theServerBase, String theResourceType) {
676    if (isLocal() || isUrn()) {
677      return new IdType(getValueAsString());
678    }
679    return new IdType(theServerBase, theResourceType, getIdPart(), getVersionIdPart());
680  }
681
682  /**
683   * Creates a new instance of this ID which is identical, but refers to the
684   * specific version of this resource ID noted by theVersion.
685   *
686   * @param theVersion The actual version string, e.g. "1". If theVersion is blank or null, returns the same as {@link #toVersionless()}}
687   * @return A new instance of IdType which is identical, but refers to the
688   *         specific version of this resource ID noted by theVersion.
689   */
690  @Override
691  public IdType withVersion(String theVersion) {
692    if (isBlank(theVersion)) {
693      return toVersionless();
694    }
695
696    if (isLocal() || isUrn()) {
697      return new IdType(getValueAsString());
698    }
699
700    String existingValue = getValue();
701
702    int i = existingValue.indexOf("_history");
703    String value;
704    if (i > 1) {
705      value = existingValue.substring(0, i - 1);
706    } else {
707      value = existingValue;
708    }
709
710    return new IdType(value + '/' + "_history" + '/' + theVersion);
711  }
712
713  private static boolean isUrlAbsolute(String theValue) {
714    String value = theValue.toLowerCase();
715    return value.startsWith("http://") || value.startsWith("https://");
716  }
717
718  private static boolean isValidLong(String id) {
719    if (StringUtils.isBlank(id)) {
720      return false;
721    }
722    for (int i = 0; i < id.length(); i++) {
723      if (Character.isDigit(id.charAt(i)) == false) {
724        return false;
725      }
726    }
727    return true;
728  }
729
730  /**
731   * Construct a new ID with with form "urn:uuid:[UUID]" where [UUID] is a new,
732   * randomly created UUID generated by {@link UUID#randomUUID()}
733   */
734  public static IdType newRandomUuid() {
735    return new IdType("urn:uuid:" + UUID.randomUUID().toString());
736  }
737
738  /**
739   * Retrieves the ID from the given resource instance
740   */
741  public static IdType of(IBaseResource theResouce) {
742    if (theResouce == null) {
743      throw new NullPointerException("theResource can not be null");
744    } else {
745      IIdType retVal = theResouce.getIdElement();
746      if (retVal == null) {
747        return null;
748      } else if (retVal instanceof IdType) {
749        return (IdType) retVal;
750      } else {
751        return new IdType(retVal.getValue());
752      }
753    }
754  }
755
756  private static String toPlainStringWithNpeThrowIfNeeded(BigDecimal theIdPart) {
757    if (theIdPart == null) {
758      throw new NullPointerException("BigDecimal ID can not be null");
759    }
760    return theIdPart.toPlainString();
761  }
762
763  private static String toPlainStringWithNpeThrowIfNeeded(Long theIdPart) {
764    if (theIdPart == null) {
765      throw new NullPointerException("Long ID can not be null");
766    }
767    return theIdPart.toString();
768  }
769
770  public String fhirType() {
771    return "id";
772  }
773
774  @Override
775  public IIdType setParts(String theBaseUrl, String theResourceType, String theIdPart, String theVersionIdPart) {
776    if (isNotBlank(theVersionIdPart)) {
777      Validate.notBlank(theResourceType, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated");
778      Validate.notBlank(theIdPart, "If theVersionIdPart is populated, theResourceType and theIdPart must be populated");
779    }
780    if (isNotBlank(theBaseUrl) && isNotBlank(theIdPart)) {
781      Validate.notBlank(theResourceType, "If theBaseUrl is populated and theIdPart is populated, theResourceType must be populated");
782    }
783
784    setValue(null);
785
786    myBaseUrl = theBaseUrl;
787    myResourceType = theResourceType;
788    myUnqualifiedId = theIdPart;
789    myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionIdPart, null);
790    myHaveComponentParts = true;
791
792    return this;
793  }
794
795}