Fix #315 - Allow declaring of custom types for specific profiles

This commit is contained in:
James Agnew 2016-03-22 11:46:08 +01:00
parent 5895881fa5
commit 1adfc4b4d9
58 changed files with 2556 additions and 11061 deletions

View File

@ -0,0 +1,7 @@
package example;
import org.hl7.fhir.dstu3.model.Observation;
public class CustomObservation extends Observation {
}

View File

@ -12,7 +12,7 @@ import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
public class Extensions { public class ExtensionsDstu2 {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static void main(String[] args) throws DataFormatException, IOException { public static void main(String[] args) throws DataFormatException, IOException {

View File

@ -0,0 +1,140 @@
package example;
import java.io.IOException;
import java.util.List;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.Extension;
import org.hl7.fhir.dstu3.model.HumanName;
import org.hl7.fhir.dstu3.model.Identifier.IdentifierUse;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.StringType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.client.IGenericClient;
public class ExtensionsDstu3 {
public void customType() {
IGenericClient client = FhirContext.forDstu3().newRestfulGenericClient("http://foo");
//START SNIPPET: customTypeClientSimple
// Create an example patient
MyPatient custPatient = new MyPatient();
custPatient.addName().addFamily("Smith").addGiven("John");
custPatient.setPetName(new StringType("Rover")); // populate the extension
// Create the resource like normal
client.create().resource(custPatient).execute();
// You can also read the resource back like normal
custPatient = client.read().resource(MyPatient.class).withId("123").execute();
//END SNIPPET: customTypeClientSimple
}
public void customTypeDeclared() {
//START SNIPPET: customTypeClientDeclared
FhirContext ctx = FhirContext.forDstu3();
// Instruct the context that if it receives a resource which
// claims to conform to the given profile (by URL), it should
// use the MyPatient type to parse this resource
ctx.setDefaultTypeForProfile("http://example.com/StructureDefinition/mypatient", MyPatient.class);
// You can declare as many default types as you like
ctx.setDefaultTypeForProfile("http://foo.com/anotherProfile", CustomObservation.class);
// Create a client
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu3");
// You can also read the resource back like normal
Patient patient = client.read().resource(Patient.class).withId("123").execute();
if (patient instanceof MyPatient) {
// If the server supplied a resource which declared to conform
// to the given profile, MyPatient will have been returned so
// process it differently..
}
//END SNIPPET: customTypeClientDeclared
}
@SuppressWarnings("unused")
public static void main(String[] args) throws DataFormatException, IOException {
// START SNIPPET: resourceExtension
// Create an example patient
Patient patient = new Patient();
patient.addIdentifier().setUse(IdentifierUse.OFFICIAL).setSystem("urn:example").setValue("7000135");
// Create an extension
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.addExtension(ext);
//END SNIPPET: resourceExtension
//START SNIPPET: resourceStringExtension
// Continuing the example from above, we will add a name to the patient, and then
// add an extension to part of that name
HumanName name = patient.addName();
name.addFamily("Shmoe");
// Add a new "given name", which is of type String
StringType given = name.addGivenElement();
given.setValue("Joe");
// Create an extension and add it to the String
Extension givenExt = new Extension("http://examples.com#moreext", new StringType("Hello"));
given.addExtension(givenExt);
//END SNIPPET: resourceStringExtension
FhirContext ctx = FhirContext.forDstu3();
String output = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
System.out.println(output);
//START SNIPPET: parseExtension
// Get all extensions (modifier or not) for a given URL
List<Extension> resourceExts = patient.getExtensionsByUrl("http://fooextensions.com#exts");
// Get all non-modifier extensions regardless of URL
List<Extension> nonModExts = patient.getExtension();
//Get all non-modifier extensions regardless of URL
List<Extension> modExts = patient.getModifierExtension();
//END SNIPPET: parseExtension
}
public void foo() {
//START SNIPPET: subExtension
Patient patient = new Patient();
// Add an extension (initially with no contents) to the resource
Extension parent = new Extension("http://example.com#parent");
patient.addExtension(parent);
// Add two extensions as children to the parent extension
Extension child1 = new Extension("http://example.com#childOne", new StringType("value1"));
parent.addExtension(child1);
Extension child2 = new Extension("http://example.com#chilwo", new StringType("value1"));
parent.addExtension(child2);
//END SNIPPET: subExtension
}
}

View File

@ -4,13 +4,14 @@ package example;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.StringType;
import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension; import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.util.ElementUtil; import ca.uhn.fhir.util.ElementUtil;
/** /**
@ -25,6 +26,8 @@ import ca.uhn.fhir.util.ElementUtil;
@ResourceDef(name="Patient", profile="http://example.com/StructureDefinition/mypatient") @ResourceDef(name="Patient", profile="http://example.com/StructureDefinition/mypatient")
public class MyPatient extends Patient { public class MyPatient extends Patient {
private static final long serialVersionUID = 1L;
/** /**
* Each extension is defined in a field. Any valid HAPI Data Type * Each extension is defined in a field. Any valid HAPI Data Type
* can be used for the field type. Note that the [name=""] attribute * can be used for the field type. Note that the [name=""] attribute
@ -34,7 +37,7 @@ public class MyPatient extends Patient {
@Child(name="petName") @Child(name="petName")
@Extension(url="http://example.com/dontuse#petname", definedLocally=false, isModifier=false) @Extension(url="http://example.com/dontuse#petname", definedLocally=false, isModifier=false)
@Description(shortDefinition="The name of the patient's favourite pet") @Description(shortDefinition="The name of the patient's favourite pet")
private StringDt myPetName; private StringType myPetName;
/** /**
* The second example extension uses a List type to provide * The second example extension uses a List type to provide
@ -46,7 +49,7 @@ public class MyPatient extends Patient {
@Child(name="importantDates", max=Child.MAX_UNLIMITED) @Child(name="importantDates", max=Child.MAX_UNLIMITED)
@Extension(url="http://example.com/dontuse#importantDates", definedLocally=false, isModifier=true) @Extension(url="http://example.com/dontuse#importantDates", definedLocally=false, isModifier=true)
@Description(shortDefinition="Some dates of note for this patient") @Description(shortDefinition="Some dates of note for this patient")
private List<DateTimeDt> myImportantDates; private List<DateTimeType> myImportantDates;
/** /**
* It is important to override the isEmpty() method, adding a check for any * It is important to override the isEmpty() method, adding a check for any
@ -67,28 +70,28 @@ public class MyPatient extends Patient {
********/ ********/
/** Getter for important dates */ /** Getter for important dates */
public List<DateTimeDt> getImportantDates() { public List<DateTimeType> getImportantDates() {
if (myImportantDates==null) { if (myImportantDates==null) {
myImportantDates = new ArrayList<DateTimeDt>(); myImportantDates = new ArrayList<DateTimeType>();
} }
return myImportantDates; return myImportantDates;
} }
/** Getter for pet name */ /** Getter for pet name */
public StringDt getPetName() { public StringType getPetName() {
if (myPetName == null) { if (myPetName == null) {
myPetName = new StringDt(); myPetName = new StringType();
} }
return myPetName; return myPetName;
} }
/** Setter for important dates */ /** Setter for important dates */
public void setImportantDates(List<DateTimeDt> theImportantDates) { public void setImportantDates(List<DateTimeType> theImportantDates) {
myImportantDates = theImportantDates; myImportantDates = theImportantDates;
} }
/** Setter for pet name */ /** Setter for pet name */
public void setPetName(StringDt thePetName) { public void setPetName(StringType thePetName) {
myPetName = thePetName; myPetName = thePetName;
} }

View File

@ -18,7 +18,7 @@ import ca.uhn.fhir.parser.IParser;
public class MyPatientUse { public class MyPatientUse {
@ResourceDef @ResourceDef()
public static class MyPatient extends Patient { public static class MyPatient extends Patient {
@Child(name="petName") @Child(name="petName")

View File

@ -6,22 +6,16 @@
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="org.eclipse.jst.component.nondependency" value=""/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry combineaccessrules="false" kind="src" path="/hapi-fhir-base"/>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/> <classpathentry kind="output" path="target/classes"/>
</classpath> </classpath>

View File

@ -5,6 +5,16 @@
<projects> <projects>
</projects> </projects>
<buildSpec> <buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.common.project.facet.core.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand> <buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name> <name>org.eclipse.m2e.core.maven2Builder</name>
<arguments> <arguments>
@ -13,5 +23,7 @@
</buildSpec> </buildSpec>
<natures> <natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature> <nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures> </natures>
</projectDescription> </projectDescription>

View File

@ -0,0 +1,7 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.7

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
<installed facet="java" version="1.7"/>
</faceted-project>

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.parser; package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import org.hamcrest.core.StringContains; import org.hamcrest.core.StringContains;
@ -22,7 +23,10 @@ public class MultiVersionJsonParserTest {
String str = FhirContext.forDstu2().newJsonParser().encodeResourceToString(p); String str = FhirContext.forDstu2().newJsonParser().encodeResourceToString(p);
ourLog.info(str); ourLog.info(str);
assertThat(str, StringContains.containsString("{\"resourceType\":\"Patient\",\"extension\":[{\"url\":\"http://foo#ext\",\"valueQuantity\":{\"value\":2.2}}],\"identifier\":[{\"system\":\"urn:sys\",\"value\":\"001\"}]}")); assertThat(str, stringContainsInOrder(
"{\"resourceType\":\"Patient\"",
"\"extension\":[{\"url\":\"http://foo#ext\",\"valueQuantity\":{\"value\":2.2}}]",
"\"identifier\":[{\"system\":\"urn:sys\",\"value\":\"001\"}]"));
} }
} }

View File

@ -50,6 +50,7 @@ import ca.uhn.fhir.rest.client.IRestfulClientFactory;
import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory; import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory;
import ca.uhn.fhir.rest.client.api.IBasicClient; import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient; import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory; import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.VersionUtil; import ca.uhn.fhir.util.VersionUtil;
@ -77,7 +78,9 @@ public class FhirContext {
private static final List<Class<? extends IBaseResource>> EMPTY_LIST = Collections.emptyList(); private static final List<Class<? extends IBaseResource>> EMPTY_LIST = Collections.emptyList();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirContext.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirContext.class);
private AddProfileTagEnum myAddProfileTagWhenEncoding = AddProfileTagEnum.ONLY_FOR_CUSTOM;
private volatile Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinition = Collections.emptyMap(); private volatile Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinition = Collections.emptyMap();
private Map<String, Class<? extends IBaseResource>> myDefaultTypeForProfile = new HashMap<String, Class<? extends IBaseResource>>();
private volatile Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = Collections.emptyMap(); private volatile Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = Collections.emptyMap();
private HapiLocalizer myLocalizer = new HapiLocalizer(); private HapiLocalizer myLocalizer = new HapiLocalizer();
private volatile Map<String, BaseRuntimeElementDefinition<?>> myNameToElementDefinition = Collections.emptyMap(); private volatile Map<String, BaseRuntimeElementDefinition<?>> myNameToElementDefinition = Collections.emptyMap();
@ -144,6 +147,26 @@ public class FhirContext {
return getLocalizer().getMessage(FhirContext.class, "unknownResourceName", theResourceName, theVersion); return getLocalizer().getMessage(FhirContext.class, "unknownResourceName", theResourceName, theVersion);
} }
/**
* When encoding resources, this setting configures the parser to include
* an entry in the resource's metadata section which indicates which profile(s) the
* resource claims to conform to. The default is {@link AddProfileTagEnum#ONLY_FOR_CUSTOM}.
*
* @see #setAddProfileTagWhenEncoding(AddProfileTagEnum) for more information
*/
public AddProfileTagEnum getAddProfileTagWhenEncoding() {
return myAddProfileTagWhenEncoding;
}
/**
* Returns the default resource type for the given profile
*
* @see #setDefaultTypeForProfile(String, Class)
*/
public Class<? extends IBaseResource> getDefaultTypeForProfile(String theProfile) {
return myDefaultTypeForProfile.get(theProfile);
}
/** /**
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library. * for extending the core library.
@ -298,14 +321,6 @@ public class FhirContext {
return myIdToResourceDefinition.values(); return myIdToResourceDefinition.values();
} }
/**
* Set the restful client factory
* @param theRestfulClientFactory
*/
public void setRestfulClientFactory(IRestfulClientFactory theRestfulClientFactory) {
this.myRestfulClientFactory = theRestfulClientFactory;
}
/** /**
* Get the restful client factory. If no factory has been set, this will be initialized with * Get the restful client factory. If no factory has been set, this will be initialized with
* a new ApacheRestfulClientFactory. * a new ApacheRestfulClientFactory.
@ -326,6 +341,17 @@ public class FhirContext {
return myVersion; return myVersion;
} }
/**
* Returns <code>true</code> if any default types for specific profiles have been defined
* within this context.
*
* @see #setDefaultTypeForProfile(String, Class)
* @see #getDefaultTypeForProfile(String)
*/
public boolean hasDefaultTypeForProfile() {
return !myDefaultTypeForProfile.isEmpty();
}
/** /**
* This method should be considered experimental and will likely change in future releases * This method should be considered experimental and will likely change in future releases
* of HAPI. Use with caution! * of HAPI. Use with caution!
@ -473,6 +499,50 @@ public class FhirContext {
return classToElementDefinition; return classToElementDefinition;
} }
/**
* When encoding resources, this setting configures the parser to include
* an entry in the resource's metadata section which indicates which profile(s) the
* resource claims to conform to. The default is {@link AddProfileTagEnum#ONLY_FOR_CUSTOM}.
* <p>
* This feature is intended for situations where custom resource types are being used,
* avoiding the need to manually add profile declarations for these custom types.
* </p>
* <p>
* See <a href="http://jamesagnew.gihhub.io/hapi-fhir/doc_extensions.html">Profiling and Extensions</a>
* for more information on using custom types.
* </p>
* <p>
* Note that this feature automatically adds the profile, but leaves any profile tags
* which have been manually added in place as well.
* </p>
*
* @param theAddProfileTagWhenEncoding
* The add profile mode (must not be <code>null</code>)
*/
public void setAddProfileTagWhenEncoding(AddProfileTagEnum theAddProfileTagWhenEncoding) {
Validate.notNull(theAddProfileTagWhenEncoding, "theAddProfileTagWhenEncoding must not be null");
myAddProfileTagWhenEncoding = theAddProfileTagWhenEncoding;
}
/**
* Sets the default type which will be used when parsing a resource that is found to be
* of the given profile.
* <p>
* For example, this method is invoked with the profile string of
* <code>"http://example.com/some_patient_profile"</code> and the type of <code>MyPatient.class</code>,
* if the parser is parsing a resource and finds that it declares that it conforms to that profile,
* the <code>MyPatient</code> type will be used unless otherwise specified.
* </p>
*
* @param theProfile The profile string, e.g. <code>"http://example.com/some_patient_profile"</code>. Must not be <code>null</code> or empty.
* @param theClass The resource type. Must not be <code>null</code> or empty.
*/
public void setDefaultTypeForProfile(String theProfile, Class<? extends IBaseResource> theClass) {
Validate.notBlank(theProfile, "theProfile must not be null or empty");
Validate.notNull(theClass, "theProfile must not be null");
myDefaultTypeForProfile.put(theProfile, theClass);
}
/** /**
* This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with * This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with
* caution * caution
@ -495,6 +565,14 @@ public class FhirContext {
myParserErrorHandler = theParserErrorHandler; myParserErrorHandler = theParserErrorHandler;
} }
/**
* Set the restful client factory
* @param theRestfulClientFactory
*/
public void setRestfulClientFactory(IRestfulClientFactory theRestfulClientFactory) {
this.myRestfulClientFactory = theRestfulClientFactory;
}
@SuppressWarnings({ "cast" }) @SuppressWarnings({ "cast" })
private List<Class<? extends IElement>> toElementList(Collection<Class<? extends IBaseResource>> theResourceTypes) { private List<Class<? extends IElement>> toElementList(Collection<Class<? extends IBaseResource>> theResourceTypes) {
if (theResourceTypes == null) { if (theResourceTypes == null) {
@ -508,21 +586,28 @@ public class FhirContext {
} }
/** /**
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU1} * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU1 DSTU1}
*/ */
public static FhirContext forDstu1() { public static FhirContext forDstu1() {
return new FhirContext(FhirVersionEnum.DSTU1); return new FhirContext(FhirVersionEnum.DSTU1);
} }
/** /**
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU 2} * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU2}
*/ */
public static FhirContext forDstu2() { public static FhirContext forDstu2() {
return new FhirContext(FhirVersionEnum.DSTU2); return new FhirContext(FhirVersionEnum.DSTU2);
} }
/** /**
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU3 DSTU 3} * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2_HL7ORG DSTU2} (using the Reference Implementation Structures)
*/
public static FhirContext forDstu2Hl7Org() {
return new FhirContext(FhirVersionEnum.DSTU2_HL7ORG);
}
/**
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU3 DSTU3}
* *
* @since 1.4 * @since 1.4
*/ */
@ -530,10 +615,6 @@ public class FhirContext {
return new FhirContext(FhirVersionEnum.DSTU3); return new FhirContext(FhirVersionEnum.DSTU3);
} }
public static FhirContext forDstu2Hl7Org() {
return new FhirContext(FhirVersionEnum.DSTU2_HL7ORG);
}
private static Collection<Class<? extends IBaseResource>> toCollection(Class<? extends IBaseResource> theResourceType) { private static Collection<Class<? extends IBaseResource>> toCollection(Class<? extends IBaseResource> theResourceType) {
ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<Class<? extends IBaseResource>>(1); ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<Class<? extends IBaseResource>>(1);
retVal.add(theResourceType); retVal.add(theResourceType);

View File

@ -683,6 +683,14 @@ class ModelScanner {
} }
} }
Class<? extends IBaseResource> builtInType = myNameToResourceType.get(resourceName.toLowerCase());
boolean standardType = builtInType != null && builtInType.equals(theClass) == true;
if (primaryNameProvider) {
if (builtInType != null && builtInType.equals(theClass) == false) {
primaryNameProvider = false;
}
}
String resourceId = resourceDefinition.id(); String resourceId = resourceDefinition.id();
if (!isBlank(resourceId)) { if (!isBlank(resourceId)) {
if (myIdToResourceDefinition.containsKey(resourceId)) { if (myIdToResourceDefinition.containsKey(resourceId)) {
@ -691,7 +699,7 @@ class ModelScanner {
} }
} }
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition(myContext, resourceName, theClass, resourceDefinition, isStandardType(theClass)); RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition(myContext, resourceName, theClass, resourceDefinition, standardType);
myClassToElementDefinitions.put(theClass, resourceDef); myClassToElementDefinitions.put(theClass, resourceDef);
if (primaryNameProvider) { if (primaryNameProvider) {
if (resourceDef.getStructureVersion() == myVersion) { if (resourceDef.getStructureVersion() == myVersion) {

View File

@ -155,10 +155,6 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
return "Bundle".equals(getName()); return "Bundle".equals(getName());
} }
public boolean isStandardProfile() {
return myResourceProfile.startsWith("http://hl7.org/fhir/profiles");
}
@Override @Override
public void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) { public void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
super.sealAndInitialize(theContext, theClassToElementDefinitions); super.sealAndInitialize(theContext, theClassToElementDefinitions);

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.model.api; package ca.uhn.fhir.model.api;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
/* /*
* #%L * #%L
* HAPI FHIR - Core Library * HAPI FHIR - Core Library
@ -62,6 +64,15 @@ public interface IResource extends ICompositeElement, org.hl7.fhir.instance.mode
*/ */
CodeDt getLanguage(); CodeDt getLanguage();
/**
* Returns a view of the {@link #getResourceMetadata() resource metadata} map.
* Note that getters from this map return immutable objects, but the <code>addFoo()</code>
* and <code>setFoo()</code> methods may be used to modify metadata.
*
* @since 1.5
*/
IBaseMetaType getMeta();
/** /**
* Returns the metadata map for this object, creating it if neccesary. Metadata entries are used to get/set feed bundle entries, such as the resource version, or the last updated timestamp. * Returns the metadata map for this object, creating it if neccesary. Metadata entries are used to get/set feed bundle entries, such as the resource version, or the last updated timestamp.
* <p> * <p>
@ -72,10 +83,23 @@ public interface IResource extends ICompositeElement, org.hl7.fhir.instance.mode
*/ */
ResourceMetadataMap getResourceMetadata(); ResourceMetadataMap getResourceMetadata();
/**
* Returns a String representing the name of this Resource. This return value is not used for anything by HAPI itself, but is provided as a convenience to developers using the API.
*
* @return the name of this resource, e.g. "Patient", or "Observation"
*/
String getResourceName();
/**
* Returns the FHIR version represented by this structure
*/
@Override
public ca.uhn.fhir.context.FhirVersionEnum getStructureFhirVersionEnum();
/** /**
* Returns the narrative block for this resource * Returns the narrative block for this resource
*/ */
BaseNarrativeDt getText(); BaseNarrativeDt<?> getText();
/** /**
* Sets the ID of this resource. Note that this identifier is the URL (or a portion of the URL) used to access this resource, and is not the same thing as any business identifiers stored within the * Sets the ID of this resource. Note that this identifier is the URL (or a portion of the URL) used to access this resource, and is not the same thing as any business identifiers stored within the
@ -99,16 +123,4 @@ public interface IResource extends ICompositeElement, org.hl7.fhir.instance.mode
*/ */
void setResourceMetadata(ResourceMetadataMap theMap); void setResourceMetadata(ResourceMetadataMap theMap);
/**
* Returns a String representing the name of this Resource. This return value is not used for anything by HAPI itself, but is provided as a convenience to developers using the API.
*
* @return the name of this resource, e.g. "Patient", or "Observation"
*/
String getResourceName();
/**
* Returns the FHIR version represented by this structure
*/
public ca.uhn.fhir.context.FhirVersionEnum getStructureFhirVersionEnum();
} }

View File

@ -26,6 +26,7 @@ import java.net.URI;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IBaseCoding;
/** /**
* A single tag * A single tag
@ -34,7 +35,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
* {@link #getScheme() scheme} and * {@link #getScheme() scheme} and
* </p> * </p>
*/ */
public class Tag extends BaseElement implements IElement { public class Tag extends BaseElement implements IElement, IBaseCoding {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -59,10 +60,6 @@ public class Tag extends BaseElement implements IElement {
private String myScheme; private String myScheme;
private String myTerm; private String myTerm;
/**
* @deprecated Tags will become immutable in a future release, so this constructor should not be used.
*/
@Deprecated
public Tag() { public Tag() {
} }
@ -149,12 +146,7 @@ public class Tag extends BaseElement implements IElement {
/** /**
* Sets the label and returns a reference to this tag * Sets the label and returns a reference to this tag
*
* @deprecated Tags will become immutable in a future release of HAPI in order to facilitate
* ensuring that the TagList acts as an unordered set. Use the constructor to set this field when creating a new
* tag object
*/ */
@Deprecated
public Tag setLabel(String theLabel) { public Tag setLabel(String theLabel) {
myLabel = theLabel; myLabel = theLabel;
return this; return this;
@ -162,12 +154,7 @@ public class Tag extends BaseElement implements IElement {
/** /**
* Sets the scheme and returns a reference to this tag * Sets the scheme and returns a reference to this tag
*
* @deprecated Tags will become immutable in a future release of HAPI in order to facilitate
* ensuring that the TagList acts as an unordered set. Use the constructor to set this field when creating a new
* tag object
*/ */
@Deprecated
public Tag setScheme(String theScheme) { public Tag setScheme(String theScheme) {
myScheme = theScheme; myScheme = theScheme;
return this; return this;
@ -175,12 +162,7 @@ public class Tag extends BaseElement implements IElement {
/** /**
* Sets the term and returns a reference to this tag * Sets the term and returns a reference to this tag
*
* @deprecated Tags will become immutable in a future release of HAPI in order to facilitate
* ensuring that the TagList acts as an unordered set. Use the constructor to set this field when creating a new
* tag object
*/ */
@Deprecated
public Tag setTerm(String theTerm) { public Tag setTerm(String theTerm) {
myTerm = theTerm; myTerm = theTerm;
return this; return this;
@ -207,4 +189,37 @@ public class Tag extends BaseElement implements IElement {
return b.toString(); return b.toString();
} }
@Override
public String getCode() {
return getTerm();
}
@Override
public String getDisplay() {
return getLabel();
}
@Override
public String getSystem() {
return getScheme();
}
@Override
public IBaseCoding setCode(String theTerm) {
setTerm(myTerm);
return this;
}
@Override
public IBaseCoding setDisplay(String theLabel) {
setLabel(theLabel);
return this;
}
@Override
public IBaseCoding setSystem(String theScheme) {
setScheme(theScheme);
return this;
}
} }

View File

@ -53,6 +53,10 @@ public @interface ResourceDef {
/** /**
* The URL indicating the profile for this resource definition. If specified, this URL will be * The URL indicating the profile for this resource definition. If specified, this URL will be
* automatically added to the meta tag when the resource is serialized. * automatically added to the meta tag when the resource is serialized.
* <p>
* This URL should be fully qualified to indicate the complete URL of
* the profile being used, e.g. <code>http://example.com/fhir/StructureDefiniton/some_profile</code>
* </p>
*/ */
String profile() default ""; String profile() default "";

View File

@ -78,12 +78,14 @@ public abstract class BaseParser implements IParser {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParser.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParser.class);
private IIdType myEncodeForceResourceId;
private ContainedResources myContainedResources; private ContainedResources myContainedResources;
private FhirContext myContext; private FhirContext myContext;
private Set<String> myDontEncodeElements;
private boolean myDontEncodeElementsIncludesStars;
private Set<String> myEncodeElements; private Set<String> myEncodeElements;
private Set<String> myEncodeElementsAppliesToResourceTypes; private Set<String> myEncodeElementsAppliesToResourceTypes;
private boolean myEncodeElementsIncludesStars; private boolean myEncodeElementsIncludesStars;
private IIdType myEncodeForceResourceId;
private IParserErrorHandler myErrorHandler; private IParserErrorHandler myErrorHandler;
private boolean myOmitResourceId; private boolean myOmitResourceId;
private String myServerBaseUrl; private String myServerBaseUrl;
@ -91,10 +93,6 @@ public abstract class BaseParser implements IParser {
private boolean mySummaryMode; private boolean mySummaryMode;
private boolean mySuppressNarratives; private boolean mySuppressNarratives;
private Set<String> myDontEncodeElements;
private boolean myDontEncodeElementsIncludesStars;
/** /**
* Constructor * Constructor
* *
@ -439,6 +437,47 @@ public abstract class BaseParser implements IParser {
return tags; return tags;
} }
@SuppressWarnings("deprecation")
protected <T extends IPrimitiveType<String>> List<T> getProfileTagsForEncoding(IBaseResource theResource, List<T> theProfiles) {
switch (myContext.getAddProfileTagWhenEncoding()) {
case NEVER:
return theProfiles;
case ONLY_FOR_CUSTOM:
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
if (resDef.isStandardType()) {
return theProfiles;
}
break;
case ALWAYS:
break;
}
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
RuntimeResourceDefinition nextDef = myContext.getResourceDefinition(theResource);
String profile = nextDef.getResourceProfile(myServerBaseUrl);
if (isNotBlank(profile)) {
for (T next : theProfiles) {
if (profile.equals(next.getValue())) {
return theProfiles;
}
}
List<T> newList = new ArrayList<T>();
newList.addAll(theProfiles);
BaseRuntimeElementDefinition<?> idElement = myContext.getElementDefinition("id");
@SuppressWarnings("unchecked")
T newId = (T) idElement.newInstance();
newId.setValue(profile);
newList.add(newId);
return newList;
}
}
return theProfiles;
}
/** /**
* If set to <code>true</code> (default is <code>false</code>), narratives will not be included in the encoded * If set to <code>true</code> (default is <code>false</code>), narratives will not be included in the encoded
* values. * values.
@ -595,6 +634,17 @@ public abstract class BaseParser implements IParser {
filterCodingsWithNoCodeOrSystem(metaValue.getTag()); filterCodingsWithNoCodeOrSystem(metaValue.getTag());
filterCodingsWithNoCodeOrSystem(metaValue.getSecurity()); filterCodingsWithNoCodeOrSystem(metaValue.getSecurity());
List<? extends IPrimitiveType<String>> newProfileList = getProfileTagsForEncoding(theResource, metaValue.getProfile());
List<? extends IPrimitiveType<String>> oldProfileList = metaValue.getProfile();
if (oldProfileList != newProfileList) {
oldProfileList.clear();
for (IPrimitiveType<String> next : newProfileList) {
if (isNotBlank(next.getValue())) {
metaValue.addProfile(next.getValue());
}
}
}
if (shouldAddSubsettedTag()) { if (shouldAddSubsettedTag()) {
IBaseCoding coding = metaValue.addTag(); IBaseCoding coding = metaValue.addTag();
coding.setCode(Constants.TAG_SUBSETTED_CODE); coding.setCode(Constants.TAG_SUBSETTED_CODE);
@ -748,11 +798,11 @@ public abstract class BaseParser implements IParser {
} }
protected static <T> List<T> extractMetadataListNotNull(IResource resource, ResourceMetadataKeyEnum<List<T>> key) { protected static <T> List<T> extractMetadataListNotNull(IResource resource, ResourceMetadataKeyEnum<List<T>> key) {
List<T> securityLabels = key.get(resource); List<? extends T> securityLabels = key.get(resource);
if (securityLabels == null) { if (securityLabels == null) {
securityLabels = Collections.emptyList(); securityLabels = Collections.emptyList();
} }
return securityLabels; return new ArrayList<T>(securityLabels);
} }
static boolean hasExtensions(IBase theElement) { static boolean hasExtensions(IBase theElement) {

View File

@ -222,7 +222,8 @@ public interface IParser {
* <li><b>Patient</b> - Don't encode patient and all its children</li> * <li><b>Patient</b> - Don't encode patient and all its children</li>
* <li><b>Patient.name</b> - Don't encode the patient's name</li> * <li><b>Patient.name</b> - Don't encode the patient's name</li>
* <li><b>Patient.name.family</b> - Don't encode the patient's family name</li> * <li><b>Patient.name.family</b> - Don't encode the patient's family name</li>
* <li><b>*.text</b> - Don't encode the text element on any resource (only the very first position may contain a wildcard)</li> * <li><b>*.text</b> - Don't encode the text element on any resource (only the very first position may contain a
* wildcard)</li>
* </ul> * </ul>
* <p> * <p>
* DSTU2 note: Note that values including meta, such as <code>Patient.meta</code> * DSTU2 note: Note that values including meta, such as <code>Patient.meta</code>
@ -244,7 +245,8 @@ public interface IParser {
* <li><b>Patient</b> - Encode patient and all its children</li> * <li><b>Patient</b> - Encode patient and all its children</li>
* <li><b>Patient.name</b> - Encode only the patient's name</li> * <li><b>Patient.name</b> - Encode only the patient's name</li>
* <li><b>Patient.name.family</b> - Encode only the patient's family name</li> * <li><b>Patient.name.family</b> - Encode only the patient's family name</li>
* <li><b>*.text</b> - Encode the text element on any resource (only the very first position may contain a wildcard)</li> * <li><b>*.text</b> - Encode the text element on any resource (only the very first position may contain a
* wildcard)</li>
* </ul> * </ul>
* *
* @param theEncodeElements * @param theEncodeElements

View File

@ -838,7 +838,9 @@ public class JsonParser extends BaseParser implements IParser {
// Object securityLabelRawObj = // Object securityLabelRawObj =
List<BaseCodingDt> securityLabels = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.SECURITY_LABELS); List<BaseCodingDt> securityLabels = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.SECURITY_LABELS);
List<IdDt> profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES); List<? extends IIdType> profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES);
profiles = super.getProfileTagsForEncoding(resource, profiles);
TagList tags = getMetaTagsForEncoding(resource); TagList tags = getMetaTagsForEncoding(resource);
InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED); InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
IdDt resourceId = resource.getId(); IdDt resourceId = resource.getId();
@ -854,7 +856,7 @@ public class JsonParser extends BaseParser implements IParser {
if (profiles != null && profiles.isEmpty() == false) { if (profiles != null && profiles.isEmpty() == false) {
theEventWriter.writeStartArray("profile"); theEventWriter.writeStartArray("profile");
for (IdDt profile : profiles) { for (IIdType profile : profiles) {
if (profile != null && isNotBlank(profile.getValue())) { if (profile != null && isNotBlank(profile.getValue())) {
theEventWriter.write(profile.getValue()); theEventWriter.write(profile.getValue());
} }

View File

@ -43,6 +43,7 @@ import org.hl7.fhir.instance.model.api.IBaseElement;
import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions; import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseReference; import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IBaseXhtml; import org.hl7.fhir.instance.model.api.IBaseXhtml;
@ -1433,6 +1434,11 @@ class ParserState<T> {
} }
@Override
protected void populateTarget() {
// nothing
}
} }
private class ContainedResourcesStateHl7Org extends PreResourceState { private class ContainedResourcesStateHl7Org extends PreResourceState {
@ -1464,6 +1470,11 @@ class ParserState<T> {
def.getChildByName("contained").getMutator().addValue(preResCurrentElement, res); def.getChildByName("contained").getMutator().addValue(preResCurrentElement, res);
} }
@Override
protected void populateTarget() {
// nothing
}
} }
private class DeclaredExtensionState extends BaseState { private class DeclaredExtensionState extends BaseState {
@ -1969,6 +1980,8 @@ class ParserState<T> {
} }
} }
protected abstract void populateTarget();
public PreResourceState(PreResourceState thePreResourcesState, FhirVersionEnum theParentVersion) { public PreResourceState(PreResourceState thePreResourcesState, FhirVersionEnum theParentVersion) {
super(thePreResourcesState); super(thePreResourcesState);
Validate.notNull(theParentVersion); Validate.notNull(theParentVersion);
@ -2045,6 +2058,40 @@ class ParserState<T> {
public void wereBack() { public void wereBack() {
final boolean bundle = "Bundle".equals(myContext.getResourceDefinition(myInstance).getName()); final boolean bundle = "Bundle".equals(myContext.getResourceDefinition(myInstance).getName());
if (myContext.hasDefaultTypeForProfile()) {
IBaseMetaType meta = myInstance.getMeta();
Class<? extends IBaseResource> wantedProfileType = null;
String usedProfile = null;
for (IPrimitiveType<String> next : meta.getProfile()) {
if (isNotBlank(next.getValue())) {
wantedProfileType = myContext.getDefaultTypeForProfile(next.getValue());
if (wantedProfileType != null) {
usedProfile = next.getValue();
break;
}
}
}
if (wantedProfileType != null && !wantedProfileType.equals(myInstance.getClass())) {
if (myResourceType == null || myResourceType.isAssignableFrom(wantedProfileType)) {
ourLog.debug("Converting resource of type {} to type defined for profile \"{}\": {}", new Object[] {myInstance.getClass().getName(), usedProfile, wantedProfileType});
/*
* This isn't the most efficient thing really.. If we want a specific
* type we just re-parse into that type. The problem is that we don't know
* until we've parsed the resource which type we want to use because the
* profile declarations are in the text of the resource itself.
*
* At some point it would be good to write code which can present a view
* of one type backed by another type and use that.
*/
IParser parser = myContext.newJsonParser();
String asString = parser.encodeResourceToString(myInstance);
myInstance = parser.parseResource(wantedProfileType, asString);
}
}
}
FhirTerser terser = myContext.newTerser(); FhirTerser terser = myContext.newTerser();
terser.visit(myInstance, new IModelVisitor() { terser.visit(myInstance, new IModelVisitor() {
@ -2114,6 +2161,7 @@ class ParserState<T> {
} }
populateTarget();
} }
} }
@ -2141,14 +2189,19 @@ class ParserState<T> {
assert theResourceType == null || IResource.class.isAssignableFrom(theResourceType); assert theResourceType == null || IResource.class.isAssignableFrom(theResourceType);
} }
// @Override
// public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
// super.enteringNewElement(theNamespaceUri, theLocalPart);
// populateTarget();
// }
@Override @Override
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException { protected void populateTarget() {
super.enteringNewElement(theNamespaceUri, theLocalPart);
if (myEntry != null) { if (myEntry != null) {
myEntry.setResource((IResource) getCurrentElement()); myEntry.setResource((IResource) getCurrentElement());
} }
if (myMutator != null) { if (myMutator != null) {
myMutator.addValue(myTarget, getCurrentElement()); myMutator.setValue(myTarget, getCurrentElement());
} }
} }
@ -2192,10 +2245,9 @@ class ParserState<T> {
} }
@Override @Override
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException { protected void populateTarget() {
super.enteringNewElement(theNamespaceUri, theLocalPart);
if (myMutator != null) { if (myMutator != null) {
myMutator.addValue(myTarget, getCurrentElement()); myMutator.setValue(myTarget, getCurrentElement());
} }
} }

View File

@ -132,16 +132,6 @@ public class XmlParser extends BaseParser implements IParser {
} catch (XMLStreamException e1) { } catch (XMLStreamException e1) {
throw new DataFormatException(e1); throw new DataFormatException(e1);
} }
// XMLEventReader streamReader;
// try {
// streamReader = myXmlInputFactory.createXMLEventReader(theReader);
// } catch (XMLStreamException e) {
// throw new DataFormatException(e);
// } catch (FactoryConfigurationError e) {
// throw new ConfigurationException("Failed to initialize STaX event factory", e);
// }
// return streamReader;
} }
private XMLStreamWriter createXmlWriter(Writer theWriter) throws XMLStreamException { private XMLStreamWriter createXmlWriter(Writer theWriter) throws XMLStreamException {
@ -433,8 +423,6 @@ public class XmlParser extends BaseParser implements IParser {
theEventWriter.writeEndElement(); theEventWriter.writeEndElement();
} }
String bundleBaseUrl = theBundle.getLinkBase().getValue();
writeOptionalTagWithValue(theEventWriter, "type", theBundle.getType().getValue()); writeOptionalTagWithValue(theEventWriter, "type", theBundle.getType().getValue());
writeOptionalTagWithValue(theEventWriter, "total", theBundle.getTotalResults().getValueAsString()); writeOptionalTagWithValue(theEventWriter, "total", theBundle.getTotalResults().getValueAsString());
@ -841,7 +829,9 @@ public class XmlParser extends BaseParser implements IParser {
versionIdPart = ResourceMetadataKeyEnum.VERSION.get(resource); versionIdPart = ResourceMetadataKeyEnum.VERSION.get(resource);
} }
List<BaseCodingDt> securityLabels = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.SECURITY_LABELS); List<BaseCodingDt> securityLabels = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.SECURITY_LABELS);
List<IdDt> profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES); List<? extends IIdType> profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES);
profiles = super.getProfileTagsForEncoding(resource, profiles);
TagList tags = getMetaTagsForEncoding((resource)); TagList tags = getMetaTagsForEncoding((resource));
if (super.shouldEncodeResourceMeta(resource) && ElementUtil.isEmpty(versionIdPart, updated, securityLabels, tags, profiles) == false) { if (super.shouldEncodeResourceMeta(resource) && ElementUtil.isEmpty(versionIdPart, updated, securityLabels, tags, profiles) == false) {
@ -851,7 +841,7 @@ public class XmlParser extends BaseParser implements IParser {
writeOptionalTagWithValue(theEventWriter, "lastUpdated", updated.getValueAsString()); writeOptionalTagWithValue(theEventWriter, "lastUpdated", updated.getValueAsString());
} }
for (IdDt profile : profiles) { for (IIdType profile : profiles) {
theEventWriter.writeStartElement("profile"); theEventWriter.writeStartElement("profile");
theEventWriter.writeAttribute("value", profile.getValue()); theEventWriter.writeAttribute("value", profile.getValue());
theEventWriter.writeEndElement(); theEventWriter.writeEndElement();

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.server; package ca.uhn.fhir.rest.server;
import ca.uhn.fhir.context.FhirContext;
/* /*
* #%L * #%L
* HAPI FHIR - Core Library * HAPI FHIR - Core Library
@ -21,9 +23,9 @@ package ca.uhn.fhir.rest.server;
*/ */
/** /**
* RESTful server behaviour for automatically adding profile tags * RESTful server behaviour for automatically adding profile tags when serializing resources
* *
* @see RestfulServer#setAddProfileTag(AddProfileTagEnum) * @see FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)
*/ */
public enum AddProfileTagEnum { public enum AddProfileTagEnum {
/** /**
@ -33,7 +35,13 @@ public enum AddProfileTagEnum {
/** /**
* Add any profile tags that returned resources appear to conform to * Add any profile tags that returned resources appear to conform to
*
* @deprecated This mode causes even FHIR's default profiles to be exported in the
* resource metadata section. This is not generally expected behaviour from other
* systems and it offers no real benefit, so it will be removed at some point. This
* option was deprecated in HAPI 1.5
*/ */
@Deprecated
ALWAYS, ALWAYS,
/** /**

View File

@ -48,7 +48,9 @@ public interface IRestfulServerDefaults {
/** /**
* @return Returns the setting for automatically adding profile tags * @return Returns the setting for automatically adding profile tags
* @deprecated As of HAPI FHIR 1.5, this property has been moved to {@link FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)}
*/ */
@Deprecated
AddProfileTagEnum getAddProfileTag(); AddProfileTagEnum getAddProfileTag();
/** /**

View File

@ -40,7 +40,7 @@ public interface IVersionSpecificBundleFactory {
void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated); void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated);
void initializeBundleFromBundleProvider(IRestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, void initializeBundleFromBundleProvider(IRestfulServer<?> theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint,
int theOffset, Integer theCount, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes); int theOffset, Integer theCount, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes);
Bundle getDstu1Bundle(); Bundle getDstu1Bundle();

View File

@ -74,6 +74,7 @@ import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.ReflectionUtil; import ca.uhn.fhir.util.ReflectionUtil;
import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.UrlUtil;
import ca.uhn.fhir.util.VersionUtil; import ca.uhn.fhir.util.VersionUtil;
@ -93,7 +94,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
private static final ExceptionHandlingInterceptor DEFAULT_EXCEPTION_HANDLER = new ExceptionHandlingInterceptor(); private static final ExceptionHandlingInterceptor DEFAULT_EXCEPTION_HANDLER = new ExceptionHandlingInterceptor();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class);
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private AddProfileTagEnum myAddProfileTag;
private BundleInclusionRule myBundleInclusionRule = BundleInclusionRule.BASED_ON_INCLUDES; private BundleInclusionRule myBundleInclusionRule = BundleInclusionRule.BASED_ON_INCLUDES;
private boolean myDefaultPrettyPrint = false; private boolean myDefaultPrettyPrint = false;
private EncodingEnum myDefaultResponseEncoding = EncodingEnum.XML; private EncodingEnum myDefaultResponseEncoding = EncodingEnum.XML;
@ -369,11 +369,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
return count; return count;
} }
@Override
public AddProfileTagEnum getAddProfileTag() {
return myAddProfileTag;
}
@Override @Override
public BundleInclusionRule getBundleInclusionRule() { public BundleInclusionRule getBundleInclusionRule() {
return myBundleInclusionRule; return myBundleInclusionRule;
@ -1117,10 +1112,13 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* *
* @param theAddProfileTag * @param theAddProfileTag
* The behaviour enum (must not be null) * The behaviour enum (must not be null)
* @deprecated As of HAPI FHIR 1.5, this property has been moved to {@link FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)}
*/ */
@Deprecated
@CoverageIgnore
public void setAddProfileTag(AddProfileTagEnum theAddProfileTag) { public void setAddProfileTag(AddProfileTagEnum theAddProfileTag) {
Validate.notNull(theAddProfileTag, "theAddProfileTag must not be null"); Validate.notNull(theAddProfileTag, "theAddProfileTag must not be null");
myAddProfileTag = theAddProfileTag; myFhirContext.setAddProfileTagWhenEncoding(theAddProfileTag);
} }
/** /**
@ -1394,4 +1392,13 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
return nextString.length() > 0 && (nextString.charAt(0) == '_' || nextString.charAt(0) == '$' || nextString.equals(Constants.URL_TOKEN_METADATA)); return nextString.length() > 0 && (nextString.charAt(0) == '_' || nextString.charAt(0) == '$' || nextString.equals(Constants.URL_TOKEN_METADATA));
} }
/**
* @deprecated As of HAPI FHIR 1.5, this property has been moved to {@link FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)}
*/
@Override
@Deprecated
public AddProfileTagEnum getAddProfileTag() {
return myFhirContext.getAddProfileTagWhenEncoding();
}
} }

View File

@ -26,7 +26,6 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.io.Writer; import java.io.Writer;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
@ -49,15 +48,12 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag; import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
@ -77,36 +73,6 @@ public class RestfulServerUtils {
private static final HashSet<String> TEXT_ENCODE_ELEMENTS = new HashSet<String>(Arrays.asList("Bundle", "*.text")); private static final HashSet<String> TEXT_ENCODE_ELEMENTS = new HashSet<String>(Arrays.asList("Bundle", "*.text"));
public static void addProfileToBundleEntry(FhirContext theContext, IBaseResource theResource, String theServerBase) {
if (theResource instanceof IResource) {
if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
RuntimeResourceDefinition nextDef = theContext.getResourceDefinition(theResource);
String profile = nextDef.getResourceProfile(theServerBase);
if (isNotBlank(profile)) {
List<IdDt> newList = new ArrayList<IdDt>();
List<IdDt> existingList = ResourceMetadataKeyEnum.PROFILES.get((IResource) theResource);
if (existingList != null) {
newList.addAll(existingList);
}
newList.add(new IdDt(profile));
ResourceMetadataKeyEnum.PROFILES.put((IResource) theResource, newList);
}
} else {
TagList tl = ResourceMetadataKeyEnum.TAG_LIST.get((IResource) theResource);
if (tl == null) {
tl = new TagList();
ResourceMetadataKeyEnum.TAG_LIST.put((IResource) theResource, tl);
}
RuntimeResourceDefinition nextDef = theContext.getResourceDefinition(theResource);
String profile = nextDef.getResourceProfile(theServerBase);
if (isNotBlank(profile)) {
tl.add(new Tag(Tag.HL7_ORG_PROFILE_TAG, profile, null));
}
}
}
}
public static void configureResponseParser(RequestDetails theRequestDetails, IParser parser) { public static void configureResponseParser(RequestDetails theRequestDetails, IParser parser) {
// Pretty print // Pretty print
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theRequestDetails.getServer(), theRequestDetails); boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theRequestDetails.getServer(), theRequestDetails);
@ -608,13 +574,6 @@ public class RestfulServerUtils {
} }
} }
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(theResource);
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
addProfileToBundleEntry(theServer.getFhirContext(), theResource, serverBase);
}
}
String contentType; String contentType;
if (theResource instanceof IBaseBinary && responseEncoding == null) { if (theResource instanceof IBaseBinary && responseEncoding == null) {
IBaseBinary bin = (IBaseBinary) theResource; IBaseBinary bin = (IBaseBinary) theResource;

View File

@ -55,8 +55,6 @@ public interface IAnyResource extends IBaseResource {
IPrimitiveType<String> getLanguageElement(); IPrimitiveType<String> getLanguageElement();
IBaseMetaType getMeta();
public Object getUserData(String name); public Object getUserData(String name);
@Override @Override

View File

@ -39,6 +39,8 @@ import ca.uhn.fhir.model.api.Include;
*/ */
public interface IBaseResource extends IBase, IElement { public interface IBaseResource extends IBase, IElement {
IBaseMetaType getMeta();
/** /**
* Include constant for <code>*</code> (return all includes) * Include constant for <code>*</code> (return all includes)
*/ */

View File

@ -32,7 +32,7 @@ package org.hl7.fhir.instance.model.api;
* which version of the strctures your application is using. * which version of the strctures your application is using.
* </p> * </p>
*/ */
public interface IIdType extends IBase { public interface IIdType extends IPrimitiveType<String> {
void applyTo(IBaseResource theResource); void applyTo(IBaseResource theResource);

View File

@ -36,5 +36,5 @@
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="output" path="bin"/> <classpathentry kind="output" path="target/classes"/>
</classpath> </classpath>

View File

@ -1,5 +1,10 @@
package ca.uhn.fhir.model.dstu.resource; package ca.uhn.fhir.model.dstu.resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/* /*
* #%L * #%L
* HAPI FHIR Structures - DSTU1 (FHIR v0.80) * HAPI FHIR Structures - DSTU1 (FHIR v0.80)
@ -23,35 +28,31 @@ package ca.uhn.fhir.model.dstu.resource;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.model.api.BaseElement; import ca.uhn.fhir.model.api.BaseElement;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.SearchParamDefinition; import ca.uhn.fhir.model.api.annotation.SearchParamDefinition;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.resource.ResourceMetadataMap; import ca.uhn.fhir.model.base.resource.ResourceMetadataMap;
import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.ContainedDt; import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt; import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.gclient.StringClientParam; import ca.uhn.fhir.rest.gclient.StringClientParam;
import ca.uhn.fhir.util.ElementUtil; import ca.uhn.fhir.util.ElementUtil;
public abstract class BaseResource extends BaseElement implements IResource { public abstract class BaseResource extends BaseElement implements IResource {
/**
* Search parameter constant for <b>_language</b>
*/
@SearchParamDefinition(name="_language", path="", description="The language of the resource", type="string" )
public static final String SP_RES_LANGUAGE = "_language";
/**
* Search parameter constant for <b>_id</b>
*/
@SearchParamDefinition(name="_id", path="", description="The ID of the resource", type="string" )
public static final String SP_RES_ID = "_id";
/** /**
* <b>Fluent Client</b> search parameter constant for <b>_id</b> * <b>Fluent Client</b> search parameter constant for <b>_id</b>
* <p> * <p>
@ -63,9 +64,24 @@ public abstract class BaseResource extends BaseElement implements IResource {
public static final StringClientParam RES_ID = new StringClientParam(BaseResource.SP_RES_ID); public static final StringClientParam RES_ID = new StringClientParam(BaseResource.SP_RES_ID);
/**
* Search parameter constant for <b>_id</b>
*/
@SearchParamDefinition(name="_id", path="", description="The ID of the resource", type="string" )
public static final String SP_RES_ID = "_id";
/**
* Search parameter constant for <b>_language</b>
*/
@SearchParamDefinition(name="_language", path="", description="The language of the resource", type="string" )
public static final String SP_RES_LANGUAGE = "_language";
@Child(name = "contained", order = 2, min = 0, max = 1) @Child(name = "contained", order = 2, min = 0, max = 1)
private ContainedDt myContained; private ContainedDt myContained;
private IdDt myId; private IdDt myId;
@Child(name = "language", order = 0, min = 0, max = Child.MAX_UNLIMITED) @Child(name = "language", order = 0, min = 0, max = Child.MAX_UNLIMITED)
@ -104,6 +120,216 @@ public abstract class BaseResource extends BaseElement implements IResource {
return myLanguage; return myLanguage;
} }
@Override
public IBaseMetaType getMeta() {
return new IBaseMetaType() {
private static final long serialVersionUID = 1L;
@Override
public IBaseMetaType addProfile(String theProfile) {
ArrayList<IdDt> newTagList = new ArrayList<IdDt>();
List<IdDt> existingTagList = ResourceMetadataKeyEnum.PROFILES.get(BaseResource.this);
if (existingTagList != null) {
newTagList.addAll(existingTagList);
}
ResourceMetadataKeyEnum.PROFILES.put(BaseResource.this, newTagList);
IdDt tag = new IdDt(theProfile);
newTagList.add(tag);
return this;
}
@Override
public IBaseCoding addSecurity() {
List<BaseCodingDt> tagList = ResourceMetadataKeyEnum.SECURITY_LABELS.get(BaseResource.this);
if (tagList == null) {
tagList = new ArrayList<BaseCodingDt>();
ResourceMetadataKeyEnum.SECURITY_LABELS.put(BaseResource.this, tagList);
}
CodingDt tag = new CodingDt();
tagList.add(tag);
return asBaseCoding(tag);
}
@Override
public IBaseCoding addTag() {
TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(BaseResource.this);
if (tagList == null) {
tagList = new TagList();
ResourceMetadataKeyEnum.TAG_LIST.put(BaseResource.this, tagList);
}
Tag tag = new Tag();
tagList.add(tag);
return tag;
}
/**
* This view is used because the old DSTU1 BaseCodingDt can't implements IBaseCoding
*/
private IBaseCoding asBaseCoding(final BaseCodingDt theCoding) {
return new IBaseCoding() {
private static final long serialVersionUID = 1L;
@Override
public String getCode() {
return theCoding.getCodeElement().getValue();
}
@Override
public String getDisplay() {
return theCoding.getDisplayElement().getValue();
}
@Override
public List<String> getFormatCommentsPost() {
return Collections.emptyList();
}
@Override
public List<String> getFormatCommentsPre() {
return Collections.emptyList();
}
@Override
public String getSystem() {
return theCoding.getSystemElement().getValue();
}
@Override
public boolean hasFormatComment() {
return false;
}
@Override
public boolean isEmpty() {
return ElementUtil.isEmpty(getSystem(), getCode(), getDisplay());
}
@Override
public IBaseCoding setCode(String theTerm) {
theCoding.setCode(theTerm);
return this;
}
@Override
public IBaseCoding setDisplay(String theLabel) {
theCoding.setDisplay(theLabel);
return this;
}
@Override
public IBaseCoding setSystem(String theScheme) {
theCoding.setSystem(theScheme);
return this;
}
};
}
@Override
public List<String> getFormatCommentsPost() {
return Collections.emptyList();
}
@Override
public List<String> getFormatCommentsPre() {
return Collections.emptyList();
}
@Override
public Date getLastUpdated() {
InstantDt lu = ResourceMetadataKeyEnum.UPDATED.get(BaseResource.this);
if (lu != null) {
return lu.getValue();
}
return null;
}
@Override
public List<? extends IPrimitiveType<String>> getProfile() {
ArrayList<IPrimitiveType<String>> retVal = new ArrayList<IPrimitiveType<String>>();
List<IdDt> profilesList = ResourceMetadataKeyEnum.PROFILES.get(BaseResource.this);
if (profilesList == null) {
return Collections.emptyList();
}
for (IdDt next : profilesList) {
retVal.add(next);
}
return Collections.unmodifiableList(retVal);
}
@Override
public List<? extends IBaseCoding> getSecurity() {
ArrayList<IBaseCoding> retVal = new ArrayList<IBaseCoding>();
List<BaseCodingDt> labelsList = ResourceMetadataKeyEnum.SECURITY_LABELS.get(BaseResource.this);
if (labelsList == null) {
return Collections.emptyList();
}
for (BaseCodingDt next : labelsList) {
retVal.add(asBaseCoding(next));
}
return Collections.unmodifiableList(retVal);
}
@Override
public IBaseCoding getSecurity(String theSystem, String theCode) {
for (BaseCodingDt next : ResourceMetadataKeyEnum.SECURITY_LABELS.get(BaseResource.this)) {
if (theSystem.equals(next.getSystemElement().getValue()) && theCode.equals(next.getCodeElement().getValue())) {
return asBaseCoding(next);
}
}
return null;
}
@Override
public List<? extends IBaseCoding> getTag() {
ArrayList<IBaseCoding> retVal = new ArrayList<IBaseCoding>();
for (Tag next : ResourceMetadataKeyEnum.TAG_LIST.get(BaseResource.this)) {
retVal.add(next);
}
return Collections.unmodifiableList(retVal);
}
@Override
public IBaseCoding getTag(String theSystem, final String theCode) {
for (final Tag next : ResourceMetadataKeyEnum.TAG_LIST.get(BaseResource.this)) {
if (next.getScheme().equals(theSystem) && next.getTerm().equals(theCode)) {
return next;
}
}
return null;
}
@Override
public String getVersionId() {
return getId().getVersionIdPart();
}
@Override
public boolean hasFormatComment() {
return false;
}
@Override
public boolean isEmpty() {
return getResourceMetadata().isEmpty();
}
@Override
public IBaseMetaType setLastUpdated(Date theHeaderDateValue) {
ResourceMetadataKeyEnum.UPDATED.put(BaseResource.this, new InstantDt(theHeaderDateValue));
return this;
}
@Override
public IBaseMetaType setVersionId(String theVersionId) {
setId(getId().withVersion(theVersionId));
return this;
}
};
}
@Override @Override
public ResourceMetadataMap getResourceMetadata() { public ResourceMetadataMap getResourceMetadata() {
if (myResourceMetadata == null) { if (myResourceMetadata == null) {
@ -120,10 +346,23 @@ public abstract class BaseResource extends BaseElement implements IResource {
return myText; return myText;
} }
/**
* Intended to be called by extending classes {@link #isEmpty()} implementations, returns <code>true</code> if all
* content in this superclass instance is empty per the semantics of {@link #isEmpty()}.
*/
@Override
protected boolean isBaseEmpty() {
return super.isBaseEmpty() && ElementUtil.isEmpty(myLanguage, myText, myId);
}
public void setContained(ContainedDt theContained) { public void setContained(ContainedDt theContained) {
myContained = theContained; myContained = theContained;
} }
public void setId(IdDt theId) {
myId = theId;
}
public BaseResource setId(IIdType theId) { public BaseResource setId(IIdType theId) {
if (theId instanceof IdDt) { if (theId instanceof IdDt) {
myId = (IdDt) theId; myId = (IdDt) theId;
@ -133,10 +372,6 @@ public abstract class BaseResource extends BaseElement implements IResource {
return this; return this;
} }
public void setId(IdDt theId) {
myId = theId;
}
public BaseResource setId(String theId) { public BaseResource setId(String theId) {
if (theId == null) { if (theId == null) {
myId = null; myId = null;
@ -168,13 +403,4 @@ public abstract class BaseResource extends BaseElement implements IResource {
return b.toString(); return b.toString();
} }
/**
* Intended to be called by extending classes {@link #isEmpty()} implementations, returns <code>true</code> if all
* content in this superclass instance is empty per the semantics of {@link #isEmpty()}.
*/
@Override
protected boolean isBaseEmpty() {
return super.isBaseEmpty() && ElementUtil.isEmpty(myLanguage, myText, myId);
}
} }

View File

@ -19,8 +19,7 @@ package ca.uhn.fhir.rest.server;
* limitations under the License. * limitations under the License.
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
@ -29,7 +28,6 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
@ -43,6 +41,8 @@ import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt; import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
@ -53,7 +53,6 @@ import ca.uhn.fhir.util.ResourceReferenceInfo;
public class Dstu1BundleFactory implements IVersionSpecificBundleFactory { public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Dstu1BundleFactory.class);
private Bundle myBundle; private Bundle myBundle;
private FhirContext myContext; private FhirContext myContext;
@ -186,7 +185,7 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
} }
@Override @Override
public void initializeBundleFromBundleProvider(IRestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, public void initializeBundleFromBundleProvider(IRestfulServer<?> theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl,
boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) { boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) {
int numToReturn; int numToReturn;
String searchId = null; String searchId = null;
@ -224,16 +223,11 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
throw new InternalErrorException("Server method returned resource of type[" + next.getClass().getSimpleName() + "] with no ID specified (IResource#setId(IdDt) must be called)"); throw new InternalErrorException("Server method returned resource of type[" + next.getClass().getSimpleName() + "] with no ID specified (IResource#setId(IdDt) must be called)");
} }
} }
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
addProfileIfNeeded(theServer, theServerBase, next);
}
} }
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
for (IBaseResource nextRes : resourceList) {
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
RestfulServerUtils.addProfileToBundleEntry(theServer.getFhirContext(), nextRes, theServerBase);
}
}
}
addResourcesToBundle(new ArrayList<IBaseResource>(resourceList), theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes); addResourcesToBundle(new ArrayList<IBaseResource>(resourceList), theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes);
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, theResult.getPublished()); addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, theResult.getPublished());
@ -256,6 +250,23 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
} }
} }
private void addProfileIfNeeded(IRestfulServer<?> theServer, String theServerBase, IBaseResource nextRes) {
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardType()) {
TagList tl = ResourceMetadataKeyEnum.TAG_LIST.get((IResource) nextRes);
if (tl == null) {
tl = new TagList();
ResourceMetadataKeyEnum.TAG_LIST.put((IResource) nextRes, tl);
}
RuntimeResourceDefinition nextDef = myContext.getResourceDefinition(nextRes);
String profile = nextDef.getResourceProfile(theServerBase);
if (isNotBlank(profile)) {
tl.add(new Tag(Tag.HL7_ORG_PROFILE_TAG, profile, null));
}
}
}
@Override @Override
public void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults, public void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults,
BundleTypeEnum theBundleType) { BundleTypeEnum theBundleType) {

View File

@ -214,9 +214,6 @@ public class CustomTypeTest {
private static boolean ourReturnExtended = false; private static boolean ourReturnExtended = false;
/**
* Created by dsotnikov on 2/25/2014.
*/
public static class DummyPatientResourceProvider implements IResourceProvider { public static class DummyPatientResourceProvider implements IResourceProvider {
@Search @Search

View File

@ -7,6 +7,7 @@ import java.util.List;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import org.hamcrest.core.StringContains; import org.hamcrest.core.StringContains;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.Test; import org.junit.Test;
@ -299,6 +300,12 @@ public class ServerInvalidDefinitionTest {
public List<String> getFormatCommentsPost() { public List<String> getFormatCommentsPost() {
return null; return null;
} }
@Override
public IBaseMetaType getMeta() {
// TODO Auto-generated method stub
return null;
}
}.getClass(); }.getClass();
} }

View File

@ -1,5 +1,10 @@
package ca.uhn.fhir.model.dstu2.resource; package ca.uhn.fhir.model.dstu2.resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/* /*
* #%L * #%L
* HAPI FHIR Structures - DSTU2 (FHIR v1.0.0) * HAPI FHIR Structures - DSTU2 (FHIR v1.0.0)
@ -23,35 +28,31 @@ package ca.uhn.fhir.model.dstu2.resource;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.model.api.BaseElement; import ca.uhn.fhir.model.api.BaseElement;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.SearchParamDefinition; import ca.uhn.fhir.model.api.annotation.SearchParamDefinition;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.resource.ResourceMetadataMap; import ca.uhn.fhir.model.base.resource.ResourceMetadataMap;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.composite.ContainedDt; import ca.uhn.fhir.model.dstu2.composite.ContainedDt;
import ca.uhn.fhir.model.dstu2.composite.NarrativeDt; import ca.uhn.fhir.model.dstu2.composite.NarrativeDt;
import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.gclient.StringClientParam; import ca.uhn.fhir.rest.gclient.StringClientParam;
import ca.uhn.fhir.util.ElementUtil; import ca.uhn.fhir.util.ElementUtil;
public abstract class BaseResource extends BaseElement implements IResource { public abstract class BaseResource extends BaseElement implements IResource {
/**
* Search parameter constant for <b>_language</b>
*/
@SearchParamDefinition(name="_language", path="", description="The language of the resource", type="string" )
public static final String SP_RES_LANGUAGE = "_language";
/**
* Search parameter constant for <b>_id</b>
*/
@SearchParamDefinition(name="_id", path="", description="The ID of the resource", type="string" )
public static final String SP_RES_ID = "_id";
/** /**
* <b>Fluent Client</b> search parameter constant for <b>_id</b> * <b>Fluent Client</b> search parameter constant for <b>_id</b>
* <p> * <p>
@ -62,10 +63,23 @@ public abstract class BaseResource extends BaseElement implements IResource {
*/ */
public static final StringClientParam RES_ID = new StringClientParam(BaseResource.SP_RES_ID); public static final StringClientParam RES_ID = new StringClientParam(BaseResource.SP_RES_ID);
/**
* Search parameter constant for <b>_id</b>
*/
@SearchParamDefinition(name="_id", path="", description="The ID of the resource", type="string" )
public static final String SP_RES_ID = "_id";
/**
* Search parameter constant for <b>_language</b>
*/
@SearchParamDefinition(name="_language", path="", description="The language of the resource", type="string" )
public static final String SP_RES_LANGUAGE = "_language";
@Child(name = "contained", order = 2, min = 0, max = 1) @Child(name = "contained", order = 2, min = 0, max = 1)
private ContainedDt myContained; private ContainedDt myContained;
private IdDt myId; private IdDt myId;
@Child(name = "language", order = 0, min = 0, max = Child.MAX_UNLIMITED) @Child(name = "language", order = 0, min = 0, max = Child.MAX_UNLIMITED)
@ -104,6 +118,157 @@ public abstract class BaseResource extends BaseElement implements IResource {
return myLanguage; return myLanguage;
} }
@Override
public IBaseMetaType getMeta() {
return new IBaseMetaType() {
private static final long serialVersionUID = 1L;
@Override
public IBaseMetaType addProfile(String theProfile) {
ArrayList<IdDt> newTagList = new ArrayList<IdDt>();
List<IdDt> existingTagList = ResourceMetadataKeyEnum.PROFILES.get(BaseResource.this);
if (existingTagList != null) {
newTagList.addAll(existingTagList);
}
ResourceMetadataKeyEnum.PROFILES.put(BaseResource.this, newTagList);
IdDt tag = new IdDt(theProfile);
newTagList.add(tag);
return this;
}
@Override
public IBaseCoding addSecurity() {
List<BaseCodingDt> tagList = ResourceMetadataKeyEnum.SECURITY_LABELS.get(BaseResource.this);
if (tagList == null) {
tagList = new ArrayList<BaseCodingDt>();
ResourceMetadataKeyEnum.SECURITY_LABELS.put(BaseResource.this, tagList);
}
CodingDt tag = new CodingDt();
tagList.add(tag);
return tag;
}
@Override
public IBaseCoding addTag() {
TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(BaseResource.this);
if (tagList == null) {
tagList = new TagList();
ResourceMetadataKeyEnum.TAG_LIST.put(BaseResource.this, tagList);
}
Tag tag = new Tag();
tagList.add(tag);
return tag;
}
@Override
public List<String> getFormatCommentsPost() {
return Collections.emptyList();
}
@Override
public List<String> getFormatCommentsPre() {
return Collections.emptyList();
}
@Override
public Date getLastUpdated() {
InstantDt lu = ResourceMetadataKeyEnum.UPDATED.get(BaseResource.this);
if (lu != null) {
return lu.getValue();
}
return null;
}
@Override
public List<? extends IPrimitiveType<String>> getProfile() {
ArrayList<IPrimitiveType<String>> retVal = new ArrayList<IPrimitiveType<String>>();
List<IdDt> profilesList = ResourceMetadataKeyEnum.PROFILES.get(BaseResource.this);
if (profilesList == null) {
return Collections.emptyList();
}
for (IdDt next : profilesList) {
retVal.add(next);
}
return Collections.unmodifiableList(retVal);
}
@Override
public List<? extends IBaseCoding> getSecurity() {
ArrayList<CodingDt> retVal = new ArrayList<CodingDt>();
List<BaseCodingDt> labelsList = ResourceMetadataKeyEnum.SECURITY_LABELS.get(BaseResource.this);
if (labelsList == null) {
return Collections.emptyList();
}
for (BaseCodingDt next : labelsList) {
retVal.add(new CodingDt(next.getSystemElement().getValue(), next.getCodeElement().getValue()).setDisplay(next.getDisplayElement().getValue()));
}
return Collections.unmodifiableList(retVal);
}
@Override
public IBaseCoding getSecurity(String theSystem, String theCode) {
for (IBaseCoding next : getSecurity()) {
if (theSystem.equals(next.getSystem()) && theCode.equals(next.getCode())) {
return next;
}
}
return null;
}
@Override
public List<? extends IBaseCoding> getTag() {
ArrayList<IBaseCoding> retVal = new ArrayList<IBaseCoding>();
TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(BaseResource.this);
if (tagList == null) {
return Collections.emptyList();
}
for (Tag next : tagList) {
retVal.add(next);
}
return Collections.unmodifiableList(retVal);
}
@Override
public IBaseCoding getTag(String theSystem, String theCode) {
for (IBaseCoding next : getTag()) {
if (next.getSystem().equals(theSystem) && next.getCode().equals(theCode)) {
return next;
}
}
return null;
}
@Override
public String getVersionId() {
return getId().getVersionIdPart();
}
@Override
public boolean hasFormatComment() {
return false;
}
@Override
public boolean isEmpty() {
return getResourceMetadata().isEmpty();
}
@Override
public IBaseMetaType setLastUpdated(Date theHeaderDateValue) {
ResourceMetadataKeyEnum.UPDATED.put(BaseResource.this, new InstantDt(theHeaderDateValue));
return this;
}
@Override
public IBaseMetaType setVersionId(String theVersionId) {
setId(getId().withVersion(theVersionId));
return this;
}
};
}
@Override @Override
public ResourceMetadataMap getResourceMetadata() { public ResourceMetadataMap getResourceMetadata() {
if (myResourceMetadata == null) { if (myResourceMetadata == null) {
@ -120,6 +285,15 @@ public abstract class BaseResource extends BaseElement implements IResource {
return myText; return myText;
} }
/**
* Intended to be called by extending classes {@link #isEmpty()} implementations, returns <code>true</code> if all
* content in this superclass instance is empty per the semantics of {@link #isEmpty()}.
*/
@Override
protected boolean isBaseEmpty() {
return super.isBaseEmpty() && ElementUtil.isEmpty(myLanguage, myText, myId);
}
public void setContained(ContainedDt theContained) { public void setContained(ContainedDt theContained) {
myContained = theContained; myContained = theContained;
} }
@ -168,13 +342,4 @@ public abstract class BaseResource extends BaseElement implements IResource {
return b.toString(); return b.toString();
} }
/**
* Intended to be called by extending classes {@link #isEmpty()} implementations, returns <code>true</code> if all
* content in this superclass instance is empty per the semantics of {@link #isEmpty()}.
*/
@Override
protected boolean isBaseEmpty() {
return super.isBaseEmpty() && ElementUtil.isEmpty(myLanguage, myText, myId);
}
} }

