diff --git a/hapi-fhir-base/src/changes/changes.xml b/hapi-fhir-base/src/changes/changes.xml
index 687e6ff7149..c76ed88a524 100644
--- a/hapi-fhir-base/src/changes/changes.xml
+++ b/hapi-fhir-base/src/changes/changes.xml
@@ -7,7 +7,7 @@
-
+
Make it easier to add HTTP Basic Authorization headers to RESTful client requests
and an example]]>
illustrating how it works
@@ -15,6 +15,10 @@
Correct a dependency on commons-io that was causing issues with the Tinder build
+
+ Fix an issue where extensions with a resource reference as the value show up incorrectly
+ (wrong element name for undeclared extensions, and empty content for declared extensions)
+
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java
index 4226477a83f..b98cc3331f2 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildDeclaredExtensionDefinition.java
@@ -23,12 +23,15 @@ package ca.uhn.fhir.context;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.lang.reflect.Field;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import ca.uhn.fhir.model.api.IElement;
+import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
@@ -133,7 +136,15 @@ public class RuntimeChildDeclaredExtensionDefinition extends BaseRuntimeDeclared
BaseRuntimeElementDefinition> elementDef = theClassToElementDefinitions.get(myChildType);
if (elementDef instanceof RuntimePrimitiveDatatypeDefinition || elementDef instanceof RuntimeCompositeDatatypeDefinition) {
myDatatypeChildName = "value" + elementDef.getName().substring(0, 1).toUpperCase() + elementDef.getName().substring(1);
- myChildDef = elementDef;
+ if ("valueResourceReference".equals(myDatatypeChildName)) {
+ // Per one of the examples here: http://hl7.org/implement/standards/fhir/extensibility.html#extension
+ myDatatypeChildName = "valueResource";
+ List> types = new ArrayList>();
+ types.add(IResource.class);
+ myChildDef = new RuntimeResourceReferenceDefinition("valueResource", types);
+ }else {
+ myChildDef = elementDef;
+ }
} else {
RuntimeResourceBlockDefinition extDef = ((RuntimeResourceBlockDefinition) elementDef);
for (RuntimeChildDeclaredExtensionDefinition next : extDef.getExtensions()) {
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildUndeclaredExtensionDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildUndeclaredExtensionDefinition.java
index e45756ebbf0..9d76a9958f3 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildUndeclaredExtensionDefinition.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildUndeclaredExtensionDefinition.java
@@ -94,7 +94,7 @@ public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildD
}
// Resource Reference
- myDatatypeToAttributeName.put(ResourceReferenceDt.class, "valueReference");
+ myDatatypeToAttributeName.put(ResourceReferenceDt.class, "valueResource");
List> types = new ArrayList>();
types.add(IResource.class);
RuntimeResourceReferenceDefinition def = new RuntimeResourceReferenceDefinition("valueResource", types);
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/annotation/Description.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/annotation/Description.java
index 49b77601619..47d96aad4fc 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/annotation/Description.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/annotation/Description.java
@@ -26,7 +26,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
-@Target(value= {ElementType.FIELD, ElementType.TYPE})
+@Target(value= {ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
public @interface Description {
/**
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java
index 1135126f994..b84be27baf1 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java
@@ -59,6 +59,11 @@ public abstract class BaseParser implements IParser {
return parseResource(null, theMessageString);
}
+ @Override
+ public Bundle parseBundle(Reader theReader) {
+ return parseBundle(null, theReader);
+ }
+
@Override
public IResource parseResource(Reader theReader) throws ConfigurationException, DataFormatException {
return parseResource(null, theReader);
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/IParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/IParser.java
index f0c06b17450..90c916b11d8 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/IParser.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/IParser.java
@@ -38,17 +38,19 @@ public interface IParser {
void encodeResourceToWriter(IResource theResource, Writer stringWriter) throws IOException, DataFormatException;
+ Bundle parseBundle(Class theResourceType, Reader theReader);
+
Bundle parseBundle(Reader theReader);
Bundle parseBundle(String theMessageString) throws ConfigurationException, DataFormatException;
- IResource parseResource(String theMessageString) throws ConfigurationException, DataFormatException;
-
- IResource parseResource(Reader theReader) throws ConfigurationException, DataFormatException;
+ T parseResource(Class theResourceType, Reader theReader);
T parseResource(Class theResourceType, String theMessageString);
- T parseResource(Class theResourceType, Reader theReader);
+ IResource parseResource(Reader theReader) throws ConfigurationException, DataFormatException;
+
+ IResource parseResource(String theMessageString) throws ConfigurationException, DataFormatException;
IParser setPrettyPrint(boolean thePrettyPrint);
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java
index 025c97f5c8f..fb44d3f7e56 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java
@@ -56,9 +56,11 @@ import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
+import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
+import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.BaseBundle;
import ca.uhn.fhir.model.api.Bundle;
@@ -83,13 +85,66 @@ import ca.uhn.fhir.narrative.INarrativeGenerator;
public class JsonParser extends BaseParser implements IParser {
+ private static final Set BUNDLE_TEXTNODE_CHILDREN;
+ static {
+ HashSet hashSet = new HashSet();
+ hashSet.add("title");
+ hashSet.add("id");
+ hashSet.add("updated");
+ hashSet.add("published");
+ BUNDLE_TEXTNODE_CHILDREN = Collections.unmodifiableSet(hashSet);
+ }
+
private FhirContext myContext;
+
private boolean myPrettyPrint;
public JsonParser(FhirContext theContext) {
myContext = theContext;
}
+ private void addToHeldExtensions(int valueIdx, ArrayList> list, RuntimeChildDeclaredExtensionDefinition theDef, IElement theValue) {
+ list.ensureCapacity(valueIdx);
+ while (list.size() <= valueIdx) {
+ list.add(null);
+ }
+ if (list.get(valueIdx) == null) {
+ list.set(valueIdx, new ArrayList());
+ }
+ list.get(valueIdx).add(new HeldExtension(theDef, theValue));
+ }
+
+ private void addToHeldExtensions(int valueIdx, List ext, ArrayList> list) {
+ if (ext.size() > 0) {
+ list.ensureCapacity(valueIdx);
+ while (list.size() <= valueIdx) {
+ list.add(null);
+ }
+ if (list.get(valueIdx) == null) {
+ list.set(valueIdx, new ArrayList());
+ }
+ for (ExtensionDt next : ext) {
+ list.get(valueIdx).add(new HeldExtension(next));
+ }
+ }
+ }
+
+ private void assertObjectOfType(JsonValue theResourceTypeObj, ValueType theValueType, String thePosition) {
+ if (theResourceTypeObj.getValueType() != theValueType) {
+ throw new DataFormatException("Invalid content of element " + thePosition + ", expected " + theValueType);
+ }
+ }
+
+ private JsonGenerator createJsonGenerator(Writer theWriter) {
+ Map properties = new HashMap(1);
+ if (myPrettyPrint) {
+ properties.put(JsonGenerator.PRETTY_PRINTING, myPrettyPrint);
+ }
+ JsonGeneratorFactory jgf = Json.createGeneratorFactory(properties);
+ JsonGenerator eventWriter = jgf.createGenerator(theWriter);
+ return eventWriter;
+ }
+
@Override
public String encodeBundleToString(Bundle theBundle) throws DataFormatException, IOException {
if (theBundle == null) {
@@ -154,324 +209,6 @@ public class JsonParser extends BaseParser implements IParser {
eventWriter.close();
}
- @Override
- public String encodeResourceToString(IResource theResource) throws DataFormatException, IOException {
- Writer stringWriter = new StringWriter();
- encodeResourceToWriter(theResource, stringWriter);
- return stringWriter.toString();
- }
-
- @Override
- public void encodeResourceToWriter(IResource theResource, Writer theWriter) throws IOException {
- Validate.notNull(theResource, "Resource can not be null");
-
- JsonGenerator eventWriter = createJsonGenerator(theWriter);
-
- RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
- encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null);
- eventWriter.flush();
- }
-
- @Override
- public Bundle parseBundle(Reader theReader) {
- JsonReader reader = Json.createReader(theReader);
- JsonObject object = reader.readObject();
-
- JsonValue resourceTypeObj = object.get("resourceType");
- assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
- String resourceType = ((JsonString) resourceTypeObj).getString();
- if (!"Bundle".equals(resourceType)) {
- throw new DataFormatException("Trying to parse bundle but found resourceType other than 'Bundle'. Found: '" + resourceType + "'");
- }
-
- ParserState state = ParserState.getPreAtomInstance(myContext, true);
- state.enteringNewElement(null, "feed");
-
- parseBundleChildren(object, state);
-
- state.endingElement();
-
- Bundle retVal = state.getObject();
-
- return retVal;
- }
-
- @Override
- public T parseResource(Class theResourceType, String theMessageString) {
- return parseResource(theResourceType, new StringReader(theMessageString));
- }
-
- @Override
- public T parseResource(Class theResourceType, Reader theReader) {
- JsonReader reader = Json.createReader(theReader);
- JsonObject object = reader.readObject();
-
- JsonValue resourceTypeObj = object.get("resourceType");
- assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
- String resourceType = ((JsonString) resourceTypeObj).getString();
-
- RuntimeResourceDefinition def;
- if (theResourceType != null) {
- def = myContext.getResourceDefinition(theResourceType);
- } else {
- def = myContext.getResourceDefinition(resourceType);
- }
-
- ParserState extends IResource> state = ParserState.getPreResourceInstance(def.getImplementingClass(), myContext, true);
- state.enteringNewElement(null, def.getName());
-
- parseChildren(object, state);
-
- state.endingElement();
-
- @SuppressWarnings("unchecked")
- T retVal = (T) state.getObject();
-
- return retVal;
- }
-
- private void parseChildren(JsonObject theObject, ParserState> theState) {
- String elementId = null;
- for (String nextName : theObject.keySet()) {
- if ("resourceType".equals(nextName)) {
- continue;
- } else if ("id".equals(nextName)) {
- elementId = theObject.getString(nextName);
- continue;
- } else if ("extension".equals(nextName)) {
- JsonArray array = theObject.getJsonArray(nextName);
- parseExtension(theState, array, false);
- continue;
- } else if ("modifierExtension".equals(nextName)) {
- JsonArray array = theObject.getJsonArray(nextName);
- parseExtension(theState, array, true);
- continue;
- } else if (nextName.charAt(0) == '_') {
- continue;
- }
-
- JsonValue nextVal = theObject.get(nextName);
- JsonValue alternateVal = theObject.get('_' + nextName);
-
- parseChildren(theState, nextName, nextVal, alternateVal);
-
- }
-
- if (elementId != null) {
- IElement object = theState.getObject();
- if (object instanceof IIdentifiableElement) {
- ((IIdentifiableElement) object).setId(new IdDt(elementId));
- }
- }
- }
-
- private static final Set BUNDLE_TEXTNODE_CHILDREN;
-
- static {
- HashSet hashSet = new HashSet();
- hashSet.add("title");
- hashSet.add("id");
- hashSet.add("updated");
- hashSet.add("published");
- BUNDLE_TEXTNODE_CHILDREN = Collections.unmodifiableSet(hashSet);
- }
-
- private void parseBundleChildren(JsonObject theObject, ParserState> theState) {
- for (String nextName : theObject.keySet()) {
- if ("resourceType".equals(nextName)) {
- continue;
- } else if ("link".equals(nextName)) {
- JsonArray entries = theObject.getJsonArray(nextName);
- for (JsonValue jsonValue : entries) {
- theState.enteringNewElement(null, "link");
- JsonObject linkObj = (JsonObject) jsonValue;
- String rel = linkObj.getString("rel", null);
- String href = linkObj.getString("href", null);
- theState.attributeValue("rel", rel);
- theState.attributeValue("href", href);
- theState.endingElement();
- }
- continue;
- } else if ("entry".equals(nextName)) {
- JsonArray entries = theObject.getJsonArray(nextName);
- for (JsonValue jsonValue : entries) {
- theState.enteringNewElement(null, "entry");
- parseBundleChildren((JsonObject) jsonValue, theState);
- theState.endingElement();
- }
- continue;
- } else if (BUNDLE_TEXTNODE_CHILDREN.contains(nextName)) {
- theState.enteringNewElement(null, nextName);
- theState.string(theObject.getString(nextName, null));
- theState.endingElement();
- continue;
- }
-
- JsonValue nextVal = theObject.get(nextName);
- parseChildren(theState, nextName, nextVal, null);
-
- }
- }
-
- private void parseChildren(ParserState> theState, String theName, JsonValue theJsonVal, JsonValue theAlternateVal) {
- switch (theJsonVal.getValueType()) {
- case ARRAY: {
- JsonArray nextArray = (JsonArray) theJsonVal;
- JsonArray nextAlternateArray = (JsonArray) theAlternateVal;
- for (int i = 0; i < nextArray.size(); i++) {
- JsonValue nextObject = nextArray.get(i);
- JsonValue nextAlternate = null;
- if (nextAlternateArray != null) {
- nextAlternate = nextAlternateArray.get(i);
- }
- parseChildren(theState, theName, nextObject, nextAlternate);
- }
- break;
- }
- case OBJECT: {
- theState.enteringNewElement(null, theName);
- parseAlternates(theAlternateVal, theState);
- JsonObject nextObject = (JsonObject) theJsonVal;
- boolean preResource = false;
- if (theState.isPreResource()) {
- String resType = nextObject.getString("resourceType");
- if (isBlank(resType)) {
- throw new DataFormatException("Missing 'resourceType' from resource");
- }
- theState.enteringNewElement(null, resType);
- preResource = true;
- }
- parseChildren(nextObject, theState);
- if (preResource) {
- theState.endingElement();
- }
- theState.endingElement();
- break;
- }
- case STRING: {
- JsonString nextValStr = (JsonString) theJsonVal;
- theState.enteringNewElement(null, theName);
- theState.attributeValue("value", nextValStr.getString());
- parseAlternates(theAlternateVal, theState);
- theState.endingElement();
- break;
- }
- case NUMBER:
- case FALSE:
- case TRUE:
- theState.enteringNewElement(null, theName);
- theState.attributeValue("value", theJsonVal.toString());
- parseAlternates(theAlternateVal, theState);
- theState.endingElement();
- break;
- case NULL:
- break;
- }
- }
-
- private void parseAlternates(JsonValue theAlternateVal, ParserState> theState) {
- if (theAlternateVal == null || theAlternateVal.getValueType() == ValueType.NULL) {
- return;
- }
- JsonObject alternate = (JsonObject) theAlternateVal;
- for (Entry nextEntry : alternate.entrySet()) {
- String nextKey = nextEntry.getKey();
- JsonValue nextVal = nextEntry.getValue();
- if ("extension".equals(nextKey)) {
- boolean isModifier = false;
- JsonArray array = (JsonArray) nextEntry.getValue();
- parseExtension(theState, array, isModifier);
- } else if ("modifierExtension".equals(nextKey)) {
- boolean isModifier = true;
- JsonArray array = (JsonArray) nextEntry.getValue();
- parseExtension(theState, array, isModifier);
- } else if ("id".equals(nextKey)) {
- switch (nextVal.getValueType()) {
- case STRING:
- theState.attributeValue("id", ((JsonString) nextVal).getString());
- break;
- case NULL:
- break;
- default:
- break;
- }
- }
- }
- }
-
- private void parseExtension(ParserState> theState, JsonArray array, boolean theIsModifier) {
- // TODO: use theIsModifier
- for (int i = 0; i < array.size(); i++) {
- JsonObject nextExtObj = array.getJsonObject(i);
- String url = nextExtObj.getString("url");
- theState.enteringNewElementExtension(null, url, theIsModifier);
- for (Iterator iter = nextExtObj.keySet().iterator(); iter.hasNext();) {
- String next = iter.next();
- if ("url".equals(next)) {
- continue;
- } else if ("extension".equals(next)) {
- JsonArray jsonVal = (JsonArray) nextExtObj.get(next);
- parseExtension(theState, jsonVal, false);
- } else if ("modifierExtension".equals(next)) {
- JsonArray jsonVal = (JsonArray) nextExtObj.get(next);
- parseExtension(theState, jsonVal, true);
- } else {
- JsonValue jsonVal = nextExtObj.get(next);
- parseChildren(theState, next, jsonVal, null);
- }
- }
- theState.endingElement();
- }
- }
-
- @Override
- public IParser setPrettyPrint(boolean thePrettyPrint) {
- myPrettyPrint = thePrettyPrint;
- return this;
- }
-
- private void addToHeldExtensions(int valueIdx, List ext, ArrayList> list) {
- if (ext.size() > 0) {
- list.ensureCapacity(valueIdx);
- while (list.size() <= valueIdx) {
- list.add(null);
- }
- if (list.get(valueIdx) == null) {
- list.set(valueIdx, new ArrayList());
- }
- for (ExtensionDt next : ext) {
- list.get(valueIdx).add(new HeldExtension(next));
- }
- }
- }
-
- private void addToHeldExtensions(int valueIdx, ArrayList> list, RuntimeChildDeclaredExtensionDefinition theDef, IElement theValue) {
- list.ensureCapacity(valueIdx);
- while (list.size() <= valueIdx) {
- list.add(null);
- }
- if (list.get(valueIdx) == null) {
- list.set(valueIdx, new ArrayList());
- }
- list.get(valueIdx).add(new HeldExtension(theDef, theValue));
- }
-
- private void assertObjectOfType(JsonValue theResourceTypeObj, ValueType theValueType, String thePosition) {
- if (theResourceTypeObj.getValueType() != theValueType) {
- throw new DataFormatException("Invalid content of element " + thePosition + ", expected " + theValueType);
- }
- }
-
- private JsonGenerator createJsonGenerator(Writer theWriter) {
- Map properties = new HashMap(1);
- if (myPrettyPrint) {
- properties.put(JsonGenerator.PRETTY_PRINTING, myPrettyPrint);
- }
- JsonGeneratorFactory jgf = Json.createGeneratorFactory(properties);
- JsonGenerator eventWriter = jgf.createGenerator(theWriter);
- return eventWriter;
- }
-
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition> theChildDef,
String theChildName) throws IOException {
@@ -736,6 +473,24 @@ public class JsonParser extends BaseParser implements IParser {
theEventWriter.writeEnd();
}
+ @Override
+ public String encodeResourceToString(IResource theResource) throws DataFormatException, IOException {
+ Writer stringWriter = new StringWriter();
+ encodeResourceToWriter(theResource, stringWriter);
+ return stringWriter.toString();
+ }
+
+ @Override
+ public void encodeResourceToWriter(IResource theResource, Writer theWriter) throws IOException {
+ Validate.notNull(theResource, "Resource can not be null");
+
+ JsonGenerator eventWriter = createJsonGenerator(theWriter);
+
+ RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
+ encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null);
+ eventWriter.flush();
+ }
+
/**
* This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object called _name): resource extensions, and extension extensions
*/
@@ -754,21 +509,20 @@ public class JsonParser extends BaseParser implements IParser {
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions);
}
- private void writeExtensionsAsDirectChild(IResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List extensions,
- List modifierExtensions) throws IOException {
- if (extensions.isEmpty() == false) {
- theEventWriter.writeStartArray("extension");
- for (HeldExtension next : extensions) {
- next.write(resDef, theResource, theEventWriter);
+ private void extractDeclaredExtensions(IElement theResource, BaseRuntimeElementDefinition> resDef, List extensions, List modifierExtensions) {
+ for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsNonModifier()) {
+ for (IElement nextValue : nextDef.getAccessor().getValues(theResource)) {
+ if (nextValue != null) {
+ extensions.add(new HeldExtension(nextDef, nextValue));
+ }
}
- theEventWriter.writeEnd();
}
- if (modifierExtensions.isEmpty() == false) {
- theEventWriter.writeStartArray("modifierExtension");
- for (HeldExtension next : modifierExtensions) {
- next.write(resDef, theResource, theEventWriter);
+ for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsModifier()) {
+ for (IElement nextValue : nextDef.getAccessor().getValues(theResource)) {
+ if (nextValue != null) {
+ modifierExtensions.add(new HeldExtension(nextDef, nextValue));
+ }
}
- theEventWriter.writeEnd();
}
}
@@ -786,23 +540,254 @@ public class JsonParser extends BaseParser implements IParser {
}
}
- private void extractDeclaredExtensions(IElement theResource, BaseRuntimeElementDefinition> resDef, List extensions, List modifierExtensions) {
- for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsNonModifier()) {
- for (IElement nextValue : nextDef.getAccessor().getValues(theResource)) {
- if (nextValue != null) {
- extensions.add(new HeldExtension(nextDef, nextValue));
+ private void parseAlternates(JsonValue theAlternateVal, ParserState> theState) {
+ if (theAlternateVal == null || theAlternateVal.getValueType() == ValueType.NULL) {
+ return;
+ }
+ JsonObject alternate = (JsonObject) theAlternateVal;
+ for (Entry nextEntry : alternate.entrySet()) {
+ String nextKey = nextEntry.getKey();
+ JsonValue nextVal = nextEntry.getValue();
+ if ("extension".equals(nextKey)) {
+ boolean isModifier = false;
+ JsonArray array = (JsonArray) nextEntry.getValue();
+ parseExtension(theState, array, isModifier);
+ } else if ("modifierExtension".equals(nextKey)) {
+ boolean isModifier = true;
+ JsonArray array = (JsonArray) nextEntry.getValue();
+ parseExtension(theState, array, isModifier);
+ } else if ("id".equals(nextKey)) {
+ switch (nextVal.getValueType()) {
+ case STRING:
+ theState.attributeValue("id", ((JsonString) nextVal).getString());
+ break;
+ case NULL:
+ break;
+ default:
+ break;
}
}
}
- for (RuntimeChildDeclaredExtensionDefinition nextDef : resDef.getExtensionsModifier()) {
- for (IElement nextValue : nextDef.getAccessor().getValues(theResource)) {
- if (nextValue != null) {
- modifierExtensions.add(new HeldExtension(nextDef, nextValue));
+ }
+
+ @Override
+ public Bundle parseBundle(Class theResourceType, Reader theReader) {
+ JsonReader reader = Json.createReader(theReader);
+ JsonObject object = reader.readObject();
+
+ JsonValue resourceTypeObj = object.get("resourceType");
+ assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
+ String resourceType = ((JsonString) resourceTypeObj).getString();
+ if (!"Bundle".equals(resourceType)) {
+ throw new DataFormatException("Trying to parse bundle but found resourceType other than 'Bundle'. Found: '" + resourceType + "'");
+ }
+
+ ParserState state = ParserState.getPreAtomInstance(myContext, theResourceType, true);
+ state.enteringNewElement(null, "feed");
+
+ parseBundleChildren(object, state);
+
+ state.endingElement();
+
+ Bundle retVal = state.getObject();
+
+ return retVal;
+ }
+
+
+ private void parseBundleChildren(JsonObject theObject, ParserState> theState) {
+ for (String nextName : theObject.keySet()) {
+ if ("resourceType".equals(nextName)) {
+ continue;
+ } else if ("link".equals(nextName)) {
+ JsonArray entries = theObject.getJsonArray(nextName);
+ for (JsonValue jsonValue : entries) {
+ theState.enteringNewElement(null, "link");
+ JsonObject linkObj = (JsonObject) jsonValue;
+ String rel = linkObj.getString("rel", null);
+ String href = linkObj.getString("href", null);
+ theState.attributeValue("rel", rel);
+ theState.attributeValue("href", href);
+ theState.endingElement();
}
+ continue;
+ } else if ("entry".equals(nextName)) {
+ JsonArray entries = theObject.getJsonArray(nextName);
+ for (JsonValue jsonValue : entries) {
+ theState.enteringNewElement(null, "entry");
+ parseBundleChildren((JsonObject) jsonValue, theState);
+ theState.endingElement();
+ }
+ continue;
+ } else if (BUNDLE_TEXTNODE_CHILDREN.contains(nextName)) {
+ theState.enteringNewElement(null, nextName);
+ theState.string(theObject.getString(nextName, null));
+ theState.endingElement();
+ continue;
+ }
+
+ JsonValue nextVal = theObject.get(nextName);
+ parseChildren(theState, nextName, nextVal, null);
+
+ }
+ }
+
+ private void parseChildren(JsonObject theObject, ParserState> theState) {
+ String elementId = null;
+ for (String nextName : theObject.keySet()) {
+ if ("resourceType".equals(nextName)) {
+ continue;
+ } else if ("id".equals(nextName)) {
+ elementId = theObject.getString(nextName);
+ continue;
+ } else if ("extension".equals(nextName)) {
+ JsonArray array = theObject.getJsonArray(nextName);
+ parseExtension(theState, array, false);
+ continue;
+ } else if ("modifierExtension".equals(nextName)) {
+ JsonArray array = theObject.getJsonArray(nextName);
+ parseExtension(theState, array, true);
+ continue;
+ } else if (nextName.charAt(0) == '_') {
+ continue;
+ }
+
+ JsonValue nextVal = theObject.get(nextName);
+ JsonValue alternateVal = theObject.get('_' + nextName);
+
+ parseChildren(theState, nextName, nextVal, alternateVal);
+
+ }
+
+ if (elementId != null) {
+ IElement object = theState.getObject();
+ if (object instanceof IIdentifiableElement) {
+ ((IIdentifiableElement) object).setId(new IdDt(elementId));
}
}
}
+ private void parseChildren(ParserState> theState, String theName, JsonValue theJsonVal, JsonValue theAlternateVal) {
+ switch (theJsonVal.getValueType()) {
+ case ARRAY: {
+ JsonArray nextArray = (JsonArray) theJsonVal;
+ JsonArray nextAlternateArray = (JsonArray) theAlternateVal;
+ for (int i = 0; i < nextArray.size(); i++) {
+ JsonValue nextObject = nextArray.get(i);
+ JsonValue nextAlternate = null;
+ if (nextAlternateArray != null) {
+ nextAlternate = nextAlternateArray.get(i);
+ }
+ parseChildren(theState, theName, nextObject, nextAlternate);
+ }
+ break;
+ }
+ case OBJECT: {
+ theState.enteringNewElement(null, theName);
+ parseAlternates(theAlternateVal, theState);
+ JsonObject nextObject = (JsonObject) theJsonVal;
+ boolean preResource = false;
+ if (theState.isPreResource()) {
+ String resType = nextObject.getString("resourceType");
+ if (isBlank(resType)) {
+ throw new DataFormatException("Missing 'resourceType' from resource");
+ }
+ theState.enteringNewElement(null, resType);
+ preResource = true;
+ }
+ parseChildren(nextObject, theState);
+ if (preResource) {
+ theState.endingElement();
+ }
+ theState.endingElement();
+ break;
+ }
+ case STRING: {
+ JsonString nextValStr = (JsonString) theJsonVal;
+ theState.enteringNewElement(null, theName);
+ theState.attributeValue("value", nextValStr.getString());
+ parseAlternates(theAlternateVal, theState);
+ theState.endingElement();
+ break;
+ }
+ case NUMBER:
+ case FALSE:
+ case TRUE:
+ theState.enteringNewElement(null, theName);
+ theState.attributeValue("value", theJsonVal.toString());
+ parseAlternates(theAlternateVal, theState);
+ theState.endingElement();
+ break;
+ case NULL:
+ break;
+ }
+ }
+
+ private void parseExtension(ParserState> theState, JsonArray array, boolean theIsModifier) {
+ // TODO: use theIsModifier
+ for (int i = 0; i < array.size(); i++) {
+ JsonObject nextExtObj = array.getJsonObject(i);
+ String url = nextExtObj.getString("url");
+ theState.enteringNewElementExtension(null, url, theIsModifier);
+ for (Iterator iter = nextExtObj.keySet().iterator(); iter.hasNext();) {
+ String next = iter.next();
+ if ("url".equals(next)) {
+ continue;
+ } else if ("extension".equals(next)) {
+ JsonArray jsonVal = (JsonArray) nextExtObj.get(next);
+ parseExtension(theState, jsonVal, false);
+ } else if ("modifierExtension".equals(next)) {
+ JsonArray jsonVal = (JsonArray) nextExtObj.get(next);
+ parseExtension(theState, jsonVal, true);
+ } else {
+ JsonValue jsonVal = nextExtObj.get(next);
+ parseChildren(theState, next, jsonVal, null);
+ }
+ }
+ theState.endingElement();
+ }
+ }
+
+ @Override
+ public T parseResource(Class theResourceType, Reader theReader) {
+ JsonReader reader = Json.createReader(theReader);
+ JsonObject object = reader.readObject();
+
+ JsonValue resourceTypeObj = object.get("resourceType");
+ assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
+ String resourceType = ((JsonString) resourceTypeObj).getString();
+
+ RuntimeResourceDefinition def;
+ if (theResourceType != null) {
+ def = myContext.getResourceDefinition(theResourceType);
+ } else {
+ def = myContext.getResourceDefinition(resourceType);
+ }
+
+ ParserState extends IResource> state = ParserState.getPreResourceInstance(def.getImplementingClass(), myContext, true);
+ state.enteringNewElement(null, def.getName());
+
+ parseChildren(object, state);
+
+ state.endingElement();
+
+ @SuppressWarnings("unchecked")
+ T retVal = (T) state.getObject();
+
+ return retVal;
+ }
+
+ @Override
+ public T parseResource(Class theResourceType, String theMessageString) {
+ return parseResource(theResourceType, new StringReader(theMessageString));
+ }
+
+ @Override
+ public IParser setPrettyPrint(boolean thePrettyPrint) {
+ myPrettyPrint = thePrettyPrint;
+ return this;
+ }
+
private void writeAtomLink(JsonGenerator theEventWriter, String theRel, StringDt theLink) {
if (isNotBlank(theLink.getValue())) {
theEventWriter.writeStartObject();
@@ -823,6 +808,24 @@ public class JsonParser extends BaseParser implements IParser {
}
}
+ private void writeExtensionsAsDirectChild(IResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List extensions,
+ List modifierExtensions) throws IOException {
+ if (extensions.isEmpty() == false) {
+ theEventWriter.writeStartArray("extension");
+ for (HeldExtension next : extensions) {
+ next.write(resDef, theResource, theEventWriter);
+ }
+ theEventWriter.writeEnd();
+ }
+ if (modifierExtensions.isEmpty() == false) {
+ theEventWriter.writeStartArray("modifierExtension");
+ for (HeldExtension next : modifierExtensions) {
+ next.write(resDef, theResource, theEventWriter);
+ }
+ theEventWriter.writeEnd();
+ }
+ }
+
private void writeOptionalTagWithTextNode(JsonGenerator theEventWriter, String theElementName, IPrimitiveDatatype> theInstantDt) {
String str = theInstantDt.getValueAsString();
if (StringUtils.isNotBlank(str)) {
@@ -830,14 +833,6 @@ public class JsonParser extends BaseParser implements IParser {
}
}
- private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, StringDt theStringDt) {
- if (StringUtils.isNotBlank(theStringDt.getValue())) {
- theEventWriter.write(theElementName, theStringDt.getValue());
- } else {
- theEventWriter.writeNull(theElementName);
- }
- }
-
private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, IdDt theIdDt) {
if (StringUtils.isNotBlank(theIdDt.getValue())) {
theEventWriter.write(theElementName, theIdDt.getValue());
@@ -846,10 +841,18 @@ public class JsonParser extends BaseParser implements IParser {
}
}
+ private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, StringDt theStringDt) {
+ if (StringUtils.isNotBlank(theStringDt.getValue())) {
+ theEventWriter.write(theElementName, theStringDt.getValue());
+ } else {
+ theEventWriter.writeNull(theElementName);
+ }
+ }
+
private class HeldExtension {
- private ExtensionDt myUndeclaredExtension;
private RuntimeChildDeclaredExtensionDefinition myDef;
+ private ExtensionDt myUndeclaredExtension;
private IElement myValue;
public HeldExtension(ExtensionDt theUndeclaredExtension) {
@@ -875,7 +878,9 @@ public class JsonParser extends BaseParser implements IParser {
if (def.getChildType() == ChildTypeEnum.RESOURCE_BLOCK) {
extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource);
} else {
- encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, "value" + WordUtils.capitalize(def.getName()));
+// encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, "value" + WordUtils.capitalize(def.getName()));
+ String childName = myDef.getChildNameByDatatype(myValue.getClass());
+ encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName);
}
// theEventWriter.name(myUndeclaredExtension.get);
@@ -898,9 +903,13 @@ public class JsonParser extends BaseParser implements IParser {
}
theEventWriter.writeEnd();
} else {
- BaseRuntimeElementDefinition> def = myContext.getElementDefinition(value.getClass());
- // theEventWriter.writeName("value" + def.getName());
- encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, def, "value" + WordUtils.capitalize(def.getName()));
+ RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition();
+ String childName = extDef.getChildNameByDatatype(value.getClass());
+ if (childName == null) {
+ throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + value.getClass().getCanonicalName());
+ }
+ BaseRuntimeElementDefinition> childDef = extDef.getChildElementDefinitionByDatatype(value.getClass());
+ encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName);
}
// theEventWriter.name(myUndeclaredExtension.get);
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java
index 64c54827aa5..3b9b2e53c6a 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java
@@ -131,9 +131,9 @@ class ParserState {
myState = theState;
}
- public static ParserState getPreAtomInstance(FhirContext theContext, boolean theJsonMode) throws DataFormatException {
+ public static ParserState getPreAtomInstance(FhirContext theContext, Class extends IResource> theResourceType, boolean theJsonMode) throws DataFormatException {
ParserState retVal = new ParserState(theContext, theJsonMode);
- retVal.push(retVal.new PreAtomState());
+ retVal.push(retVal.new PreAtomState(theResourceType));
return retVal;
}
@@ -209,10 +209,12 @@ class ParserState {
public class AtomEntryState extends BaseState {
private BundleEntry myEntry;
+ private Class extends IResource> myResourceType;
- public AtomEntryState(Bundle theInstance) {
+ public AtomEntryState(Bundle theInstance, Class extends IResource> theResourceType) {
super(null);
myEntry = new BundleEntry();
+ myResourceType = theResourceType;
theInstance.getEntries().add(myEntry);
}
@@ -275,7 +277,7 @@ class ParserState {
} else if ("author".equals(theLocalPart)) {
push(new AtomAuthorState(myEntry));
} else if ("content".equals(theLocalPart)) {
- push(new PreResourceState(myEntry));
+ push(new PreResourceState(myEntry, myResourceType));
} else if ("summary".equals(theLocalPart)) {
push(new XhtmlState(getPreResourceState(), myEntry.getSummary(), false));
} else if ("category".equals(theLocalPart)) {
@@ -387,10 +389,12 @@ class ParserState {
private class AtomState extends BaseState {
private Bundle myInstance;
+ private Class extends IResource> myResourceType;
- public AtomState(Bundle theInstance) {
+ public AtomState(Bundle theInstance, Class extends IResource> theResourceType) {
super(null);
myInstance = theInstance;
+ myResourceType = theResourceType;
}
@Override
@@ -401,7 +405,7 @@ class ParserState {
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if ("entry".equals(theLocalPart) && verifyNamespace(XmlParser.ATOM_NS, theNamespaceURI)) {
- push(new AtomEntryState(myInstance));
+ push(new AtomEntryState(myInstance, myResourceType));
} else if (theLocalPart.equals("published")) {
push(new AtomPrimitiveState(myInstance.getPublished()));
} else if (theLocalPart.equals("title")) {
@@ -829,9 +833,11 @@ class ParserState {
private class PreAtomState extends BaseState {
private Bundle myInstance;
+ private Class extends IResource> myResourceType;
- public PreAtomState() {
+ public PreAtomState(Class extends IResource> theResourceType) {
super(null);
+ myResourceType = theResourceType;
}
@Override
@@ -846,7 +852,7 @@ class ParserState {
}
myInstance = new Bundle();
- push(new AtomState(myInstance));
+ push(new AtomState(myInstance, myResourceType));
}
@@ -877,9 +883,10 @@ class ParserState {
return true;
}
- public PreResourceState(BundleEntry theEntry) {
+ public PreResourceState(BundleEntry theEntry, Class extends IResource> theResourceType) {
super(null);
myEntry = theEntry;
+ myResourceType=theResourceType;
}
/**
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java
index 0a767a27d6a..8e27d9e4f35 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java
@@ -193,8 +193,9 @@ public class XmlParser extends BaseParser implements IParser {
}
}
+
@Override
- public Bundle parseBundle(Reader theReader) {
+ public Bundle parseBundle(Class theResourceType, Reader theReader) {
XMLEventReader streamReader;
try {
streamReader = myXmlInputFactory.createXMLEventReader(theReader);
@@ -204,7 +205,7 @@ public class XmlParser extends BaseParser implements IParser {
throw new ConfigurationException("Failed to initialize STaX event factory", e);
}
- return parseBundle(streamReader);
+ return parseBundle(streamReader, theResourceType);
}
@Override
@@ -590,8 +591,8 @@ public class XmlParser extends BaseParser implements IParser {
}
}
- private Bundle parseBundle(XMLEventReader theStreamReader) {
- ParserState parserState = ParserState.getPreAtomInstance(myContext, false);
+ private Bundle parseBundle(XMLEventReader theStreamReader, Class extends IResource> theResourceType) {
+ ParserState parserState = ParserState.getPreAtomInstance(myContext, theResourceType, false);
return doXmlLoop(theStreamReader, parserState);
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java
index 0f1faa0efa3..f33de1fd781 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java
@@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.client;
+/*
+ * #%L
+ * HAPI FHIR Library
+ * %%
+ * Copyright (C) 2014 University Health Network
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java
index 4ef499f992a..85837457736 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java
@@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.client;
+/*
+ * #%L
+ * HAPI FHIR Library
+ * %%
+ * Copyright (C) 2014 University Health Network
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
import java.io.IOException;
import java.io.Reader;
import java.util.List;
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IGenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IGenericClient.java
index 2a401aae748..35ed398657d 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IGenericClient.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IGenericClient.java
@@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.client;
+/*
+ * #%L
+ * HAPI FHIR Library
+ * %%
+ * Copyright (C) 2014 University Health Network
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.IdDt;
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/FhirClientConnectionException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/FhirClientConnectionException.java
index 82640be603e..7a8c5a3fd24 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/FhirClientConnectionException.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/FhirClientConnectionException.java
@@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.client.exceptions;
+/*
+ * #%L
+ * HAPI FHIR Library
+ * %%
+ * Copyright (C) 2014 University Health Network
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java
index 9408dc2a3b1..51fb7bad673 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseResourceReturningMethodBinding.java
@@ -77,8 +77,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
private MethodReturnTypeEnum myMethodReturnType;
private String myResourceName;
+ private Class extends IResource> myResourceType;
-
public BaseResourceReturningMethodBinding(Class extends IResource> theReturnResourceType, Method theMethod, FhirContext theConetxt, Object theProvider) {
super(theMethod, theConetxt, theProvider);
@@ -90,9 +90,11 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
} else if (Bundle.class.isAssignableFrom(methodReturnType)) {
myMethodReturnType = MethodReturnTypeEnum.BUNDLE;
} else {
- throw new ConfigurationException("Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
+ throw new ConfigurationException("Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: "
+ + theMethod.getDeclaringClass().getCanonicalName());
}
+ myResourceType = theReturnResourceType;
if (theReturnResourceType != null) {
ResourceDef resourceDefAnnotation = theReturnResourceType.getAnnotation(ResourceDef.class);
if (resourceDefAnnotation == null) {
@@ -119,7 +121,12 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
switch (getReturnType()) {
case BUNDLE: {
- Bundle bundle = parser.parseBundle(theResponseReader);
+ Bundle bundle;
+ if (myResourceType != null) {
+ bundle = parser.parseBundle(myResourceType, theResponseReader);
+ }else {
+ bundle = parser.parseBundle(theResponseReader);
+ }
switch (getMethodReturnType()) {
case BUNDLE:
return bundle;
@@ -138,7 +145,12 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
break;
}
case RESOURCE: {
- IResource resource = parser.parseResource(theResponseReader);
+ IResource resource;
+ if (myResourceType != null) {
+ resource = parser.parseResource(myResourceType, theResponseReader);
+ } else {
+ resource = parser.parseResource(theResponseReader);
+ }
List lmHeaders = theHeaders.get(Constants.HEADER_LAST_MODIFIED_LOWERCASE);
if (lmHeaders != null && lmHeaders.size() > 0 && StringUtils.isNotBlank(lmHeaders.get(0))) {
@@ -238,7 +250,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
return (IdDt) retValObj;
}
}
- throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + IdDt.class.getCanonicalName());
+ throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
+ + IdDt.class.getCanonicalName());
}
private InstantDt getInstantFromMetadataOrNullIfNone(Map theResourceMetadata, ResourceMetadataKeyEnum theKey) {
@@ -254,7 +267,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
return (InstantDt) retValObj;
}
}
- throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName());
+ throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
+ + InstantDt.class.getCanonicalName());
}
private IParser getNewParser(EncodingUtil theResponseEncoding, boolean thePrettyPrint, NarrativeModeEnum theNarrativeMode) {
@@ -271,8 +285,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
return parser.setPrettyPrint(thePrettyPrint).setSuppressNarratives(theNarrativeMode == NarrativeModeEnum.SUPPRESS);
}
- private void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, List theResult, EncodingUtil theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser,
- NarrativeModeEnum theNarrativeMode) throws IOException {
+ private void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, List theResult, EncodingUtil theResponseEncoding, String theServerBase,
+ String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
assert !theServerBase.endsWith("/");
theHttpResponse.setStatus(200);
@@ -317,10 +331,10 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
b.append(resId);
/*
- * If this is a history operation, we add the version of the
- * resource to the self link to indicate the version
+ * If this is a history operation, we add the version of the resource to the self link to indicate the version
*/
- if (getResourceOperationType() == RestfulOperationTypeEnum.HISTORY_INSTANCE || getResourceOperationType() == RestfulOperationTypeEnum.HISTORY_TYPE || getSystemOperationType() == RestfulOperationSystemEnum.HISTORY_SYSTEM) {
+ if (getResourceOperationType() == RestfulOperationTypeEnum.HISTORY_INSTANCE || getResourceOperationType() == RestfulOperationTypeEnum.HISTORY_TYPE
+ || getSystemOperationType() == RestfulOperationSystemEnum.HISTORY_SYSTEM) {
IdDt versionId = getIdFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.VERSION_ID);
if (versionId != null) {
b.append('/');
@@ -382,7 +396,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
}
}
- private void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingUtil theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
+ private void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingUtil theResponseEncoding, boolean thePrettyPrint,
+ boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
theHttpResponse.setStatus(200);
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IClientResponseHandler.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IClientResponseHandler.java
index 97cabb109ae..0998eec318a 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IClientResponseHandler.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IClientResponseHandler.java
@@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.method;
+/*
+ * #%L
+ * HAPI FHIR Library
+ * %%
+ * Copyright (C) 2014 University Health Network
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
import java.io.IOException;
import java.io.Reader;
import java.util.List;
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java
index b75c26ae676..502188d58e6 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java
@@ -130,6 +130,20 @@ class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceP
return retVal;
}
+ @Override
+ public boolean matches(Request theRequest) {
+ if (super.matches(theRequest)) {
+ if (myVersionIdParameterIndex != null) {
+ if (theRequest.getVersion() == null) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
@Override
protected Set provideAllowableRequestTypes() {
return Collections.singleton(RequestType.PUT);
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java
index d7ac51ced0b..c88919b5b05 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java
@@ -46,6 +46,7 @@ import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.method.ConformanceMethodBinding;
import ca.uhn.fhir.rest.method.Request;
import ca.uhn.fhir.rest.method.SearchMethodBinding;
+import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@@ -74,6 +75,8 @@ public class RestfulServer extends HttpServlet {
private boolean myUseBrowserFriendlyContentTypes;
private ResourceBinding myNullResourceBinding = new ResourceBinding();
+ private boolean myStarted;
+
/**
* Constructor
*/
@@ -222,6 +225,7 @@ public class RestfulServer extends HttpServlet {
throw new ServletException("Failed to initialize FHIR Restful server", ex);
}
+ myStarted = true;
ourLog.info("A FHIR has been lit on this server");
}
@@ -293,7 +297,7 @@ public class RestfulServer extends HttpServlet {
* {@link IllegalStateException} if called after that.
*/
public void setServerConformanceProvider(Object theServerConformanceProvider) {
- if (myFhirContext != null) {
+ if (myStarted) {
throw new IllegalStateException("Server is already started");
}
myServerConformanceProvider = theServerConformanceProvider;
@@ -524,6 +528,18 @@ public class RestfulServer extends HttpServlet {
String nextString = tok.nextToken();
versionId = new IdDt(nextString);
}
+
+ if (theRequestType==RequestType.PUT && versionId==null) {
+ String contentLocation = theRequest.getHeader("Content-Location");
+ if (contentLocation!=null) {
+ int idx = contentLocation.indexOf("/_history/");
+ if (idx != -1) {
+ String versionIdString = contentLocation.substring(idx + "/_history/".length());
+ versionId = new IdDt(versionIdString);
+ }
+ }
+ }
+
// TODO: look for more tokens for version, compartments, etc...
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.java
index f1734e8ff31..69aaa0aee9b 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.java
@@ -38,6 +38,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
registerExceptionType(MethodNotAllowedException.STATUS_CODE, MethodNotAllowedException.class);
registerExceptionType(ResourceNotFoundException.STATUS_CODE, ResourceNotFoundException.class);
registerExceptionType(ResourceVersionNotSpecifiedException.STATUS_CODE, ResourceVersionNotSpecifiedException.class);
+ registerExceptionType(ResourceVersionConflictException.STATUS_CODE, ResourceVersionConflictException.class);
registerExceptionType(UnprocessableEntityException.STATUS_CODE, UnprocessableEntityException.class);
}
@@ -143,5 +144,6 @@ public abstract class BaseServerResponseException extends RuntimeException {
if (ourStatusCodeToExceptionType.containsKey(theStatusCode)) {
throw new Error("Can not register " + theType + " to status code " + theStatusCode + " because " + ourStatusCodeToExceptionType.get(theStatusCode) + " already registers that code");
}
+ ourStatusCodeToExceptionType.put(theStatusCode, theType);
}
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/tester/PublicTesterServlet.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/tester/PublicTesterServlet.java
index c2ac752f905..112226cd48a 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/tester/PublicTesterServlet.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/tester/PublicTesterServlet.java
@@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.server.tester;
+/*
+ * #%L
+ * HAPI FHIR Library
+ * %%
+ * Copyright (C) 2014 University Health Network
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
@@ -11,8 +31,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringEscapeUtils;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType;
import org.thymeleaf.TemplateEngine;
@@ -145,7 +165,7 @@ public class PublicTesterServlet extends HttpServlet {
ctx.setVariable("requestUrl", requestUrl);
ctx.setVariable("action", action);
ctx.setVariable("resultStatus", resultStatus);
- ctx.setVariable("resultBody", StringEscapeUtils.escapeHtml(resultBody));
+ ctx.setVariable("resultBody", StringEscapeUtils.escapeHtml4(resultBody));
ctx.setVariable("resultSyntaxHighlighterClass", resultSyntaxHighlighterClass);
myTemplateEngine.process(PUBLIC_TESTER_RESULT_HTML, ctx, theResp.getWriter());
diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java
index 5688f1c7fed..60847c3692d 100644
--- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java
+++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java
@@ -25,6 +25,10 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.ExtensionDt;
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.Extension;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import ca.uhn.fhir.model.dstu.composite.AddressDt;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
@@ -38,6 +42,7 @@ import ca.uhn.fhir.model.dstu.resource.Specimen;
import ca.uhn.fhir.model.dstu.resource.ValueSet;
import ca.uhn.fhir.model.dstu.resource.ValueSet.Define;
import ca.uhn.fhir.model.dstu.resource.ValueSet.DefineConcept;
+import ca.uhn.fhir.model.dstu.valueset.AddressUseEnum;
import ca.uhn.fhir.model.dstu.valueset.NarrativeStatusEnum;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.StringDt;
@@ -48,8 +53,7 @@ public class JsonParserTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserTest.class);
/**
- * This sample has extra elements in that are not actually a
- * part of the spec any more..
+ * This sample has extra elements in that are not actually a part of the spec any more..
*/
@Test
public void testParseFuroreMetadataWithExtraElements() throws IOException {
@@ -61,6 +65,122 @@ public class JsonParserTest {
assertEquals("_id", res.getSearchParam().get(1).getName().getValue());
}
+ @Test
+ public void testEncodeExtensionWithResourceContent() throws IOException {
+ IParser parser = new FhirContext().newJsonParser();
+
+ Patient patient = new Patient();
+ patient.addAddress().setUse(AddressUseEnum.HOME);
+ patient.addUndeclaredExtension(false, "urn:foo", new ResourceReferenceDt(Organization.class, "123"));
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueResource\":{\"resource\":\"Organization/123\"}}]"));
+
+ Patient actual = parser.parseResource(Patient.class, val);
+ assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
+ List ext = actual.getUndeclaredExtensionsByUrl("urn:foo");
+ assertEquals(1, ext.size());
+ ResourceReferenceDt ref = (ResourceReferenceDt) ext.get(0).getValue();
+ assertEquals("Organization/123", ref.getResourceUrl());
+
+ }
+
+ @Test
+ public void testEncodeDeclaredExtensionWithResourceContent() throws IOException {
+ IParser parser = new FhirContext().newJsonParser();
+
+ MyPatientWithOneDeclaredExtension patient = new MyPatientWithOneDeclaredExtension();
+ patient.addAddress().setUse(AddressUseEnum.HOME);
+ patient.setFoo(new ResourceReferenceDt(Organization.class, "123"));
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueResource\":{\"resource\":\"Organization/123\"}}]"));
+
+ MyPatientWithOneDeclaredExtension actual = parser.parseResource(MyPatientWithOneDeclaredExtension.class, val);
+ assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
+ ResourceReferenceDt ref = actual.getFoo();
+ assertEquals("Organization/123", ref.getResourceUrl());
+
+ }
+
+
+ @Test
+ public void testEncodeDeclaredExtensionWithAddressContent() throws IOException {
+ IParser parser = new FhirContext().newJsonParser();
+
+ MyPatientWithOneDeclaredAddressExtension patient = new MyPatientWithOneDeclaredAddressExtension();
+ patient.addAddress().setUse(AddressUseEnum.HOME);
+ patient.setFoo(new AddressDt().addLine("line1"));
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueAddress\":{\"line\":[\"line1\"]}}]"));
+
+ MyPatientWithOneDeclaredAddressExtension actual = parser.parseResource(MyPatientWithOneDeclaredAddressExtension.class, val);
+ assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
+ AddressDt ref = actual.getFoo();
+ assertEquals("line1", ref.getLineFirstRep().getValue());
+
+ }
+
+
+ @Test
+ public void testEncodeUndeclaredExtensionWithAddressContent() throws IOException {
+ IParser parser = new FhirContext().newJsonParser();
+
+ Patient patient = new Patient();
+ patient.addAddress().setUse(AddressUseEnum.HOME);
+ patient.addUndeclaredExtension(false, "urn:foo", new AddressDt().addLine("line1"));
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueAddress\":{\"line\":[\"line1\"]}}]"));
+
+ MyPatientWithOneDeclaredAddressExtension actual = parser.parseResource(MyPatientWithOneDeclaredAddressExtension.class, val);
+ assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
+ AddressDt ref = actual.getFoo();
+ assertEquals("line1", ref.getLineFirstRep().getValue());
+
+ }
+
+
+ @ResourceDef(name = "Patient")
+ public static class MyPatientWithOneDeclaredExtension extends Patient {
+
+ @Child(order = 0, name = "foo")
+ @Extension(url = "urn:foo", definedLocally = true, isModifier = false)
+ private ResourceReferenceDt myFoo;
+
+ public ResourceReferenceDt getFoo() {
+ return myFoo;
+ }
+
+ public void setFoo(ResourceReferenceDt theFoo) {
+ myFoo = theFoo;
+ }
+
+ }
+
+
+ @ResourceDef(name = "Patient")
+ public static class MyPatientWithOneDeclaredAddressExtension extends Patient {
+
+ @Child(order = 0, name = "foo")
+ @Extension(url = "urn:foo", definedLocally = true, isModifier = false)
+ private AddressDt myFoo;
+
+ public AddressDt getFoo() {
+ return myFoo;
+ }
+
+ public void setFoo(AddressDt theFoo) {
+ myFoo = theFoo;
+ }
+
+ }
+
@Test
public void testEncodeExt() throws Exception {
@@ -73,9 +193,9 @@ public class JsonParserTest {
String encoded = new FhirContext().newJsonParser().encodeResourceToString(valueSet);
ourLog.info(encoded);
-
+
}
-
+
@Test
public void testEncodeResourceRef() throws DataFormatException, IOException {
@@ -150,9 +270,6 @@ public class JsonParserTest {
}
-
-
-
@Test
public void testSimpleResourceEncodeWithCustomType() throws IOException {
@@ -164,7 +281,7 @@ public class JsonParserTest {
assertEquals("aaaa", obs.getExtAtt().getContentType().getValue());
assertEquals("str1", obs.getMoreExt().getStr1().getValue());
assertEquals("2011-01-02", obs.getModExt().getValueAsString());
-
+
List undeclaredExtensions = obs.getContact().get(0).getName().getFamily().get(0).getUndeclaredExtensions();
ExtensionDt undeclaredExtension = undeclaredExtensions.get(0);
assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl().getValue());
@@ -185,7 +302,7 @@ public class JsonParserTest {
assertEquals(expected.toString(), actual.toString());
}
-
+
@Test
public void testSimpleBundleEncode() throws IOException {
@@ -239,7 +356,8 @@ public class JsonParserTest {
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeBundleToString(bundle);
ourLog.info(encoded);
- assertEquals("http://fhir.healthintersections.com.au/open/DiagnosticReport/_search?_format=application/json+fhir&search-id=46d5f0e7-9240-4d4f-9f51-f8ac975c65&search-sort=_id", bundle.getLinkSelf().getValue());
+ assertEquals("http://fhir.healthintersections.com.au/open/DiagnosticReport/_search?_format=application/json+fhir&search-id=46d5f0e7-9240-4d4f-9f51-f8ac975c65&search-sort=_id", bundle
+ .getLinkSelf().getValue());
assertEquals("urn:uuid:0b754ff9-03cf-4322-a119-15019af8a3", bundle.getBundleId().getValue());
BundleEntry entry = bundle.getEntries().get(0);
@@ -349,13 +467,13 @@ public class JsonParserTest {
IParser newJsonParser = new FhirContext().newJsonParser();
StringReader reader = new StringReader(enc);
Patient parsed = newJsonParser.parseResource(Patient.class, reader);
-
+
ourLog.info(new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(parsed));
-
+
assertEquals(1, parsed.getNameFirstRep().getUndeclaredExtensionsByUrl("http://examples.com#givenext").size());
ExtensionDt ext = parsed.getNameFirstRep().getUndeclaredExtensionsByUrl("http://examples.com#givenext").get(0);
assertEquals("Hello", ext.getValueAsPrimitive().getValue());
-
+
}
@Test
diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java
index 9d75df41567..9e38442bee9 100644
--- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java
+++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java
@@ -31,6 +31,7 @@ import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.IResource;
+import ca.uhn.fhir.model.dstu.composite.AddressDt;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
@@ -51,6 +52,8 @@ import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
+import ca.uhn.fhir.parser.JsonParserTest.MyPatientWithOneDeclaredAddressExtension;
+import ca.uhn.fhir.parser.JsonParserTest.MyPatientWithOneDeclaredExtension;
public class XmlParserTest {
@@ -69,7 +72,83 @@ public class XmlParserTest {
}
+ @Test
+ public void testEncodeExtensionWithResourceContent() throws IOException {
+ IParser parser = new FhirContext().newXmlParser();
+
+ Patient patient = new Patient();
+ patient.addAddress().setUse(AddressUseEnum.HOME);
+ patient.addUndeclaredExtension(false, "urn:foo", new ResourceReferenceDt(Organization.class, "123"));
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString(""));
+
+ Patient actual = parser.parseResource(Patient.class, val);
+ assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
+ List ext = actual.getUndeclaredExtensionsByUrl("urn:foo");
+ assertEquals(1, ext.size());
+ ResourceReferenceDt ref = (ResourceReferenceDt) ext.get(0).getValue();
+ assertEquals("Organization/123", ref.getResourceUrl());
+
+ }
+
+ @Test
+ public void testEncodeDeclaredExtensionWithResourceContent() throws IOException {
+ IParser parser = new FhirContext().newXmlParser();
+
+ MyPatientWithOneDeclaredExtension patient = new MyPatientWithOneDeclaredExtension();
+ patient.addAddress().setUse(AddressUseEnum.HOME);
+ patient.setFoo(new ResourceReferenceDt(Organization.class, "123"));
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString(""));
+
+ MyPatientWithOneDeclaredExtension actual = parser.parseResource(MyPatientWithOneDeclaredExtension.class, val);
+ assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
+ ResourceReferenceDt ref = actual.getFoo();
+ assertEquals("Organization/123", ref.getResourceUrl());
+
+ }
+ @Test
+ public void testEncodeDeclaredExtensionWithAddressContent() throws IOException {
+ IParser parser = new FhirContext().newXmlParser();
+
+ MyPatientWithOneDeclaredAddressExtension patient = new MyPatientWithOneDeclaredAddressExtension();
+ patient.addAddress().setUse(AddressUseEnum.HOME);
+ patient.setFoo(new AddressDt().addLine("line1"));
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString(""));
+
+ MyPatientWithOneDeclaredAddressExtension actual = parser.parseResource(MyPatientWithOneDeclaredAddressExtension.class, val);
+ assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
+ AddressDt ref = actual.getFoo();
+ assertEquals("line1", ref.getLineFirstRep().getValue());
+
+ }
+
+ @Test
+ public void testEncodeUndeclaredExtensionWithAddressContent() throws IOException {
+ IParser parser = new FhirContext().newXmlParser();
+
+ Patient patient = new Patient();
+ patient.addAddress().setUse(AddressUseEnum.HOME);
+ patient.addUndeclaredExtension(false, "urn:foo", new AddressDt().addLine("line1"));
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString(""));
+
+ MyPatientWithOneDeclaredAddressExtension actual = parser.parseResource(MyPatientWithOneDeclaredAddressExtension.class, val);
+ assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
+ AddressDt ref = actual.getFoo();
+ assertEquals("line1", ref.getLineFirstRep().getValue());
+
+ }
@Test
public void testEncodeBundleResultCount() throws IOException {
diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/ClientTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/ClientTest.java
index d47b6113e15..de9d42ae17f 100644
--- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/ClientTest.java
+++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/ClientTest.java
@@ -36,6 +36,7 @@ import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.PathSpecification;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Conformance;
@@ -46,7 +47,12 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.model.primitive.StringDt;
+import ca.uhn.fhir.rest.annotation.Create;
+import ca.uhn.fhir.rest.annotation.RequiredParam;
+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.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.param.CodingListParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QualifiedDateParam;
@@ -131,7 +137,7 @@ public class ClientTest {
ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
- when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 400, "OK"));
+ when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 400, "foobar"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_TEXT + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("foobar"), Charset.forName("UTF-8")));
@@ -481,6 +487,62 @@ public class ClientTest {
}
+
+ @Test
+ public void testSearchWithCustomType() throws Exception {
+
+ String msg = getPatientFeedWithOneResult();
+
+ ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
+ when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
+ when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
+ when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
+ when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
+
+ ITestClientWithCustomType client = ctx.newRestfulClient(ITestClientWithCustomType.class, "http://foo");
+ CustomPatient response = client.getPatientByDob(new QualifiedDateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
+
+ assertEquals("http://foo/Patient?birthdate=%3E%3D2011-01-02", capt.getValue().getURI().toString());
+ assertEquals("PRP1660", response.getIdentifier().get(0).getValue().getValue());
+
+ }
+
+ public interface ITestClientWithCustomType extends IBasicClient {
+ @Search()
+ public CustomPatient getPatientByDob(@RequiredParam(name=Patient.SP_BIRTHDATE) QualifiedDateParam theBirthDate);
+ }
+
+ @Test
+ public void testSearchWithCustomTypeList() throws Exception {
+
+ String msg = getPatientFeedWithOneResult();
+
+ ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
+ when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
+ when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
+ when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
+ when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
+
+ ITestClientWithCustomTypeList client = ctx.newRestfulClient(ITestClientWithCustomTypeList.class, "http://foo");
+ List response = client.getPatientByDob(new QualifiedDateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
+
+ assertEquals("http://foo/Patient?birthdate=%3E%3D2011-01-02", capt.getValue().getURI().toString());
+ assertEquals("PRP1660", response.get(0).getIdentifier().get(0).getValue().getValue());
+
+ }
+
+ public interface ITestClientWithCustomTypeList extends IBasicClient {
+ @Search()
+ public List getPatientByDob(@RequiredParam(name=Patient.SP_BIRTHDATE) QualifiedDateParam theBirthDate);
+ }
+
+ @ResourceDef(name="Patient")
+ public static class CustomPatient extends Patient
+ {
+ // nothing
+ }
+
+
@Test
public void testSearchByToken() throws Exception {
diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/InvalidClientDefinitionTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/InvalidClientDefinitionTest.java
new file mode 100644
index 00000000000..0aa7d9c20a9
--- /dev/null
+++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/InvalidClientDefinitionTest.java
@@ -0,0 +1,26 @@
+package ca.uhn.fhir.rest.client;
+
+import org.junit.Test;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.model.dstu.resource.Patient;
+import ca.uhn.fhir.rest.annotation.RequiredParam;
+import ca.uhn.fhir.rest.annotation.Search;
+import ca.uhn.fhir.rest.client.ClientTest.CustomPatient;
+import ca.uhn.fhir.rest.client.api.IBasicClient;
+import ca.uhn.fhir.rest.param.QualifiedDateParam;
+
+public class InvalidClientDefinitionTest {
+
+ @Test
+ public void testAnnotationTypeIsNotAssignableToMethodReturnType() {
+ // TODO: this should fail
+ new FhirContext().newRestfulClient(ITestClientWithCustomType.class, "http://example.com");
+ }
+
+ public interface ITestClientWithCustomType extends IBasicClient {
+ @Search(type=Patient.class)
+ public CustomPatient getPatientByDob(@RequiredParam(name=Patient.SP_BIRTHDATE) QualifiedDateParam theBirthDate);
+ }
+
+}
diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/DocumentationTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/DocumentationTest.java
new file mode 100644
index 00000000000..dac313a14f7
--- /dev/null
+++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/DocumentationTest.java
@@ -0,0 +1,52 @@
+package ca.uhn.fhir.rest.server;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.junit.Test;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.model.api.annotation.Description;
+import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
+import ca.uhn.fhir.model.dstu.resource.Conformance;
+import ca.uhn.fhir.model.dstu.resource.Patient;
+import ca.uhn.fhir.parser.DataFormatException;
+import ca.uhn.fhir.rest.annotation.RequiredParam;
+import ca.uhn.fhir.rest.annotation.Search;
+import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
+
+public class DocumentationTest {
+
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DocumentationTest.class);
+
+ @Test
+ public void testSearchParameterDocumentation() throws Exception {
+
+ RestfulServer rs = new RestfulServer();
+ rs.setProviders(new SearchProvider());
+
+ ServerConformanceProvider sc = new ServerConformanceProvider(rs);
+ rs.setServerConformanceProvider(sc);
+
+ rs.init(null);
+
+ Conformance conformance = sc.getServerConformance();
+ String conf = new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
+ ourLog.info(conf);
+
+ }
+
+ /**
+ * Created by dsotnikov on 2/25/2014.
+ */
+ public static class SearchProvider {
+
+ @Search(type = Patient.class)
+ public Patient findPatient(@Description(shortDefinition = "The patient's identifier (MRN or other card number)") @RequiredParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier) {
+ return null;
+ }
+
+ }
+
+}
diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResfulServerMethodTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResfulServerMethodTest.java
index fc1abfad8f9..a1eceda0437 100644
--- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResfulServerMethodTest.java
+++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResfulServerMethodTest.java
@@ -1,8 +1,6 @@
package ca.uhn.fhir.rest.server;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
import java.io.StringReader;
import java.util.ArrayList;
@@ -49,6 +47,7 @@ import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Conformance;
+import ca.uhn.fhir.model.dstu.resource.DiagnosticOrder;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu.resource.Patient;
@@ -860,11 +859,10 @@ public class ResfulServerMethodTest {
}
- @Test
public void testUpdateWrongResourceType() throws Exception {
// TODO: this method sends in the wrong resource type vs. the URL so it should
- // give a useful error message
+ // give a useful error message (and then make this unit test actually run)
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
@@ -902,16 +900,17 @@ public class ResfulServerMethodTest {
patient.getIdentifier().setValue("001");
HttpPut httpPut = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
- httpPut.addHeader("Content-Location", "/DiagnosticReport/001/_history/002");
+ httpPut.addHeader("Content-Location", "/DiagnosticReport/001/_history/004");
httpPut.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPut);
- String responseContent = IOUtils.toString(status.getEntity().getContent());
- ourLog.info("Response was:\n{}", responseContent);
+// String responseContent = IOUtils.toString(status.getEntity().getContent());
+// ourLog.info("Response was:\n{}", responseContent);
- assertEquals(201, status.getStatusLine().getStatusCode());
- assertEquals("http://localhost:" + ourPort + "/DiagnosticReport/001/_history/002", status.getFirstHeader("Location").getValue());
+ assertEquals(204, status.getStatusLine().getStatusCode());
+ assertNull(status.getEntity());
+ assertEquals("http://localhost:" + ourPort + "/DiagnosticReport/001/_history/004", status.getFirstHeader("Location").getValue());
}
@@ -977,11 +976,12 @@ public class ResfulServerMethodTest {
status = ourClient.execute(httpPost);
- responseContent = IOUtils.toString(status.getEntity().getContent());
- ourLog.info("Response was:\n{}", responseContent);
+// responseContent = IOUtils.toString(status.getEntity().getContent());
+// ourLog.info("Response was:\n{}", responseContent);
- assertEquals(200, status.getStatusLine().getStatusCode());
- assertEquals("", responseContent);
+ assertEquals(204, status.getStatusLine().getStatusCode());
+ assertNull(status.getEntity());
+// assertEquals("", responseContent);
}
@@ -1043,6 +1043,14 @@ public class ResfulServerMethodTest {
return DiagnosticReport.class;
}
+ @SuppressWarnings("unused")
+ @Update()
+ public MethodOutcome updateDiagnosticReportWithNoResponse(@IdParam IdDt theId, @VersionIdParam IdDt theVersionId, @ResourceParam DiagnosticReport thePatient) {
+ IdDt id = theId;
+ IdDt version = theVersionId;
+ return new MethodOutcome(id, version);
+ }
+
@SuppressWarnings("unused")
@Update()
public MethodOutcome updateDiagnosticReportWithVersionAndNoResponse(@IdParam IdDt theId, @ResourceParam DiagnosticReport thePatient) {
@@ -1281,10 +1289,10 @@ public class ResfulServerMethodTest {
@SuppressWarnings("unused")
@Update()
- public MethodOutcome updateDiagnosticReportWithVersion(@IdParam IdDt theId, @VersionIdParam IdDt theVersionId, @ResourceParam DiagnosticReport thePatient) {
+ public MethodOutcome updateDiagnosticReportWithVersion(@IdParam IdDt theId, @VersionIdParam IdDt theVersionId, @ResourceParam DiagnosticOrder thePatient) {
/*
* TODO: THIS METHOD IS NOT USED. It's the wrong type
- * (DiagnosticReport), so it should cause an exception on startup.
+ * (DiagnosticOrder), so it should cause an exception on startup.
* Also we should detect if there are multiple resource params on an
* update/create/etc method
*/
diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TesterTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TesterTest.java
index f2fd73d753e..49df8c727cf 100644
--- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TesterTest.java
+++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/TesterTest.java
@@ -1,5 +1,6 @@
package ca.uhn.fhir.rest.server;
+import java.lang.annotation.Documented;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -16,8 +17,10 @@ import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
+import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
+import ca.uhn.fhir.model.dstu.composite.QuantityDt;
import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
@@ -75,7 +78,7 @@ public class TesterTest {
@Test
public void testTester() throws Exception {
-// if (true) return;
+ if (true) return;
myRestfulServer.setProviders(new SearchProvider(), new GlobalHistoryProvider());
myServer.start();
@@ -111,7 +114,10 @@ public class TesterTest {
}
@Search(type = Patient.class)
- public Patient findPatient(@RequiredParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier) {
+ public Patient findPatient(
+ @Description(shortDefinition="The patient's identifier (MRN or other card number)")
+ @RequiredParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier
+ ) {
for (Patient next : getIdToPatient().values()) {
for (IdentifierDt nextId : next.getIdentifier()) {
if (nextId.matchesSystemAndValue(theIdentifier)) {