From 0f012603012c47d66f8a1378cd263e0db82a93f5 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 20 Feb 2015 17:34:13 -0500 Subject: [PATCH] Get extensions working for HL7.org structures --- .../main/java/example/ExampleProviders.java | 20 ++ .../java/example/PagingPatientProvider.java | 28 +- hapi-fhir-base/pom.xml | 63 ---- .../BaseRuntimeChildDatatypeDefinition.java | 4 +- ...BaseRuntimeElementCompositeDefinition.java | 3 - .../context/BaseRuntimeElementDefinition.java | 5 +- .../ca/uhn/fhir/context/ModelScanner.java | 8 +- ...imeChildEnumerationDatatypeDefinition.java | 58 ++++ ...ntimeChildPrimitiveDatatypeDefinition.java | 2 + .../ca/uhn/fhir/model/api/BaseElement.java | 3 +- .../ca/uhn/fhir/model/api/ExtensionDt.java | 42 ++- .../java/ca/uhn/fhir/model/api/IDatatype.java | 4 +- .../api/ISupportsUndeclaredExtensions.java | 6 +- .../java/ca/uhn/fhir/model/api/TagList.java | 4 +- .../uhn/fhir/model/api/annotation/Child.java | 27 ++ .../composite/BaseResourceReferenceDt.java | 6 +- .../java/ca/uhn/fhir/parser/JsonParser.java | 274 +++++++----------- .../java/ca/uhn/fhir/parser/ParserState.java | 55 +++- .../java/ca/uhn/fhir/parser/XmlParser.java | 125 ++++---- .../fhir/rest/method/BaseMethodBinding.java | 19 +- .../instance/model/api/IBaseEnumFactory.java | 25 ++ .../instance/model/api/IBaseExtension.java | 4 + .../model/api/IBaseHasExtensions.java | 11 + .../model/api/IBaseHasModifierExtensions.java | 11 + .../parser/MultiVersionJsonParserTest.java | 2 +- .../uhn/fhir/testmindeps/ValidatorTest.java | 35 +-- .../ca/uhn/fhir/jpa/demo/JpaServerDemo.java | 3 +- .../java/ca/uhn/fhir/model/dev/FhirDev.java | 4 +- .../ca/uhn/fhir/model/dstu/FhirDstu1.java | 3 - .../ca/uhn/fhir/parser/JsonParserTest.java | 138 ++++++++- .../ca/uhn/fhir/parser/XmlParserTest.java | 94 +++++- .../ca/uhn/fhir/model/dstu2/FhirDstu2.java | 2 + .../ca/uhn/fhir/parser/JsonParserTest.java | 216 +++++++------- .../ca/uhn/fhir/parser/XmlParserTest.java | 108 ++++++- .../ca/uhn/fhir/rest/server/IncludeTest.java | 17 -- .../validation/ResourceValidatorTest.java | 2 +- .../hl7/fhir/instance/FhirDstu2Hl7Org.java | 3 + .../fhir/instance/model/BackboneElement.java | 4 +- .../fhir/instance/model/DomainResource.java | 4 +- .../org/hl7/fhir/instance/model/Element.java | 3 +- .../hl7/fhir/instance/model/EnumFactory.java | 4 +- .../hl7/fhir/instance/model/Enumeration.java | 11 + .../hl7/fhir/instance/model/Extension.java | 9 +- .../hl7/fhir/instance/model/Identifier.java | 2 +- .../fhir/instance/model/PrimitiveType.java | 3 +- .../instance/model/annotations/Child.java | 26 ++ .../uhn/fhir/model/ModelInheritanceTest.java | 35 ++- .../ca/uhn/fhir/parser/JsonParserTest.java | 130 ++++++++- .../ca/uhn/fhir/parser/XmlParserTest.java | 78 +++-- .../java/ca/uhn/fhir/model/dev/FhirDev.java | 7 + .../ca/uhn/fhir/model/dstu/FhirDstu1.java | 6 + .../ca/uhn/fhir/model/dstu2/FhirDstu2.java | 6 + src/site/xdoc/doc_rest_server.xml | 23 ++ 53 files changed, 1254 insertions(+), 531 deletions(-) create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildEnumerationDatatypeDefinition.java create mode 100644 hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseEnumFactory.java create mode 100644 hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseHasExtensions.java create mode 100644 hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseHasModifierExtensions.java diff --git a/examples/src/main/java/example/ExampleProviders.java b/examples/src/main/java/example/ExampleProviders.java index a55c5b30610..e3c17420884 100644 --- a/examples/src/main/java/example/ExampleProviders.java +++ b/examples/src/main/java/example/ExampleProviders.java @@ -8,6 +8,7 @@ import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; @@ -36,6 +37,7 @@ public class PlainProvider { //START SNIPPET: plainProviderServer public class ExampleServlet extends RestfulServer { + /** Constructor */ public ExampleServlet() { /* * Plain providers are passed to the server in the same way @@ -54,5 +56,23 @@ public class ExampleServlet extends RestfulServer { } //END SNIPPET: plainProviderServer +//START SNIPPET: addressStrategy +public class MyServlet extends RestfulServer { + + /** Constructor */ + public MyServlet() { + + String serverBaseUrl = "http://foo.com/fhir"; + setServerAddressStrategy(new HardcodedServerAddressStrategy(serverBaseUrl)); + + // ...add some resource providers, etc... + List resourceProviders = new ArrayList(); + setResourceProviders(resourceProviders); + } + + } +//END SNIPPET: addressStrategy + + } diff --git a/examples/src/main/java/example/PagingPatientProvider.java b/examples/src/main/java/example/PagingPatientProvider.java index d1cb53a1708..0e4de55f57a 100644 --- a/examples/src/main/java/example/PagingPatientProvider.java +++ b/examples/src/main/java/example/PagingPatientProvider.java @@ -12,6 +12,7 @@ import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.IResourceProvider; +@SuppressWarnings("null") //START SNIPPET: provider public class PagingPatientProvider implements IResourceProvider { @@ -21,11 +22,22 @@ public class PagingPatientProvider implements IResourceProvider { @Search public IBundleProvider search(@RequiredParam(name = Patient.SP_FAMILY) StringParam theFamily) { final InstantDt searchTime = InstantDt.withCurrentTime(); - final List matchingResourceIds = findIdsByFamily(theFamily); + /* + * First, we'll search the database for a set of database row IDs that + * match the given search criteria. That way we can keep just the + * row IDs around, and load the actual resources on demand later + * as the client pages through them. + */ + final List matchingResourceIds = null; // <-- implement this + + /* + * Return a bundle provider which can page through the IDs and + * return the resources that go with them. + */ return new IBundleProvider() { - @Override + @Override public int size() { return matchingResourceIds.size(); } @@ -33,7 +45,7 @@ public class PagingPatientProvider implements IResourceProvider { @Override public List getResources(int theFromIndex, int theToIndex) { int end = Math.max(theToIndex, matchingResourceIds.size() - 1); - List idsToReturn = matchingResourceIds.subList(theFromIndex, end); + List idsToReturn = matchingResourceIds.subList(theFromIndex, end); return loadResourcesByIds(idsToReturn); } @@ -44,18 +56,10 @@ public class PagingPatientProvider implements IResourceProvider { }; } - /** - * Get a list of resource IDs which match a given family name - */ - private List findIdsByFamily(StringParam theFamily) { - // .. implement this search against the database .. - return null; - } - /** * Load a list of patient resources given their IDs */ - private List loadResourcesByIds(List theFamily) { + private List loadResourcesByIds(List theIdsToReturn) { // .. implement this search against the database .. return null; } diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 45697b18602..b7664d67257 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -195,69 +195,6 @@ - - ANDROID - - - ca.uhn.hapi.fhir - hapi-fhir-structures-dstu - 0.9-SNAPSHOT - - - org.slf4j - slf4j-android - ${slf4j_version} - - - - - - org.apache.maven.plugins - maven-shade-plugin - 1.4 - - - package - - shade - - - false - true - - - - ca.uhn.hapi.fhir:hapi-fhir-structures-dstu - ca.uhn.hapi.fhir:hapi-fhir-structures-dstu - org.glassfish:javax.json - org.codehaus.woodstox:woodstox-core-asl - javax.xml.stream:stax-api - org.codehaus.woodstox:stax2-api - org.slf4j:slf4j* - org.apache.commons:* - org.apache.httpcomponents:* - - - - - javax.xml.stream - ca.uhn.fhir.repackage.javax.xml.stream - - - javax.json - ca.uhn.fhir.repackage.javax.json - - - - - - - - - - diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDatatypeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDatatypeDefinition.java index c0f780870e6..7328cff0b9c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDatatypeDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDatatypeDefinition.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.context; */ import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.Collections; import java.util.Map; import java.util.Set; @@ -28,7 +29,6 @@ import java.util.Set; import org.hl7.fhir.instance.model.IBase; import ca.uhn.fhir.model.api.ICodeEnum; -import ca.uhn.fhir.model.api.IDatatype; import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Description; @@ -41,7 +41,7 @@ public abstract class BaseRuntimeChildDatatypeDefinition extends BaseRuntimeDecl public BaseRuntimeChildDatatypeDefinition(Field theField, String theElementName, Child theChildAnnotation, Description theDescriptionAnnotation, Class theDatatype) { super(theField, theChildAnnotation, theDescriptionAnnotation, theElementName); - assert theDatatype != IDatatype.class; // should use RuntimeChildAny + assert Modifier.isInterface(theDatatype.getModifiers()) == false : "Type of " + theDatatype + " shouldn't be here"; // should use RuntimeChildAny myDatatype = theDatatype; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java index 68ed38095bb..806f9296be8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java @@ -45,9 +45,6 @@ public abstract class BaseRuntimeElementCompositeDefinition ext if (theNext == null) { throw new NullPointerException(); } -// if (theNext.getValidChildNames().contains("performetPractitioner")) { -// throw new NullPointerException(); -// } if (theNext.getExtensionUrl() != null) { throw new IllegalArgumentException("Shouldn't haven an extension URL, use addExtension instead"); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java index d1735113aea..df594b2bd84 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java @@ -29,6 +29,7 @@ import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.IBase; +import org.hl7.fhir.instance.model.api.IBaseEnumFactory; import ca.uhn.fhir.model.api.IValueSetEnumBinder; @@ -102,8 +103,10 @@ public abstract class BaseRuntimeElementDefinition { try { if (theArgument == null) { return getImplementingClass().newInstance(); - } else { + } else if (theArgument instanceof IValueSetEnumBinder) { return getImplementingClass().getConstructor(IValueSetEnumBinder.class).newInstance(theArgument); + }else { + return getImplementingClass().getConstructor(IBaseEnumFactory.class).newInstance(theArgument); } } catch (InstantiationException e) { throw new ConfigurationException("Failed to instantiate type:" + getImplementingClass().getName(), e); 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 893fc0d7481..4dc9edef6d9 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 @@ -55,6 +55,7 @@ import org.hl7.fhir.instance.model.IPrimitiveType; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBackboneElement; import org.hl7.fhir.instance.model.api.IBaseDatatype; +import org.hl7.fhir.instance.model.api.IBaseEnumFactory; import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IDatatypeElement; import org.hl7.fhir.instance.model.api.IDomainResource; @@ -555,7 +556,7 @@ class ModelScanner { RuntimeChildResourceBlockDefinition def = new RuntimeChildResourceBlockDefinition(next, childAnnotation, descriptionAnnotation, elementName, blockDef); orderMap.put(order, def); - } else if (IDatatype.class.equals(nextElementType) || IElement.class.equals(nextElementType) || "org.hl7.fhir.instance.model.Type".equals(nextElementType.getName())) { + } else if (IDatatype.class.equals(nextElementType) || IElement.class.equals(nextElementType) || "org.hl7.fhir.instance.model.Type".equals(nextElementType.getName()) || IBaseDatatype.class.equals(nextElementType)) { RuntimeChildAny def = new RuntimeChildAny(next, elementName, childAnnotation, descriptionAnnotation); orderMap.put(order, def); @@ -565,10 +566,13 @@ class ModelScanner { addScanAlso(nextDatatype); BaseRuntimeChildDatatypeDefinition def; - if (IPrimitiveDatatype.class.isAssignableFrom(nextElementType)) { + if (IPrimitiveType.class.isAssignableFrom(nextElementType)) { if (nextElementType.equals(BoundCodeDt.class)) { IValueSetEnumBinder> binder = getBoundCodeBinder(next); def = new RuntimeChildPrimitiveBoundCodeDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder); + } else if (childAnnotation.enumFactory().getSimpleName().equals("NoEnumFactory") == false) { + Class> enumFactory = childAnnotation.enumFactory(); + def = new RuntimeChildEnumerationDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, enumFactory); } else { def = new RuntimeChildPrimitiveDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildEnumerationDatatypeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildEnumerationDatatypeDefinition.java new file mode 100644 index 00000000000..dc659f3b826 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildEnumerationDatatypeDefinition.java @@ -0,0 +1,58 @@ +package ca.uhn.fhir.context; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2015 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.lang.reflect.Field; + +import org.hl7.fhir.instance.model.IBase; +import org.hl7.fhir.instance.model.api.IBaseEnumFactory; + +import ca.uhn.fhir.model.api.annotation.Child; +import ca.uhn.fhir.model.api.annotation.Description; + +public class RuntimeChildEnumerationDatatypeDefinition extends RuntimeChildPrimitiveDatatypeDefinition { + + private Class> myBinderType; + private volatile IBaseEnumFactory myBinder; + + public RuntimeChildEnumerationDatatypeDefinition(Field theField, String theElementName, Child theChildAnnotation, Description theDescriptionAnnotation, Class theDatatype, Class> theBinderType) { + super(theField, theElementName, theDescriptionAnnotation, theChildAnnotation, theDatatype); + + myBinderType = theBinderType; + } + + @Override + public IBaseEnumFactory getInstanceConstructorArguments() { + IBaseEnumFactory retVal = myBinder; + if (retVal == null) { + try { + retVal = myBinderType.newInstance(); + } catch (InstantiationException e) { + throw new IllegalStateException("Failed to instantiate " + myBinderType, e); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Failed to instantiate " + myBinderType, e); + } + myBinder = retVal; + } + return retVal; + } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildPrimitiveDatatypeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildPrimitiveDatatypeDefinition.java index efdb23180ed..ef5a7ecdefe 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildPrimitiveDatatypeDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildPrimitiveDatatypeDefinition.java @@ -32,5 +32,7 @@ public class RuntimeChildPrimitiveDatatypeDefinition extends BaseRuntimeChildDat public RuntimeChildPrimitiveDatatypeDefinition(Field theField, String theElementName, Description theDescriptionAnnotation, Child theChildAnnotation, Class theDatatype) { super(theField, theElementName, theChildAnnotation, theDescriptionAnnotation, theDatatype); } + + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java index 266231cca0c..c86d26bf293 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.List; import org.apache.commons.lang3.Validate; +import org.hl7.fhir.instance.model.api.IBaseDatatype; public abstract class BaseElement implements IElement, ISupportsUndeclaredExtensions { @@ -32,7 +33,7 @@ public abstract class BaseElement implements IElement, ISupportsUndeclaredExtens private List myUndeclaredModifierExtensions; @Override - public ExtensionDt addUndeclaredExtension(boolean theIsModifier, String theUrl, IDatatype theValue) { + public ExtensionDt addUndeclaredExtension(boolean theIsModifier, String theUrl, IBaseDatatype theValue) { Validate.notEmpty(theUrl, "URL must be populated"); Validate.notNull(theValue, "Value must not be null"); ExtensionDt retVal = new ExtensionDt(theIsModifier, theUrl, theValue); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ExtensionDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ExtensionDt.java index 479bfbdef24..95a4374e695 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ExtensionDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ExtensionDt.java @@ -24,13 +24,15 @@ import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.Validate; +import org.hl7.fhir.instance.model.api.IBaseDatatype; +import org.hl7.fhir.instance.model.api.IBaseExtension; import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.DatatypeDef; import ca.uhn.fhir.model.primitive.StringDt; @DatatypeDef(name="Extension") -public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDatatype { +public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDatatype, IBaseExtension { private boolean myModifier; @@ -38,7 +40,7 @@ public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDa private StringDt myUrl; @Child(name="value", type=IDatatype.class, order=1, min=0, max=1) - private IElement myValue; + private IBaseDatatype myValue; public ExtensionDt() { } @@ -54,7 +56,7 @@ public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDa myUrl = new StringDt(theUrl); } - public ExtensionDt(boolean theIsModifier, String theUrl, IDatatype theValue) { + public ExtensionDt(boolean theIsModifier, String theUrl, IBaseDatatype theValue) { Validate.notEmpty(theUrl, "URL must be populated"); Validate.notNull(theValue, "Value must not be null"); @@ -63,15 +65,25 @@ public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDa myValue=theValue; } - public StringDt getUrl() { - if (myUrl==null) { - myUrl=new StringDt(); - } - return myUrl; + /** + * Returns the URL for this extension. + *

+ * Note that before HAPI 0.9 this method returned a {@link StringDt} but as of + * HAPI 0.9 this method returns a plain string. This was changed because it does not make sense to use a StringDt here + * since the URL itself can not contain extensions and it was therefore misleading. + *

+ */ + public String getUrl() { + return myUrl != null ? myUrl.getValue() : null; } + /** + * Retained for backward compatibility + * + * @see ExtensionDt#getUrl() + */ public String getUrlAsString() { - return getUrl().getValue(); + return getUrl(); } /** @@ -81,7 +93,7 @@ public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDa * {@link #getUndeclaredModifierExtensions()} to retrieve the child extensions. *

*/ - public IElement getValue() { + public IBaseDatatype getValue() { return myValue; } @@ -117,7 +129,7 @@ public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDa } public ExtensionDt setUrl(String theUrl) { - myUrl = new StringDt(theUrl); + myUrl = theUrl != null ? new StringDt(theUrl) : myUrl; return this; } @@ -126,8 +138,9 @@ public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDa return this; } - public void setValue(IElement theValue) { + public ExtensionDt setValue(IBaseDatatype theValue) { myValue = theValue; + return this; } @Override @@ -135,4 +148,9 @@ public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDa return new ArrayList(); } + @Override + public List getExtension() { + return getAllUndeclaredExtensions(); + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IDatatype.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IDatatype.java index 7b67dfdb40f..a42c6721fbb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IDatatype.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IDatatype.java @@ -1,5 +1,7 @@ package ca.uhn.fhir.model.api; +import org.hl7.fhir.instance.model.api.IBaseDatatype; + /* * #%L * HAPI FHIR - Core Library @@ -20,6 +22,6 @@ package ca.uhn.fhir.model.api; * #L% */ -public interface IDatatype extends IElement { +public interface IDatatype extends IElement, IBaseDatatype { } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ISupportsUndeclaredExtensions.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ISupportsUndeclaredExtensions.java index cc8c858055a..1c34760264b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ISupportsUndeclaredExtensions.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ISupportsUndeclaredExtensions.java @@ -22,6 +22,8 @@ package ca.uhn.fhir.model.api; import java.util.List; +import org.hl7.fhir.instance.model.api.IBaseDatatype; + public interface ISupportsUndeclaredExtensions extends IElement { /** @@ -52,7 +54,7 @@ public interface ISupportsUndeclaredExtensions extends IElement { *
  • {@link ExtensionDt#setUrl(String) URL}
  • *
  • And one of: *
      - *
    • {@link ExtensionDt#setValue(IElement) A datatype value}
    • + *
    • {@link ExtensionDt#setValue(IBaseDatatype) A datatype value}
    • *
    • {@link #addUndeclaredExtension(ExtensionDt) Further sub-extensions}
    • *
    * @@ -64,7 +66,7 @@ public interface ISupportsUndeclaredExtensions extends IElement { /** * Adds an extension to this object */ - ExtensionDt addUndeclaredExtension(boolean theIsModifier, String theUrl, IDatatype theValue); + ExtensionDt addUndeclaredExtension(boolean theIsModifier, String theUrl, IBaseDatatype theValue); /** * Adds an extension to this object. This method is intended for use when diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/TagList.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/TagList.java index 110e23a2c80..a0ae718023d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/TagList.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/TagList.java @@ -28,6 +28,8 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import org.hl7.fhir.instance.model.IBase; + /** * A collection of tags present on a single resource. TagList is backed by a {@link LinkedHashSet}, so the order of added tags will be consistent, but duplicates will not be preserved. * @@ -35,7 +37,7 @@ import java.util.Set; * Thread safety: This class is not thread safe *

    */ -public class TagList implements Set, Serializable { +public class TagList implements Set, Serializable, IBase { public static final String ATTR_CATEGORY = "category"; public static final String ELEMENT_NAME = "TagList"; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/annotation/Child.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/annotation/Child.java index 7bc22405846..3e80a0c0203 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/annotation/Child.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/annotation/Child.java @@ -24,6 +24,9 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.Enumeration; + +import org.hl7.fhir.instance.model.api.IBaseEnumFactory; import ca.uhn.fhir.model.api.IElement; @@ -97,4 +100,28 @@ public @interface Child { // */ // String replaces() default ""; + /** + * For children which accept an {@link Enumeration} as the type, this + * field indicates the type to use for the enum factory + */ + Class> enumFactory() default NoEnumFactory.class; + + public static class NoEnumFactory implements IBaseEnumFactory> { + + private NoEnumFactory() { + // non instantiable + } + + @Override + public Enum fromCode(String theCodeString) throws IllegalArgumentException { + return null; + } + + @Override + public String toCode(Enum theCode) { + return null; + } + + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseResourceReferenceDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseResourceReferenceDt.java index 6a92afa8f80..37186c7c505 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseResourceReferenceDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseResourceReferenceDt.java @@ -22,14 +22,12 @@ package ca.uhn.fhir.model.base.composite; import java.io.IOException; import java.io.Reader; -import java.util.List; - -import javax.json.JsonValue; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; +import org.hl7.fhir.instance.model.api.IBaseDatatype; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.BaseIdentifiableElement; @@ -40,7 +38,7 @@ import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.client.BaseClient; import ca.uhn.fhir.rest.client.api.IRestfulClient; -public abstract class BaseResourceReferenceDt extends BaseIdentifiableElement { +public abstract class BaseResourceReferenceDt extends BaseIdentifiableElement implements IBaseDatatype { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseResourceReferenceDt.class); private IResource myResource; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java index 7c11fae1054..6ebb02a9352 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java @@ -20,7 +20,9 @@ package ca.uhn.fhir.parser; * #L% */ -import static org.apache.commons.lang3.StringUtils.*; +import static org.apache.commons.lang3.StringUtils.defaultString; +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.io.IOException; import java.io.Reader; @@ -54,7 +56,11 @@ import org.hl7.fhir.instance.model.IBaseResource; import org.hl7.fhir.instance.model.IPrimitiveType; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseBooleanDatatype; +import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseDecimalDatatype; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions; import org.hl7.fhir.instance.model.api.IBaseIntegerDatatype; import ca.uhn.fhir.context.BaseRuntimeChildDefinition; @@ -85,7 +91,6 @@ import ca.uhn.fhir.model.base.composite.BaseContainedDt; import ca.uhn.fhir.model.base.composite.BaseNarrativeDt; import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt; import ca.uhn.fhir.model.base.resource.BaseBinary; -import ca.uhn.fhir.model.primitive.BooleanDt; import ca.uhn.fhir.model.primitive.DecimalDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IntegerDt; @@ -127,7 +132,7 @@ public class JsonParser extends BaseParser implements IParser { myContext = theContext; } - private void addToHeldExtensions(int valueIdx, List ext, ArrayList> list) { + private void addToHeldExtensions(int valueIdx, List> ext, ArrayList> list, boolean theIsModifier) { if (ext.size() > 0) { list.ensureCapacity(valueIdx); while (list.size() <= valueIdx) { @@ -136,8 +141,8 @@ public class JsonParser extends BaseParser implements IParser { if (list.get(valueIdx) == null) { list.set(valueIdx, new ArrayList()); } - for (ExtensionDt next : ext) { - list.get(valueIdx).add(new HeldExtension(next)); + for (IBaseExtension next : ext) { + list.get(valueIdx).add(new HeldExtension(next, theIsModifier)); } } } @@ -283,7 +288,7 @@ public class JsonParser extends BaseParser implements IParser { RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource); encodeResourceToJsonStreamWriter(resDef, resource, theEventWriter, "resource", false); } - + if (nextEntry.getSearchMode().isEmpty() == false || nextEntry.getScore().isEmpty() == false) { theEventWriter.writeStartObject("search"); writeOptionalTagWithTextNode(theEventWriter, "mode", nextEntry.getSearchMode().getValueAsString()); @@ -325,7 +330,6 @@ public class JsonParser extends BaseParser implements IParser { // // writeAuthor(nextEntry, theEventWriter); - if (nextEntry.getSummary().isEmpty() == false) { theEventWriter.write("summary", nextEntry.getSummary().getValueAsString()); } @@ -411,8 +415,7 @@ public class JsonParser extends BaseParser implements IParser { } case CONTAINED_RESOURCES: { /* - * Disabled per #103 - * ContainedDt value = (ContainedDt) theNextValue; for (IResource next : value.getContainedResources()) { if (getContainedResources().getResourceId(next) != null) { + * Disabled per #103 ContainedDt value = (ContainedDt) theNextValue; for (IResource next : value.getContainedResources()) { if (getContainedResources().getResourceId(next) != null) { * continue; } encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true, fixContainedResourceId(next.getId().getValue())); } */ List containedResources = getContainedResources().getContainedResources(); @@ -460,11 +463,15 @@ public class JsonParser extends BaseParser implements IParser { private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter, List theChildren, boolean theIsSubElementWithinResource) throws IOException { for (BaseRuntimeChildDefinition nextChild : theChildren) { + if (nextChild.getElementName().equals("extension") || nextChild.getElementName().equals("modifierExtension")) { + continue; + } + if (nextChild instanceof RuntimeChildNarrativeDefinition) { INarrativeGenerator gen = myContext.getNarrativeGenerator(); if (gen != null) { - BaseNarrativeDt narr = ((IResource)theResource).getText(); + BaseNarrativeDt narr = ((IResource) theResource).getText(); gen.generateNarrative(theResDef.getResourceProfile(), theResource, narr); if (narr != null) { RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild; @@ -540,12 +547,25 @@ public class JsonParser extends BaseParser implements IParser { encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theIsSubElementWithinResource); } - if (nextValue instanceof ISupportsUndeclaredExtensions && primitive) { - List ext = ((ISupportsUndeclaredExtensions) nextValue).getUndeclaredExtensions(); - addToHeldExtensions(valueIdx, ext, extensions); + if (primitive) { + if (nextValue instanceof ISupportsUndeclaredExtensions) { + List ext = ((ISupportsUndeclaredExtensions) nextValue).getUndeclaredExtensions(); + addToHeldExtensions(valueIdx, ext, extensions,false); - ext = ((ISupportsUndeclaredExtensions) nextValue).getUndeclaredModifierExtensions(); - addToHeldExtensions(valueIdx, ext, modifierExtensions); + ext = ((ISupportsUndeclaredExtensions) nextValue).getUndeclaredModifierExtensions(); + addToHeldExtensions(valueIdx, ext, modifierExtensions,true); + }else { + if (nextValue instanceof IBaseHasExtensions) { + IBaseHasExtensions element = (IBaseHasExtensions) nextValue; + List> ext = element.getExtension(); + addToHeldExtensions(valueIdx, ext, extensions, false); + } + if (nextValue instanceof IBaseHasModifierExtensions) { + IBaseHasModifierExtensions element = (IBaseHasModifierExtensions) nextValue; + List> ext = element.getModifierExtension(); + addToHeldExtensions(valueIdx, ext, extensions, true); + } + } } } @@ -663,7 +683,7 @@ public class JsonParser extends BaseParser implements IParser { } else { encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, resDef, theIsSubElementWithinResource); } - + theEventWriter.writeEnd(); } @@ -751,22 +771,44 @@ public class JsonParser extends BaseParser implements IParser { } } - private void extractUndeclaredExtensions(IBase theResource, List extensions, List modifierExtensions) { - if (theResource instanceof ISupportsUndeclaredExtensions) { - List ext = ((ISupportsUndeclaredExtensions) theResource).getUndeclaredExtensions(); + private void extractUndeclaredExtensions(IBase theElement, List extensions, List modifierExtensions) { + if (theElement instanceof ISupportsUndeclaredExtensions) { + ISupportsUndeclaredExtensions element = (ISupportsUndeclaredExtensions) theElement; + List ext = element.getUndeclaredExtensions(); for (ExtensionDt next : ext) { if (next == null || next.isEmpty()) { continue; } - extensions.add(new HeldExtension(next)); + extensions.add(new HeldExtension(next, false)); } - ext = ((ISupportsUndeclaredExtensions) theResource).getUndeclaredModifierExtensions(); + ext = element.getUndeclaredModifierExtensions(); for (ExtensionDt next : ext) { if (next == null || next.isEmpty()) { continue; } - modifierExtensions.add(new HeldExtension(next)); + modifierExtensions.add(new HeldExtension(next, true)); + } + } else { + if (theElement instanceof IBaseHasExtensions) { + IBaseHasExtensions element = (IBaseHasExtensions) theElement; + List> ext = element.getExtension(); + for (IBaseExtension next : ext) { + if (next == null || next.isEmpty()) { + continue; + } + extensions.add(new HeldExtension(next, false)); + } + } + if (theElement instanceof IBaseHasModifierExtensions) { + IBaseHasModifierExtensions element = (IBaseHasModifierExtensions) theElement; + List> ext = element.getModifierExtension(); + for (IBaseExtension next : ext) { + if (next == null || next.isEmpty()) { + continue; + } + modifierExtensions.add(new HeldExtension(next, true)); + } } } } @@ -788,16 +830,15 @@ public class JsonParser extends BaseParser implements IParser { return; } - boolean newerThanDstu1 = myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1); JsonObject alternate = (JsonObject) theAlternateVal; for (Entry nextEntry : alternate.entrySet()) { String nextKey = nextEntry.getKey(); JsonValue nextVal = nextEntry.getValue(); - if (!newerThanDstu1 && "extension".equals(nextKey)) { + if ("extension".equals(nextKey)) { boolean isModifier = false; JsonArray array = (JsonArray) nextEntry.getValue(); parseExtension(theState, array, isModifier); - } else if (!newerThanDstu1 && "modifierExtension".equals(nextKey)) { + } else if ("modifierExtension".equals(nextKey)) { boolean isModifier = true; JsonArray array = (JsonArray) nextEntry.getValue(); parseExtension(theState, array, isModifier); @@ -811,12 +852,6 @@ public class JsonParser extends BaseParser implements IParser { default: break; } - } else if (newerThanDstu1) { - if (nextKey.indexOf(':') > -1 && newerThanDstu1) { - JsonArray array = (JsonArray) nextEntry.getValue(); - parseExtensionInDstu2Style(false, theState, null, nextKey, array); - continue; - } } } } @@ -935,7 +970,6 @@ public class JsonParser extends BaseParser implements IParser { private void parseChildren(JsonObject theObject, ParserState theState) { String elementId = null; - boolean newerThanDstu1 = myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1); for (String nextName : theObject.keySet()) { if ("resourceType".equals(nextName)) { continue; @@ -948,29 +982,16 @@ public class JsonParser extends BaseParser implements IParser { // _id is incorrect, but some early examples in the FHIR spec used it elementId = theObject.getString(nextName); continue; - } else if (!newerThanDstu1 && "extension".equals(nextName)) { + } else if ("extension".equals(nextName)) { JsonArray array = theObject.getJsonArray(nextName); parseExtension(theState, array, false); continue; - } else if (!newerThanDstu1 && "modifierExtension".equals(nextName)) { + } else if ("modifierExtension".equals(nextName)) { JsonArray array = theObject.getJsonArray(nextName); parseExtension(theState, array, true); continue; - } else if (newerThanDstu1 && "modifier".equals(nextName)) { - JsonObject obj = theObject.getJsonObject(nextName); - for (String nextUrl : obj.keySet()) { - JsonArray array = obj.getJsonArray(nextUrl); - parseExtensionInDstu2Style(true, theState, null, nextUrl, array); - } - continue; } else if (nextName.charAt(0) == '_') { continue; - } else { - if (newerThanDstu1 && nextName.indexOf(':') > -1) { - JsonArray array = theObject.getJsonArray(nextName); - parseExtensionInDstu2Style(false, theState, null, nextName, array); - continue; - } } JsonValue nextVal = theObject.get(nextName); @@ -1210,42 +1231,18 @@ public class JsonParser extends BaseParser implements IParser { private void writeExtensionsAsDirectChild(IBaseResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List extensions, List modifierExtensions, String theParentExtensionUrl) throws IOException { if (extensions.isEmpty() == false) { - if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { - Collections.sort(extensions); - String currentlyWritingExtensionUrl = null; - for (HeldExtension next : extensions) { - currentlyWritingExtensionUrl = next.writeExtensionInDstu2Format(resDef, theResource, theEventWriter, currentlyWritingExtensionUrl, theParentExtensionUrl); - } - if (currentlyWritingExtensionUrl != null) { - theEventWriter.writeEnd(); - } - } else { - theEventWriter.writeStartArray("extension"); - for (HeldExtension next : extensions) { - next.write(resDef, theResource, theEventWriter); - } - theEventWriter.writeEnd(); + theEventWriter.writeStartArray("extension"); + for (HeldExtension next : extensions) { + next.write(resDef, theResource, theEventWriter); } + theEventWriter.writeEnd(); } if (modifierExtensions.isEmpty() == false) { - if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { - Collections.sort(modifierExtensions); - theEventWriter.writeStartObject("modifier"); - String currentlyWritingExtensionUrl = null; - for (HeldExtension next : modifierExtensions) { - currentlyWritingExtensionUrl = next.writeExtensionInDstu2Format(resDef, theResource, theEventWriter, currentlyWritingExtensionUrl, theParentExtensionUrl); - } - if (currentlyWritingExtensionUrl != null) { - theEventWriter.writeEnd(); - } - theEventWriter.writeEnd(); - } else { - theEventWriter.writeStartArray("modifierExtension"); - for (HeldExtension next : modifierExtensions) { - next.write(resDef, theResource, theEventWriter); - } - theEventWriter.writeEnd(); + theEventWriter.writeStartArray("modifierExtension"); + for (HeldExtension next : modifierExtensions) { + next.write(resDef, theResource, theEventWriter); } + theEventWriter.writeEnd(); } } @@ -1283,14 +1280,6 @@ public class JsonParser extends BaseParser implements IParser { } } - private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, String theValue) { - if (theValue != null && !theValue.isEmpty()) { - theEventWriter.write(theElementName, theValue); - } else { - theEventWriter.writeNull(theElementName); - } - } - private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, StringDt theStringDt) { if (StringUtils.isNotBlank(theStringDt.getValue())) { theEventWriter.write(theElementName, theStringDt.getValue()); @@ -1303,12 +1292,14 @@ public class JsonParser extends BaseParser implements IParser { private class HeldExtension implements Comparable { private RuntimeChildDeclaredExtensionDefinition myDef; - private ExtensionDt myUndeclaredExtension; + private IBaseExtension myUndeclaredExtension; private IBase myValue; + private boolean myModifier; - public HeldExtension(ExtensionDt theUndeclaredExtension) { + public HeldExtension(IBaseExtension theUndeclaredExtension, boolean theModifier) { assert theUndeclaredExtension != null; myUndeclaredExtension = theUndeclaredExtension; + myModifier = theModifier; } public HeldExtension(RuntimeChildDeclaredExtensionDefinition theDef, IBase theValue) { @@ -1337,66 +1328,38 @@ public class JsonParser extends BaseParser implements IParser { } } - public String writeExtensionInDstu2Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theCurrentlyWritingExtensionUrl, - String theParentExtensionUrl) throws IOException { - if (myUndeclaredExtension != null) { - return writeUndeclaredExtInDstu2Format(theResDef, theResource, theEventWriter, myUndeclaredExtension, theCurrentlyWritingExtensionUrl, theParentExtensionUrl); - } else { - String extensionUrl = myDef.getExtensionUrl(); - checkIfNewExtensionUrlArrayIsNeeded(theEventWriter, extensionUrl, theCurrentlyWritingExtensionUrl, theParentExtensionUrl); - - theEventWriter.writeStartObject(); - - BaseRuntimeElementDefinition def = myDef.getChildElementDefinitionByDatatype(myValue.getClass()); - if (def.getChildType() == ChildTypeEnum.RESOURCE_BLOCK) { - extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource, extensionUrl); - } else { - String childName = myDef.getChildNameByDatatype(myValue.getClass()); - encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false); - } - - theEventWriter.writeEnd(); - - return extensionUrl; - } - } - - private void checkIfNewExtensionUrlArrayIsNeeded(JsonGenerator theEventWriter, String theExtensionUrl, String theCurrentlyWritingExtensionUrl, String theParentExtensionUrl) { - if (StringUtils.equals(theCurrentlyWritingExtensionUrl, theExtensionUrl) == false) { - if (isNotBlank(theCurrentlyWritingExtensionUrl)) { - theEventWriter.writeEnd(); - } - - String urlToWrite = UrlUtil.constructRelativeUrl(theParentExtensionUrl, theExtensionUrl); - theEventWriter.writeStartArray(urlToWrite); - } - } - - private void writeUndeclaredExtInDstu1Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, ExtensionDt ext) throws IOException { - IElement value = ext.getValue(); - String extensionUrl = ext.getUrl().getValue(); + private void writeUndeclaredExtInDstu1Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, IBaseExtension ext) throws IOException { + IBaseDatatype value = ext.getValue(); + String extensionUrl = ext.getUrl(); theEventWriter.writeStartObject(); theEventWriter.write("url", extensionUrl); boolean noValue = value == null || value.isEmpty(); - if (noValue && ext.getAllUndeclaredExtensions().isEmpty()) { + if (noValue && ext.getExtension().isEmpty()) { ourLog.debug("Extension with URL[{}] has no value", extensionUrl); } else if (noValue) { - theEventWriter.writeStartArray("extension"); + if (myModifier) { + theEventWriter.writeStartArray("modifierExtension"); + }else { + theEventWriter.writeStartArray("extension"); + } - for (ExtensionDt next : ext.getUndeclaredExtensions()) { - writeUndeclaredExtInDstu1Format(theResDef, theResource, theEventWriter, next); + for (Object next : ext.getExtension()) { + writeUndeclaredExtInDstu1Format(theResDef, theResource, theEventWriter, (IBaseExtension) next); } theEventWriter.writeEnd(); } else { RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition(); String childName = extDef.getChildNameByDatatype(value.getClass()); if (childName == null) { + childName = "value" + myContext.getElementDefinition(value.getClass()).getName(); + } + BaseRuntimeElementDefinition childDef = myContext.getElementDefinition(value.getClass()); + if (childDef == null) { throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + value.getClass().getCanonicalName()); } - BaseRuntimeElementDefinition childDef = extDef.getChildElementDefinitionByDatatype(value.getClass()); encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true); } @@ -1405,53 +1368,10 @@ public class JsonParser extends BaseParser implements IParser { theEventWriter.writeEnd(); } - private String writeUndeclaredExtInDstu2Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, ExtensionDt ext, - String theCurrentlyWritingExtensionUrl, String theParentExtensionUrl) throws IOException { - IElement value = ext.getValue(); - String extensionUrl = ext.getUrl().getValue(); - - checkIfNewExtensionUrlArrayIsNeeded(theEventWriter, extensionUrl, theCurrentlyWritingExtensionUrl, theParentExtensionUrl); - theEventWriter.writeStartObject(); - - boolean noValue = value == null || value.isEmpty(); - if (noValue && ext.getAllUndeclaredExtensions().isEmpty()) { - - ourLog.debug("Extension with URL[{}] has no value", extensionUrl); - - } else if (noValue) { - - BaseRuntimeElementDefinition elemDef = null; - RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource); - extractAndWriteExtensionsAsDirectChild(ext, theEventWriter, elemDef, resDef, theResource, extensionUrl); - - } else { - - RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition(); - String childName = extDef.getChildNameByDatatype(value.getClass()); - BaseRuntimeElementDefinition childDef; - if (childName == null) { - childDef = myContext.getElementDefinition(value.getClass()); - if (childDef == null) { - throw new ConfigurationException("Unable to encode extension, unrecognized child element type: " + value.getClass().getCanonicalName()); - } else { - childName = RuntimeChildUndeclaredExtensionDefinition.createExtensionChildName(childDef); - } - } else { - childDef = extDef.getChildElementDefinitionByDatatype(value.getClass()); - } - encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true); - - } - - theEventWriter.writeEnd(); - - return extensionUrl; - } - @Override public int compareTo(HeldExtension theArg0) { - String url1 = myDef != null ? myDef.getExtensionUrl() : myUndeclaredExtension.getUrlAsString(); - String url2 = theArg0.myDef != null ? theArg0.myDef.getExtensionUrl() : theArg0.myUndeclaredExtension.getUrlAsString(); + String url1 = myDef != null ? myDef.getExtensionUrl() : myUndeclaredExtension.getUrl(); + String url2 = theArg0.myDef != null ? theArg0.myDef.getExtensionUrl() : theArg0.myUndeclaredExtension.getUrl(); url1 = defaultString(url1); url2 = defaultString(url2); return url1.compareTo(url2); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java index 1df2a0c1b1b..e234453da87 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java @@ -37,7 +37,11 @@ import org.hl7.fhir.instance.model.IBaseResource; import org.hl7.fhir.instance.model.ICompositeType; import org.hl7.fhir.instance.model.IPrimitiveType; import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseElement; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions; import org.hl7.fhir.instance.model.api.IReference; import ca.uhn.fhir.context.BaseRuntimeChildDefinition; @@ -61,6 +65,7 @@ import ca.uhn.fhir.model.api.ExtensionDt; import ca.uhn.fhir.model.api.ICompositeDatatype; import ca.uhn.fhir.model.api.ICompositeElement; import ca.uhn.fhir.model.api.IElement; +import ca.uhn.fhir.model.api.IExtension; import ca.uhn.fhir.model.api.IFhirVersion; import ca.uhn.fhir.model.api.IIdentifiableElement; import ca.uhn.fhir.model.api.IPrimitiveDatatype; @@ -819,11 +824,29 @@ class ParserState { ExtensionState newState = new ExtensionState(myPreResourceState, newExtension); push(newState); } else { - throw new DataFormatException("Type " + getCurrentElement() + " does not support undeclared extentions, and found an extension with URL: " + theUrlAttr); + if (theIsModifier == false) { + if (getCurrentElement() instanceof IBaseHasExtensions) { + IBaseExtension ext = ((IBaseHasExtensions)getCurrentElement()).addExtension(); + ext.setUrl(theUrlAttr); + ParserState.ExtensionState newState = new ExtensionState(myPreResourceState, ext); + push(newState); + } else { + throw new DataFormatException("Type " + getCurrentElement() + " does not support undeclared extentions, and found an extension with URL: " + theUrlAttr); + } + }else { + if (getCurrentElement() instanceof IBaseHasModifierExtensions) { + IBaseExtension ext = ((IBaseHasModifierExtensions)getCurrentElement()).addModifierExtension(); + ext.setUrl(theUrlAttr); + ParserState.ExtensionState newState = new ExtensionState(myPreResourceState, ext); + push(newState); + } else { + throw new DataFormatException("Type " + getCurrentElement() + " does not support undeclared extentions, and found an extension with URL: " + theUrlAttr); + } + } } } - protected Object getCurrentElement() { + protected IBase getCurrentElement() { return null; } @@ -879,7 +902,7 @@ class ParserState { if ("id".equals(theName)) { if (myInstance instanceof IIdentifiableElement) { ((IIdentifiableElement) myInstance).setElementSpecificId((theValue)); - } else if (myInstance instanceof IBaseResource) { + } else { (myInstance).setId(new IdDt(theValue)); } } else if ("contentType".equals(theName)) { @@ -1578,16 +1601,16 @@ class ParserState { private class ExtensionState extends BaseState { - private ExtensionDt myExtension; + private IBaseExtension myExtension; - public ExtensionState(PreResourceState thePreResourceState, ExtensionDt theExtension) { + public ExtensionState(PreResourceState thePreResourceState, IBaseExtension theExtension) { super(thePreResourceState); myExtension = theExtension; } @Override public void endingElement() throws DataFormatException { - if (myExtension.getValue() != null && myExtension.getUndeclaredExtensions().size() > 0) { + if (myExtension.getValue() != null && myExtension.getExtension().size() > 0) { throw new DataFormatException("Extension must not have both a value and other contained extensions"); } pop(); @@ -1612,7 +1635,7 @@ class ParserState { case PRIMITIVE_DATATYPE: { RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target; IPrimitiveType newChildInstance = primitiveTarget.newInstance(); - myExtension.setValue((IElement) newChildInstance); + myExtension.setValue(newChildInstance); PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance); push(newState); return; @@ -1635,7 +1658,7 @@ class ParserState { } @Override - protected IElement getCurrentElement() { + protected IBaseExtension getCurrentElement() { return myExtension; } @@ -1805,6 +1828,15 @@ class ParserState { super(theResourceType); } + @SuppressWarnings("unchecked") + @Override + public void wereBack() { + super.wereBack(); + if (myTarget == null) { + myObject = (T) getCurrentElement(); + } + } + @Override public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException { super.enteringNewElement(theNamespaceURI, theLocalPart); @@ -1953,7 +1985,7 @@ class ParserState { } @Override - protected TagList getCurrentElement() { + protected IBase getCurrentElement() { return myTagList; } @@ -2237,6 +2269,11 @@ class ParserState { pop(); } + @Override + protected IBase getCurrentElement() { + return myTagList; + } + @Override public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException { if (TagList.ATTR_CATEGORY.equals(theLocalPart)) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java index 1e050b3ecd9..4a3f95d9794 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java @@ -27,8 +27,10 @@ import java.io.Reader; import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import javax.xml.namespace.QName; import javax.xml.stream.FactoryConfigurationError; @@ -49,6 +51,10 @@ import org.hl7.fhir.instance.model.IBase; import org.hl7.fhir.instance.model.IBaseResource; import org.hl7.fhir.instance.model.IPrimitiveType; import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseDatatype; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions; import org.hl7.fhir.instance.model.api.IDomainResource; import org.hl7.fhir.instance.model.api.INarrative; @@ -171,7 +177,6 @@ public class XmlParser extends BaseParser implements IParser { throw new DataFormatException("Extension element has no 'url' attribute"); } parserState.enteringNewElementExtension(elem, urlAttr.getValue(), true); - } else { String elementName = elem.getName().getLocalPart(); @@ -433,8 +438,8 @@ public class XmlParser extends BaseParser implements IParser { theEventWriter.close(); } - private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, XMLStreamWriter theEventWriter, IBase nextValue, String childName, - BaseRuntimeElementDefinition childDef, String theExtensionUrl, boolean theIncludedResource) throws XMLStreamException, DataFormatException { + private void encodeChildElementToStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, IBase nextValue, String childName, BaseRuntimeElementDefinition childDef, + String theExtensionUrl, boolean theIncludedResource) throws XMLStreamException, DataFormatException { if (nextValue.isEmpty()) { if (childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES && getContainedResources().isEmpty() == false && theIncludedResource == false) { // We still want to go in.. @@ -450,7 +455,7 @@ public class XmlParser extends BaseParser implements IParser { if (value != null) { theEventWriter.writeStartElement(childName); theEventWriter.writeAttribute("value", value); - encodeExtensionsIfPresent(theResDef, theResource, theEventWriter, nextValue, theIncludedResource); + encodeExtensionsIfPresent(theResource, theEventWriter, nextValue, theIncludedResource); theEventWriter.writeEndElement(); } break; @@ -462,7 +467,7 @@ public class XmlParser extends BaseParser implements IParser { theEventWriter.writeAttribute("url", theExtensionUrl); } BaseRuntimeElementCompositeDefinition childCompositeDef = (BaseRuntimeElementCompositeDefinition) childDef; - encodeCompositeElementToStreamWriter(theResDef, theResource, nextValue, theEventWriter, childCompositeDef, theIncludedResource); + encodeCompositeElementToStreamWriter(theResource, nextValue, theEventWriter, childCompositeDef, theIncludedResource); theEventWriter.writeEndElement(); break; } @@ -512,21 +517,26 @@ public class XmlParser extends BaseParser implements IParser { } - private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, - List children, boolean theIncludedResource) throws XMLStreamException, DataFormatException { + private void encodeCompositeElementChildrenToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, List children, + boolean theIncludedResource) throws XMLStreamException, DataFormatException { for (BaseRuntimeChildDefinition nextChild : children) { + if (nextChild.getElementName().equals("extension") || nextChild.getElementName().equals("modifierExtension")) { + continue; + } + if (nextChild instanceof RuntimeChildNarrativeDefinition && !theIncludedResource) { INarrativeGenerator gen = myContext.getNarrativeGenerator(); if (theResource instanceof IResource) { BaseNarrativeDt narr = ((IResource) theResource).getText(); if (gen != null && narr.isEmpty()) { - gen.generateNarrative(theResDef.getResourceProfile(), theResource, narr); + String resourceProfile = myContext.getResourceDefinition(theResource).getResourceProfile(); + gen.generateNarrative(resourceProfile, theResource, narr); } if (narr.isEmpty() == false) { RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild; String childName = nextChild.getChildNameByDatatype(child.getDatatype()); BaseRuntimeElementDefinition type = child.getChildByName(childName); - encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, narr, childName, type, null, theIncludedResource); + encodeChildElementToStreamWriter(theResource, theEventWriter, narr, childName, type, null, theIncludedResource); continue; } } else { @@ -534,13 +544,14 @@ public class XmlParser extends BaseParser implements IParser { BaseNarrativeDt narr2 = null; if (gen != null && narr1.isEmpty()) { // TODO: need to implement this - gen.generateNarrative(theResDef.getResourceProfile(), theResource, null); + String resourceProfile = myContext.getResourceDefinition(theResource).getResourceProfile(); + gen.generateNarrative(resourceProfile, theResource, null); } if (narr2 != null) { RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild; String childName = nextChild.getChildNameByDatatype(child.getDatatype()); BaseRuntimeElementDefinition type = child.getChildByName(childName); - encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, narr2, childName, type, null, theIncludedResource); + encodeChildElementToStreamWriter(theResource, theEventWriter, narr2, childName, type, null, theIncludedResource); continue; } } @@ -563,11 +574,11 @@ public class XmlParser extends BaseParser implements IParser { super.throwExceptionForUnknownChildType(nextChild, type); } - if (nextValue instanceof ExtensionDt) { - - extensionUrl = ((ExtensionDt) nextValue).getUrlAsString(); - encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theIncludedResource); - + if (nextValue instanceof IBaseExtension && myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) { + // This is called for the Query resource in DSTU1 only + extensionUrl = ((IBaseExtension) nextValue).getUrl(); + encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theIncludedResource); + } else if (extensionUrl != null && childName.equals("extension") == false) { RuntimeChildDeclaredExtensionDefinition extDef = (RuntimeChildDeclaredExtensionDefinition) nextChild; if (extDef.isModifier()) { @@ -577,31 +588,48 @@ public class XmlParser extends BaseParser implements IParser { } theEventWriter.writeAttribute("url", extensionUrl); - encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childName, childDef, null, theIncludedResource); + encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, null, theIncludedResource); theEventWriter.writeEndElement(); } else if (nextChild instanceof RuntimeChildNarrativeDefinition && theIncludedResource) { // suppress narratives from contained resources } else { - encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theIncludedResource); + encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theIncludedResource); } } } } - private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, - BaseRuntimeElementCompositeDefinition resDef, boolean theIncludedResource) throws XMLStreamException, DataFormatException { - encodeExtensionsIfPresent(theResDef, theResource, theEventWriter, theElement, theIncludedResource); - encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions(), theIncludedResource); - encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren(), theIncludedResource); + private void encodeCompositeElementToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition theElementDefinition, + boolean theIncludedResource) throws XMLStreamException, DataFormatException { + encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource); + encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, theElementDefinition.getExtensions(), theIncludedResource); + encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, theElementDefinition.getChildren(), theIncludedResource); } - private void encodeExtensionsIfPresent(RuntimeResourceDefinition theResDef, IBaseResource theResource, XMLStreamWriter theWriter, IBase theElement, boolean theIncludedResource) - throws XMLStreamException, DataFormatException { + private void encodeExtensionsIfPresent(IBaseResource theResource, XMLStreamWriter theWriter, IBase theElement, boolean theIncludedResource) throws XMLStreamException, DataFormatException { if (theElement instanceof ISupportsUndeclaredExtensions) { ISupportsUndeclaredExtensions res = (ISupportsUndeclaredExtensions) theElement; - encodeUndeclaredExtensions(theResDef, theResource, theWriter, res.getUndeclaredExtensions(), "extension", theIncludedResource); - encodeUndeclaredExtensions(theResDef, theResource, theWriter, res.getUndeclaredModifierExtensions(), "modifierExtension", theIncludedResource); + encodeUndeclaredExtensions(theResource, theWriter, toBaseExtensionList(res.getUndeclaredExtensions()), "extension", theIncludedResource); + encodeUndeclaredExtensions(theResource, theWriter, toBaseExtensionList(res.getUndeclaredModifierExtensions()), "modifierExtension", theIncludedResource); } + if (theElement instanceof IBaseHasExtensions) { + IBaseHasExtensions res = (IBaseHasExtensions) theElement; + encodeUndeclaredExtensions(theResource, theWriter, res.getExtension(), "extension", theIncludedResource); + } + if (theElement instanceof IBaseHasModifierExtensions) { + IBaseHasModifierExtensions res = (IBaseHasModifierExtensions) theElement; + encodeUndeclaredExtensions(theResource, theWriter, res.getModifierExtension(), "modifierExtension", theIncludedResource); + } + } + + /** + * This is just to work around the fact that casting java.util.List to java.util.List> seems to be + * rejected by the compiler some of the time. + */ + private > List> toBaseExtensionList(final List theList) { + List> retVal = new ArrayList>(theList.size()); + retVal.addAll(theList); + return retVal; } private void encodeResourceReferenceToStreamWriter(XMLStreamWriter theEventWriter, BaseResourceReferenceDt theRef) throws XMLStreamException { @@ -638,12 +666,12 @@ public class XmlParser extends BaseParser implements IParser { postExtensionChildren.add(next); } } - encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, preExtensionChildren, theIncludedResource); + encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, preExtensionChildren, theIncludedResource); - encodeExtensionsIfPresent(theResDef, theResource, theEventWriter, theElement, theIncludedResource); - encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions(), theIncludedResource); + encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource); + encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, resDef.getExtensions(), theIncludedResource); - encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, postExtensionChildren, theIncludedResource); + encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, postExtensionChildren, theIncludedResource); } @@ -706,7 +734,7 @@ public class XmlParser extends BaseParser implements IParser { if (theResource instanceof IAnyResource) { // HL7.org Structures - encodeCompositeElementToStreamWriter(resDef, theResource, theResource, theEventWriter, resDef, theContainedResource); + encodeCompositeElementToStreamWriter(theResource, theResource, theEventWriter, resDef, theContainedResource); } else { @@ -753,7 +781,7 @@ public class XmlParser extends BaseParser implements IParser { } theEventWriter.writeCharacters(bin.getContentAsBase64()); } else { - encodeCompositeElementToStreamWriter(resDef, theResource, theResource, theEventWriter, resDef, theContainedResource); + encodeCompositeElementToStreamWriter(theResource, theResource, theEventWriter, resDef, theContainedResource); } } @@ -794,25 +822,20 @@ public class XmlParser extends BaseParser implements IParser { } } - private void encodeUndeclaredExtensions(RuntimeResourceDefinition theResDef, IBaseResource theResource, XMLStreamWriter theWriter, List theExtensions, String tagName, - boolean theIncludedResource) throws XMLStreamException, DataFormatException { - for (ExtensionDt next : theExtensions) { + private void encodeUndeclaredExtensions(IBaseResource theResource, XMLStreamWriter theWriter, List> theExtensions, String tagName, boolean theIncludedResource) + throws XMLStreamException, DataFormatException { + for (IBaseExtension next : theExtensions) { + if (next == null) { + continue; + } + theWriter.writeStartElement(tagName); - theWriter.writeAttribute("url", next.getUrl().getValue()); + + String url = next.getUrl(); + theWriter.writeAttribute("url", url); if (next.getValue() != null) { - IElement value = next.getValue(); - // RuntimeChildUndeclaredExtensionDefinition extDef = - // myContext.getRuntimeChildUndeclaredExtensionDefinition(); - // String childName = extDef.getChildNameByDatatype(nextValue.getClass()); - // if (childName == null) { - // throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + - // nextValue.getClass().getCanonicalName()); - // } - // BaseRuntimeElementDefinition childDef = - // extDef.getChildElementDefinitionByDatatype(nextValue.getClass()); - // - // + IBaseDatatype value = next.getValue(); RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition(); String childName = extDef.getChildNameByDatatype(value.getClass()); BaseRuntimeElementDefinition childDef; @@ -826,11 +849,11 @@ public class XmlParser extends BaseParser implements IParser { } else { childDef = extDef.getChildElementDefinitionByDatatype(value.getClass()); } - encodeChildElementToStreamWriter(theResDef, theResource, theWriter, value, childName, childDef, null, theIncludedResource); + encodeChildElementToStreamWriter(theResource, theWriter, value, childName, childDef, null, theIncludedResource); } // child extensions - encodeExtensionsIfPresent(theResDef, theResource, theWriter, next, theIncludedResource); + encodeExtensionsIfPresent(theResource, theWriter, next, theIncludedResource); theWriter.writeEndElement(); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java index d595ae49665..27868f5d46d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java @@ -26,15 +26,12 @@ import java.io.IOException; import java.io.Reader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.TreeSet; -import ca.uhn.fhir.rest.annotation.*; - import org.apache.commons.io.IOUtils; import org.hl7.fhir.instance.model.IBaseResource; @@ -47,6 +44,18 @@ import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum; import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum; import ca.uhn.fhir.parser.IParser; +import ca.uhn.fhir.rest.annotation.AddTags; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.DeleteTags; +import ca.uhn.fhir.rest.annotation.GetTags; +import ca.uhn.fhir.rest.annotation.History; +import ca.uhn.fhir.rest.annotation.Metadata; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.annotation.Transaction; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.BaseHttpClientInvocation; import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException; @@ -61,9 +70,9 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; +import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; -import ca.uhn.fhir.rest.server.exceptions.ResourceVersionNotSpecifiedException; import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.ReflectionUtil; @@ -217,7 +226,7 @@ public abstract class BaseMethodBinding implements IClientResponseHandler ex = new ResourceVersionConflictException("Server responded with HTTP 409"); break; case Constants.STATUS_HTTP_412_PRECONDITION_FAILED: - ex = new ResourceVersionNotSpecifiedException("Server responded with HTTP 412"); + ex = new PreconditionFailedException("Server responded with HTTP 412"); break; case Constants.STATUS_HTTP_422_UNPROCESSABLE_ENTITY: IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theStatusCode); diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseEnumFactory.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseEnumFactory.java new file mode 100644 index 00000000000..ab56defe694 --- /dev/null +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseEnumFactory.java @@ -0,0 +1,25 @@ +package org.hl7.fhir.instance.model.api; + +public interface IBaseEnumFactory> { + + /** + * Read an enumeration value from the string that represents it on the XML or JSON + * + * @param codeString + * the value found in the XML or JSON + * @return the enumeration value + * @throws IllegalArgumentException + * is the value is not known + */ + public T fromCode(String codeString) throws IllegalArgumentException; + + /** + * Get the XML/JSON representation for an enumerated value + * + * @param code + * - the enumeration value + * @return the XML/JSON representation + */ + public String toCode(T code); + +} diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseExtension.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseExtension.java index d48da6dd004..0d0dbf01aec 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseExtension.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseExtension.java @@ -11,5 +11,9 @@ public interface IBaseExtension extends ICompositeType { String getUrl(); IBaseDatatype getValue(); + + T setUrl(String theUrl); + T setValue(IBaseDatatype theValue); + } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseHasExtensions.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseHasExtensions.java new file mode 100644 index 00000000000..257b7466478 --- /dev/null +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseHasExtensions.java @@ -0,0 +1,11 @@ +package org.hl7.fhir.instance.model.api; + +import java.util.List; + +public interface IBaseHasExtensions { + + public List> getExtension(); + + public IBaseExtension addExtension(); + +} diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseHasModifierExtensions.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseHasModifierExtensions.java new file mode 100644 index 00000000000..23cba0358a3 --- /dev/null +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseHasModifierExtensions.java @@ -0,0 +1,11 @@ +package org.hl7.fhir.instance.model.api; + +import java.util.List; + +public interface IBaseHasModifierExtensions { + + public List> getModifierExtension(); + + public IBaseExtension addModifierExtension(); + +} diff --git a/hapi-fhir-base/testmindeps/src/test/java/ca/uhn/fhir/parser/MultiVersionJsonParserTest.java b/hapi-fhir-base/testmindeps/src/test/java/ca/uhn/fhir/parser/MultiVersionJsonParserTest.java index a51236d25ff..113baa19ce1 100644 --- a/hapi-fhir-base/testmindeps/src/test/java/ca/uhn/fhir/parser/MultiVersionJsonParserTest.java +++ b/hapi-fhir-base/testmindeps/src/test/java/ca/uhn/fhir/parser/MultiVersionJsonParserTest.java @@ -22,7 +22,7 @@ public class MultiVersionJsonParserTest { String str = FhirContext.forDstu2().newJsonParser().encodeResourceToString(p); ourLog.info(str); - assertThat(str,StringContains.containsString("{\"resourceType\":\"Patient\",\"http://foo#ext\":[{\"valueQuantity\":{\"value\":2.2}}],\"identifier\":[{\"system\":\"urn:sys\",\"value\":\"001\"}]}")); + assertThat(str,StringContains.containsString("{\"resourceType\":\"Patient\",\"extension\":[{\"url\":\"http://foo#ext\",\"valueQuantity\":{\"value\":2.2}}],\"identifier\":[{\"system\":\"urn:sys\",\"value\":\"001\"}]}")); } } diff --git a/hapi-fhir-base/testmindeps/src/test/java/ca/uhn/fhir/testmindeps/ValidatorTest.java b/hapi-fhir-base/testmindeps/src/test/java/ca/uhn/fhir/testmindeps/ValidatorTest.java index 0a591bc2443..f83848dd45b 100644 --- a/hapi-fhir-base/testmindeps/src/test/java/ca/uhn/fhir/testmindeps/ValidatorTest.java +++ b/hapi-fhir-base/testmindeps/src/test/java/ca/uhn/fhir/testmindeps/ValidatorTest.java @@ -9,22 +9,23 @@ import ca.uhn.fhir.validation.FhirValidator; public class ValidatorTest { - @Test - public void testValidator() { - - FhirContext ctx = new FhirContext(); - FhirValidator val = ctx.newValidator(); - - // Phloc is not onthe classpath - assertTrue(val.isValidateAgainstStandardSchema()); - assertFalse(val.isValidateAgainstStandardSchematron()); + @Test + public void testValidator() { + + FhirContext ctx = new FhirContext(); + FhirValidator val = ctx.newValidator(); + + // Phloc is not onthe classpath + assertTrue(val.isValidateAgainstStandardSchema()); + assertFalse(val.isValidateAgainstStandardSchematron()); + + try { + val.setValidateAgainstStandardSchematron(true); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Phloc-schematron library not found on classpath, can not enable perform schematron validation", e.getMessage()); + } + + } - try { val.setValidateAgainstStandardSchematron(true); - fail(); - } catch (IllegalArgumentException e) { - assertEquals("Phloc-schematron library not found on classpath, can not enable perform schematron validation", e.getMessage()); - } - - } - } diff --git a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java index 731afbca793..5775f0ce6c1 100644 --- a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java +++ b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java @@ -9,6 +9,7 @@ import org.springframework.web.context.WebApplicationContext; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu1; +import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.rest.server.ETagSupportEnum; import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; @@ -48,7 +49,7 @@ public class JpaServerDemo extends RestfulServer { * The system provider implements non-resource-type methods, such as * transaction, and global history. */ - JpaSystemProviderDstu1 systemProvider = myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu1.class); + JpaSystemProviderDstu2 systemProvider = myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class); setPlainProviders(systemProvider); /* diff --git a/hapi-fhir-structures-dev/src/main/java/ca/uhn/fhir/model/dev/FhirDev.java b/hapi-fhir-structures-dev/src/main/java/ca/uhn/fhir/model/dev/FhirDev.java index 72731a1c9c1..3c94a4bf79d 100644 --- a/hapi-fhir-structures-dev/src/main/java/ca/uhn/fhir/model/dev/FhirDev.java +++ b/hapi-fhir-structures-dev/src/main/java/ca/uhn/fhir/model/dev/FhirDev.java @@ -23,10 +23,12 @@ package ca.uhn.fhir.model.dev; import java.io.InputStream; import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.instance.model.api.IBaseExtension; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.RuntimeResourceDefinition; +import ca.uhn.fhir.model.api.ExtensionDt; import ca.uhn.fhir.model.api.IFhirVersion; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.base.composite.BaseContainedDt; @@ -102,6 +104,4 @@ public class FhirDev implements IFhirVersion { return ContainedDt.class; } - - } diff --git a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java index 36713dc4a1b..e271fca91e4 100644 --- a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java +++ b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java @@ -57,7 +57,6 @@ import ca.uhn.fhir.context.RuntimeResourceBlockDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceReferenceDefinition; import ca.uhn.fhir.model.api.ICompositeDatatype; -import ca.uhn.fhir.model.api.IDatatype; import ca.uhn.fhir.model.api.IFhirVersion; import ca.uhn.fhir.model.api.IPrimitiveDatatype; import ca.uhn.fhir.model.api.IResource; @@ -378,6 +377,4 @@ public class FhirDstu1 implements IFhirVersion { return ContainedDt.class; } - - } diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java index 77de4d8cff5..9a924807c49 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java @@ -1,9 +1,15 @@ package ca.uhn.fhir.parser; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.stringContainsInOrder; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; import java.io.IOException; import java.io.OutputStreamWriter; @@ -24,8 +30,6 @@ import org.hamcrest.text.StringContainsInOrder; import org.hl7.fhir.instance.model.IBaseResource; import org.junit.BeforeClass; import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Matchers; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; @@ -41,7 +45,6 @@ import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.model.base.composite.BaseNarrativeDt; import ca.uhn.fhir.model.dstu.composite.AddressDt; import ca.uhn.fhir.model.dstu.composite.HumanNameDt; -import ca.uhn.fhir.model.dstu.composite.NarrativeDt; import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu.resource.Binary; import ca.uhn.fhir.model.dstu.resource.Conformance; @@ -59,7 +62,10 @@ import ca.uhn.fhir.model.dstu.resource.ValueSet; import ca.uhn.fhir.model.dstu.resource.ValueSet.Define; import ca.uhn.fhir.model.dstu.resource.ValueSet.DefineConcept; import ca.uhn.fhir.model.dstu.valueset.AddressUseEnum; +import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum; import ca.uhn.fhir.model.dstu.valueset.NarrativeStatusEnum; +import ca.uhn.fhir.model.primitive.DateDt; +import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.DecimalDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.InstantDt; @@ -94,6 +100,120 @@ public class JsonParserTest { } + @Test + public void testEncodeAndParseExtensions() throws Exception { + + Patient patient = new Patient(); + patient.addIdentifier().setUse(IdentifierUseEnum.OFFICIAL).setSystem("urn:example").setValue("7000135"); + + ExtensionDt ext = new ExtensionDt(); + ext.setUrl("http://example.com/extensions#someext"); + ext.setValue(new DateTimeDt("2011-01-02T11:13:15")); + patient.addUndeclaredExtension(ext); + + ExtensionDt parent = new ExtensionDt().setUrl("http://example.com#parent"); + patient.addUndeclaredExtension(parent); + ExtensionDt child1 = new ExtensionDt().setUrl( "http://example.com#child").setValue( new StringDt("value1")); + parent.addUndeclaredExtension(child1); + ExtensionDt child2 = new ExtensionDt().setUrl( "http://example.com#child").setValue( new StringDt("value2")); + parent.addUndeclaredExtension(child2); + + ExtensionDt modExt = new ExtensionDt(); + modExt.setUrl("http://example.com/extensions#modext"); + modExt.setValue(new DateDt("1995-01-02")); + modExt.setModifier(true); + patient.addUndeclaredExtension(modExt); + + HumanNameDt name = patient.addName(); + name.addFamily("Blah"); + StringDt given = name.addGiven(); + given.setValue("Joe"); + ExtensionDt ext2 = new ExtensionDt().setUrl("http://examples.com#givenext").setValue(new StringDt("given")); + given.addUndeclaredExtension(ext2); + + StringDt given2 = name.addGiven(); + given2.setValue("Shmoe"); + ExtensionDt given2ext = new ExtensionDt().setUrl("http://examples.com#givenext_parent"); + given2.addUndeclaredExtension(given2ext); + given2ext.addUndeclaredExtension(new ExtensionDt().setUrl("http://examples.com#givenext_child").setValue(new StringDt("CHILD"))); + + String output = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient); + ourLog.info(output); + + String enc = ourCtx.newJsonParser().encodeResourceToString(patient); + assertThat(enc, org.hamcrest.Matchers.stringContainsInOrder("{\"resourceType\":\"Patient\",", + "\"extension\":[{\"url\":\"http://example.com/extensions#someext\",\"valueDateTime\":\"2011-01-02T11:13:15\"}", + "{\"url\":\"http://example.com#parent\",\"extension\":[{\"url\":\"http://example.com#child\",\"valueString\":\"value1\"},{\"url\":\"http://example.com#child\",\"valueString\":\"value2\"}]}" + )); + assertThat(enc, org.hamcrest.Matchers.stringContainsInOrder("\"modifierExtension\":[" + + "{" + + "\"url\":\"http://example.com/extensions#modext\"," + + "\"valueDate\":\"1995-01-02\"" + + "}" + + "],")); + assertThat(enc, containsString("\"_given\":[" + + "{" + + "\"extension\":[" + + "{" + + "\"url\":\"http://examples.com#givenext\"," + + "\"valueString\":\"given\"" + + "}" + + "]" + + "}," + + "{" + + "\"extension\":[" + + "{" + + "\"url\":\"http://examples.com#givenext_parent\"," + + "\"extension\":[" + + "{" + + "\"url\":\"http://examples.com#givenext_child\"," + + "\"valueString\":\"CHILD\"" + + "}" + + "]" + + "}" + + "]" + + "}")); + + /* + * Now parse this back + */ + + Patient parsed =ourCtx.newJsonParser().parseResource(Patient.class, enc); + ext = parsed.getUndeclaredExtensions().get(0); + assertEquals("http://example.com/extensions#someext", ext.getUrl()); + assertEquals("2011-01-02T11:13:15", ((DateTimeDt)ext.getValue()).getValueAsString()); + + parent = patient.getUndeclaredExtensions().get(1); + assertEquals("http://example.com#parent", parent.getUrl()); + assertNull(parent.getValue()); + child1 = parent.getExtension().get(0); + assertEquals( "http://example.com#child", child1.getUrl()); + assertEquals("value1", ((StringDt)child1.getValue()).getValueAsString()); + child2 = parent.getExtension().get(1); + assertEquals( "http://example.com#child", child2.getUrl()); + assertEquals("value2", ((StringDt)child2.getValue()).getValueAsString()); + + modExt = parsed.getUndeclaredModifierExtensions().get(0); + assertEquals("http://example.com/extensions#modext", modExt.getUrl()); + assertEquals("1995-01-02", ((DateDt)modExt.getValue()).getValueAsString()); + + name = parsed.getName().get(0); + + ext2 = name.getGiven().get(0).getUndeclaredExtensions().get(0); + assertEquals("http://examples.com#givenext", ext2.getUrl()); + assertEquals("given", ((StringDt)ext2.getValue()).getValueAsString()); + + given2ext = name.getGiven().get(1).getUndeclaredExtensions().get(0); + assertEquals("http://examples.com#givenext_parent", given2ext.getUrl()); + assertNull(given2ext.getValue()); + ExtensionDt given2ext2 = given2ext.getExtension().get(0); + assertEquals("http://examples.com#givenext_child", given2ext2.getUrl()); + assertEquals("CHILD", ((StringDt)given2ext2.getValue()).getValue()); + + } + + + /** * #65 */ @@ -1136,7 +1256,7 @@ public class JsonParserTest { List undeclaredExtensions = obs.getContact().get(0).getName().getFamily().get(0).getUndeclaredExtensions(); ExtensionDt undeclaredExtension = undeclaredExtensions.get(0); - assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl().getValue()); + assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl()); ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out)); @@ -1184,7 +1304,7 @@ public class JsonParserTest { List undeclaredExtensions = obs.getContact().get(0).getName().getFamily().get(0).getUndeclaredExtensions(); ExtensionDt undeclaredExtension = undeclaredExtensions.get(0); - assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl().getValue()); + assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl()); IParser jsonParser = fhirCtx.newJsonParser().setPrettyPrint(true); String encoded = jsonParser.encodeResourceToString(obs); diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java index def9035d86c..5e154491072 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java @@ -67,6 +67,7 @@ import ca.uhn.fhir.model.dstu.valueset.AdministrativeGenderCodesEnum; import ca.uhn.fhir.model.dstu.valueset.DocumentReferenceStatusEnum; import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum; import ca.uhn.fhir.model.dstu.valueset.NarrativeStatusEnum; +import ca.uhn.fhir.model.primitive.DateDt; import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.DecimalDt; import ca.uhn.fhir.model.primitive.IdDt; @@ -151,6 +152,95 @@ public class XmlParserTest { } + + @Test + public void testEncodeAndParseExtensions() throws Exception { + + Patient patient = new Patient(); + patient.addIdentifier().setUse(IdentifierUseEnum.OFFICIAL).setSystem("urn:example").setValue("7000135"); + + ExtensionDt ext = new ExtensionDt(); + ext.setUrl("http://example.com/extensions#someext"); + ext.setValue(new DateTimeDt("2011-01-02T11:13:15")); + patient.addUndeclaredExtension(ext); + + ExtensionDt parent = new ExtensionDt().setUrl("http://example.com#parent"); + patient.addUndeclaredExtension(parent); + ExtensionDt child1 = new ExtensionDt().setUrl( "http://example.com#child").setValue( new StringDt("value1")); + parent.addUndeclaredExtension(child1); + ExtensionDt child2 = new ExtensionDt().setUrl( "http://example.com#child").setValue( new StringDt("value2")); + parent.addUndeclaredExtension(child2); + + ExtensionDt modExt = new ExtensionDt(); + modExt.setUrl("http://example.com/extensions#modext"); + modExt.setValue(new DateDt("1995-01-02")); + modExt.setModifier(true); + patient.addUndeclaredExtension(modExt); + + HumanNameDt name = patient.addName(); + name.addFamily("Blah"); + StringDt given = name.addGiven(); + given.setValue("Joe"); + ExtensionDt ext2 = new ExtensionDt().setUrl("http://examples.com#givenext").setValue(new StringDt("given")); + given.addUndeclaredExtension(ext2); + + StringDt given2 = name.addGiven(); + given2.setValue("Shmoe"); + ExtensionDt given2ext = new ExtensionDt().setUrl("http://examples.com#givenext_parent"); + given2.addUndeclaredExtension(given2ext); + given2ext.addUndeclaredExtension(new ExtensionDt().setUrl("http://examples.com#givenext_child").setValue(new StringDt("CHILD"))); + + String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient); + ourLog.info(output); + + String enc = ourCtx.newXmlParser().encodeResourceToString(patient); + assertThat(enc, containsString("")); + assertThat(enc, containsString("")); + assertThat( + enc, + containsString("")); + assertThat(enc, containsString("")); + assertThat(enc, containsString("")); + + /* + * Now parse this back + */ + + Patient parsed =ourCtx.newXmlParser().parseResource(Patient.class, enc); + ext = parsed.getUndeclaredExtensions().get(0); + assertEquals("http://example.com/extensions#someext", ext.getUrl()); + assertEquals("2011-01-02T11:13:15", ((DateTimeDt)ext.getValue()).getValueAsString()); + + parent = patient.getUndeclaredExtensions().get(1); + assertEquals("http://example.com#parent", parent.getUrl()); + assertNull(parent.getValue()); + child1 = parent.getExtension().get(0); + assertEquals( "http://example.com#child", child1.getUrl()); + assertEquals("value1", ((StringDt)child1.getValue()).getValueAsString()); + child2 = parent.getExtension().get(1); + assertEquals( "http://example.com#child", child2.getUrl()); + assertEquals("value2", ((StringDt)child2.getValue()).getValueAsString()); + + modExt = parsed.getUndeclaredModifierExtensions().get(0); + assertEquals("http://example.com/extensions#modext", modExt.getUrl()); + assertEquals("1995-01-02", ((DateDt)modExt.getValue()).getValueAsString()); + + name = parsed.getName().get(0); + + ext2 = name.getGiven().get(0).getUndeclaredExtensions().get(0); + assertEquals("http://examples.com#givenext", ext2.getUrl()); + assertEquals("given", ((StringDt)ext2.getValue()).getValueAsString()); + + given2ext = name.getGiven().get(1).getUndeclaredExtensions().get(0); + assertEquals("http://examples.com#givenext_parent", given2ext.getUrl()); + assertNull(given2ext.getValue()); + ExtensionDt given2ext2 = given2ext.getExtension().get(0); + assertEquals("http://examples.com#givenext_child", given2ext2.getUrl()); + assertEquals("CHILD", ((StringDt)given2ext2.getValue()).getValue()); + + } + + @Test public void testEncodeBundle() throws InterruptedException { Bundle b = new Bundle(); @@ -1490,7 +1580,7 @@ public class XmlParserTest { List undeclaredExtensions = obs.getContact().get(0).getName().getFamily().get(0).getUndeclaredExtensions(); ExtensionDt undeclaredExtension = undeclaredExtensions.get(0); - assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl().getValue()); + assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl()); ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out)); @@ -1545,7 +1635,7 @@ public class XmlParserTest { List undeclaredExtensions = obs.getContact().get(0).getName().getFamily().get(0).getUndeclaredExtensions(); ExtensionDt undeclaredExtension = undeclaredExtensions.get(0); - assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl().getValue()); + assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl()); fhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out)); diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java index c57c7d8fd14..615c4e8bce2 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java @@ -23,10 +23,12 @@ package ca.uhn.fhir.model.dstu2; import java.io.InputStream; import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.instance.model.api.IBaseExtension; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.RuntimeResourceDefinition; +import ca.uhn.fhir.model.api.ExtensionDt; import ca.uhn.fhir.model.api.IDatatype; import ca.uhn.fhir.model.api.IFhirVersion; import ca.uhn.fhir.model.api.IResource; diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java index cf7b148df43..e41c511eddc 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java @@ -13,6 +13,7 @@ import net.sf.json.JSONSerializer; import net.sf.json.JsonConfig; import org.apache.commons.io.IOUtils; +import org.hamcrest.Matchers; import org.junit.Test; import ca.uhn.fhir.context.ConfigurationException; @@ -20,13 +21,16 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.ExtensionDt; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; +import ca.uhn.fhir.model.dstu2.composite.HumanNameDt; import ca.uhn.fhir.model.dstu2.resource.Binary; import ca.uhn.fhir.model.dstu2.resource.MedicationPrescription; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.resource.QuestionnaireAnswers; +import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum; import ca.uhn.fhir.model.primitive.BooleanDt; import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.DateDt; +import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.StringDt; @@ -65,6 +69,120 @@ public class JsonParserTest { //@formatter:on } + @Test + public void testEncodeAndParseExtensions() throws Exception { + + Patient patient = new Patient(); + patient.addIdentifier().setUse(IdentifierUseEnum.OFFICIAL).setSystem("urn:example").setValue("7000135"); + + ExtensionDt ext = new ExtensionDt(); + ext.setUrl("http://example.com/extensions#someext"); + ext.setValue(new DateTimeDt("2011-01-02T11:13:15")); + patient.addUndeclaredExtension(ext); + + ExtensionDt parent = new ExtensionDt().setUrl("http://example.com#parent"); + patient.addUndeclaredExtension(parent); + ExtensionDt child1 = new ExtensionDt().setUrl( "http://example.com#child").setValue( new StringDt("value1")); + parent.addUndeclaredExtension(child1); + ExtensionDt child2 = new ExtensionDt().setUrl( "http://example.com#child").setValue( new StringDt("value2")); + parent.addUndeclaredExtension(child2); + + ExtensionDt modExt = new ExtensionDt(); + modExt.setUrl("http://example.com/extensions#modext"); + modExt.setValue(new DateDt("1995-01-02")); + modExt.setModifier(true); + patient.addUndeclaredExtension(modExt); + + HumanNameDt name = patient.addName(); + name.addFamily("Blah"); + StringDt given = name.addGiven(); + given.setValue("Joe"); + ExtensionDt ext2 = new ExtensionDt().setUrl("http://examples.com#givenext").setValue(new StringDt("given")); + given.addUndeclaredExtension(ext2); + + StringDt given2 = name.addGiven(); + given2.setValue("Shmoe"); + ExtensionDt given2ext = new ExtensionDt().setUrl("http://examples.com#givenext_parent"); + given2.addUndeclaredExtension(given2ext); + given2ext.addUndeclaredExtension(new ExtensionDt().setUrl("http://examples.com#givenext_child").setValue(new StringDt("CHILD"))); + + String output = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient); + ourLog.info(output); + + String enc = ourCtx.newJsonParser().encodeResourceToString(patient); + assertThat(enc, Matchers.stringContainsInOrder("{\"resourceType\":\"Patient\",", + "\"extension\":[{\"url\":\"http://example.com/extensions#someext\",\"valueDateTime\":\"2011-01-02T11:13:15\"}", + "{\"url\":\"http://example.com#parent\",\"extension\":[{\"url\":\"http://example.com#child\",\"valueString\":\"value1\"},{\"url\":\"http://example.com#child\",\"valueString\":\"value2\"}]}" + )); + assertThat(enc, Matchers.stringContainsInOrder("\"modifierExtension\":[" + + "{" + + "\"url\":\"http://example.com/extensions#modext\"," + + "\"valueDate\":\"1995-01-02\"" + + "}" + + "],")); + assertThat(enc, containsString("\"_given\":[" + + "{" + + "\"extension\":[" + + "{" + + "\"url\":\"http://examples.com#givenext\"," + + "\"valueString\":\"given\"" + + "}" + + "]" + + "}," + + "{" + + "\"extension\":[" + + "{" + + "\"url\":\"http://examples.com#givenext_parent\"," + + "\"extension\":[" + + "{" + + "\"url\":\"http://examples.com#givenext_child\"," + + "\"valueString\":\"CHILD\"" + + "}" + + "]" + + "}" + + "]" + + "}")); + + /* + * Now parse this back + */ + + Patient parsed =ourCtx.newJsonParser().parseResource(Patient.class, enc); + ext = parsed.getUndeclaredExtensions().get(0); + assertEquals("http://example.com/extensions#someext", ext.getUrl()); + assertEquals("2011-01-02T11:13:15", ((DateTimeDt)ext.getValue()).getValueAsString()); + + parent = patient.getUndeclaredExtensions().get(1); + assertEquals("http://example.com#parent", parent.getUrl()); + assertNull(parent.getValue()); + child1 = parent.getExtension().get(0); + assertEquals( "http://example.com#child", child1.getUrl()); + assertEquals("value1", ((StringDt)child1.getValue()).getValueAsString()); + child2 = parent.getExtension().get(1); + assertEquals( "http://example.com#child", child2.getUrl()); + assertEquals("value2", ((StringDt)child2.getValue()).getValueAsString()); + + modExt = parsed.getUndeclaredModifierExtensions().get(0); + assertEquals("http://example.com/extensions#modext", modExt.getUrl()); + assertEquals("1995-01-02", ((DateDt)modExt.getValue()).getValueAsString()); + + name = parsed.getName().get(0); + + ext2 = name.getGiven().get(0).getUndeclaredExtensions().get(0); + assertEquals("http://examples.com#givenext", ext2.getUrl()); + assertEquals("given", ((StringDt)ext2.getValue()).getValueAsString()); + + given2ext = name.getGiven().get(1).getUndeclaredExtensions().get(0); + assertEquals("http://examples.com#givenext_parent", given2ext.getUrl()); + assertNull(given2ext.getValue()); + ExtensionDt given2ext2 = given2ext.getExtension().get(0); + assertEquals("http://examples.com#givenext_child", given2ext2.getUrl()); + assertEquals("CHILD", ((StringDt)given2ext2.getValue()).getValue()); + + } + + + /** * #65 */ @@ -77,7 +195,7 @@ public class JsonParserTest { String encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(parsed); ourLog.info(encoded); - assertThat(encoded, containsString("{\"linkId\":\"value123\",\"_linkId\":{\"http://123\":[{\"valueString\":\"HELLO\"}]}}")); + assertThat(encoded, containsString("{\"linkId\":\"value123\",\"_linkId\":{\"extension\":[{\"url\":\"http://123\",\"valueString\":\"HELLO\"}]}}")); } @@ -244,102 +362,6 @@ public class JsonParserTest { } - @Test - public void testParseAndEncodeNewExtensionFormat() { - - //@formatter:off - String resource = "{\n" + - " \"resourceType\" : \"Patient\",\n" + - " \"http://acme.org/fhir/ExtensionDefinition/trial-status\" : [{\n" + - " \"code\" : [{ \"valueCode\" : \"unsure\" }],\n" + - " \"date\" : [{ \"valueDate\" : \"2009-03-14\" }], \n" + - " \"registrar\" : [{ \"valueReference\" : {\n" + - " \"reference\" : \"Practitioner/example\"\n" + - " }\n" + - " }]\n" + - " }],\n" + - " \"modifier\" : { \n" + - " \"http://example.org/fhir/ExtensionDefinition/some-kind-of-modifier\" : [{\n" + - " \"valueBoolean\" : true\n" + - " }]\n" + - " }," + - " \"name\" : [{\n" + - " \"family\": [\n" + - " \"du\",\n" + - " \"Marché\"\n" + - " ],\n" + - " \"_family\": [\n" + - " {\n" + - " \"http://hl7.org/fhir/ExtensionDefinition/iso21090-EN-qualifier\": [\n" + - " {\n" + - " \"valueCode\": \"VV\"\n" + - " }\n" + - " ]\n" + - " },\n" + - " null\n" + - " ],\n" + - " \"given\": [\n" + - " \"Bénédicte\"\n" + - " ]\n" + - " }],\n" + - " \"gender\" : \"M\"\n" + - "}"; - //@formatter:on - -// ourLog.info(resource); - - Patient parsed = ourCtx.newJsonParser().parseResource(Patient.class, resource); - - // Modifier extension - assertEquals(1, parsed.getUndeclaredExtensionsByUrl("http://example.org/fhir/ExtensionDefinition/some-kind-of-modifier").size()); - assertTrue( parsed.getUndeclaredExtensionsByUrl("http://example.org/fhir/ExtensionDefinition/some-kind-of-modifier").get(0).isModifier()); - assertEquals(BooleanDt.class, parsed.getUndeclaredExtensionsByUrl("http://example.org/fhir/ExtensionDefinition/some-kind-of-modifier").get(0).getValueAsPrimitive().getClass()); - assertEquals("true", parsed.getUndeclaredExtensionsByUrl("http://example.org/fhir/ExtensionDefinition/some-kind-of-modifier").get(0).getValueAsPrimitive().getValueAsString()); - - // Gender - assertEquals("M",parsed.getGender()); - assertEquals(1, parsed.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/trial-status").size()); - - // Trial status - ExtensionDt ext = parsed.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/trial-status").get(0); - assertNull(ext.getValue()); - assertEquals(1, ext.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/code").size()); - assertEquals(CodeDt.class, ext.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/code").get(0).getValue().getClass()); - assertEquals("unsure", ext.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/code").get(0).getValueAsPrimitive().getValueAsString()); - assertEquals(1, ext.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/date").size()); - assertEquals(DateDt.class, ext.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/date").get(0).getValue().getClass()); - assertEquals("2009-03-14", ext.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/date").get(0).getValueAsPrimitive().getValueAsString()); - - // Name - assertEquals(1, parsed.getName().size()); - assertEquals(2, parsed.getName().get(0).getFamily().size()); - assertEquals("du Marché", parsed.getName().get(0).getFamilyAsSingleString()); - assertEquals(1, parsed.getName().get(0).getGiven().size()); - assertEquals("Bénédicte", parsed.getName().get(0).getGivenAsSingleString()); - - // Patient.name[0].family extensions - assertEquals(1, parsed.getNameFirstRep().getFamily().get(0).getUndeclaredExtensionsByUrl("http://hl7.org/fhir/ExtensionDefinition/iso21090-EN-qualifier").size()); - assertEquals(CodeDt.class, parsed.getNameFirstRep().getFamily().get(0).getUndeclaredExtensionsByUrl("http://hl7.org/fhir/ExtensionDefinition/iso21090-EN-qualifier").get(0).getValueAsPrimitive().getClass()); - assertEquals("VV", parsed.getNameFirstRep().getFamily().get(0).getUndeclaredExtensionsByUrl("http://hl7.org/fhir/ExtensionDefinition/iso21090-EN-qualifier").get(0).getValueAsPrimitive().getValueAsString()); - - /* - * Now re-encode - */ - String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(parsed); - ourLog.info(encoded); - - JSON expected = JSONSerializer.toJSON(resource.trim()); - JSON actual = JSONSerializer.toJSON(encoded.trim()); - - String exp = expected.toString().replace("\\r\\n", "\\n"); // .replace("§", "§"); - String act = actual.toString().replace("\\r\\n", "\\n"); - - ourLog.info("Expected: {}", exp); - ourLog.info("Actual : {}", act); - - assertEquals(exp, act); - - } @Test public void testParseAndEncodeBundleWithDeletedEntry() { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java index 392e1425786..f93e9f862c4 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java @@ -1,7 +1,13 @@ package ca.uhn.fhir.parser; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.emptyOrNullString; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import java.io.StringReader; @@ -13,8 +19,10 @@ import org.junit.Test; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Bundle; +import ca.uhn.fhir.model.api.ExtensionDt; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.dstu2.composite.DurationDt; +import ca.uhn.fhir.model.dstu2.composite.HumanNameDt; import ca.uhn.fhir.model.dstu2.resource.AllergyIntolerance; import ca.uhn.fhir.model.dstu2.resource.Binary; import ca.uhn.fhir.model.dstu2.resource.Composition; @@ -22,7 +30,11 @@ import ca.uhn.fhir.model.dstu2.resource.Encounter; import ca.uhn.fhir.model.dstu2.resource.MedicationPrescription; import ca.uhn.fhir.model.dstu2.resource.Organization; import ca.uhn.fhir.model.dstu2.resource.Patient; +import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum; +import ca.uhn.fhir.model.primitive.DateDt; +import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.InstantDt; +import ca.uhn.fhir.model.primitive.StringDt; public class XmlParserTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParserTest.class); @@ -89,7 +101,8 @@ public class XmlParserTest { public void testParseAndEncodeBundleNewStyle() throws Exception { String content = IOUtils.toString(XmlParserTest.class.getResourceAsStream("/bundle-example.xml")); - ca.uhn.fhir.model.dstu2.resource.Bundle parsed = ourCtx.newXmlParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, content); + IParser newXmlParser = ourCtx.newXmlParser(); + ca.uhn.fhir.model.dstu2.resource.Bundle parsed = newXmlParser.parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, content); assertEquals("http://example.com/base/Bundle/example/_history/1", parsed.getId().getValue()); assertEquals("1", parsed.getResourceMetadata().get(ResourceMetadataKeyEnum.VERSION)); assertEquals(new InstantDt("2014-08-18T01:43:30Z"), parsed.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED)); @@ -275,4 +288,93 @@ public class XmlParserTest { assertEquals(2, parsed.getContained().getContainedResources().size()); } + @Test + public void testEncodeAndParseExtensions() throws Exception { + + Patient patient = new Patient(); + patient.addIdentifier().setUse(IdentifierUseEnum.OFFICIAL).setSystem("urn:example").setValue("7000135"); + + ExtensionDt ext = new ExtensionDt(); + ext.setUrl("http://example.com/extensions#someext"); + ext.setValue(new DateTimeDt("2011-01-02T11:13:15")); + patient.addUndeclaredExtension(ext); + + ExtensionDt parent = new ExtensionDt().setUrl("http://example.com#parent"); + patient.addUndeclaredExtension(parent); + ExtensionDt child1 = new ExtensionDt().setUrl( "http://example.com#child").setValue( new StringDt("value1")); + parent.addUndeclaredExtension(child1); + ExtensionDt child2 = new ExtensionDt().setUrl( "http://example.com#child").setValue( new StringDt("value2")); + parent.addUndeclaredExtension(child2); + + ExtensionDt modExt = new ExtensionDt(); + modExt.setUrl("http://example.com/extensions#modext"); + modExt.setValue(new DateDt("1995-01-02")); + modExt.setModifier(true); + patient.addUndeclaredExtension(modExt); + + HumanNameDt name = patient.addName(); + name.addFamily("Blah"); + StringDt given = name.addGiven(); + given.setValue("Joe"); + ExtensionDt ext2 = new ExtensionDt().setUrl("http://examples.com#givenext").setValue(new StringDt("given")); + given.addUndeclaredExtension(ext2); + + StringDt given2 = name.addGiven(); + given2.setValue("Shmoe"); + ExtensionDt given2ext = new ExtensionDt().setUrl("http://examples.com#givenext_parent"); + given2.addUndeclaredExtension(given2ext); + given2ext.addUndeclaredExtension(new ExtensionDt().setUrl("http://examples.com#givenext_child").setValue(new StringDt("CHILD"))); + + String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient); + ourLog.info(output); + + String enc = ourCtx.newXmlParser().encodeResourceToString(patient); + assertThat(enc, containsString("")); + assertThat(enc, containsString("")); + assertThat( + enc, + containsString("")); + assertThat(enc, containsString("")); + assertThat(enc, containsString("")); + + /* + * Now parse this back + */ + + Patient parsed =ourCtx.newXmlParser().parseResource(Patient.class, enc); + ext = parsed.getUndeclaredExtensions().get(0); + assertEquals("http://example.com/extensions#someext", ext.getUrl()); + assertEquals("2011-01-02T11:13:15", ((DateTimeDt)ext.getValue()).getValueAsString()); + + parent = patient.getUndeclaredExtensions().get(1); + assertEquals("http://example.com#parent", parent.getUrl()); + assertNull(parent.getValue()); + child1 = parent.getExtension().get(0); + assertEquals( "http://example.com#child", child1.getUrl()); + assertEquals("value1", ((StringDt)child1.getValue()).getValueAsString()); + child2 = parent.getExtension().get(1); + assertEquals( "http://example.com#child", child2.getUrl()); + assertEquals("value2", ((StringDt)child2.getValue()).getValueAsString()); + + modExt = parsed.getUndeclaredModifierExtensions().get(0); + assertEquals("http://example.com/extensions#modext", modExt.getUrl()); + assertEquals("1995-01-02", ((DateDt)modExt.getValue()).getValueAsString()); + + name = parsed.getName().get(0); + + ext2 = name.getGiven().get(0).getUndeclaredExtensions().get(0); + assertEquals("http://examples.com#givenext", ext2.getUrl()); + assertEquals("given", ((StringDt)ext2.getValue()).getValueAsString()); + + given2ext = name.getGiven().get(1).getUndeclaredExtensions().get(0); + assertEquals("http://examples.com#givenext_parent", given2ext.getUrl()); + assertNull(given2ext.getValue()); + ExtensionDt given2ext2 = given2ext.getExtension().get(0); + assertEquals("http://examples.com#givenext_child", given2ext2.getUrl()); + assertEquals("CHILD", ((StringDt)given2ext2.getValue()).getValue()); + + } + + + } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java index ac37009394b..e0b35e711b4 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java @@ -475,21 +475,4 @@ public class IncludeTest { } - public static void main(String[] args) { - - Organization org = new Organization(); - org.setId("Organization/65546"); - org.getNameElement().setValue("Contained Test Organization"); - - Patient patient = new Patient(); - patient.setId("Patient/1333"); - patient.addIdentifier().setSystem("urn:mrns").setValue("253345"); - patient.getManagingOrganization().setResource(patient); - - System.out.println(new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(patient)); - - patient.getManagingOrganization().getReference(); - - } - } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/validation/ResourceValidatorTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/validation/ResourceValidatorTest.java index de90f1a85d4..e95587dfcbb 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/validation/ResourceValidatorTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/validation/ResourceValidatorTest.java @@ -27,7 +27,7 @@ import ca.uhn.fhir.model.primitive.DateTimeDt; public class ResourceValidatorTest { - private static FhirContext ourCtx = new FhirContext(); + private static FhirContext ourCtx = FhirContext.forDstu2(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceValidatorTest.class); @SuppressWarnings("deprecation") diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/FhirDstu2Hl7Org.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/FhirDstu2Hl7Org.java index b43f57f2985..c007c5a0e57 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/FhirDstu2Hl7Org.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/FhirDstu2Hl7Org.java @@ -26,12 +26,15 @@ import java.util.ArrayList; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.conf.ServerConformanceProvider; import org.hl7.fhir.instance.conf.ServerProfileProvider; +import org.hl7.fhir.instance.model.Extension; import org.hl7.fhir.instance.model.Profile; import org.hl7.fhir.instance.model.Reference; +import org.hl7.fhir.instance.model.api.IBaseExtension; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.RuntimeResourceDefinition; +import ca.uhn.fhir.model.api.ExtensionDt; import ca.uhn.fhir.model.api.IFhirVersion; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.base.composite.BaseContainedDt; diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/BackboneElement.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/BackboneElement.java index 7c1cbede262..8558a11dcc6 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/BackboneElement.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/BackboneElement.java @@ -37,11 +37,13 @@ import org.hl7.fhir.instance.model.annotations.Child; import org.hl7.fhir.instance.model.annotations.Description; import org.hl7.fhir.instance.model.annotations.DatatypeDef; import org.hl7.fhir.instance.model.api.IBackboneElement; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions; /** * Base definition for all elements that are defined inside a resource - but not those in a data type. */ @DatatypeDef(name="BackboneElement") -public abstract class BackboneElement extends Element implements IBackboneElement { +public abstract class BackboneElement extends Element implements IBackboneElement, IBaseHasExtensions, IBaseHasModifierExtensions { /** * May be used to represent additional information that is not part of the basic definition of the element, and that modifies the understanding of the element that contains it. Usually modifier elements provide negation or qualification. In order to make the use of extensions safe and manageable, there is a strict set of governance applied to the definition and use of extensions. Though any implementer is allowed to define an extension, there is a set of requirements that SHALL be met as part of the definition of the extension. Applications processing a resource are required to check for modifier extensions. diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/DomainResource.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/DomainResource.java index 74fa55b4636..0cdcebb4a41 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/DomainResource.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/DomainResource.java @@ -38,11 +38,13 @@ import org.hl7.fhir.instance.model.annotations.SearchParamDefinition; import org.hl7.fhir.instance.model.annotations.Block; import org.hl7.fhir.instance.model.annotations.Child; import org.hl7.fhir.instance.model.annotations.Description; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions; /** * A resource that includes narrative, extensions, and contained resources. */ @ResourceDef(name="DomainResource", profile="http://hl7.org/fhir/Profile/DomainResource") -public abstract class DomainResource extends Resource { +public abstract class DomainResource extends Resource implements IBaseHasExtensions, IBaseHasModifierExtensions { /** * A human-readable narrative that contains a summary of the resource, and may be used to represent the content of the resource to a human. The narrative need not encode all the structured data, but is required to contain sufficient detail to make it "clinically safe" for a human to just read the narrative. Resource definitions may define what content should be represented in the narrative to ensure clinical safety. diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Element.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Element.java index b6841533751..732337bccc2 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Element.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Element.java @@ -37,10 +37,11 @@ import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.instance.model.annotations.Child; import org.hl7.fhir.instance.model.annotations.Description; import org.hl7.fhir.instance.model.annotations.DatatypeDef; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; /** * Base definition for all elements in a resource. */ -public abstract class Element extends Base { +public abstract class Element extends Base implements IBaseHasExtensions { /** * unique id for the element within a resource (for internal references). diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/EnumFactory.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/EnumFactory.java index dc27717ca51..c722c39e25e 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/EnumFactory.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/EnumFactory.java @@ -1,5 +1,7 @@ package org.hl7.fhir.instance.model; +import org.hl7.fhir.instance.model.api.IBaseEnumFactory; + /* Copyright (c) 2011+, HL7, Inc @@ -33,7 +35,7 @@ POSSIBILITY OF SUCH DAMAGE. /** * Helper class to help manage generic enumerated types */ -public interface EnumFactory> { +public interface EnumFactory> extends IBaseEnumFactory { /** * Read an enumeration value from the string that represents it on the XML or JSON diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Enumeration.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Enumeration.java index 6eaa8ebd3d9..c0ed0894587 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Enumeration.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Enumeration.java @@ -1,6 +1,7 @@ package org.hl7.fhir.instance.model; import org.hl7.fhir.instance.model.annotations.DatatypeDef; +import org.hl7.fhir.instance.model.api.IBaseEnumFactory; /* Copyright (c) 2011+, HL7, Inc @@ -50,6 +51,16 @@ public class Enumeration> extends PrimitiveType { myEnumFactory = theEnumFactory; } + /** + * Constructor + */ + public Enumeration(IBaseEnumFactory theEnumFactory) { + if (theEnumFactory == null) + throw new IllegalArgumentException("An enumeration factory must be provided"); + myEnumFactory = (EnumFactory) theEnumFactory; + } + + /** * Constructor */ diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Extension.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Extension.java index 22d10a27fc3..8d10bb2c5bc 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Extension.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Extension.java @@ -36,12 +36,14 @@ import java.util.List; import org.hl7.fhir.instance.model.annotations.Child; import org.hl7.fhir.instance.model.annotations.DatatypeDef; import org.hl7.fhir.instance.model.annotations.Description; +import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; /** * Optional Extensions Element - found in all resources. */ @DatatypeDef(name="Extension") -public class Extension extends Element implements IBaseExtension { +public class Extension extends Element implements IBaseExtension, IBaseHasExtensions { /** * Source of the definition for the extension code - a logical name or a URL. @@ -175,6 +177,11 @@ public class Extension extends Element implements IBaseExtension { ; } + @Override + public Extension setValue(IBaseDatatype theValue) { + return setValue((Type)theValue); + } + } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Identifier.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Identifier.java index 49bcfad5ba0..c749600981c 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Identifier.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/Identifier.java @@ -146,7 +146,7 @@ public class Identifier extends Type implements ICompositeType { /** * The purpose of this identifier. */ - @Child(name="use", type={CodeType.class}, order=0, min=0, max=1) + @Child(name="use", type={CodeType.class}, order=0, min=0, max=1, enumFactory=IdentifierUseEnumFactory.class) @Description(shortDefinition="usual | official | temp | secondary (If known)", formalDefinition="The purpose of this identifier." ) protected Enumeration use; diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/PrimitiveType.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/PrimitiveType.java index d58917a643d..8344292e9e0 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/PrimitiveType.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/PrimitiveType.java @@ -2,8 +2,9 @@ package org.hl7.fhir.instance.model; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -public abstract class PrimitiveType extends Type implements IPrimitiveType { +public abstract class PrimitiveType extends Type implements IPrimitiveType, IBaseHasExtensions { private static final long serialVersionUID = 3L; diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/annotations/Child.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/annotations/Child.java index 97fac708847..bb0cda27e09 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/annotations/Child.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/model/annotations/Child.java @@ -34,7 +34,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.hl7.fhir.instance.model.Enumeration; import org.hl7.fhir.instance.model.IBase; +import org.hl7.fhir.instance.model.api.IBaseEnumFactory; /** * Field annotation for fields within resource and datatype definitions, indicating @@ -96,6 +98,12 @@ public @interface Child { */ Class[] type() default {}; + /** + * For children which accept an {@link Enumeration} as the type, this + * field indicates the type to use for the enum factory + */ + Class> enumFactory() default NoEnumFactory.class; + // Not implemented // /** // * This value is used when extending a built-in model class and defining a @@ -105,5 +113,23 @@ public @interface Child { // * HumanNameDt which adds extensions of your choosing) you could do that using a replacement field. // */ // String replaces() default ""; + + public static class NoEnumFactory implements IBaseEnumFactory> { + + private NoEnumFactory() { + // non instantiable + } + @Override + public Enum fromCode(String theCodeString) throws IllegalArgumentException { + return null; + } + + @Override + public String toCode(Enum theCode) { + return null; + } + + } + } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/model/ModelInheritanceTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/model/ModelInheritanceTest.java index 65a64de79d7..e695285dde4 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/model/ModelInheritanceTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/model/ModelInheritanceTest.java @@ -1,19 +1,24 @@ package ca.uhn.fhir.model; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import org.hl7.fhir.instance.model.Address; +import org.hl7.fhir.instance.model.BackboneElement; import org.hl7.fhir.instance.model.Base; import org.hl7.fhir.instance.model.BooleanType; import org.hl7.fhir.instance.model.Bundle; import org.hl7.fhir.instance.model.Coding; import org.hl7.fhir.instance.model.DecimalType; +import org.hl7.fhir.instance.model.DomainResource; +import org.hl7.fhir.instance.model.Element; import org.hl7.fhir.instance.model.Extension; import org.hl7.fhir.instance.model.IBase; import org.hl7.fhir.instance.model.ICompositeType; import org.hl7.fhir.instance.model.IPrimitiveType; import org.hl7.fhir.instance.model.IdType; +import org.hl7.fhir.instance.model.Identifier; import org.hl7.fhir.instance.model.IntegerType; import org.hl7.fhir.instance.model.Meta; import org.hl7.fhir.instance.model.Narrative; @@ -22,7 +27,9 @@ import org.hl7.fhir.instance.model.Reference; import org.hl7.fhir.instance.model.Resource; import org.hl7.fhir.instance.model.Timing; import org.hl7.fhir.instance.model.Type; +import org.hl7.fhir.instance.model.Identifier.IdentifierUseEnumFactory; import org.hl7.fhir.instance.model.annotations.Block; +import org.hl7.fhir.instance.model.annotations.Child; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBackboneElement; import org.hl7.fhir.instance.model.api.IBaseBooleanDatatype; @@ -30,6 +37,8 @@ import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseDecimalDatatype; import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions; import org.hl7.fhir.instance.model.api.IBaseIntegerDatatype; import org.hl7.fhir.instance.model.api.ICoding; import org.hl7.fhir.instance.model.api.IDatatypeElement; @@ -56,6 +65,9 @@ public class ModelInheritanceTest { * * ElementDefinition * * Backbone elements (eg .ElementDefinitionSlicingComponent) do not extend BackboneElement or have a @Block annotation for some reason + * + * Extension + * * Should URL not be StringType since it can't take extensions? * */ @@ -76,13 +88,20 @@ public class ModelInheritanceTest { public void testBase() { assertTrue(IBase.class.isAssignableFrom(Base.class)); } + + public void testIdentifierUse() throws Exception { + Child child = Identifier.class.getField("use").getAnnotation(Child.class); + assertEquals(IdentifierUseEnumFactory.class, child.enumFactory()); + } + /** * Should be "implements IBaseExtension" */ @Test public void testExtension() { assertTrue(IBaseExtension.class.isAssignableFrom(Extension.class)); + assertTrue(IBaseHasExtensions.class.isAssignableFrom(Extension.class)); } @Test @@ -108,6 +127,7 @@ public class ModelInheritanceTest { @Test public void testPrimitiveType() { assertTrue(IPrimitiveType.class.isAssignableFrom(PrimitiveType.class)); + assertTrue(IBaseHasExtensions.class.isAssignableFrom(PrimitiveType.class)); } @Test @@ -136,7 +156,20 @@ public class ModelInheritanceTest { @Test public void testBackboneElement() { - assertTrue(IBackboneElement.class.isAssignableFrom(IBackboneElement.class)); + assertTrue(IBackboneElement.class.isAssignableFrom(BackboneElement.class)); + assertTrue(IBaseHasExtensions.class.isAssignableFrom(BackboneElement.class)); + assertTrue(IBaseHasModifierExtensions.class.isAssignableFrom(BackboneElement.class)); + } + + @Test + public void testElement() { + assertTrue(IBaseHasExtensions.class.isAssignableFrom(Element.class)); + } + + @Test + public void testDomainResource() { + assertTrue(IBaseHasExtensions.class.isAssignableFrom(DomainResource.class)); + assertTrue(IBaseHasModifierExtensions.class.isAssignableFrom(DomainResource.class)); } @Test diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java index 54e9bedcf2e..881c80f8b41 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java @@ -1,7 +1,16 @@ package ca.uhn.fhir.parser; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.stringContainsInOrder; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.IOException; import java.io.OutputStreamWriter; @@ -25,15 +34,16 @@ import org.hl7.fhir.instance.model.Bundle; import org.hl7.fhir.instance.model.Bundle.BundleEntryComponent; import org.hl7.fhir.instance.model.Conformance; import org.hl7.fhir.instance.model.DateTimeType; +import org.hl7.fhir.instance.model.DateType; import org.hl7.fhir.instance.model.DecimalType; import org.hl7.fhir.instance.model.DiagnosticReport; import org.hl7.fhir.instance.model.Extension; import org.hl7.fhir.instance.model.HumanName; import org.hl7.fhir.instance.model.IBaseResource; import org.hl7.fhir.instance.model.IPrimitiveType; +import org.hl7.fhir.instance.model.Identifier.IdentifierUse; import org.hl7.fhir.instance.model.InstantType; import org.hl7.fhir.instance.model.List_; -import org.hl7.fhir.instance.model.Identifier.IdentifierUse; import org.hl7.fhir.instance.model.Narrative.NarrativeStatus; import org.hl7.fhir.instance.model.Observation; import org.hl7.fhir.instance.model.Organization; @@ -83,6 +93,119 @@ public class JsonParserTest { } + + @Test + public void testEncodeAndParseExtensions() throws Exception { + + Patient patient = new Patient(); + patient.addIdentifier().setUse(IdentifierUse.OFFICIAL).setSystem("urn:example").setValue("7000135"); + + Extension ext = new Extension(); + ext.setUrl("http://example.com/extensions#someext"); + ext.setValue(new DateTimeType("2011-01-02T11:13:15")); + patient.getExtension().add(ext); + + Extension parent = new Extension().setUrl("http://example.com#parent"); + patient.getExtension().add(parent); + Extension child1 = new Extension().setUrl( "http://example.com#child").setValue( new StringType("value1")); + parent.getExtension().add(child1); + Extension child2 = new Extension().setUrl( "http://example.com#child").setValue( new StringType("value2")); + parent.getExtension().add(child2); + + Extension modExt = new Extension(); + modExt.setUrl("http://example.com/extensions#modext"); + modExt.setValue(new DateType("1995-01-02")); + patient.getModifierExtension().add(modExt); + + HumanName name = patient.addName(); + name.addFamily("Blah"); + StringType given = name.addGivenElement(); + given.setValue("Joe"); + Extension ext2 = new Extension().setUrl("http://examples.com#givenext").setValue(new StringType("given")); + given.getExtension().add(ext2); + + StringType given2 = name.addGivenElement(); + given2.setValue("Shmoe"); + Extension given2ext = new Extension().setUrl("http://examples.com#givenext_parent"); + given2.getExtension().add(given2ext); + given2ext.addExtension().setUrl("http://examples.com#givenext_child").setValue(new StringType("CHILD")); + + String output = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient); + ourLog.info(output); + + String enc = ourCtx.newJsonParser().encodeResourceToString(patient); + assertThat(enc, org.hamcrest.Matchers.stringContainsInOrder("{\"resourceType\":\"Patient\",", + "\"extension\":[{\"url\":\"http://example.com/extensions#someext\",\"valueDateTime\":\"2011-01-02T11:13:15\"}", + "{\"url\":\"http://example.com#parent\",\"extension\":[{\"url\":\"http://example.com#child\",\"valueString\":\"value1\"},{\"url\":\"http://example.com#child\",\"valueString\":\"value2\"}]}" + )); + assertThat(enc, org.hamcrest.Matchers.stringContainsInOrder("\"modifierExtension\":[" + + "{" + + "\"url\":\"http://example.com/extensions#modext\"," + + "\"valueDate\":\"1995-01-02\"" + + "}" + + "],")); + assertThat(enc, containsString("\"_given\":[" + + "{" + + "\"extension\":[" + + "{" + + "\"url\":\"http://examples.com#givenext\"," + + "\"valueString\":\"given\"" + + "}" + + "]" + + "}," + + "{" + + "\"extension\":[" + + "{" + + "\"url\":\"http://examples.com#givenext_parent\"," + + "\"extension\":[" + + "{" + + "\"url\":\"http://examples.com#givenext_child\"," + + "\"valueString\":\"CHILD\"" + + "}" + + "]" + + "}" + + "]" + + "}")); + + /* + * Now parse this back + */ + + Patient parsed =ourCtx.newJsonParser().parseResource(Patient.class, enc); + ext = parsed.getExtension().get(0); + assertEquals("http://example.com/extensions#someext", ext.getUrl()); + assertEquals("2011-01-02T11:13:15", ((DateTimeType)ext.getValue()).getValueAsString()); + + parent = patient.getExtension().get(1); + assertEquals("http://example.com#parent", parent.getUrl()); + assertNull(parent.getValue()); + child1 = parent.getExtension().get(0); + assertEquals( "http://example.com#child", child1.getUrl()); + assertEquals("value1", ((StringType)child1.getValue()).getValueAsString()); + child2 = parent.getExtension().get(1); + assertEquals( "http://example.com#child", child2.getUrl()); + assertEquals("value2", ((StringType)child2.getValue()).getValueAsString()); + + modExt = parsed.getModifierExtension().get(0); + assertEquals("http://example.com/extensions#modext", modExt.getUrl()); + assertEquals("1995-01-02", ((DateType)modExt.getValue()).getValueAsString()); + + name = parsed.getName().get(0); + + ext2 = name.getGiven().get(0).getExtension().get(0); + assertEquals("http://examples.com#givenext", ext2.getUrl()); + assertEquals("given", ((StringType)ext2.getValue()).getValueAsString()); + + given2ext = name.getGiven().get(1).getExtension().get(0); + assertEquals("http://examples.com#givenext_parent", given2ext.getUrl()); + assertNull(given2ext.getValue()); + Extension given2ext2 = given2ext.getExtension().get(0); + assertEquals("http://examples.com#givenext_child", given2ext2.getUrl()); + assertEquals("CHILD", ((StringType)given2ext2.getValue()).getValue()); + + } + + @Test public void testEncodeNonContained() { Organization org = new Organization(); @@ -740,6 +863,7 @@ public class JsonParserTest { enc, containsString("")); assertThat(enc, containsString("")); + assertThat(enc, containsString("")); } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java index fa45b0174f3..7c643b944bc 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java @@ -37,6 +37,7 @@ import org.hl7.fhir.instance.model.Composition; import org.hl7.fhir.instance.model.Conformance; import org.hl7.fhir.instance.model.Conformance.ConformanceRestResourceComponent; import org.hl7.fhir.instance.model.DateTimeType; +import org.hl7.fhir.instance.model.DateType; import org.hl7.fhir.instance.model.DecimalType; import org.hl7.fhir.instance.model.DiagnosticReport; import org.hl7.fhir.instance.model.DocumentManifest; @@ -876,7 +877,7 @@ public class XmlParserTest { } @Test - public void testMoreExtensions() throws Exception { + public void testEncodeAndParseExtensions() throws Exception { Patient patient = new Patient(); patient.addIdentifier().setUse(IdentifierUse.OFFICIAL).setSystem("urn:example").setValue("7000135"); @@ -884,14 +885,22 @@ public class XmlParserTest { Extension ext = new Extension(); ext.setUrl("http://example.com/extensions#someext"); ext.setValue(new DateTimeType("2011-01-02T11:13:15")); - - // Add the extension to the resource patient.getExtension().add(ext); - // END SNIPPET: resourceExtension - // START SNIPPET: resourceStringExtension + Extension parent = new Extension().setUrl("http://example.com#parent"); + patient.getExtension().add(parent); + Extension child1 = new Extension().setUrl( "http://example.com#child").setValue( new StringType("value1")); + parent.getExtension().add(child1); + Extension child2 = new Extension().setUrl( "http://example.com#child").setValue( new StringType("value2")); + parent.getExtension().add(child2); + + Extension modExt = new Extension(); + modExt.setUrl("http://example.com/extensions#modext"); + modExt.setValue(new DateType("1995-01-02")); + patient.getModifierExtension().add(modExt); + HumanName name = patient.addName(); - name.addFamily("Shmoe"); + name.addFamily("Blah"); StringType given = name.addGivenElement(); given.setValue("Joe"); Extension ext2 = new Extension().setUrl("http://examples.com#givenext").setValue(new StringType("given")); @@ -902,28 +911,55 @@ public class XmlParserTest { Extension given2ext = new Extension().setUrl("http://examples.com#givenext_parent"); given2.getExtension().add(given2ext); given2ext.addExtension().setUrl("http://examples.com#givenext_child").setValue(new StringType("CHILD")); - // END SNIPPET: resourceStringExtension - - // START SNIPPET: subExtension - Extension parent = new Extension().setUrl("http://example.com#parent"); - patient.getExtension().add(parent); - - Extension child1 = new Extension().setUrl( "http://example.com#child").setValue( new StringType("value1")); - parent.getExtension().add(child1); - - Extension child2 = new Extension().setUrl( "http://example.com#child").setValue( new StringType("value1")); - parent.getExtension().add(child2); - // END SNIPPET: subExtension String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient); ourLog.info(output); String enc = ourCtx.newXmlParser().encodeResourceToString(patient); - assertThat(enc, containsString("")); + assertThat(enc, containsString("")); + assertThat(enc, containsString("")); assertThat( enc, - containsString("")); - assertThat(enc, containsString("")); + containsString("")); + assertThat(enc, containsString("")); + assertThat(enc, containsString("")); + + /* + * Now parse this back + */ + + Patient parsed =ourCtx.newXmlParser().parseResource(Patient.class, enc); + ext = parsed.getExtension().get(0); + assertEquals("http://example.com/extensions#someext", ext.getUrl()); + assertEquals("2011-01-02T11:13:15", ((DateTimeType)ext.getValue()).getValueAsString()); + + parent = patient.getExtension().get(1); + assertEquals("http://example.com#parent", parent.getUrl()); + assertNull(parent.getValue()); + child1 = parent.getExtension().get(0); + assertEquals( "http://example.com#child", child1.getUrl()); + assertEquals("value1", ((StringType)child1.getValue()).getValueAsString()); + child2 = parent.getExtension().get(1); + assertEquals( "http://example.com#child", child2.getUrl()); + assertEquals("value2", ((StringType)child2.getValue()).getValueAsString()); + + modExt = parsed.getModifierExtension().get(0); + assertEquals("http://example.com/extensions#modext", modExt.getUrl()); + assertEquals("1995-01-02", ((DateType)modExt.getValue()).getValueAsString()); + + name = parsed.getName().get(0); + + ext2 = name.getGiven().get(0).getExtension().get(0); + assertEquals("http://examples.com#givenext", ext2.getUrl()); + assertEquals("given", ((StringType)ext2.getValue()).getValueAsString()); + + given2ext = name.getGiven().get(1).getExtension().get(0); + assertEquals("http://examples.com#givenext_parent", given2ext.getUrl()); + assertNull(given2ext.getValue()); + Extension given2ext2 = given2ext.getExtension().get(0); + assertEquals("http://examples.com#givenext_child", given2ext2.getUrl()); + assertEquals("CHILD", ((StringType)given2ext2.getValue()).getValue()); + } @Test diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dev/FhirDev.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dev/FhirDev.java index 4ff8d686c22..4fc2a43ac68 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dev/FhirDev.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dev/FhirDev.java @@ -24,6 +24,7 @@ import java.io.InputStream; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.IBaseResource; +import org.hl7.fhir.instance.model.api.IBaseExtension; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirVersionEnum; @@ -107,5 +108,11 @@ public class FhirDev implements IFhirVersion { } + @Override + public IBaseExtension newExtension() { + return null; + } + + } diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java index 66bd2e7837f..d3b0383ada0 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java @@ -35,6 +35,7 @@ import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.text.WordUtils; import org.hl7.fhir.instance.model.IBaseResource; +import org.hl7.fhir.instance.model.api.IBaseExtension; import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.BaseRuntimeDeclaredChildDefinition; @@ -379,6 +380,11 @@ public class FhirDstu1 implements IFhirVersion { return ContainedDt.class; } + @Override + public IBaseExtension newExtension() { + return null; + } + } diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java index 255585d26e8..54fc0759cb3 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java @@ -23,6 +23,7 @@ package ca.uhn.fhir.model.dstu2; import java.io.InputStream; import org.hl7.fhir.instance.model.IBaseResource; +import org.hl7.fhir.instance.model.api.IBaseExtension; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirVersionEnum; @@ -92,4 +93,9 @@ public class FhirDstu2 implements IFhirVersion { throw new UnsupportedOperationException(); } + @Override + public IBaseExtension newExtension() { + return null; + } + } diff --git a/src/site/xdoc/doc_rest_server.xml b/src/site/xdoc/doc_rest_server.xml index 9d24dda5776..7f18525bfc3 100644 --- a/src/site/xdoc/doc_rest_server.xml +++ b/src/site/xdoc/doc_rest_server.xml @@ -141,6 +141,29 @@ + +

    + The server will return data in a number of places that includes the + complete "identity" of a resource. Identity in this case refers to the + web address that a user can use to access the resource. +

    +

    + For instance, if your server is hosted at http://foo.com/fhir + and your resource provider returns a Patient resource with the ID "123", + the server should translate that ID to "http://foo.com/fhir/Patient/123". +

    +

    + The server will attempt to determine what the base URL should be based on + what the request it receives looks like, but if it is not getting + the right address you may wish to hardcode the base URL, as shown + in the following example: +

    + + + + +
    +