View File

@ -35,7 +35,6 @@ import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
@ -51,7 +50,6 @@ import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum; import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum; import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.rest.server.BundleInclusionRule; import ca.uhn.fhir.rest.server.BundleInclusionRule;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
@ -65,7 +63,6 @@ import ca.uhn.fhir.util.ResourceReferenceInfo;
public class Dstu2BundleFactory implements IVersionSpecificBundleFactory { public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Dstu2BundleFactory.class);
private Bundle myBundle; private Bundle myBundle;
private FhirContext myContext; private FhirContext myContext;
private String myBase; private String myBase;
@ -302,7 +299,7 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
} }
@Override @Override
public void initializeBundleFromBundleProvider(IRestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, public void initializeBundleFromBundleProvider(IRestfulServer<?> theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl,
boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) { boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) {
myBase = theServerBase; myBase = theServerBase;
@ -352,15 +349,6 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
} }
} }
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
for (IBaseResource nextRes : resourceList) {
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
RestfulServerUtils.addProfileToBundleEntry(theServer.getFhirContext(), nextRes, theServerBase);
}
}
}
addResourcesToBundle(new ArrayList<IBaseResource>(resourceList), theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes); addResourcesToBundle(new ArrayList<IBaseResource>(resourceList), theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes);
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, theResult.getPublished()); addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, theResult.getPublished());

