diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java index bfc74a10193..79b204fc342 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java @@ -51,16 +51,20 @@ import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.validation.FhirValidator; /** - * The FHIR context is the central starting point for the use of the HAPI FHIR API. It should be created once, and then used as a factory for various other types of objects (parsers, clients, etc.). + * The FHIR context is the central starting point for the use of the HAPI FHIR API. It should be created once, and then + * used as a factory for various other types of objects (parsers, clients, etc.). * *
* Important usage notes: *
- * Thread safety: Parsers are not guaranteed to be thread safe. Create a new parser instance for every thread or every message being parsed/encoded. + * Thread safety: Parsers are not guaranteed to be thread safe. Create a new parser instance for every thread + * or every message being parsed/encoded. *
*- * Performance Note: This method is cheap to call, and may be called once for every message being processed without incurring any performance penalty + * Performance Note: This method is cheap to call, and may be called once for every message being processed + * without incurring any performance penalty *
*/ public IParser newJsonParser() { @@ -232,12 +258,16 @@ public class FhirContext { } /** - * Instantiates a new client instance. This method requires an interface which is defined specifically for your use cases to contain methods for each of the RESTful operations you wish to - * implement (e.g. "read ImagingStudy", "search Patient by identifier", etc.). This interface must extend {@link IRestfulClient} (or commonly its sub-interface {@link IBasicClient}). See the RESTful Client documentation for more information on how to define this interface. + * Instantiates a new client instance. This method requires an interface which is defined specifically for your use + * cases to contain methods for each of the RESTful operations you wish to implement (e.g. "read ImagingStudy", + * "search Patient by identifier", etc.). This interface must extend {@link IRestfulClient} (or commonly its + * sub-interface {@link IBasicClient}). See the RESTful Client documentation for more + * information on how to define this interface. * *- * Performance Note: This method is cheap to call, and may be called once for every operation invocation without incurring any performance penalty + * Performance Note: This method is cheap to call, and may be called once for every operation invocation + * without incurring any performance penalty *
* * @param theClientType @@ -253,11 +283,13 @@ public class FhirContext { } /** - * Instantiates a new generic client. A generic client is able to perform any of the FHIR RESTful operations against a compliant server, but does not have methods defining the specific - * functionality required (as is the case with {@link #newRestfulClient(Class, String) non-generic clients}). + * Instantiates a new generic client. A generic client is able to perform any of the FHIR RESTful operations against + * a compliant server, but does not have methods defining the specific functionality required (as is the case with + * {@link #newRestfulClient(Class, String) non-generic clients}). * *- * Performance Note: This method is cheap to call, and may be called once for every operation invocation without incurring any performance penalty + * Performance Note: This method is cheap to call, and may be called once for every operation invocation + * without incurring any performance penalty *
* * @param theServerBase @@ -284,10 +316,12 @@ public class FhirContext { * Create and return a new XML parser. * *- * Thread safety: Parsers are not guaranteed to be thread safe. Create a new parser instance for every thread or every message being parsed/encoded. + * Thread safety: Parsers are not guaranteed to be thread safe. Create a new parser instance for every thread + * or every message being parsed/encoded. *
*- * Performance Note: This method is cheap to call, and may be called once for every message being processed without incurring any performance penalty + * Performance Note: This method is cheap to call, and may be called once for every message being processed + * without incurring any performance penalty *
*/ public IParser newXmlParser() { @@ -329,7 +363,8 @@ public class FhirContext { } /** - * This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with caution + * This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with + * caution */ public void setLocalizer(HapiLocalizer theMessages) { myLocalizer = theMessages; @@ -357,4 +392,18 @@ public class FhirContext { return retVal; } + /** + * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU1} + */ + public static FhirContext forDstu1() { + return new FhirContext(FhirVersionEnum.DSTU1); + } + + /** + * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DEV} + */ + public static FhirContext forDev() { + return new FhirContext(FhirVersionEnum.DEV); + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java index a34d7e71c53..0e444940573 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirVersionEnum.java @@ -25,7 +25,15 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; public enum FhirVersionEnum { - DSTU1("ca.uhn.fhir.model.dstu.FhirDstu1"); + /* + * *********************** + * Don't sort this type!!! + * *********************** + */ + + DSTU1("ca.uhn.fhir.model.dstu.FhirDstu1"), + + DEV("ca.uhn.fhir.model.dev.FhirDev"); private final String myVersionClass; private volatile Boolean myPresentOnClasspath; @@ -35,6 +43,10 @@ public enum FhirVersionEnum { myVersionClass = theVersionClass; } + public boolean isNewerThan(FhirVersionEnum theVersion) { + return ordinal() > theVersion.ordinal(); + } + /** * Returns true if the given version is present on the classpath */ diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java index 544d266b147..5e8e167cf37 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java @@ -20,7 +20,7 @@ package ca.uhn.fhir.context; * #L% */ -import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.*; import java.io.IOException; import java.io.InputStream; @@ -118,8 +118,14 @@ class ModelScanner { } ModelScanner(FhirContext theContext, Map
- * Important usage notes: This method ignores base URLs (so passing in an ID of http://foo/Patient/123
will return a resource if it has the logical ID of
- * http://bar/Patient/123
. Also, this method is intended to be used for bundles which have already been populated. It will cache its results for fast performance, but that means that
- * modifications to the bundle after this method is called may not be accurately reflected.
+ * Important usage notes: This method ignores base URLs (so passing in an ID of
+ * http://foo/Patient/123
will return a resource if it has the logical ID of
+ * http://bar/Patient/123
. Also, this method is intended to be used for bundles which have already been
+ * populated. It will cache its results for fast performance, but that means that modifications to the bundle after
+ * this method is called may not be accurately reflected.
*
+ * Values for this key are of type {@link String} + *
+ */ + public static final ResourceMetadataKeyEnumrel="search"
). Server implementations may populate this with a
* complete URL, in which case the URL will be placed as-is in the bundle. They may alternately specify a resource relative URL (e.g. "Patient?name=tester") in which case the server will convert
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/resource/ResourceMetadataMap.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/resource/ResourceMetadataMap.java
new file mode 100644
index 00000000000..e2c20397144
--- /dev/null
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/resource/ResourceMetadataMap.java
@@ -0,0 +1,11 @@
+package ca.uhn.fhir.model.base.resource;
+
+import java.util.HashMap;
+
+import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
+
+public class ResourceMetadataMap extends HashMap+ * Description: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length + * limit of 36 characters. + *
+ *+ * regex: [a-z0-9\-\.]{1,36} + *
+ */ +@DatatypeDef(name = "id") +public class IdDt implements IPrimitiveDatatype+ * Description: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length + * limit of 36 characters. + *
+ *+ * regex: [a-z0-9\-\.]{1,36} + *
+ */ + @SimpleSetter + public IdDt(@SimpleSetter.Parameter(name = "theId") String theValue) { + setValue(theValue); + } + + /** + * Constructor + * + * @param theResourceType + * The resource type (e.g. "Patient") + * @param theId + * The ID (e.g. "123") + */ + public IdDt(String theResourceType, BigDecimal theIdPart) { + this(theResourceType, toPlainStringWithNpeThrowIfNeeded(theIdPart)); + } + + /** + * Constructor + * + * @param theResourceType + * The resource type (e.g. "Patient") + * @param theId + * The ID (e.g. "123") + */ + public IdDt(String theResourceType, String theId) { + this(theResourceType, theId, null); + } + + /** + * Constructor + * + * @param theResourceType + * The resource type (e.g. "Patient") + * @param theId + * The ID (e.g. "123") + * @param theVersionId + * The version ID ("e.g. "456") + */ + public IdDt(String theResourceType, String theId, String theVersionId) { + this(null,theResourceType,theId,theVersionId); + } + + /** + * Constructor + * + * @param theBaseUrl + * The server base URL (e.g. "http://example.com/fhir") + * @param theResourceType + * The resource type (e.g. "Patient") + * @param theId + * The ID (e.g. "123") + * @param theVersionId + * The version ID ("e.g. "456") + */ + public IdDt(String theBaseUrl, String theResourceType, String theId, String theVersionId) { + myBaseUrl = theBaseUrl; + myResourceType = theResourceType; + myUnqualifiedId = theId; + myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionId, null); + myHaveComponentParts = true; + } + + /** + * Creates an ID based on a given URL + */ + public IdDt(UriDt theUrl) { + setValue(theUrl.getValueAsString()); + } + + /** + * @deprecated Use {@link #getIdPartAsBigDecimal()} instead (this method was deprocated because its name is ambiguous) + */ + public BigDecimal asBigDecimal() { + return getIdPartAsBigDecimal(); + } + + /** + * Returns true if this IdDt matches the given IdDt in terms of resource type and ID, but ignores the URL base + */ + @SuppressWarnings("deprecation") + public boolean equalsIgnoreBase(IdDt theId) { + if (theId == null) { + return false; + } + if (theId.isEmpty()) { + return isEmpty(); + } + return ObjectUtils.equals(getResourceType(), theId.getResourceType()) && ObjectUtils.equals(getIdPart(), theId.getIdPart()) && ObjectUtils.equals(getVersionIdPart(), theId.getVersionIdPart()); + } + + + + @Override + public boolean equals(Object theArg0) { + if (!(theArg0 instanceof IdDt)) { + return false; + } + IdDt id = (IdDt)theArg0; + return StringUtils.equals(getValueAsString(), id.getValueAsString()); + } + + @Override + public int hashCode() { + HashCodeBuilder b = new HashCodeBuilder(); + b.append(getValueAsString()); + return b.toHashCode(); + } + + /** + * Returns the portion of this resource ID which corresponds to the server base URL. For example given the resource IDhttp://example.com/fhir/Patient/123
the base URL would be
+ * http://example.com/fhir
.
+ * + * This method may return null if the ID contains no base (e.g. "Patient/123") + *
+ */ + public String getBaseUrl() { + return myBaseUrl; + } + + public String getIdPart() { + return myUnqualifiedId; + } + + /** + * Returns the unqualified portion of this ID as a big decimal, ornull
if the value is null
+ *
+ * @throws NumberFormatException
+ * If the value is not a valid BigDecimal
+ */
+ public BigDecimal getIdPartAsBigDecimal() {
+ String val = getIdPart();
+ if (isBlank(val)) {
+ return null;
+ }
+ return new BigDecimal(val);
+ }
+
+ /**
+ * Returns the unqualified portion of this ID as a {@link Long}, or null
if the value is null
+ *
+ * @throws NumberFormatException
+ * If the value is not a valid Long
+ */
+ public Long getIdPartAsLong() {
+ String val = getIdPart();
+ if (isBlank(val)) {
+ return null;
+ }
+ return Long.parseLong(val);
+ }
+
+ public String getResourceType() {
+ return myResourceType;
+ }
+
+ /**
+ * Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or a simple ID. Use {@link #getIdPart()} to get just the ID portion.
+ *
+ * @see #getIdPart()
+ */
+ @Override
+ public String getValue() {
+ if (myValue == null && myHaveComponentParts) {
+ StringBuilder b = new StringBuilder();
+ if (isNotBlank(myBaseUrl)) {
+ b.append(myBaseUrl);
+ if (myBaseUrl.charAt(myBaseUrl.length()-1)!='/') {
+ b.append('/');
+ }
+ }
+
+ if (isNotBlank(myResourceType)) {
+ b.append(myResourceType);
+ }
+
+ if (b.length() > 0) {
+ b.append('/');
+ }
+
+ b.append(myUnqualifiedId);
+ if (isNotBlank(myUnqualifiedVersionId)) {
+ b.append('/');
+ b.append(Constants.PARAM_HISTORY);
+ b.append('/');
+ b.append(myUnqualifiedVersionId);
+ }
+ myValue = b.toString();
+ }
+ return myValue;
+ }
+
+ @Override
+ public String getValueAsString() {
+ return getValue();
+ }
+
+ public String getVersionIdPart() {
+ return myUnqualifiedVersionId;
+ }
+
+ public Long getVersionIdPartAsLong() {
+ if (!hasVersionIdPart()) {
+ return null;
+ } else {
+ return Long.parseLong(getVersionIdPart());
+ }
+ }
+
+ /**
+ * Returns true if this ID has a base url
+ *
+ * @see #getBaseUrl()
+ */
+ public boolean hasBaseUrl() {
+ return isNotBlank(myBaseUrl);
+ }
+
+ public boolean hasIdPart() {
+ return isNotBlank(getIdPart());
+ }
+
+ public boolean hasResourceType() {
+ return isNotBlank(myResourceType);
+ }
+
+ public boolean hasVersionIdPart() {
+ return isNotBlank(getVersionIdPart());
+ }
+
+ /**
+ * Returns true
if this ID contains an absolute URL (in other words, a URL starting with "http://" or "https://"
+ */
+ public boolean isAbsolute() {
+ if (StringUtils.isBlank(getValue())) {
+ return false;
+ }
+ return UrlUtil.isAbsolute(getValue());
+ }
+
+ /**
+ * Returns true
if the unqualified ID is a valid {@link Long} value (in other words, it consists only of digits)
+ */
+ public boolean isIdPartValidLong() {
+ String id = getIdPart();
+ if (StringUtils.isBlank(id)) {
+ return false;
+ }
+ for (int i = 0; i < id.length(); i++) {
+ if (Character.isDigit(id.charAt(i)) == false) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns true
if the ID is a local reference (in other words, it begins with the '#' character)
+ */
+ public boolean isLocal() {
+ return myUnqualifiedId != null && myUnqualifiedId.isEmpty() == false && myUnqualifiedId.charAt(0) == '#';
+ }
+
+ /**
+ * Copies the value from the given IdDt to this
IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API.
+ */
+ public void setId(IdDt theId) {
+ setValue(theId.getValue());
+ }
+
+ /**
+ * Set the value
+ *
+ * + * Description: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length + * limit of 36 characters. + *
+ *+ * regex: [a-z0-9\-\.]{1,36} + *
+ * @return + */ + @Override + public IdDt setValue(String theValue) throws DataFormatException { + // TODO: add validation + myValue = theValue; + myHaveComponentParts = false; + if (StringUtils.isBlank(theValue)) { + myValue = null; + myUnqualifiedId = null; + myUnqualifiedVersionId = null; + myResourceType = null; + } else { + int vidIndex = theValue.indexOf("/_history/"); + int idIndex; + if (vidIndex != -1) { + myUnqualifiedVersionId = theValue.substring(vidIndex + "/_history/".length()); + idIndex = theValue.lastIndexOf('/', vidIndex - 1); + myUnqualifiedId = theValue.substring(idIndex + 1, vidIndex); + } else { + idIndex = theValue.lastIndexOf('/'); + myUnqualifiedId = theValue.substring(idIndex + 1); + myUnqualifiedVersionId = null; + } + + myBaseUrl = null; + if (idIndex <= 0) { + myResourceType = null; + } else { + int typeIndex = theValue.lastIndexOf('/', idIndex - 1); + if (typeIndex == -1) { + myResourceType = theValue.substring(0, idIndex); + } else { + myResourceType = theValue.substring(typeIndex + 1, idIndex); + + if (typeIndex > 4) { + myBaseUrl = theValue.substring(0, typeIndex); + } + + } + } + + } + return this; + } + + /** + * Set the value + * + *+ * Description: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length + * limit of 36 characters. + *
+ *+ * regex: [a-z0-9\-\.]{1,36} + *
+ */ + @Override + public void setValueAsString(String theValue) throws DataFormatException { + setValue(theValue); + } + + @Override + public String toString() { + return getValue(); + } + + public IdDt toUnqualified() { + return new IdDt(getResourceType(), getIdPart(), getVersionIdPart()); + } + + public IdDt toUnqualifiedVersionless() { + return new IdDt(getResourceType(), getIdPart()); + } + + public IdDt toVersionless() { + String value = getValue(); + int i = value.indexOf(Constants.PARAM_HISTORY); + if (i > 1) { + return new IdDt(value.substring(0, i - 1)); + } else { + return this; + } + } + + public IdDt withResourceType(String theResourceName) { + return new IdDt(theResourceName, getIdPart(), getVersionIdPart()); + } + + /** + * Returns a view of this ID as a fully qualified URL, given a server base and resource name (which will only be used if the ID does not already contain those respective parts). Essentially, + * because IdDt can contain either a complete URL or a partial one (or even jut a simple ID), this method may be used to translate into a complete URL. + * + * @param theServerBase + * The server base (e.g. "http://example.com/fhir") + * @param theResourceType + * The resource name (e.g. "Patient") + * @return A fully qualified URL for this ID (e.g. "http://example.com/fhir/Patient/1") + */ + public String withServerBase(String theServerBase, String theResourceType) { + if (getValue().startsWith("http")) { + return getValue(); + } + StringBuilder retVal = new StringBuilder(); + retVal.append(theServerBase); + if (retVal.charAt(retVal.length() - 1) != '/') { + retVal.append('/'); + } + if (isNotBlank(getResourceType())) { + retVal.append(getResourceType()); + } else { + retVal.append(theResourceType); + } + retVal.append('/'); + retVal.append(getIdPart()); + + if (hasVersionIdPart()) { + retVal.append('/'); + retVal.append(Constants.PARAM_HISTORY); + retVal.append('/'); + retVal.append(getVersionIdPart()); + } + + return retVal.toString(); + } + + /** + * Creates a new instance of this ID which is identical, but refers to the specific version of this resource ID noted by theVersion. + * + * @param theVersion + * The actual version string, e.g. "1" + * @return A new instance of IdDt which is identical, but refers to the specific version of this resource ID noted by theVersion. + */ + public IdDt withVersion(String theVersion) { + Validate.notBlank(theVersion, "Version may not be null or empty"); + + String existingValue = getValue(); + + int i = existingValue.indexOf(Constants.PARAM_HISTORY); + String value; + if (i > 1) { + value = existingValue.substring(0, i - 1); + } else { + value = existingValue; + } + + return new IdDt(value + '/' + Constants.PARAM_HISTORY + '/' + theVersion); + } + + private static String toPlainStringWithNpeThrowIfNeeded(BigDecimal theIdPart) { + if (theIdPart == null) { + throw new NullPointerException("BigDecimal ID can not be null"); + } + return theIdPart.toPlainString(); + } + + @Override + public boolean isEmpty() { + return isBlank(getValue()); + } + + public void applyTo(IBaseResource theResouce) { + if (theResouce == null) { + throw new NullPointerException("theResource can not be null"); + } else if (theResouce instanceof IResource) { + ((IResource) theResouce).setId(new IdDt(getValue())); + } else if (theResouce instanceof Resource) { + ((Resource) theResouce).setId(getIdPart()); + } else { + throw new IllegalArgumentException("Unknown resource class type, does not implement IResource or extend Resource"); + } + } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/valueset/BundleEntryStatusEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/valueset/BundleEntryStatusEnum.java new file mode 100644 index 00000000000..bb162f926a5 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/valueset/BundleEntryStatusEnum.java @@ -0,0 +1,128 @@ + +package ca.uhn.fhir.model.valueset; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import java.util.HashMap; +import java.util.Map; + +import ca.uhn.fhir.model.api.IValueSetEnumBinder; + +public enum BundleEntryStatusEnum { + + CREATE("create", "http://hl7.org/fhir/bundle-entry-status"), + UPDATE("update", "http://hl7.org/fhir/bundle-entry-status"), + MATCH("match", "http://hl7.org/fhir/bundle-entry-status"), + INCLUDE("include", "http://hl7.org/fhir/bundle-entry-status"), + + ; + + /** + * Identifier for this Value Set: + * http://hl7.org/fhir/vs/address-use + */ + public static final String VALUESET_IDENTIFIER = "http://hl7.org/fhir/bundle-entry-status"; + + /** + * Name for this Value Set: + * AddressUse + */ + public static final String VALUESET_NAME = "BundleEntryStatus"; + + private static MapPenicillin VK 5ml suspension to be administered by oral route
+ONE 5ml spoonful to be taken THREE times a day
+100ml bottle
+to patient ref: a23
+by doctor X
+
+
+
+ Penicillin VK 5ml suspension to be administered by oral route
\nONE 5ml spoonful to be taken THREE times a day
\n100ml bottle
\nto patient ref: a23
\nby doctor X
\nMap<ResourceMetadataKeyEnum<?>, Object>
+ to returning a new type called
+ ResourceMetadataMap
. This new type implements
+ Map<ResourceMetadataKeyEnum<?>, Object>
+ itself, so this change should not break existing code, but may
+ require a clean build in order to run correctly.
+ ]]>
+