Almost have unit tests passing

This commit is contained in:
jamesagnew 2014-12-14 22:29:15 -05:00
parent ebd0f222f4
commit 59ba1c9f7b
11 changed files with 408 additions and 105 deletions

View File

@ -288,7 +288,7 @@ class ModelScanner {
scanCompositeDatatype(resClass, datatypeDefinition);
} else if (IPrimitiveType.class.isAssignableFrom(theClass)) {
@SuppressWarnings({ "unchecked" })
Class<? extends IPrimitiveType> resClass = (Class<? extends IPrimitiveType>) theClass;
Class<? extends IPrimitiveType<?>> resClass = (Class<? extends IPrimitiveType<?>>) theClass;
scanPrimitiveDatatype(resClass, datatypeDefinition);
} else {
throw new ConfigurationException("Resource type contains a @" + DatatypeDef.class.getSimpleName() + " annotation but does not implement " + IDatatype.class.getCanonicalName() + ": " + theClass.getCanonicalName());

View File

@ -300,10 +300,10 @@ public class Bundle extends BaseBundle /* implements IElement */{
}
public InstantDt getPublished() {
InstantDt retVal = (InstantDt) getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
InstantDt retVal = (InstantDt) getResourceMetadata().get(ResourceMetadataKeyEnum.PUBLISHED);
if (retVal == null) {
retVal= new InstantDt();
getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, retVal);
getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, retVal);
}
return retVal;
}

View File