View File

@ -0,0 +1,510 @@
package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu2.composite.QuantityDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.util.ElementUtil;
public class CustomTypeDstu2Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeDstu2Test.class);
private static final FhirContext ourCtx = FhirContext.forDstu2();
@Test
public void testAccessEmptyMetaLists() {
Patient p = new Patient();
assertThat(p.getMeta().getProfile(), empty());
assertThat(p.getMeta().getFormatCommentsPost(), empty());
assertThat(p.getMeta().getFormatCommentsPre(), empty());
assertThat(p.getMeta().getLastUpdated(), nullValue());
assertThat(p.getMeta().getSecurity(), empty());
assertThat(p.getMeta().getSecurity("foo", "bar"), nullValue());
assertThat(p.getMeta().getTag(), empty());
assertThat(p.getMeta().getTag("foo", "bar"), nullValue());
assertThat(p.getMeta().getVersionId(), nullValue());
}
@Test
public void testEncodeCompleteMetaLists() {
Patient p = new Patient();
p.getMeta().addProfile("http://foo/profile1");
p.getMeta().addProfile("http://foo/profile2");
p.getMeta().addSecurity().setSystem("SEC_S1").setCode("SEC_C1").setDisplay("SED_D1");
p.getMeta().addSecurity().setSystem("SEC_S2").setCode("SEC_C2").setDisplay("SED_D2");
p.getMeta().addTag().setSystem("TAG_S1").setCode("TAG_C1").setDisplay("TAG_D1");
p.getMeta().addTag().setSystem("TAG_S2").setCode("TAG_C2").setDisplay("TAG_D2");
String out = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(out);
//@formatter:off
assertThat(out, stringContainsInOrder(
"<meta>",
"<profile value=\"http://foo/profile1\"/>",
"<profile value=\"http://foo/profile2\"/>",
"<security>",
"<system value=\"SEC_S1\"/>",
"<code value=\"SEC_C1\"/>",
"<display value=\"SED_D1\"/>",
"</security>",
"<security>",
"<system value=\"SEC_S2\"/>",
"<code value=\"SEC_C2\"/>",
"<display value=\"SED_D2\"/>",
"</security>",
"<tag>",
"<system value=\"TAG_S1\"/>",
"<display value=\"TAG_D1\"/>",
"</tag>",
"<tag>",
"<system value=\"TAG_S2\"/>",
"<display value=\"TAG_D2\"/>",
"</tag>",
"</meta>"));
//@formatter:on
}
@Before
public void before() {
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
}
@Test
public void testEncodeWithCustomType() {
MyCustomPatient patient = new MyCustomPatient();
patient.addIdentifier().setSystem("urn:system").setValue("1234");
patient.addName().addFamily("Rossi").addGiven("Mario");
patient.setInsulinLevel(new QuantityDt());
patient.setGlucoseLevel(new QuantityDt());
patient.setHbA1c(new QuantityDt());
patient.setBloodPressure(new QuantityDt());
patient.setCholesterol(new QuantityDt());
patient.setWeight(new StringDt("80 kg"));
patient.setWeight(new StringDt("185 cm"));
patient.setCheckDates(new ArrayList<DateTimeDt>());
patient.getCheckDates().add(new DateTimeDt("2014-01-26T11:11:11"));
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);
ourLog.info(messageString);
//@formatter:off
assertThat(messageString, stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"/>",
"</meta>"));
//@formatter:on
//@formatter:off
assertThat(messageString, not(stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"", "/>",
"<profile value=\"http://example.com/foo\"/>",
"</meta>")));
//@formatter:on
}
@Test
public void testEncodeWithCustomTypeAndAutoInsertedProfile() {
MyCustomPatient patient = new MyCustomPatient();
patient.getMeta().addProfile("http://example.com/foo");
patient.getMeta().addProfile("http://example.com/bar");
patient.addIdentifier().setSystem("urn:system").setValue("1234");
patient.addName().addFamily("Rossi").addGiven("Mario");
patient.setInsulinLevel(new QuantityDt());
patient.setGlucoseLevel(new QuantityDt());
patient.setHbA1c(new QuantityDt());
patient.setBloodPressure(new QuantityDt());
patient.setCholesterol(new QuantityDt());
patient.setWeight(new StringDt("80 kg"));
patient.setWeight(new StringDt("185 cm"));
patient.setCheckDates(new ArrayList<DateTimeDt>());
patient.getCheckDates().add(new DateTimeDt("2014-01-26T11:11:11"));
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);
ourLog.info(messageString);
//@formatter:off
assertThat(messageString, stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"/>",
"<profile value=\"http://example.com/bar\"/>",
"</meta>"));
//@formatter:on
//@formatter:off
assertThat(messageString, not(stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"", "/>",
"<profile value=\"http://example.com/foo\"/>",
"</meta>")));
//@formatter:on
}
@Test
public void testEncodeNormalType() {
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("1234");
patient.addName().addFamily("Rossi").addGiven("Mario");
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);
ourLog.info(messageString);
assertThat(messageString, not(containsString("<profile")));
}
@Test
public void parseResourceWithNoDirective() {
String input = createResource(true);
FhirContext ctx = FhirContext.forDstu2();
Patient parsed = (Patient) ctx.newXmlParser().parseResource(input);
assertEquals(1, parsed.getMeta().getProfile().size());
assertEquals("http://example.com/foo", parsed.getMeta().getProfile().get(0).getValue());
List<ExtensionDt> exts = parsed.getUndeclaredExtensionsByUrl("http://example.com/Weight");
assertEquals(1, exts.size());
assertEquals("185 cm", ((StringDt)exts.get(0).getValueAsPrimitive()).getValue());
}
@Test
public void parseResourceWithDirective() {
String input = createResource(true);
FhirContext ctx = FhirContext.forDstu2();
ctx.setDefaultTypeForProfile("http://example.com/foo", MyCustomPatient.class);
MyCustomPatient parsed = (MyCustomPatient) ctx.newXmlParser().parseResource(input);
assertEquals(1, parsed.getMeta().getProfile().size());
assertEquals("http://example.com/foo", parsed.getMeta().getProfile().get(0).getValue());
List<ExtensionDt> exts = parsed.getUndeclaredExtensionsByUrl("http://example.com/Weight");
assertEquals(0, exts.size());
assertEquals("185 cm", parsed.getWeight().getValue());
}
@Test
public void parseBundleWithResourceDirective() {
String input = createBundle(createResource(false), createResource(true));
FhirContext ctx = FhirContext.forDstu2();
ctx.setDefaultTypeForProfile("http://example.com/foo", MyCustomPatient.class);
Bundle bundle = ctx.newXmlParser().parseResource(Bundle.class, input);
Patient res0 = (Patient) bundle.getEntry().get(0).getResource();
assertEquals(0, res0.getMeta().getProfile().size());
List<ExtensionDt> exts = res0.getUndeclaredExtensionsByUrl("http://example.com/Weight");
assertEquals(1, exts.size());
assertEquals("185 cm", ((StringDt)exts.get(0).getValueAsPrimitive()).getValue());
MyCustomPatient res1 = (MyCustomPatient) bundle.getEntry().get(1).getResource();
assertEquals(1, res1.getMeta().getProfile().size());
assertEquals("http://example.com/foo", res1.getMeta().getProfile().get(0).getValue());
exts = res1.getUndeclaredExtensionsByUrl("http://example.com/Weight");
assertEquals(0, exts.size());
assertEquals("185 cm", res1.getWeight().getValue());
}
private String createBundle(String... theResources) {
StringBuilder b = new StringBuilder();
b.append("<Bundle xmlns=\"http://hl7.org/fhir\">\n");
for (String next : theResources) {
b.append(" <entry>\n");
b.append(" <resource>\n");
b.append(next);
b.append(" </resource>\n");
b.append(" </entry>\n");
}
b.append("</Bundle>");
return b.toString();
}
private String createResource(boolean theWithProfile) {
StringBuilder b = new StringBuilder();
b.append("<Patient xmlns=\"http://hl7.org/fhir\">\n");
if (theWithProfile) {
b.append(" <meta>\n");
b.append(" <profile value=\"http://example.com/foo\"/>\n");
b.append(" </meta>\n");
}
b.append(" <extension url=\"http://example.com/BloodPressure\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"110\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mmHg\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <modifierExtension url=\"http://example.com/diabetes2\">\n");
b.append(" <valueDateTime value=\"2010-01-02\"/>\n");
b.append(" </modifierExtension>\n");
b.append(" <modifierExtension url=\"http://example.com/diabetes2\">\n");
b.append(" <valueDateTime value=\"2014-01-26T11:11:11\"/>\n");
b.append(" </modifierExtension>\n");
b.append(" <extension url=\"http://example.com/Cholesterol\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"2\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mmol/l\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/Glucose\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"95\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mg/dl\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/HbA1c\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"48\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mmol/mol\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/Insuline\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"125\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"pmol/l\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/Weight\">\n");
b.append(" <valueString value=\"185 cm\"/>\n");
b.append(" </extension>\n");
b.append(" <identifier>\n");
b.append(" <system value=\"urn:system\"/>\n");
b.append(" <value value=\"1234\"/>\n");
b.append(" </identifier>\n");
b.append(" <name>\n");
b.append(" <family value=\"Rossi\"/>\n");
b.append(" <given value=\"Mario\"/>\n");
b.append(" </name>\n");
b.append("</Patient>");
String input =
b.toString();
return input;
}
@ResourceDef(name = "Patient", profile = "http://example.com/foo")
public static class MyCustomPatient extends Patient {
private static final long serialVersionUID = 1L;
@Child(name = "bloodPressure") // once every 3 month. The average target is 130/80 mmHg or less
@Extension(url = "http://example.com/BloodPressure", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's blood pressure")
private QuantityDt myBloodPressure;
// Dates of periodic tests
@Child(name = "CheckDates", max = Child.MAX_UNLIMITED)
@Extension(url = "http://example.com/diabetes2", definedLocally = false, isModifier = true)
@Description(shortDefinition = "Dates of periodic tests")
private List<DateTimeDt> myCheckDates;
@Child(name = "cholesterol") // once a year. The target is triglycerides =< 2 mmol/l e cholesterol =< 4 mmol/l
@Extension(url = "http://example.com/Cholesterol", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's cholesterol")
private QuantityDt myCholesterol;
@Child(name = "glucoseLevel") // fingerprick test
@Extension(url = "http://example.com/Glucose", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's blood glucose")
private QuantityDt myGlucoseLevel;
// Periodic Tests
@Child(name = "hbA1c") // once every 6 month. The average target is 53 mmol/mol (or 7%) or less.
@Extension(url = "http://example.com/HbA1c", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's glucose")
private QuantityDt myHbA1c;
@Child(name = "Height")
@Extension(url = "http://example.com/Height", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The patient's height in cm")
private StringDt myHeight;
@Child(name = "insulinLevel") // Normal range is [43,208] pmol/l
@Extension(url = "http://example.com/Insuline", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's insulin")
private QuantityDt myInsulinLevel;
// Other parameters
@Child(name = "weight")
@Extension(url = "http://example.com/Weight", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The patient's weight in Kg")
private StringDt myWeight;
public QuantityDt Cholesterol() {
if (myCholesterol == null) {
myCholesterol = new QuantityDt();
}
myCholesterol.getValue();
myCholesterol.getSystem();
myCholesterol.getCode();
return myCholesterol;
}
public QuantityDt getBloodPressure() {
if (myBloodPressure == null) {
myBloodPressure = new QuantityDt();
}
myBloodPressure.getValue();
myBloodPressure.getSystem();
myBloodPressure.getCode();
return myBloodPressure;
}
public List<DateTimeDt> getCheckDates() {
if (myCheckDates == null) {
myCheckDates = new ArrayList<DateTimeDt>();
}
return myCheckDates;
}
public QuantityDt getGlucoseLevel() {
if (myGlucoseLevel == null) {
myGlucoseLevel = new QuantityDt();
}
myGlucoseLevel.getValue();
myGlucoseLevel.getSystem();
myGlucoseLevel.getCode();
return myGlucoseLevel;
}
public QuantityDt getHbA1c() {
if (myHbA1c == null) {
myHbA1c = new QuantityDt();
}
myHbA1c.getValue();
myHbA1c.getSystem();
myHbA1c.getCode();
return myHbA1c;
}
public StringDt getHeight() {
if (myHeight == null) {
myHeight = new StringDt();
}
return myHeight;
}
public QuantityDt getInsulinLevel() {
if (myInsulinLevel == null) {
myInsulinLevel = new QuantityDt();
}
myInsulinLevel.getValue();
myInsulinLevel.getSystem();
myInsulinLevel.getCode();
return myInsulinLevel;
}
public StringDt getWeight() {
if (myWeight == null) {
myWeight = new StringDt();
}
return myWeight;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(myInsulinLevel, myGlucoseLevel, myHbA1c, myBloodPressure, myCholesterol, myWeight, myHeight, myCheckDates);
}
public void setBloodPressure(QuantityDt bloodPressure) {
myBloodPressure = bloodPressure;
myBloodPressure.setValue(110);
myBloodPressure.setSystem("http://unitsofmeasure.org");
myBloodPressure.setCode("mmHg");
}
public void setCheckDates(List<DateTimeDt> theCheckDates) {
myCheckDates = theCheckDates;
myCheckDates.add(new DateTimeDt("2010-01-02"));
}
public void setCholesterol(QuantityDt cholesterol) {
myCholesterol = cholesterol;
myCholesterol.setValue(2);
myCholesterol.setSystem("http://unitsofmeasure.org");
myCholesterol.setCode("mmol/l");
}
public void setGlucoseLevel(QuantityDt glucoseLevel) {
myGlucoseLevel = glucoseLevel;
myGlucoseLevel.setValue(95);
myGlucoseLevel.setSystem("http://unitsofmeasure.org");
myGlucoseLevel.setCode("mg/dl");
}
public void setHbA1c(QuantityDt hba1c) {
myHbA1c = hba1c;
myHbA1c.setValue(48);
myHbA1c.setSystem("http://unitsofmeasure.org");
myHbA1c.setCode("mmol/mol");
}
public void setHeight(StringDt height) {
myHeight = height;
}
// Setter/Getter methods
public void setInsulinLevel(QuantityDt insulinLevel) {
myInsulinLevel = insulinLevel;
myInsulinLevel.setValue(125);
myInsulinLevel.setSystem("http://unitsofmeasure.org");
myInsulinLevel.setCode("pmol/l");
}
public void setWeight(StringDt weight) {
myWeight = weight;
}
}
}

View File

@ -1,228 +0,0 @@
package ca.uhn.fhir.parser;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu2.composite.QuantityDt;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.util.ElementUtil;
public class CustomTypeTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeTest.class);
@Test
public void testEncode() {
FhirContext ctx = FhirContext.forDstu2();
MyCustomPatient patient = new MyCustomPatient();
patient.addIdentifier().setSystem("urn:system").setValue("1234");
patient.addName().addFamily("Rossi").addGiven("Mario");
patient.setInsulinLevel(new QuantityDt());
patient.setGlucoseLevel(new QuantityDt());
patient.setHbA1c(new QuantityDt());
patient.setBloodPressure(new QuantityDt());
patient.setCholesterol(new QuantityDt());
patient.setWeight(new StringDt("80 kg"));
patient.setWeight(new StringDt("185 cm"));
patient.setCheckDates(new ArrayList<DateTimeDt>());
patient.getCheckDates().add(new DateTimeDt("2014-01-26T11:11:11"));
IParser p = ctx.newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);
ourLog.info(messageString);
}
@ResourceDef(name = "Patient")
public static class MyCustomPatient extends Patient {
private static final long serialVersionUID = 1L;
@Child(name = "bloodPressure") // once every 3 month. The average target is 130/80 mmHg or less
@Extension(url = "http://example.com/BloodPressure", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's blood pressure")
private QuantityDt myBloodPressure;
// Dates of periodic tests
@Child(name = "CheckDates", max = Child.MAX_UNLIMITED)
@Extension(url = "http://example.com/diabetes2", definedLocally = false, isModifier = true)
@Description(shortDefinition = "Dates of periodic tests")
private List<DateTimeDt> myCheckDates;
@Child(name = "cholesterol") // once a year. The target is triglycerides =< 2 mmol/l e cholesterol =< 4 mmol/l
@Extension(url = "http://example.com/Cholesterol", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's cholesterol")
private QuantityDt myCholesterol;
@Child(name = "glucoseLevel") // fingerprick test
@Extension(url = "http://example.com/Glucose", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's blood glucose")
private QuantityDt myGlucoseLevel;
// Periodic Tests
@Child(name = "hbA1c") // once every 6 month. The average target is 53 mmol/mol (or 7%) or less.
@Extension(url = "http://example.com/HbA1c", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's glucose")
private QuantityDt myHbA1c;
@Child(name = "Height")
@Extension(url = "http://example.com/Height", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The patient's height in cm")
private StringDt myHeight;
@Child(name = "insulinLevel") // Normal range is [43,208] pmol/l
@Extension(url = "http://example.com/Insuline", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's insulin")
private QuantityDt myInsulinLevel;
// Other parameters
@Child(name = "weight")
@Extension(url = "http://example.com/Weight", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The patient's weight in Kg")
private StringDt myWeight;
public QuantityDt Cholesterol() {
if (myCholesterol == null) {
myCholesterol = new QuantityDt();
}
myCholesterol.getValue();
myCholesterol.getSystem();
myCholesterol.getCode();
return myCholesterol;
}
public QuantityDt getBloodPressure() {
if (myBloodPressure == null) {
myBloodPressure = new QuantityDt();
}
myBloodPressure.getValue();
myBloodPressure.getSystem();
myBloodPressure.getCode();
return myBloodPressure;
}
public List<DateTimeDt> getCheckDates() {
if (myCheckDates == null) {
myCheckDates = new ArrayList<DateTimeDt>();
}
return myCheckDates;
}
public QuantityDt getGlucoseLevel() {
if (myGlucoseLevel == null) {
myGlucoseLevel = new QuantityDt();
}
myGlucoseLevel.getValue();
myGlucoseLevel.getSystem();
myGlucoseLevel.getCode();
return myGlucoseLevel;
}
public QuantityDt getHbA1c() {
if (myHbA1c == null) {
myHbA1c = new QuantityDt();
}
myHbA1c.getValue();
myHbA1c.getSystem();
myHbA1c.getCode();
return myHbA1c;
}
public StringDt getHeight() {
if (myHeight == null) {
myHeight = new StringDt();
}
return myHeight;
}
public QuantityDt getInsulinLevel() {
if (myInsulinLevel == null) {
myInsulinLevel = new QuantityDt();
}
myInsulinLevel.getValue();
myInsulinLevel.getSystem();
myInsulinLevel.getCode();
return myInsulinLevel;
}
public StringDt getWeight() {
if (myWeight == null) {
myWeight = new StringDt();
}
return myWeight;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(myInsulinLevel, myGlucoseLevel, myHbA1c, myBloodPressure, myCholesterol, myWeight, myHeight, myCheckDates);
}
public void setBloodPressure(QuantityDt bloodPressure) {
myBloodPressure = bloodPressure;
myBloodPressure.setValue(110);
myBloodPressure.setSystem("http://unitsofmeasure.org");
myBloodPressure.setCode("mmHg");
}
public void setCheckDates(List<DateTimeDt> theCheckDates) {
myCheckDates = theCheckDates;
myCheckDates.add(new DateTimeDt("2010-01-02"));
}
public void setCholesterol(QuantityDt cholesterol) {
myCholesterol = cholesterol;
myCholesterol.setValue(2);
myCholesterol.setSystem("http://unitsofmeasure.org");
myCholesterol.setCode("mmol/l");
}
public void setGlucoseLevel(QuantityDt glucoseLevel) {
myGlucoseLevel = glucoseLevel;
myGlucoseLevel.setValue(95);
myGlucoseLevel.setSystem("http://unitsofmeasure.org");
myGlucoseLevel.setCode("mg/dl");
}
public void setHbA1c(QuantityDt hba1c) {
myHbA1c = hba1c;
myHbA1c.setValue(48);
myHbA1c.setSystem("http://unitsofmeasure.org");
myHbA1c.setCode("mmol/mol");
}
public void setHeight(StringDt height) {
myHeight = height;
}
// Setter/Getter methods
public void setInsulinLevel(QuantityDt insulinLevel) {
myInsulinLevel = insulinLevel;
myInsulinLevel.setValue(125);
myInsulinLevel.setSystem("http://unitsofmeasure.org");
myInsulinLevel.setCode("pmol/l");
}
public void setWeight(StringDt weight) {
myWeight = weight;
}
}
}

View File

@ -94,6 +94,24 @@ public class JsonParserDstu2Test {
assertEquals(true, obs.getReadOnly().getValue().booleanValue()); assertEquals(true, obs.getReadOnly().getValue().booleanValue());
} }
@Test
public void testParseBundleWithCustomObservationType() {
ReportObservation obs = new ReportObservation();
obs.setReadOnly(true);
IParser p = ourCtx.newJsonParser();
// p.set
p.setParserErrorHandler(mock(IParserErrorHandler.class, new ThrowsException(new IllegalStateException())));
String encoded = p.encodeResourceToString(obs);
ourLog.info(encoded);
obs = p.parseResource(ReportObservation.class, encoded);
assertEquals(true, obs.getReadOnly().getValue().booleanValue());
}
@Test @Test
public void testEncodeAndParseExtensions() throws Exception { public void testEncodeAndParseExtensions() throws Exception {

View File

@ -9,7 +9,7 @@ import ca.uhn.fhir.model.primitive.BooleanDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.util.ElementUtil; import ca.uhn.fhir.util.ElementUtil;
@ResourceDef(name = "Observation", id = "reportobservation") @ResourceDef(name = "Observation", id = "http://example.com/reportobservation")
public class ReportObservation extends Observation { public class ReportObservation extends Observation {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,4 @@
<Bundle <Bundle xmlns="http://hl7.org/fhir">
xmlns="http://hl7.org/fhir">
<id value="487d3669-0e1c-4867-b124-400d1849548d"></id> <id value="487d3669-0e1c-4867-b124-400d1849548d"></id>
<type value="searchset"></type> <type value="searchset"></type>
<base value="http://localhost:19080/fhir/dstu1"></base> <base value="http://localhost:19080/fhir/dstu1"></base>
@ -17,8 +16,7 @@
<url value="Observation"></url> <url value="Observation"></url>
</link> </link>
<resource> <resource>
<Observation <Observation xmlns="http://hl7.org/fhir">
xmlns="http://hl7.org/fhir">
<id value="0d87f02c-da2c-4551-9ead-2956f0165a4f"></id> <id value="0d87f02c-da2c-4551-9ead-2956f0165a4f"></id>
<extension url="http://orionhealth.com/fhir/extensions#created-by"></extension> <extension url="http://orionhealth.com/fhir/extensions#created-by"></extension>
<extension url="http://orionhealth.com/fhir/extensions#last-modified-by"></extension> <extension url="http://orionhealth.com/fhir/extensions#last-modified-by"></extension>
@ -46,8 +44,7 @@
<url value="Observation"></url> <url value="Observation"></url>
</link> </link>
<resource> <resource>
<Observation <Observation xmlns="http://hl7.org/fhir">
xmlns="http://hl7.org/fhir">
<id value="c54ac0cc-a99f-40aa-9541-c5aa853a2e88"></id> <id value="c54ac0cc-a99f-40aa-9541-c5aa853a2e88"></id>
<extension url="http://orionhealth.com/fhir/extensions#created-by"></extension> <extension url="http://orionhealth.com/fhir/extensions#created-by"></extension>
<extension url="http://orionhealth.com/fhir/extensions#last-modified-by"></extension> <extension url="http://orionhealth.com/fhir/extensions#last-modified-by"></extension>

View File

@ -31,27 +31,24 @@ import java.util.UUID;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.DomainResource;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Bundle.BundleLinkComponent; import org.hl7.fhir.dstu3.model.Bundle.BundleLinkComponent;
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb; import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
import org.hl7.fhir.dstu3.model.Bundle.SearchEntryMode; import org.hl7.fhir.dstu3.model.Bundle.SearchEntryMode;
import org.hl7.fhir.dstu3.model.DomainResource;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt; import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.rest.server.BundleInclusionRule; import ca.uhn.fhir.rest.server.BundleInclusionRule;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
@ -65,7 +62,6 @@ import ca.uhn.fhir.util.ResourceReferenceInfo;
public class Dstu3BundleFactory implements IVersionSpecificBundleFactory { public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Dstu3BundleFactory.class);
private Bundle myBundle; private Bundle myBundle;
private FhirContext myContext; private FhirContext myContext;
private String myBase; private String myBase;
@ -308,7 +304,7 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
} }
@Override @Override
public void initializeBundleFromBundleProvider(IRestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, public void initializeBundleFromBundleProvider(IRestfulServer<?> theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl,
boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) { boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) {
myBase = theServerBase; myBase = theServerBase;
@ -358,15 +354,6 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
} }
} }
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
for (IBaseResource nextRes : resourceList) {
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
RestfulServerUtils.addProfileToBundleEntry(theServer.getFhirContext(), nextRes, theServerBase);
}
}
}
addResourcesToBundle(new ArrayList<IBaseResource>(resourceList), theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes); addResourcesToBundle(new ArrayList<IBaseResource>(resourceList), theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes);
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, theResult.getPublished()); addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, theResult.getPublished());

View File

@ -9,12 +9,24 @@ import org.junit.Test;
import ca.uhn.fhir.context.BaseRuntimeChildDatatypeDefinition; import ca.uhn.fhir.context.BaseRuntimeChildDatatypeDefinition;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.rest.client.MyPatientWithExtensions;
public class FhirContextDstu3Test { public class FhirContextDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirContextDstu3Test.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirContextDstu3Test.class);
private FhirContext ourCtx = FhirContext.forDstu3(); private FhirContext ourCtx = FhirContext.forDstu3();
@Test
public void testCustomTypeDoesntBecomeDefault() {
FhirContext ctx = FhirContext.forDstu3();
MyPatientWithExtensions pt = new MyPatientWithExtensions();
pt.addName().addGiven("FOO");
ctx.newXmlParser().encodeResourceToString(pt);
assertEquals(Patient.class, ctx.getResourceDefinition("Patient").getImplementingClass());
}
@Test @Test
public void testQueryBoundCode() { public void testQueryBoundCode() {
RuntimeResourceDefinition patientType = ourCtx.getResourceDefinition(Patient.class); RuntimeResourceDefinition patientType = ourCtx.getResourceDefinition(Patient.class);

View File

@ -1,11 +1,20 @@
package ca.uhn.fhir.parser; package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.hl7.fhir.dstu3.model.DateTimeType; import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.Quantity; import org.hl7.fhir.dstu3.model.Quantity;
import org.hl7.fhir.dstu3.model.StringType;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
@ -13,17 +22,215 @@ import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension; import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.util.ElementUtil; import ca.uhn.fhir.util.ElementUtil;
public class CustomTypeDstu3Test { public class CustomTypeDstu3Test {
private static final FhirContext ourCtx = FhirContext.forDstu3();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeDstu3Test.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeDstu3Test.class);
@Before
public void before() {
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
}
private String createBundle(String... theResources) {
StringBuilder b = new StringBuilder();
b.append("<Bundle xmlns=\"http://hl7.org/fhir\">\n");
for (String next : theResources) {
b.append(" <entry>\n");
b.append(" <resource>\n");
b.append(next);
b.append(" </resource>\n");
b.append(" </entry>\n");
}
b.append("</Bundle>");
return b.toString();
}
private String createResource(boolean theWithProfile) {
StringBuilder b = new StringBuilder();
b.append("<Patient xmlns=\"http://hl7.org/fhir\">\n");
if (theWithProfile) {
b.append(" <meta>\n");
b.append(" <profile value=\"http://example.com/foo\"/>\n");
b.append(" </meta>\n");
}
b.append(" <extension url=\"http://example.com/BloodPressure\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"110\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mmHg\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <modifierExtension url=\"http://example.com/diabetes2\">\n");
b.append(" <valueDateTime value=\"2010-01-02\"/>\n");
b.append(" </modifierExtension>\n");
b.append(" <modifierExtension url=\"http://example.com/diabetes2\">\n");
b.append(" <valueDateTime value=\"2014-01-26T11:11:11\"/>\n");
b.append(" </modifierExtension>\n");
b.append(" <extension url=\"http://example.com/Cholesterol\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"2\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mmol/l\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/Glucose\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"95\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mg/dl\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/HbA1c\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"48\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mmol/mol\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/Insuline\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"125\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"pmol/l\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/Weight\">\n");
b.append(" <valueString value=\"185 cm\"/>\n");
b.append(" </extension>\n");
b.append(" <identifier>\n");
b.append(" <system value=\"urn:system\"/>\n");
b.append(" <value value=\"1234\"/>\n");
b.append(" </identifier>\n");
b.append(" <name>\n");
b.append(" <family value=\"Rossi\"/>\n");
b.append(" <given value=\"Mario\"/>\n");
b.append(" </name>\n");
b.append("</Patient>");
String input =
b.toString();
return input;
}
@Test @Test
public void testEncode() { public void parseBundleWithResourceDirective() {
String input = createBundle(createResource(false), createResource(true));
FhirContext ctx = FhirContext.forDstu3(); FhirContext ctx = FhirContext.forDstu3();
ctx.setDefaultTypeForProfile("http://example.com/foo", MyCustomPatient.class);
Bundle bundle = ctx.newXmlParser().parseResource(Bundle.class, input);
Patient res0 = (Patient) bundle.getEntry().get(0).getResource();
assertEquals(0, res0.getMeta().getProfile().size());
List<org.hl7.fhir.dstu3.model.Extension> exts = res0.getExtensionsByUrl("http://example.com/Weight");
assertEquals(1, exts.size());
assertEquals("185 cm", ((StringType)exts.get(0).getValue()).getValue());
MyCustomPatient res1 = (MyCustomPatient) bundle.getEntry().get(1).getResource();
assertEquals(1, res1.getMeta().getProfile().size());
assertEquals("http://example.com/foo", res1.getMeta().getProfile().get(0).getValue());
exts = res1.getExtensionsByUrl("http://example.com/Weight");
assertEquals(0, exts.size());
assertEquals("185 cm", res1.getWeight().getValue());
}
@Test
public void parseResourceWithDirective() {
String input = createResource(true);
FhirContext ctx = FhirContext.forDstu3();
ctx.setDefaultTypeForProfile("http://example.com/foo", MyCustomPatient.class);
MyCustomPatient parsed = (MyCustomPatient) ctx.newXmlParser().parseResource(input);
assertEquals(1, parsed.getMeta().getProfile().size());
assertEquals("http://example.com/foo", parsed.getMeta().getProfile().get(0).getValue());
List<org.hl7.fhir.dstu3.model.Extension> exts = parsed.getExtensionsByUrl("http://example.com/Weight");
assertEquals(0, exts.size());
assertEquals("185 cm", parsed.getWeight().getValue());
}
@Test
public void parseResourceWithNoDirective() {
String input = createResource(true);
FhirContext ctx = FhirContext.forDstu3();
Patient parsed = (Patient) ctx.newXmlParser().parseResource(input);
assertEquals(1, parsed.getMeta().getProfile().size());
assertEquals("http://example.com/foo", parsed.getMeta().getProfile().get(0).getValue());
List<org.hl7.fhir.dstu3.model.Extension> exts = parsed.getExtensionsByUrl("http://example.com/Weight");
assertEquals(1, exts.size());
assertEquals("185 cm", ((StringType)exts.get(0).getValue()).getValue());
}
@Test
public void testAccessEmptyMetaLists() {
Patient p = new Patient();
assertThat(p.getMeta().getProfile(), empty());
assertThat(p.getMeta().getFormatCommentsPost(), empty());
assertThat(p.getMeta().getFormatCommentsPre(), empty());
assertThat(p.getMeta().getLastUpdated(), nullValue());
assertThat(p.getMeta().getSecurity(), empty());
assertThat(p.getMeta().getSecurity("foo", "bar"), nullValue());
assertThat(p.getMeta().getTag(), empty());
assertThat(p.getMeta().getTag("foo", "bar"), nullValue());
assertThat(p.getMeta().getVersionId(), nullValue());
}
@Test
public void testEncodeCompleteMetaLists() {
Patient p = new Patient();
p.getMeta().addProfile("http://foo/profile1");
p.getMeta().addProfile("http://foo/profile2");
p.getMeta().addSecurity().setSystem("SEC_S1").setCode("SEC_C1").setDisplay("SED_D1");
p.getMeta().addSecurity().setSystem("SEC_S2").setCode("SEC_C2").setDisplay("SED_D2");
p.getMeta().addTag().setSystem("TAG_S1").setCode("TAG_C1").setDisplay("TAG_D1");
p.getMeta().addTag().setSystem("TAG_S2").setCode("TAG_C2").setDisplay("TAG_D2");
String out = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(out);
//@formatter:off
assertThat(out, stringContainsInOrder(
"<meta>",
"<profile value=\"http://foo/profile1\"/>",
"<profile value=\"http://foo/profile2\"/>",
"<security>",
"<system value=\"SEC_S1\"/>",
"<code value=\"SEC_C1\"/>",
"<display value=\"SED_D1\"/>",
"</security>",
"<security>",
"<system value=\"SEC_S2\"/>",
"<code value=\"SEC_C2\"/>",
"<display value=\"SED_D2\"/>",
"</security>",
"<tag>",
"<system value=\"TAG_S1\"/>",
"<display value=\"TAG_D1\"/>",
"</tag>",
"<tag>",
"<system value=\"TAG_S2\"/>",
"<display value=\"TAG_D2\"/>",
"</tag>",
"</meta>"));
//@formatter:on
}
@Test
public void testEncodeWithCustomType() {
MyCustomPatient patient = new MyCustomPatient(); MyCustomPatient patient = new MyCustomPatient();
@ -36,17 +243,76 @@ public class CustomTypeDstu3Test {
patient.setCholesterol(new Quantity()); patient.setCholesterol(new Quantity());
patient.setWeight(new StringDt("80 kg")); patient.setWeight(new StringDt("80 kg"));
patient.setWeight(new StringDt("185 cm")); patient.setWeight(new StringDt("185 cm"));
patient.setCheckDates(new ArrayList<DateTimeType>()); patient.setCheckDates(new ArrayList<DateTimeDt>());
patient.getCheckDates().add(new DateTimeType("2014-01-26T11:11:11")); patient.getCheckDates().add(new DateTimeDt("2014-01-26T11:11:11"));
IParser p = ctx.newXmlParser().setPrettyPrint(true); ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient); String messageString = p.encodeResourceToString(patient);
ourLog.info(messageString); ourLog.info(messageString);
//@formatter:off
assertThat(messageString, stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"/>",
"</meta>"));
//@formatter:on
//@formatter:off
assertThat(messageString, not(stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"", "/>",
"<profile value=\"http://example.com/foo\"/>",
"</meta>")));
//@formatter:on
} }
@ResourceDef(name = "Patient") @Test
public void testEncodeWithCustomTypeAndAutoInsertedProfile() {
MyCustomPatient patient = new MyCustomPatient();
patient.getMeta().addProfile("http://example.com/foo");
patient.getMeta().addProfile("http://example.com/bar");
patient.addIdentifier().setSystem("urn:system").setValue("1234");
patient.addName().addFamily("Rossi").addGiven("Mario");
patient.setInsulinLevel(new Quantity());
patient.setGlucoseLevel(new Quantity());
patient.setHbA1c(new Quantity());
patient.setBloodPressure(new Quantity());
patient.setCholesterol(new Quantity());
patient.setWeight(new StringDt("80 kg"));
patient.setWeight(new StringDt("185 cm"));
patient.setCheckDates(new ArrayList<DateTimeDt>());
patient.getCheckDates().add(new DateTimeDt("2014-01-26T11:11:11"));
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);
ourLog.info(messageString);
//@formatter:off
assertThat(messageString, stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"/>",
"<profile value=\"http://example.com/bar\"/>",
"</meta>"));
//@formatter:on
//@formatter:off
assertThat(messageString, not(stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"", "/>",
"<profile value=\"http://example.com/foo\"/>",
"</meta>")));
//@formatter:on
}
@ResourceDef(name = "Patient", profile = "http://example.com/foo")
public static class MyCustomPatient extends Patient { public static class MyCustomPatient extends Patient {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -60,7 +326,7 @@ public class CustomTypeDstu3Test {
@Child(name = "CheckDates", max = Child.MAX_UNLIMITED) @Child(name = "CheckDates", max = Child.MAX_UNLIMITED)
@Extension(url = "http://example.com/diabetes2", definedLocally = false, isModifier = true) @Extension(url = "http://example.com/diabetes2", definedLocally = false, isModifier = true)
@Description(shortDefinition = "Dates of periodic tests") @Description(shortDefinition = "Dates of periodic tests")
private List<DateTimeType> myCheckDates; private List<DateTimeDt> myCheckDates;
@Child(name = "cholesterol") // once a year. The target is triglycerides =< 2 mmol/l e cholesterol =< 4 mmol/l @Child(name = "cholesterol") // once a year. The target is triglycerides =< 2 mmol/l e cholesterol =< 4 mmol/l
@Extension(url = "http://example.com/Cholesterol", definedLocally = false, isModifier = false) @Extension(url = "http://example.com/Cholesterol", definedLocally = false, isModifier = false)
@ -116,9 +382,9 @@ public class CustomTypeDstu3Test {
return myBloodPressure; return myBloodPressure;
} }
public List<DateTimeType> getCheckDates() { public List<DateTimeDt> getCheckDates() {
if (myCheckDates == null) { if (myCheckDates == null) {
myCheckDates = new ArrayList<DateTimeType>(); myCheckDates = new ArrayList<DateTimeDt>();
} }
return myCheckDates; return myCheckDates;
} }
@ -182,9 +448,9 @@ public class CustomTypeDstu3Test {
myBloodPressure.setCode("mmHg"); myBloodPressure.setCode("mmHg");
} }
public void setCheckDates(List<DateTimeType> theCheckDates) { public void setCheckDates(List<DateTimeDt> theCheckDates) {
myCheckDates = theCheckDates; myCheckDates = theCheckDates;
myCheckDates.add(new DateTimeType("2010-01-02")); myCheckDates.add(new DateTimeDt("2010-01-02"));
} }
public void setCholesterol(Quantity cholesterol) { public void setCholesterol(Quantity cholesterol) {

View File

@ -0,0 +1,116 @@
package ca.uhn.fhir.rest.client;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.Arrays;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.hl7.fhir.dstu3.model.Binary;
import org.hl7.fhir.dstu3.model.Conformance;
import org.hl7.fhir.dstu3.model.OperationOutcome;
import org.hl7.fhir.dstu3.model.Patient;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.VersionUtil;
public class ClientWithCustomTypeDstu3Test {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ClientWithCustomTypeDstu3Test.class);
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;
@Before
public void before() {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
private byte[] extractBodyAsByteArray(ArgumentCaptor<HttpUriRequest> capt) throws IOException {
byte[] body = IOUtils.toByteArray(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(0)).getEntity().getContent());
return body;
}
private String extractBodyAsString(ArgumentCaptor<HttpUriRequest> capt) throws IOException {
String body = IOUtils.toString(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(0)).getEntity().getContent(), "UTF-8");
return body;
}
@Test
public void testReadCustomType() throws Exception {
IParser p = ourCtx.newXmlParser();
MyPatientWithExtensions response = new MyPatientWithExtensions();
response.addName().addFamily("FAMILY");
response.getStringExt().setValue("STRINGVAL");
response.getDateExt().setValueAsString("2011-01-02");
final String respString = p.encodeResourceToString(response);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
//@formatter:off
MyPatientWithExtensions value = client
.read()
.resource(MyPatientWithExtensions.class)
.withId("123")
.execute();
//@formatter:on
HttpUriRequest request = capt.getAllValues().get(0);
assertEquals("http://example.com/fhir/Patient/123", request.getURI().toASCIIString());
assertEquals("GET", request.getMethod());
assertEquals(1, value.getName().size());
assertEquals("FAMILY", value.getName().get(0).getFamilyAsSingleString());
assertEquals("STRINGVAL", value.getStringExt().getValue());
assertEquals("2011-01-02", value.getDateExt().getValueAsString());
}
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forDstu3();
}
}

View File

@ -0,0 +1,94 @@
package ca.uhn.fhir.rest.client;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.dstu3.model.BaseResource;
import org.hl7.fhir.dstu3.model.DateType;
import org.hl7.fhir.dstu3.model.DomainResource;
import org.hl7.fhir.dstu3.model.HumanName;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.ResourceType;
import org.hl7.fhir.dstu3.model.StringType;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.util.ElementUtil;
@ResourceDef(name="Patient", profile="http://example.com/StructureDefinition/patient_with_extensions")
public class MyPatientWithExtensions extends DomainResource {
private static final long serialVersionUID = 1L;
@Extension(url = "http://example.com/ext/date", definedLocally = false, isModifier = true)
@Child(name = "modExt")
private DateType myDateExt;
@Extension(url = "http://example.com/ext/string", definedLocally = false, isModifier = false)
@Child(name = "extAtt")
private StringType myStringExt;
@Child(name = "name", type = {HumanName.class}, min=0, max=Child.MAX_UNLIMITED, modifier=false, summary=true)
@Description(shortDefinition="A name associated with the patient", formalDefinition="A name associated with the individual." )
private List<HumanName> myName;
public List<HumanName> getName() {
if (myName == null) {
myName = new ArrayList<HumanName>();
}
return myName;
}
public void setName(List<HumanName> theName) {
myName = theName;
}
public DateType getDateExt() {
if (myDateExt == null) {
myDateExt = new DateType();
}
return myDateExt;
}
public StringType getStringExt() {
if (myStringExt == null) {
myStringExt = new StringType();
}
return myStringExt;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(myStringExt, myDateExt);
}
public void setDateExt(DateType theDateExt) {
myDateExt = theDateExt;
}
public void setStringExt(StringType theStringExt) {
myStringExt = theStringExt;
}
@Override
public DomainResource copy() {
return null;
}
@Override
public ResourceType getResourceType() {
return ResourceType.Patient;
}
public HumanName addName() {
HumanName retVal = new HumanName();
getName().add(retVal);
return retVal;
}
}

View File

@ -1,6 +1,8 @@
package ca.uhn.fhir.rest.server; package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
@ -10,120 +12,88 @@ import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.dstu3.model.DateType;
import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.OperationOutcome;
import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.MyPatientWithExtensions;
import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.PortUtil;
public class CreateDstu3Test { public class CreateDstu3Test {
private static CloseableHttpClient ourClient; private static CloseableHttpClient ourClient;
private static String ourLastConditionalUrl; private static FhirContext ourCtx = FhirContext.forDstu3();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateDstu3Test.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateDstu3Test.class);
private static int ourPort; private static int ourPort;
private static Server ourServer; private static Server ourServer;
private static IdType ourLastId;
private static IdType ourLastIdParam;
private static boolean ourLastRequestWasSearch;
private static FhirContext ourCtx = FhirContext.forDstu3();
@Before
public void before() {
ourLastId = null;
ourLastConditionalUrl = null;
ourLastIdParam = null;
ourLastRequestWasSearch = false;
}
@Test @Test
public void testCreateWithIdInBody() throws Exception { public void testRead() throws Exception {
Patient patient = new Patient(); HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2?_format=xml&_pretty=true");
patient.setId("2"); HttpResponse status = ourClient.execute(httpGet);
patient.addIdentifier().setValue("002");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
// httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent()); String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent); ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode()); assertEquals(200, status.getStatusLine().getStatusCode());
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("Can not create resource with ID \"2\", an ID element must not be supplied in the resource body on a create (POST) operation", oo.getIssue().get(0).getDiagnostics()); //@formatter:off
assertThat(responseContent, stringContainsInOrder("<Patient xmlns=\"http://hl7.org/fhir\">",
"<id value=\"2\"/>",
"<modifierExtension url=\"http://example.com/ext/date\">",
"<valueDate value=\"2011-01-01\"/>",
"</modifierExtension>",
"<meta>",
"<profile value=\"http://example.com/StructureDefinition/patient_with_extensions\"/>",
"</meta>",
"</Patient>"));
//@formatter:on
} }
@Test @Test
public void testCreateWithIdInUrl() throws Exception { public void testSearch() throws Exception {
Patient patient = new Patient(); HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml&_pretty=true");
patient.addIdentifier().setValue("002"); HttpResponse status = ourClient.execute(httpGet);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2");
// httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent()); String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent); ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode()); assertEquals(200, status.getStatusLine().getStatusCode());
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics());
}
@Test //@formatter:off
public void testCreateWithIdInUrlForConditional() throws Exception { assertThat(responseContent, stringContainsInOrder("<Patient xmlns=\"http://hl7.org/fhir\">",
"<id value=\"0\"/>",
"<modifierExtension url=\"http://example.com/ext/date\">",
"<valueDate value=\"2011-01-01\"/>",
"</modifierExtension>",
"<meta>",
"<profile value=\"http://example.com/StructureDefinition/patient_with_extensions\"/>",
"</meta>",
"</Patient>"));
//@formatter:on
Patient patient = new Patient(); assertThat(responseContent, not(containsString("http://hl7.org/fhir/")));
patient.addIdentifier().setValue("002");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2");
httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics());
} }
@AfterClass @AfterClass
@ -140,6 +110,7 @@ public class CreateDstu3Test {
ServletHandler proxyHandler = new ServletHandler(); ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx); RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setResourceProviders(patientProvider); servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet); ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*"); proxyHandler.addServletWithMapping(servletHolder, "/*");
@ -160,18 +131,29 @@ public class CreateDstu3Test {
return Patient.class; return Patient.class;
} }
@Search @Read()
public List<IResource> search(@OptionalParam(name = "foo") StringDt theString) { public MyPatientWithExtensions read(@IdParam IdType theIdParam) {
ourLastRequestWasSearch = true; MyPatientWithExtensions p0 = new MyPatientWithExtensions();
return new ArrayList<IResource>(); p0.setId(theIdParam);
p0.setDateExt(new DateType("2011-01-01"));
return p0;
} }
@Create() @Search
public MethodOutcome createPatient(@ResourceParam Patient thePatient, @ConditionalUrlParam String theConditional, @IdParam IdType theIdParam) { public List<IBaseResource> search() {
ourLastConditionalUrl = theConditional; ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
ourLastId = thePatient.getIdElement();
ourLastIdParam = theIdParam; MyPatientWithExtensions p0 = new MyPatientWithExtensions();
return new MethodOutcome(new IdType("Patient/001/_history/002")); p0.setId(new IdType("Patient/0"));
p0.setDateExt(new DateType("2011-01-01"));
retVal.add(p0);
Patient p1 = new Patient();
p1.setId(new IdType("Patient/1"));
p1.addName().addFamily("The Family");
retVal.add(p1);
return retVal;
} }
} }

View File

@ -0,0 +1,179 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.OperationOutcome;
import org.hl7.fhir.dstu3.model.Patient;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.util.PortUtil;
public class CustomTypeServerDstu3 {
private static CloseableHttpClient ourClient;
private static String ourLastConditionalUrl;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeServerDstu3.class);
private static int ourPort;
private static Server ourServer;
private static IdType ourLastId;
private static IdType ourLastIdParam;
private static boolean ourLastRequestWasSearch;
private static FhirContext ourCtx = FhirContext.forDstu3();
@Before
public void before() {
ourLastId = null;
ourLastConditionalUrl = null;
ourLastIdParam = null;
ourLastRequestWasSearch = false;
}
@Test
public void testCreateWithIdInBody() throws Exception {
Patient patient = new Patient();
patient.setId("2");
patient.addIdentifier().setValue("002");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
// httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("Can not create resource with ID \"2\", an ID element must not be supplied in the resource body on a create (POST) operation", oo.getIssue().get(0).getDiagnostics());
}
@Test
public void testCreateWithIdInUrl() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2");
// httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics());
}
@Test
public void testCreateWithIdInUrlForConditional() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2");
httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics());
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
PatientProvider patientProvider = new PatientProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class PatientProvider implements IResourceProvider {
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Search
public List<IResource> search(@OptionalParam(name = "foo") StringDt theString) {
ourLastRequestWasSearch = true;
return new ArrayList<IResource>();
}
@Create()
public MethodOutcome createPatient(@ResourceParam Patient thePatient, @ConditionalUrlParam String theConditional, @IdParam IdType theIdParam) {
ourLastConditionalUrl = theConditional;
ourLastId = thePatient.getIdElement();
ourLastIdParam = theIdParam;
return new MethodOutcome(new IdType("Patient/001/_history/002"));
}
}
}

View File

@ -48,10 +48,8 @@ import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.rest.server.BundleInclusionRule; import ca.uhn.fhir.rest.server.BundleInclusionRule;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
@ -59,7 +57,6 @@ import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.IPagingProvider; import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.IRestfulServer; import ca.uhn.fhir.rest.server.IRestfulServer;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory; import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.ResourceReferenceInfo; import ca.uhn.fhir.util.ResourceReferenceInfo;
@ -298,7 +295,7 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
} }
@Override @Override
public void initializeBundleFromBundleProvider(IRestfulServer theServer, IBundleProvider theResult, public void initializeBundleFromBundleProvider(IRestfulServer<?> theServer, IBundleProvider theResult,
EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint,
int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) { int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) {
myBase = theServerBase; myBase = theServerBase;
@ -344,15 +341,6 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
} }
} }
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
for (IBaseResource nextRes : resourceList) {
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
RestfulServerUtils.addProfileToBundleEntry(theServer.getFhirContext(), nextRes, theServerBase);
}
}
}
addResourcesToBundle(resourceList, theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes); addResourcesToBundle(resourceList, theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes);
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType,
theResult.getPublished()); theResult.getPublished());

View File

@ -60,6 +60,7 @@ import org.hl7.fhir.instance.model.StringType;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.INarrative; import org.hl7.fhir.instance.model.api.INarrative;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.junit.After;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -67,11 +68,11 @@ import org.xml.sax.SAXException;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.base.composite.BaseNarrativeDt;
import ca.uhn.fhir.narrative.INarrativeGenerator; import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.parser.JsonParserHl7OrgTest.MyPatientWithOneDeclaredAddressExtension; import ca.uhn.fhir.parser.JsonParserHl7OrgTest.MyPatientWithOneDeclaredAddressExtension;
import ca.uhn.fhir.parser.JsonParserHl7OrgTest.MyPatientWithOneDeclaredEnumerationExtension; import ca.uhn.fhir.parser.JsonParserHl7OrgTest.MyPatientWithOneDeclaredEnumerationExtension;
import ca.uhn.fhir.parser.JsonParserHl7OrgTest.MyPatientWithOneDeclaredExtension; import ca.uhn.fhir.parser.JsonParserHl7OrgTest.MyPatientWithOneDeclaredExtension;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import net.sf.json.JSON; import net.sf.json.JSON;
import net.sf.json.JSONSerializer; import net.sf.json.JSONSerializer;
@ -81,6 +82,11 @@ public class XmlParserHl7OrgDstu2Test {
private static FhirContext ourCtx; private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParserHl7OrgDstu2Test.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParserHl7OrgDstu2Test.class);
@After
public void after() {
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
}
private String fixDivNodeText(String htmlNoNs) { private String fixDivNodeText(String htmlNoNs) {
return htmlNoNs.replace("<div>", "<div xmlns=\"http://www.w3.org/1999/xhtml\">"); return htmlNoNs.replace("<div>", "<div xmlns=\"http://www.w3.org/1999/xhtml\">");
} }
@ -89,25 +95,6 @@ public class XmlParserHl7OrgDstu2Test {
return htmlNoNs.replace("<div>", "<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">"); return htmlNoNs.replace("<div>", "<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">");
} }
/**
* See #216 - Profiled datatypes should use their unprofiled parent type as the choice[x] name
*
* Disabled after conversation with Grahame
*/
@Test
@Ignore
public void testEncodeAndParseProfiledDatatypeChoice() throws Exception {
IParser xmlParser = ourCtx.newXmlParser();
String input = IOUtils.toString(XmlParser.class.getResourceAsStream("/medicationstatement_invalidelement.xml"));
MedicationStatement ms = xmlParser.parseResource(MedicationStatement.class, input);
SimpleQuantity q = (SimpleQuantity) ms.getDosage().get(0).getQuantity();
assertEquals("1", q.getValueElement().getValueAsString());
String output = xmlParser.encodeResourceToString(ms);
assertThat(output, containsString("<quantityQuantity><value value=\"1\"/></quantityQuantity>"));
}
@Test @Test
public void testComposition() { public void testComposition() {
@ -575,6 +562,26 @@ public class XmlParserHl7OrgDstu2Test {
} }
/**
* See #216 - Profiled datatypes should use their unprofiled parent type as the choice[x] name
*
* Disabled after conversation with Grahame
*/
@Test
@Ignore
public void testEncodeAndParseProfiledDatatypeChoice() throws Exception {
IParser xmlParser = ourCtx.newXmlParser();
String input = IOUtils.toString(XmlParser.class.getResourceAsStream("/medicationstatement_invalidelement.xml"));
MedicationStatement ms = xmlParser.parseResource(MedicationStatement.class, input);
SimpleQuantity q = (SimpleQuantity) ms.getDosage().get(0).getQuantity();
assertEquals("1", q.getValueElement().getValueAsString());
String output = xmlParser.encodeResourceToString(ms);
assertThat(output, containsString("<quantityQuantity><value value=\"1\"/></quantityQuantity>"));
}
@Test @Test
public void testEncodeBinaryResource() { public void testEncodeBinaryResource() {
@ -587,7 +594,6 @@ public class XmlParserHl7OrgDstu2Test {
} }
@Test @Test
public void testEncodeBinaryWithNoContentType() { public void testEncodeBinaryWithNoContentType() {
Binary b = new Binary(); Binary b = new Binary();
@ -613,7 +619,6 @@ public class XmlParserHl7OrgDstu2Test {
assertThat(val, containsString("home")); assertThat(val, containsString("home"));
assertThat(val, containsString("male")); assertThat(val, containsString("male"));
} }
@Test @Test
public void testEncodeBundle() throws InterruptedException { public void testEncodeBundle() throws InterruptedException {
Bundle b = new Bundle(); Bundle b = new Bundle();
@ -650,6 +655,7 @@ public class XmlParserHl7OrgDstu2Test {
assertThat(bundleString, StringContainsInOrder.stringContainsInOrder(strings)); assertThat(bundleString, StringContainsInOrder.stringContainsInOrder(strings));
} }
@Test @Test
public void testEncodeBundleCategory() { public void testEncodeBundleCategory() {
@ -718,6 +724,7 @@ public class XmlParserHl7OrgDstu2Test {
} }
@Test @Test
public void testEncodeContainedWithNarrativeIsSuppresed() throws Exception { public void testEncodeContainedWithNarrativeIsSuppresed() throws Exception {
IParser parser = ourCtx.newXmlParser().setPrettyPrint(true); IParser parser = ourCtx.newXmlParser().setPrettyPrint(true);
@ -745,7 +752,6 @@ public class XmlParserHl7OrgDstu2Test {
} }
@Test @Test
public void testEncodeDeclaredExtensionWithAddressContent() { public void testEncodeDeclaredExtensionWithAddressContent() {
IParser parser = ourCtx.newXmlParser(); IParser parser = ourCtx.newXmlParser();
@ -1110,6 +1116,7 @@ public class XmlParserHl7OrgDstu2Test {
} }
@Test @Test
public void testExtensionOnComposite() throws Exception { public void testExtensionOnComposite() throws Exception {
@ -1133,7 +1140,6 @@ public class XmlParserHl7OrgDstu2Test {
} }
@Test @Test
public void testExtensionOnPrimitive() throws Exception { public void testExtensionOnPrimitive() throws Exception {
@ -1219,6 +1225,7 @@ public class XmlParserHl7OrgDstu2Test {
@Test @Test
public void testLoadAndEncodeDeclaredExtensions() throws ConfigurationException, DataFormatException, SAXException, IOException { public void testLoadAndEncodeDeclaredExtensions() throws ConfigurationException, DataFormatException, SAXException, IOException {
IParser p = ourCtx.newXmlParser(); IParser p = ourCtx.newXmlParser();
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.NEVER);
//@formatter:off //@formatter:off
String msg = "<ResourceWithExtensionsA xmlns=\"http://hl7.org/fhir\">\n" + String msg = "<ResourceWithExtensionsA xmlns=\"http://hl7.org/fhir\">\n" +

View File

@ -26,9 +26,6 @@ import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class BundleTypeInResponseHl7OrgTest { public class BundleTypeInResponseHl7OrgTest {
private static CloseableHttpClient ourClient; private static CloseableHttpClient ourClient;

View File

@ -1,180 +0,0 @@
package ca.uhn.fhir.model.dstu2.resource;
/*
* #%L
* HAPI FHIR Structures - DSTU2 (FHIR v0.4.0)
* %%
* 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 org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.model.api.BaseElement;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.SearchParamDefinition;
import ca.uhn.fhir.model.base.resource.ResourceMetadataMap;
import ca.uhn.fhir.model.dstu2.composite.ContainedDt;
import ca.uhn.fhir.model.dstu2.composite.NarrativeDt;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.gclient.StringClientParam;
import ca.uhn.fhir.util.ElementUtil;
public abstract class BaseResource extends BaseElement implements IResource {
/**
* Search parameter constant for <b>_language</b>
*/
@SearchParamDefinition(name="_language", path="", description="The language of the resource", type="string" )
public static final String SP_RES_LANGUAGE = "_language";
/**
* Search parameter constant for <b>_id</b>
*/
@SearchParamDefinition(name="_id", path="", description="The ID of the resource", type="string" )
public static final String SP_RES_ID = "_id";
/**
* <b>Fluent Client</b> search parameter constant for <b>_id</b>
* <p>
* Description: <b>the _id of a resource</b><br>
* Type: <b>string</b><br>
* Path: <b>Resource._id</b><br>
* </p>
*/
public static final StringClientParam RES_ID = new StringClientParam(BaseResource.SP_RES_ID);
@Child(name = "contained", order = 2, min = 0, max = 1)
private ContainedDt myContained;
private IdDt myId;
@Child(name = "language", order = 0, min = 0, max = Child.MAX_UNLIMITED)
private CodeDt myLanguage;
private ResourceMetadataMap myResourceMetadata;
@Child(name = "text", order = 1, min = 0, max = 1)
private NarrativeDt myText;
@Override
public ContainedDt getContained() {
if (myContained == null) {
myContained = new ContainedDt();
}
return myContained;
}
public IdDt getId() {
if (myId == null) {
myId = new IdDt();
}
return myId;
}
@Override
public IIdType getIdElement() {
return getId();
}
@Override
public CodeDt getLanguage() {
if (myLanguage == null) {
myLanguage = new CodeDt();
}
return myLanguage;
}
@Override
public ResourceMetadataMap getResourceMetadata() {
if (myResourceMetadata == null) {
myResourceMetadata = new ResourceMetadataMap();
}
return myResourceMetadata;
}
@Override
public NarrativeDt getText() {
if (myText == null) {
myText = new NarrativeDt();
}
return myText;
}
public void setContained(ContainedDt theContained) {
myContained = theContained;
}
public void setId(IdDt theId) {
myId = theId;
}
public BaseResource setId(IIdType theId) {
if (theId instanceof IdDt) {
myId = (IdDt) theId;
} else if (theId != null) {
myId = new IdDt(theId.getValue());
}
return this;
}
public BaseResource setId(String theId) {
if (theId == null) {
myId = null;
} else {
myId = new IdDt(theId);
}
return this;
}
@Override
public void setLanguage(CodeDt theLanguage) {
myLanguage = theLanguage;
}
@Override
public void setResourceMetadata(ResourceMetadataMap theMap) {
Validate.notNull(theMap, "The Map must not be null");
myResourceMetadata = theMap;
}
public void setText(NarrativeDt theText) {
myText = theText;
}
@Override
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
b.append("id", getId().toUnqualified());
return b.toString();
}
/**
* Intended to be called by extending classes {@link #isEmpty()} implementations, returns <code>true</code> if all
* content in this superclass instance is empty per the semantics of {@link #isEmpty()}.
*/
@Override
protected boolean isBaseEmpty() {
return super.isBaseEmpty() && ElementUtil.isEmpty(myLanguage, myText, myId);
}
}

View File

@ -51,6 +51,7 @@
} }
#foreach ( $child in $childElements ) #foreach ( $child in $childElements )
#if (${child.methodName} != 'Meta')
/** /**
* Gets the value(s) for <b>${child.elementName}</b> ($esc.html(${child.shortName})). * Gets the value(s) for <b>${child.elementName}</b> ($esc.html(${child.shortName})).
* creating it if it does * creating it if it does
@ -73,7 +74,7 @@
#end #end
return ${child.variableName}; return ${child.variableName};
} }
#end
/** /**
* Gets the value(s) for <b>${child.elementName}</b> ($esc.html(${child.shortName})). * Gets the value(s) for <b>${child.elementName}</b> ($esc.html(${child.shortName})).

View File

@ -261,6 +261,44 @@
header containing "application/xml" as some browsers header containing "application/xml" as some browsers
do. do.
</action> </action>
<action type="add">
DSTU2 resources now have a
<![CDATA[<code>getMeta()</code>]]> method which returns a
modifiable view of the resource metadata for convenience. This
matches the equivalent method in the DSTU3 structures.
</action>
<action type="add" issue="315">
Add a new method to FhirContext called
<![CDATA[
<code><a href="./apidocs/ca/uhn/fhir/context/FhirContext.html#setDefaultTypeForProfile-java.lang.String-java.lang.Class-">setDefaultTypeForProfile</a></code>
]]>
which can be used to specify that when recources are received which declare
support for specific profiles, a specific custom structures should be used
instead of the default. For example, if you have created a custom Observation
class for a specific profile, you could use this method to cause your custom
type to be used by the parser for resources in a search bundle you receive.
<![CDATA[
<br/><br/>
See the documentation page on
<a href="./doc_extensions.html">Profiles and Extensions</a>
for more information.
]]>
</action>
<action type="fix" issue="315">
Parsing/Encoding a custom resource type which extends a
base type sometimes caused the FhirContext to treat all future
parses of the same resource as using the custom type even when
this was not wanted.
<![CDATA[<br/><br/>]]>
Custom structures may now be explicitly declared by profile
using the
<![CDATA[
<code><a href="./apidocs/ca/uhn/fhir/context/FhirContext.html#setDefaultTypeForProfile-java.lang.String-java.lang.Class-">setDefaultTypeForProfile</a></code>
]]>
method.
<![CDATA[<br/><br/>]]>
This issue was discovered and fixed as a part of the implementation of issue #315.
</action>
</release> </release>
<release version="1.4" date="2016-02-04"> <release version="1.4" date="2016-02-04">
<action type="add"> <action type="add">

View File

@ -3,13 +3,20 @@
<properties> <properties>
<title>Profiles and Extensions</title> <title>Profiles and Extensions</title>
<author email="jamesagnew@users.sourceforge.net">James Agnew</author> <author email="jamesagnew@gmail.com">James Agnew</author>
</properties> </properties>
<body> <body>
<section name="Undeclared Extensions"> <section name="Undeclared Extensions">
<p class="doc_info_bubble">
Note on FHIR Versions: Because of the differences in the way the structures
work between DSTU2 and DSTU3, we have provided two versions of many of the
examples on this page. See the <a href="./download.html">download page</a>
for more information on FHIR versions.
</p>
<p> <p>
Extensions are a key part of the FHIR specification, providing a standardized Extensions are a key part of the FHIR specification, providing a standardized
way of placing additional data in a resource. way of placing additional data in a resource.
@ -21,17 +28,40 @@
Undeclared extensions can be added to any of the built in FHIR resource types that come with HAPI-FHIR. Undeclared extensions can be added to any of the built in FHIR resource types that come with HAPI-FHIR.
</p> </p>
<p>
<b>DSTU2</b>
</p>
<macro name="snippet"> <macro name="snippet">
<param name="id" value="resourceExtension" /> <param name="id" value="resourceExtension" />
<param name="file" value="examples/src/main/java/example/Extensions.java" /> <param name="file" value="examples/src/main/java/example/ExtensionsDstu2.java" />
</macro>
<p>
<b>DSTU3</b>
</p>
<macro name="snippet">
<param name="id" value="resourceExtension" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu3.java" />
</macro> </macro>
<p> <p>
Undeclared extensions can also be added to datatypes (composite or primitive). Undeclared extensions can also be added to datatypes (composite or primitive).
</p> </p>
<p>
<b>DSTU2</b>
</p>
<macro name="snippet"> <macro name="snippet">
<param name="id" value="resourceStringExtension" /> <param name="id" value="resourceStringExtension" />
<param name="file" value="examples/src/main/java/example/Extensions.java" /> <param name="file" value="examples/src/main/java/example/ExtensionsDstu2.java" />
</macro>
<p>
<b>DSTU3</b>
</p>
<macro name="snippet">
<param name="id" value="resourceStringExtension" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu3.java" />
</macro> </macro>
<subsection name="Sub-Extensions"> <subsection name="Sub-Extensions">
@ -41,9 +71,21 @@
of a datatype. This is done by adding a child undeclared extension to the of a datatype. This is done by adding a child undeclared extension to the
parent extension. parent extension.
</p> </p>
<p>
<b>DSTU2</b>
</p>
<macro name="snippet"> <macro name="snippet">
<param name="id" value="subExtension" /> <param name="id" value="subExtension" />
<param name="file" value="examples/src/main/java/example/Extensions.java" /> <param name="file" value="examples/src/main/java/example/ExtensionsDstu2.java" />
</macro>
<p>
<b>DSTU3</b>
</p>
<macro name="snippet">
<param name="id" value="subExtension" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu3.java" />
</macro> </macro>
</subsection> </subsection>
@ -52,11 +94,23 @@
<p> <p>
HAPI provides a few ways of accessing extension values in resources HAPI provides a few ways of accessing extension values in resources
which are receieved from other sources (i.e. downloaded by a client). which are received from other sources (i.e. downloaded by a client).
</p>
<p>
<b>DSTU2</b>
</p> </p>
<macro name="snippet"> <macro name="snippet">
<param name="id" value="parseExtension" /> <param name="id" value="parseExtension" />
<param name="file" value="examples/src/main/java/example/Extensions.java" /> <param name="file" value="examples/src/main/java/example/ExtensionsDstu2.java" />
</macro>
<p>
<b>DSTU3</b>
</p>
<macro name="snippet">
<param name="id" value="parseExtension" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu3.java" />
</macro> </macro>
</subsection> </subsection>
@ -119,84 +173,43 @@
<param name="file" value="examples/src/main/java/example/MyPatientUse.java" /> <param name="file" value="examples/src/main/java/example/MyPatientUse.java" />
</macro> </macro>
<subsection name="Documentation"> <subsection name="Using Custom Types in a Client">
<p> <p>
Note in the example above the use of the <code>@Description</code> If you are using a client and wish to use a specific custom structure,
annotation on the extension fields. This documentation is important, you may simply use the custom structure as you would a build in
as the FHIR Server will automatically add it to the generated Profile HAPI type.
resources which are exported by the server.
</p> </p>
<macro name="snippet">
<param name="id" value="customTypeClientSimple" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu3.java" />
</macro>
<p> <p>
These bits of documentation will then be re-added to any automatically Sometimes you may not know in advance exactly which
generated clients built using <a href="./doc_tinder.html">Tinder</a> type you will be receiving. For example, there are Patient resources
against your server. which conform to several different profiles on a server and you
aren't sure which profile you will get back for a specific read,
you can declare the "primary" type for a given profile.
</p> </p>
<macro name="snippet">
<param name="id" value="customTypeClientDeclared" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu3.java" />
</macro>
</subsection>
<subsection name="Using Custom Types in a Server">
<p> <p>
The following is a snippet from the generated Profile resource If you are using a client and wish to use a specific custom structure,
which is automatically created by a HAPI RESTful server using you may simply use the custom structure as you would a build in
your new type. (Don't worry if this part doens't make sense to you HAPI type.
yet, this is an advanced feature)
</p> </p>
<macro name="snippet">
<source><![CDATA[<element> <param name="id" value="customTypeClientSimple" />
<path value="Patient.extension"/> <param name="file" value="examples/src/main/java/example/ExtensionsDstu3.java" />
<name value="extension"/> </macro>
<slicing>
<discriminator value="url"/>
<ordered value="false"/>
<rules value="open"/>
</slicing>
<definition>
<type>
<code value="Extension"/>
</type>
</definition>
</element>
<element>
<path value="Patient.extension"/>
<name value="extension"/>
<definition>
<short value="The name of the patient's favourite pet"/>
<min value="0"/>
<max value="1"/>
<type>
<code value="Extension"/>
<profile value="http://example.com/dontuse#petname"/>
</type>
<isModifier value="false"/>
</definition>
</element>
<element>
<path value="Patient.modifierExtension"/>
<name value="modifierExtension"/>
<slicing>
<discriminator value="url"/>
<ordered value="false"/>
<rules value="open"/>
</slicing>
<definition>
<type>
<code value="Extension"/>
</type>
</definition>
</element>
<element>
<path value="Patient.modifierExtension"/>
<name value="modifierExtension"/>
<definition>
<short value="Some dates of note for the patient"/>
<min value="0"/>
<max value="*"/>
<type>
<code value="Extension"/>
<profile value="http://example.com/dontuse#importantDates"/>
</type>
<isModifier value="true"/>
</definition>
</element>]]></source>
</subsection> </subsection>