@ -85,11 +85,12 @@ import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.primitive.BooleanDt;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.util.ElementUtil;
import ca.uhn.fhir.util.UrlUtil;
public class JsonParser extends BaseParser implements IParser {
@ -116,7 +117,8 @@ public class JsonParser extends BaseParser implements IParser {
private boolean myPrettyPrint;
/**
* Do not use this constructor, the recommended way to obtain a new instance of the JSON parser is to invoke {@link FhirContext#newJsonParser()}.
* Do not use this constructor, the recommended way to obtain a new instance of the JSON parser is to invoke
* {@link FhirContext#newJsonParser()}.
*/
public JsonParser(FhirContext theContext) {
super(theContext);
@ -246,10 +248,12 @@ public class JsonParser extends BaseParser implements IParser {
writeTagWithTextNode(theEventWriter, "id", theBundle.getId().getIdPart());
if (!ElementUtil.isEmpty(theBundle.getId().getVersionIdPart(), theBundle.getUpdated())) {
theEventWriter.writeStartObject("meta");
writeOptionalTagWithTextNode(theEventWriter, "versionId", theBundle.getId().getVersionIdPart());
writeOptionalTagWithTextNode(theEventWriter, "lastUpdated", theBundle.getUpdated());
theEventWriter.writeEnd();
}
writeOptionalTagWithTextNode(theEventWriter, "type", theBundle.getType());
writeOptionalTagWithTextNode(theEventWriter, "base", theBundle.getLinkBase());
@ -311,8 +315,7 @@ public class JsonParser extends BaseParser implements IParser {
theEventWriter.writeEnd();
}
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theWriter, IBase theNextValue,
BaseRuntimeElementDefinition<?> theChildDef, String theChildName, boolean theIsSubElementWithinResource) throws IOException {
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theWriter, IBase theNextValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName, boolean theIsSubElementWithinResource) throws IOException {
switch (theChildDef.getChildType()) {
case PRIMITIVE_DATATYPE: {
@ -423,8 +426,7 @@ public class JsonParser extends BaseParser implements IParser {
}
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter,
List<? extends BaseRuntimeChildDefinition> theChildren, boolean theIsSubElementWithinResource) throws IOException {
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter, List<? extends BaseRuntimeChildDefinition> theChildren, boolean theIsSubElementWithinResource) throws IOException {
for (BaseRuntimeChildDefinition nextChild : theChildren) {
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
@ -527,20 +529,25 @@ public class JsonParser extends BaseParser implements IParser {
for (int i = 0; i < valueIdx; i++) {
boolean haveContent = false;
List<HeldExtension> heldExts = Collections.emptyList();
List<HeldExtension> heldModExts = Collections.emptyList();
if (extensions.size() > i && extensions.get(i) != null && extensions.get(i).isEmpty() == false) {
haveContent = true;
theEventWriter.writeStartObject();
theEventWriter.writeStartArray("extension");
for (HeldExtension nextExt : extensions.get(i)) {
nextExt.write(theResDef, theResource, theEventWriter);
heldExts = extensions.get(i);
}
theEventWriter.writeEnd();
theEventWriter.writeEnd();
if (modifierExtensions.size() > i && modifierExtensions.get(i) != null && modifierExtensions.get(i).isEmpty() == false) {
haveContent = true;
heldModExts = modifierExtensions.get(i);
}
if (!haveContent) {
// theEventWriter.writeEnd();
theEventWriter.writeNull();
} else {
theEventWriter.writeStartObject();
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, heldExts, heldModExts, null);
theEventWriter.writeEnd();
}
}
@ -549,15 +556,13 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter,
BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIsSubElementWithinResource) throws IOException, DataFormatException {
extractAndWriteExtensionsAsDirectChild(theNextValue, theEventWriter, resDef, theResDef, theResource);
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIsSubElementWithinResource) throws IOException, DataFormatException {
extractAndWriteExtensionsAsDirectChild(theNextValue, theEventWriter, resDef, theResDef, theResource, null);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getExtensions(), theIsSubElementWithinResource);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getChildren(), theIsSubElementWithinResource);
}
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull,
boolean theIsSubElementWithinResource) throws IOException {
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theIsSubElementWithinResource) throws IOException {
String resourceId = null;
if (theResource instanceof IResource) {
IResource res = (IResource) theResource;
@ -578,8 +583,7 @@ public class JsonParser extends BaseParser implements IParser {
encodeResourceToJsonStreamWriter(theResDef, theResource, theEventWriter, theObjectNameOrNull, theIsSubElementWithinResource, resourceId);
}
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull,
boolean theIsSubElementWithinResource, String theResourceId) throws IOException {
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theIsSubElementWithinResource, String theResourceId) throws IOException {
if (!theIsSubElementWithinResource) {
super.containResourcesForEncoding(theResource);
}
@ -599,11 +603,13 @@ public class JsonParser extends BaseParser implements IParser {
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1) && theResource instanceof IResource) {
IResource resource = (IResource) theResource;
if (!ElementUtil.isEmpty(resource.getId().getVersionIdPart(), ResourceMetadataKeyEnum.UPDATED.get(resource))) {
theEventWriter.writeStartObject("meta");
writeOptionalTagWithTextNode(theEventWriter, "versionId", resource.getId().getVersionIdPart());
writeOptionalTagWithTextNode(theEventWriter, "lastUpdated", ResourceMetadataKeyEnum.UPDATED.get(resource));
theEventWriter.writeEnd();
}
}
if (theResource instanceof Binary) {
Binary bin = (Binary) theResource;
@ -657,10 +663,10 @@ public class JsonParser extends BaseParser implements IParser {
}
/**
* 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
* 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
*/
private void extractAndWriteExtensionsAsDirectChild(IBase theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef,
IBaseResource theResource) throws IOException {
private void extractAndWriteExtensionsAsDirectChild(IBase theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, IBaseResource theResource, String theParentExtensionUrl) throws IOException {
List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
@ -668,10 +674,12 @@ public class JsonParser extends BaseParser implements IParser {
extractUndeclaredExtensions(theElement, extensions, modifierExtensions);
// Declared extensions
if (theElementDef != null) {
extractDeclaredExtensions(theElement, theElementDef, extensions, modifierExtensions);
}
// Write the extensions
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions);
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions, theParentExtensionUrl);
}
private void extractDeclaredExtensions(IBase theResource, BaseRuntimeElementDefinition<?> resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions) {
@ -721,15 +729,17 @@ public class JsonParser extends BaseParser implements IParser {
if (theAlternateVal == null || theAlternateVal.getValueType() == ValueType.NULL) {
return;
}
boolean newerThanDstu1 = myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1);
JsonObject alternate = (JsonObject) theAlternateVal;
for (Entry<String, JsonValue> nextEntry : alternate.entrySet()) {
String nextKey = nextEntry.getKey();
JsonValue nextVal = nextEntry.getValue();
if ("extension".equals(nextKey)) {
if (!newerThanDstu1 && "extension".equals(nextKey)) {
boolean isModifier = false;
JsonArray array = (JsonArray) nextEntry.getValue();
parseExtension(theState, array, isModifier);
} else if ("modifierExtension".equals(nextKey)) {
} else if (!newerThanDstu1 && "modifierExtension".equals(nextKey)) {
boolean isModifier = true;
JsonArray array = (JsonArray) nextEntry.getValue();
parseExtension(theState, array, isModifier);
@ -743,6 +753,12 @@ public class JsonParser extends BaseParser implements IParser {
default:
break;
}
} else if (newerThanDstu1) {
if (nextKey.indexOf(':') > -1 && newerThanDstu1) {
JsonArray array = (JsonArray) nextEntry.getValue();
parseExtensionInDstu2Style(false, theState, null, nextKey, array);
continue;
}
}
}
}
@ -757,8 +773,7 @@ public class JsonParser extends BaseParser implements IParser {
object = reader.readObject();
} catch (JsonParsingException e) {
if (e.getMessage().startsWith("Unexpected char 39")) {
throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage()
+ " - This may indicate that single quotes are being used as JSON escapes where double quotes are required", e);
throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage() + " - This may indicate that single quotes are being used as JSON escapes where double quotes are required", e);
}
throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage(), e);
}
@ -861,6 +876,7 @@ public class JsonParser extends BaseParser implements IParser {
private void parseChildren(JsonObject theObject, ParserState<?> theState) {
String elementId = null;
boolean newerThanDstu1 = myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1);
for (String nextName : theObject.keySet()) {
if ("resourceType".equals(nextName)) {
continue;
@ -873,19 +889,29 @@ public class JsonParser extends BaseParser implements IParser {
// _id is incorrect, but some early examples in the FHIR spec used it
elementId = theObject.getString(nextName);
continue;
} else if ("extension".equals(nextName)) {
} else if (!newerThanDstu1 && "extension".equals(nextName)) {
JsonArray array = theObject.getJsonArray(nextName);
parseExtension(theState, array, false);
continue;
} else if ("modifierExtension".equals(nextName)) {
} else if (!newerThanDstu1 && "modifierExtension".equals(nextName)) {
JsonArray array = theObject.getJsonArray(nextName);
parseExtension(theState, array, true);
continue;
} else if (newerThanDstu1 && "modifier".equals(nextName)) {
JsonObject obj = theObject.getJsonObject(nextName);
for (String nextUrl : obj.keySet()) {
JsonArray array = obj.getJsonArray(nextUrl);
parseExtensionInDstu2Style(true, theState, null, nextUrl, array);
}
continue;
} else if (nextName.charAt(0) == '_') {
continue;
} else if (nextName.contains(":") && myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
} else {
if (newerThanDstu1 && nextName.indexOf(':') > -1) {
JsonArray array = theObject.getJsonArray(nextName);
parseExtensionInDstu2Style(theState, nextName, array);
parseExtensionInDstu2Style(false, theState, null, nextName, array);
continue;
}
}
JsonValue nextVal = theObject.get(nextName);
@ -905,7 +931,6 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void parseChildren(ParserState<?> theState, String theName, JsonValue theJsonVal, JsonValue theAlternateVal) {
switch (theJsonVal.getValueType()) {
case ARRAY: {
@ -986,13 +1011,26 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void parseExtensionInDstu2Style(ParserState<?> theState, String theUrl, JsonArray theValues) {
theState.enteringNewElementExtension(null, theUrl, false);
private void parseExtensionInDstu2Style(boolean theModifier, ParserState<?> theState, String theParentExtensionUrl, String theExtensionUrl, JsonArray theValues) {
String extUrl = UrlUtil.constructAbsoluteUrl(theParentExtensionUrl, theExtensionUrl);
theState.enteringNewElementExtension(null, extUrl, theModifier);
for (int extIdx = 0; extIdx < theValues.size(); extIdx++) {
JsonObject nextExt = theValues.getJsonObject(extIdx);
for (String nextKey : nextExt.keySet()) {
if (nextKey.startsWith("value") && nextKey.length() > 5 && )
// if (nextKey.startsWith("value") && nextKey.length() > 5 &&
// myContext.getRuntimeChildUndeclaredExtensionDefinition().getChildByName(nextKey) != null) {
JsonValue jsonVal = nextExt.get(nextKey);
if (jsonVal.getValueType() == ValueType.ARRAY) {
/*
* Extension children which are arrays are sub-extensions. Any other value type should be treated as
* a value.
*/
JsonArray arrayValue = (JsonArray) jsonVal;
parseExtensionInDstu2Style(theModifier, theState, extUrl, nextKey, arrayValue);
} else {
parseChildren(theState, nextKey, jsonVal, null);
}
}
}
@ -1015,7 +1053,7 @@ public class JsonParser extends BaseParser implements IParser {
def = myContext.getResourceDefinition(resourceType);
}
ParserState<? extends IResource> state = (ParserState<? extends IResource>) ParserState.getPreResourceInstance(def.getImplementingClass(), myContext, true);
ParserState<? extends IBaseResource> state = (ParserState<? extends IBaseResource>) ParserState.getPreResourceInstance(def.getImplementingClass(), myContext, true);
state.enteringNewElement(null, def.getName());
parseChildren(object, state);
@ -1115,16 +1153,38 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void writeExtensionsAsDirectChild(IBaseResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions,
List<HeldExtension> modifierExtensions) throws IOException {
private void writeExtensionsAsDirectChild(IBaseResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions, String theParentExtensionUrl) throws IOException {
if (extensions.isEmpty() == false) {
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
Collections.sort(extensions);
String currentlyWritingExtensionUrl = null;
for (HeldExtension next : extensions) {
currentlyWritingExtensionUrl = next.writeExtensionInDstu2Format(resDef, theResource, theEventWriter, currentlyWritingExtensionUrl, theParentExtensionUrl);
}
if (currentlyWritingExtensionUrl != null) {
theEventWriter.writeEnd();
}
} else {
theEventWriter.writeStartArray("extension");
for (HeldExtension next : extensions) {
next.write(resDef, theResource, theEventWriter);
}
theEventWriter.writeEnd();
}
}
if (modifierExtensions.isEmpty() == false) {
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
Collections.sort(modifierExtensions);
theEventWriter.writeStartObject("modifier");
String currentlyWritingExtensionUrl = null;
for (HeldExtension next : modifierExtensions) {
currentlyWritingExtensionUrl = next.writeExtensionInDstu2Format(resDef, theResource, theEventWriter, currentlyWritingExtensionUrl, theParentExtensionUrl);
}
if (currentlyWritingExtensionUrl != null) {
theEventWriter.writeEnd();
}
theEventWriter.writeEnd();
} else {
theEventWriter.writeStartArray("modifierExtension");
for (HeldExtension next : modifierExtensions) {
next.write(resDef, theResource, theEventWriter);
@ -1132,6 +1192,7 @@ public class JsonParser extends BaseParser implements IParser {
theEventWriter.writeEnd();
}
}
}
private void writeOptionalTagWithNumberNode(JsonGenerator theEventWriter, String theElementName, IntegerDt theValue) {
if (theValue != null && theValue.isEmpty() == false) {
@ -1184,7 +1245,7 @@ public class JsonParser extends BaseParser implements IParser {
// }
}
private class HeldExtension {
private class HeldExtension implements Comparable<HeldExtension> {
private RuntimeChildDeclaredExtensionDefinition myDef;
private ExtensionDt myUndeclaredExtension;
@ -1204,41 +1265,73 @@ public class JsonParser extends BaseParser implements IParser {
public void write(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter) throws IOException {
if (myUndeclaredExtension != null) {
writeUndeclaredExt(theResDef, theResource, theEventWriter, myUndeclaredExtension);
writeUndeclaredExtInDstu1Format(theResDef, theResource, theEventWriter, myUndeclaredExtension);
} else {
theEventWriter.writeStartObject();
theEventWriter.write("url", myDef.getExtensionUrl());
BaseRuntimeElementDefinition<?> def = myDef.getChildElementDefinitionByDatatype(myValue.getClass());
if (def.getChildType() == ChildTypeEnum.RESOURCE_BLOCK) {
extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource);
extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource, myDef.getExtensionUrl());
} else {
// encodeChildElementToStreamWriter(theResDef, theResource,
// theEventWriter, myValue, def, "value" +
// WordUtils.capitalize(def.getName()));
String childName = myDef.getChildNameByDatatype(myValue.getClass());
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false);
}
// theEventWriter.name(myUndeclaredExtension.get);
theEventWriter.writeEnd();
}
}
private void writeUndeclaredExt(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, ExtensionDt ext) throws IOException {
IElement value = ext.getValue();
public String writeExtensionInDstu2Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theCurrentlyWritingExtensionUrl, String theParentExtensionUrl) throws IOException {
if (myUndeclaredExtension != null) {
return writeUndeclaredExtInDstu2Format(theResDef, theResource, theEventWriter, myUndeclaredExtension, theCurrentlyWritingExtensionUrl, theParentExtensionUrl);
} else {
String extensionUrl = myDef.getExtensionUrl();
checkIfNewExtensionUrlArrayIsNeeded(theEventWriter, extensionUrl, theCurrentlyWritingExtensionUrl, theParentExtensionUrl);
theEventWriter.writeStartObject();
theEventWriter.write("url", ext.getUrl().getValue());
BaseRuntimeElementDefinition<?> def = myDef.getChildElementDefinitionByDatatype(myValue.getClass());
if (def.getChildType() == ChildTypeEnum.RESOURCE_BLOCK) {
extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource, extensionUrl);
} else {
String childName = myDef.getChildNameByDatatype(myValue.getClass());
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false);
}
theEventWriter.writeEnd();
return extensionUrl;
}
}
private void checkIfNewExtensionUrlArrayIsNeeded(JsonGenerator theEventWriter, String theExtensionUrl, String theCurrentlyWritingExtensionUrl, String theParentExtensionUrl) {
if (StringUtils.equals(theCurrentlyWritingExtensionUrl, theExtensionUrl)==false) {
if (isNotBlank(theCurrentlyWritingExtensionUrl)) {
theEventWriter.writeEnd();
}
String urlToWrite = UrlUtil.constructRelativeUrl(theParentExtensionUrl, theExtensionUrl);
theEventWriter.writeStartArray(urlToWrite);
}
}
private void writeUndeclaredExtInDstu1Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, ExtensionDt ext) throws IOException {
IElement value = ext.getValue();
String extensionUrl = ext.getUrl().getValue();
theEventWriter.writeStartObject();
theEventWriter.write("url", extensionUrl);
boolean noValue = value == null || value.isEmpty();
if (noValue && ext.getAllUndeclaredExtensions().isEmpty()) {
ourLog.debug("Extension with URL[{}] has no value", ext.getUrl().getValue());
ourLog.debug("Extension with URL[{}] has no value", extensionUrl);
} else if (noValue) {
theEventWriter.writeStartArray("extension");
for (ExtensionDt next : ext.getUndeclaredExtensions()) {
writeUndeclaredExt(theResDef, theResource, theEventWriter, next);
writeUndeclaredExtInDstu1Format(theResDef, theResource, theEventWriter, next);
}
theEventWriter.writeEnd();
} else {
@ -1256,5 +1349,45 @@ public class JsonParser extends BaseParser implements IParser {
theEventWriter.writeEnd();
}
private String writeUndeclaredExtInDstu2Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, ExtensionDt ext, String theCurrentlyWritingExtensionUrl, String theParentExtensionUrl) throws IOException {
IElement value = ext.getValue();
String extensionUrl = ext.getUrl().getValue();
checkIfNewExtensionUrlArrayIsNeeded(theEventWriter, extensionUrl, theCurrentlyWritingExtensionUrl, theParentExtensionUrl);
theEventWriter.writeStartObject();
boolean noValue = value == null || value.isEmpty();
if (noValue && ext.getAllUndeclaredExtensions().isEmpty()) {
ourLog.debug("Extension with URL[{}] has no value", extensionUrl);
} else if (noValue) {
BaseRuntimeElementDefinition<?> elemDef = null;
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
extractAndWriteExtensionsAsDirectChild(ext, theEventWriter, elemDef, resDef, theResource, extensionUrl);
} else {
RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition();
String childName = extDef.getChildNameByDatatype(value.getClass());
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, true);
}
theEventWriter.writeEnd();
return extensionUrl;
}
@Override
public int compareTo(HeldExtension theArg0) {
String url1 = myDef != null ? myDef.getExtensionUrl() : myUndeclaredExtension.getUrlAsString();
String url2 = theArg0.myDef != null ? theArg0.myDef.getExtensionUrl() : theArg0.myUndeclaredExtension.getUrlAsString();
url1 = defaultString(url1);
url2 = defaultString(url2);
return url1.compareTo(url2);
}
}
}

View File

@ -1,5 +1,8 @@
package ca.uhn.fhir.util;
import java.net.MalformedURLException;
import java.net.URL;
/*
* #%L
* HAPI FHIR - Core Library
@ -21,10 +24,64 @@ package ca.uhn.fhir.util;
*/
public class UrlUtil {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UrlUtil.class);
/**
* Resolve a relative URL - THIS METHOD WILL NOT FAIL but will log a warning
* and return theEndpoint if the input is invalid.
*/
public static String constructAbsoluteUrl(String theBase, String theEndpoint) {
if (theEndpoint == null) {
return null;
}
if (isAbsolute(theEndpoint)) {
return theEndpoint;
}
if (theBase == null) {
return theEndpoint;
}
try {
return new URL(new URL(theBase), theEndpoint).toString();
} catch (MalformedURLException e) {
ourLog.warn("Failed to resolve relative URL[" + theEndpoint + "] against absolute base[" + theBase + "]", e);
return theEndpoint;
}
}
public static boolean isAbsolute(String theValue) {
String value = theValue.toLowerCase();
return value.startsWith("http://") || value.startsWith("https://");
}
public static String constructRelativeUrl(String theParentExtensionUrl, String theExtensionUrl) {
if (theParentExtensionUrl == null) {
return theExtensionUrl;
}
if (theExtensionUrl == null) {
return theExtensionUrl;
}
int parentLastSlashIdx = theParentExtensionUrl.lastIndexOf('/');
int childLastSlashIdx = theExtensionUrl.lastIndexOf('/');
if (parentLastSlashIdx == -1 || childLastSlashIdx == -1) {
return theExtensionUrl;
}
if (parentLastSlashIdx != childLastSlashIdx) {
return theExtensionUrl;
}
if (!theParentExtensionUrl.substring(0, parentLastSlashIdx).equals(theExtensionUrl.substring(0, parentLastSlashIdx))) {
return theExtensionUrl;
}
if (theExtensionUrl.length() > parentLastSlashIdx) {
return theExtensionUrl.substring(parentLastSlashIdx+1);
}
return theExtensionUrl;
}
}

View File

@ -0,0 +1,29 @@
package ca.uhn.fhir.util;
import static org.junit.Assert.*;
import org.junit.Test;
public class UrlUtilTest {
@Test
public void testConstructAbsoluteUrl() {
assertEquals("http://foo/bar/baz", UrlUtil.constructAbsoluteUrl(null, "http://foo/bar/baz"));
assertEquals("http://foo/bar/baz", UrlUtil.constructAbsoluteUrl("http://foo/bar/","baz"));
assertEquals("http://foo/bar/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/","baz/"));
assertEquals("http://foo/bar/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/","./baz/"));
assertEquals("http://foo/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/","../baz/"));
assertEquals("http://foo/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/","/baz/"));
}
@Test
public void testConstructRelativeUrl() {
assertEquals("http://foo/bar/baz", UrlUtil.constructRelativeUrl("http://boo/far/faz", "http://foo/bar/baz"));
assertEquals("http://foo/bar/baz", UrlUtil.constructRelativeUrl("http://foo/far/faz", "http://foo/bar/baz"));
assertEquals("baz", UrlUtil.constructRelativeUrl("http://foo/bar/boo", "http://foo/bar/baz"));
}
}

View File

@ -33,6 +33,7 @@ import javax.persistence.criteria.Root;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.instance.model.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
@ -117,7 +118,7 @@ public abstract class BaseFhirDao implements IDao {
@Autowired
private List<IFhirResourceDao<?>> myResourceDaos;
private Map<Class<? extends IResource>, IFhirResourceDao<?>> myResourceTypeToDao;
private Map<Class<? extends IBaseResource>,IFhirResourceDao<?>> myResourceTypeToDao;
public FhirContext getContext() {
return myContext;
@ -343,7 +344,7 @@ public abstract class BaseFhirDao implements IDao {
if (isBlank(typeString)) {
throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource type - " + nextValue.getReference().getValue());
}
Class<? extends IResource> type = getContext().getResourceDefinition(typeString).getImplementingClass();
Class<? extends IBaseResource> type = getContext().getResourceDefinition(typeString).getImplementingClass();
String id = nextValue.getReference().getIdPart();
if (StringUtils.isBlank(id)) {
continue;
@ -798,9 +799,9 @@ public abstract class BaseFhirDao implements IDao {
return myConfig;
}
protected IFhirResourceDao<? extends IResource> getDao(Class<? extends IResource> theType) {
protected IFhirResourceDao<? extends IResource> getDao(Class<? extends IBaseResource> theType) {
if (myResourceTypeToDao == null) {
myResourceTypeToDao = new HashMap<Class<? extends IResource>, IFhirResourceDao<?>>();
myResourceTypeToDao = new HashMap<Class<? extends IBaseResource>, IFhirResourceDao<?>>();
for (IFhirResourceDao<?> next : myResourceDaos) {
myResourceTypeToDao.put(next.getResourceType(), next);
}
@ -928,7 +929,7 @@ public abstract class BaseFhirDao implements IDao {
ArrayList<IResource> retVal = new ArrayList<IResource>();
for (BaseHasResource next : resEntities) {
retVal.add(toResource(next));
retVal.add((IResource) toResource(next));
}
return retVal;
}
@ -971,7 +972,7 @@ public abstract class BaseFhirDao implements IDao {
ArrayList<IResource> retVal = new ArrayList<IResource>();
for (ResourceTable next : q.getResultList()) {
IResource resource = toResource(next);
IResource resource = (IResource) toResource(next);
retVal.add(resource);
}
@ -1056,12 +1057,12 @@ public abstract class BaseFhirDao implements IDao {
return retVal;
}
protected IResource toResource(BaseHasResource theEntity) {
protected IBaseResource toResource(BaseHasResource theEntity) {
RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType());
return toResource(type.getImplementingClass(), theEntity);
}
protected <T extends IResource> T toResource(Class<T> theResourceType, BaseHasResource theEntity) {
protected <T extends IBaseResource> T toResource(Class<T> theResourceType, BaseHasResource theEntity) {
String resourceText = null;
switch (theEntity.getEncoding()) {
case JSON:
@ -1079,18 +1080,19 @@ public abstract class BaseFhirDao implements IDao {
IParser parser = theEntity.getEncoding().newParser(getContext());
T retVal = parser.parseResource(theResourceType, resourceText);
retVal.setId(theEntity.getIdDt());
IResource res = (IResource)retVal;
res.setId(theEntity.getIdDt());
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, theEntity.getVersion());
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, theEntity.getPublished());
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, theEntity.getUpdated());
res.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, theEntity.getVersion());
res.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, theEntity.getPublished());
res.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, theEntity.getUpdated());
if (theEntity.getTitle() != null) {
ResourceMetadataKeyEnum.TITLE.put(retVal, theEntity.getTitle());
ResourceMetadataKeyEnum.TITLE.put(res, theEntity.getTitle());
}
if (theEntity.getDeleted() != null) {
ResourceMetadataKeyEnum.DELETED_AT.put(retVal, new InstantDt(theEntity.getDeleted()));
ResourceMetadataKeyEnum.DELETED_AT.put(res, new InstantDt(theEntity.getDeleted()));
}
Collection<? extends BaseTag> tags = theEntity.getTags();
@ -1099,8 +1101,9 @@ public abstract class BaseFhirDao implements IDao {
for (BaseTag next : tags) {
tagList.add(new Tag(next.getTag().getScheme(), next.getTag().getTerm(), next.getTag().getLabel()));
}
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tagList);
res.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tagList);
}
return retVal;
}

View File

@ -32,6 +32,7 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.transaction.PlatformTransactionManager;
@ -1125,16 +1126,16 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
if (!(def instanceof RuntimeChildResourceDefinition)) {
throw new ConfigurationException("Property " + chain + " of type " + myResourceName + " is not a resource: " + def.getClass());
}
List<Class<? extends IResource>> resourceTypes;
List<Class<? extends IBaseResource>> resourceTypes;
if (isBlank(ref.getResourceType())) {
RuntimeChildResourceDefinition resDef = (RuntimeChildResourceDefinition) def;
resourceTypes = resDef.getResourceTypes();
} else {
resourceTypes = new ArrayList<Class<? extends IResource>>();
resourceTypes = new ArrayList<Class<? extends IBaseResource>>();
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(ref.getResourceType());
resourceTypes.add(resDef.getImplementingClass());
}
for (Class<? extends IResource> nextType : resourceTypes) {
for (Class<? extends IBaseResource> nextType : resourceTypes) {
RuntimeResourceDefinition typeDef = getContext().getResourceDefinition(nextType);
RuntimeSearchParam param = typeDef.getSearchParam(ref.getChain());
if (param == null) {

View File

@ -1,3 +1,4 @@
myUnitTestDB/
target/
/bin
/target/

View File

@ -336,6 +336,17 @@
</plugin>
</plugins>
</pluginManagement>
<resources>
<resource>
<directory>${baseDir}/src/main/resources</directory>
</resource>
<resource>
<directory>${baseDir}/target/generated-sources/tinder</directory>
</resource>
<resource>
<directory>${baseDir}/target/generated-resources/tinder</directory>
</resource>
</resources>
</build>
<reporting>

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.parser;
import static org.junit.Assert.*;
import net.sf.json.JSON;
import net.sf.json.JSONSerializer;
import net.sf.json.JsonConfig;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
@ -13,8 +14,11 @@ import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dev.resource.MedicationPrescription;
import ca.uhn.fhir.model.dev.resource.Patient;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.primitive.BooleanDt;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.DateDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
public class JsonParserTest {
@ -23,7 +27,13 @@ public class JsonParserTest {
@Test
public void testParseBundleWithBinary() {
// TODO: implement this test, make sure we handle ID and meta correctly in Binary
Binary patient = new Binary();
patient.setId(new IdDt("http://base/Binary/11/_history/22"));
patient.setContentType("foo");
patient.setContent(new byte[] { 1, 2, 3, 4 });
String val = ourCtx.newJsonParser().encodeResourceToString(patient);
assertEquals("{\"resourceType\":\"Binary\",\"id\":\"11\",\"meta\":{\"versionId\":\"22\"},\"contentType\":\"foo\",\"content\":\"AQIDBA==\"}", val);
}
@Test
@ -52,8 +62,10 @@ public class JsonParserTest {
String reencoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeBundleToString(parsed);
ourLog.info(reencoded);
JSON expected = JSONSerializer.toJSON(content.trim());
JSON actual = JSONSerializer.toJSON(reencoded.trim());
JsonConfig cfg = new JsonConfig();
JSON expected = JSONSerializer.toJSON(content.trim(), cfg);
JSON actual = JSONSerializer.toJSON(reencoded.trim(), cfg);
String exp = expected.toString().replace("\\r\\n", "\\n"); // .replace("&sect;", "§");
String act = actual.toString().replace("\\r\\n", "\\n");
@ -67,6 +79,7 @@ public class JsonParserTest {
@Test
public void testParseAndEncodeNewExtensionFormat() {
//@formatter:off
String resource = "{\n" +
" \"resourceType\" : \"Patient\",\n" +
@ -78,7 +91,11 @@ public class JsonParserTest {
" }\n" +
" }]\n" +
" }],\n" +
" \"gender\" : \"M\",\n" +
" \"modifier\" : { \n" +
" \"http://example.org/fhir/ExtensionDefinition/some-kind-of-modifier\" : [{\n" +
" \"valueBoolean\" : true\n" +
" }]\n" +
" }," +
" \"name\" : [{\n" +
" \"family\": [\n" +
" \"du\",\n" +
@ -97,14 +114,21 @@ public class JsonParserTest {
" \"given\": [\n" +
" \"Bénédicte\"\n" +
" ]\n" +
" }]" +
" }],\n" +
" \"gender\" : \"M\"\n" +
"}";
//@formatter:on
ourLog.info(resource);
// ourLog.info(resource);
Patient parsed = ourCtx.newJsonParser().parseResource(Patient.class, resource);
// Modifier extension
assertEquals(1, parsed.getUndeclaredExtensionsByUrl("http://example.org/fhir/ExtensionDefinition/some-kind-of-modifier").size());
assertTrue( parsed.getUndeclaredExtensionsByUrl("http://example.org/fhir/ExtensionDefinition/some-kind-of-modifier").get(0).isModifier());
assertEquals(BooleanDt.class, parsed.getUndeclaredExtensionsByUrl("http://example.org/fhir/ExtensionDefinition/some-kind-of-modifier").get(0).getValueAsPrimitive().getClass());
assertEquals("true", parsed.getUndeclaredExtensionsByUrl("http://example.org/fhir/ExtensionDefinition/some-kind-of-modifier").get(0).getValueAsPrimitive().getValueAsString());
// Gender
assertEquals("M",parsed.getGender());
assertEquals(1, parsed.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/trial-status").size());
@ -131,6 +155,23 @@ public class JsonParserTest {
assertEquals(CodeDt.class, parsed.getNameFirstRep().getFamily().get(0).getUndeclaredExtensionsByUrl("http://hl7.org/fhir/ExtensionDefinition/iso21090-EN-qualifier").get(0).getValueAsPrimitive().getClass());
assertEquals("VV", parsed.getNameFirstRep().getFamily().get(0).getUndeclaredExtensionsByUrl("http://hl7.org/fhir/ExtensionDefinition/iso21090-EN-qualifier").get(0).getValueAsPrimitive().getValueAsString());
/*
* Now re-encode
*/
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(parsed);
ourLog.info(encoded);
JSON expected = JSONSerializer.toJSON(resource.trim());
JSON actual = JSONSerializer.toJSON(encoded.trim());
String exp = expected.toString().replace("\\r\\n", "\\n"); // .replace("&sect;", "§");
String act = actual.toString().replace("\\r\\n", "\\n");
ourLog.info("Expected: {}", exp);
ourLog.info("Actual : {}", act);
assertEquals(exp, act);
}

View File

@ -0,0 +1,27 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] - %msg%n
</pattern>
</encoder>
</appender>
<logger name="org.eclipse" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.apache" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<!--
<logger name="ca.uhn.fhir.rest.client" additivity="false" level="trace">
<appender-ref ref="STDOUT" />
</logger>
-->
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>