Automatically populate Bundle.entry.base when encoding a bunde

This commit is contained in:
James Agnew 2015-06-17 14:32:11 -04:00
parent 6f7ef96b97
commit 39dd48bc41
16 changed files with 386 additions and 107 deletions

View File

@ -58,6 +58,8 @@ public abstract class BaseRuntimeChildDefinition {
public abstract int getMin();
public interface IMutator {
void setValue(Object theTarget, IBase theValue);
void addValue(Object theTarget, IBase theValue);
}

View File

@ -194,6 +194,11 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
throw new ConfigurationException("Failed to set value", e);
}
}
@Override
public void setValue(Object theTarget, IBase theValue) {
addValue(theTarget, theValue);
}
}
private final class FieldPlainAccessor implements IAccessor {
@ -217,6 +222,15 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
protected final class FieldListMutator implements IMutator {
@Override
public void addValue(Object theTarget, IBase theValue) {
addValue(theTarget, theValue, false);
}
@Override
public void setValue(Object theTarget, IBase theValue) {
addValue(theTarget, theValue, true);
}
private void addValue(Object theTarget, IBase theValue, boolean theClear) {
try {
@SuppressWarnings("unchecked")
List<IBase> existingList = (List<IBase>) myField.get(theTarget);
@ -224,6 +238,9 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
existingList = new ArrayList<IBase>(2);
myField.set(theTarget, existingList);
}
if (theClear) {
existingList.clear();
}
existingList.add(theValue);
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to set value", e);
@ -283,6 +300,10 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
@Override
public void addValue(Object theTarget, IBase theValue) {
addValue(theTarget, false, theValue);
}
private void addValue(Object theTarget, boolean theClear, IBase theValue) {
List<IBase> existingList = myAccessor.getValues(theTarget);
if (existingList == null) {
existingList = new ArrayList<IBase>();
@ -296,8 +317,16 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
throw new ConfigurationException("Failed to get value", e);
}
}
if (theClear) {
existingList.clear();
}
existingList.add(theValue);
}
@Override
public void setValue(Object theTarget, IBase theValue) {
addValue(theTarget, true, theValue);
}
}
private final class PlainAccessor implements IAccessor {
@ -348,6 +377,11 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
throw new ConfigurationException("Failed to get value", e);
}
}
@Override
public void setValue(Object theTarget, IBase theValue) {
addValue(theTarget, theValue);
}
}
}

View File

@ -104,6 +104,17 @@ public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildD
target.getUndeclaredExtensions().add((ExtensionDt) theValue);
}
}
@Override
public void setValue(Object theTarget, IBase theValue) {
ExtensionDt target = (ExtensionDt) theTarget;
if (theValue instanceof IDatatype) {
target.setValue((IDatatype) theTarget);
} else {
target.getUndeclaredExtensions().clear();
target.getUndeclaredExtensions().add((ExtensionDt) theValue);
}
}
};
}

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.model.primitive;
import static org.apache.commons.lang3.StringUtils.*;
import java.math.BigDecimal;
import java.util.UUID;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
@ -634,4 +635,12 @@ public class IdDt extends UriDt implements IPrimitiveDatatype<String>, IIdType {
return theIdPart.toString();
}
/**
* Construct a new ID with with form "urn:uuid:[UUID]" where [UUID] is a new, randomly
* created UUID generated by {@link UUID#randomUUID()}
*/
public static IdDt newRandomUuid() {
return new IdDt("urn:uuid:" + UUID.randomUUID().toString());
}
}

View File

@ -39,6 +39,7 @@ import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -61,6 +62,8 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.ObjectUtil;
public abstract class BaseParser implements IParser {
@ -74,7 +77,8 @@ public abstract class BaseParser implements IParser {
/**
* Constructor
* @param theParserErrorHandler
*
* @param theParserErrorHandler
*/
public BaseParser(FhirContext theContext, IParserErrorHandler theParserErrorHandler) {
myContext = theContext;
@ -205,6 +209,10 @@ public abstract class BaseParser implements IParser {
return resourceBaseUrl;
}
protected abstract void doEncodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException, DataFormatException;
protected abstract void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException, DataFormatException;
protected abstract <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) throws DataFormatException;
@Override
@ -222,6 +230,13 @@ public abstract class BaseParser implements IParser {
return stringWriter.toString();
}
@Override
public final void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException, DataFormatException {
Validate.notNull(theBundle, "theBundle must not be null");
Validate.notNull(theWriter, "theWriter must not be null");
doEncodeBundleToWriter(theBundle, theWriter);
}
@Override
public String encodeResourceToString(IBaseResource theResource) throws DataFormatException {
Writer stringWriter = new StringWriter();
@ -233,6 +248,18 @@ public abstract class BaseParser implements IParser {
return stringWriter.toString();
}
@Override
public final void encodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException, DataFormatException {
Validate.notNull(theResource, "theResource can not be null");
Validate.notNull(theWriter, "theWriter can not be null");
if (theResource instanceof IBaseBundle) {
fixBaseLinksForBundle((IBaseBundle) theResource);
}
doEncodeResourceToWriter(theResource, theWriter);
}
@Override
public String encodeTagListToString(TagList theTagList) {
Writer stringWriter = new StringWriter();
@ -244,6 +271,49 @@ public abstract class BaseParser implements IParser {
return stringWriter.toString();
}
/**
* If individual resources in the bundle have an ID that has the base set, we make sure that Bundle.entry.base gets set as needed.
*/
private void fixBaseLinksForBundle(IBaseBundle theBundle) {
/*
* ATTENTION IF YOU ARE EDITING THIS:
* There are two versions of this method, one for DSTU1/atom bundle and
* one for DSTU2/resource bundle. If you edit one, edit both and also
* update unit tests for both.
*/
FhirTerser t = myContext.newTerser();
IPrimitiveType<?> element = t.getSingleValueOrNull(theBundle, "base", IPrimitiveType.class);
String bundleBase = element != null ? element.getValueAsString() : null;
for (IBase nextEntry : t.getValues(theBundle, "Bundle.entry", IBase.class)) {
IBaseResource resource = t.getSingleValueOrNull(nextEntry, "resource", IBaseResource.class);
if (resource == null) {
continue;
}
IPrimitiveType<?> baseElement = t.getSingleValueOrNull(nextEntry, "base", IPrimitiveType.class);
String entryBase = baseElement != null ? baseElement.getValueAsString() : null;
if (isNotBlank(entryBase)) {
continue;
}
IIdType resourceId = resource.getIdElement();
String resourceIdBase = resourceId.getBaseUrl();
if (isNotBlank(resourceIdBase)) {
if (!ObjectUtil.equals(bundleBase, resourceIdBase)) {
if (baseElement == null) {
baseElement = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
BaseRuntimeElementCompositeDefinition<?> entryDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(nextEntry.getClass());
entryDef.getChildByNameOrThrowDataFormatException("base").getMutator().setValue(nextEntry, baseElement);
}
baseElement.setValueAsString(resourceIdBase);
}
}
}
}
protected String fixContainedResourceId(String theValue) {
if (StringUtils.isNotBlank(theValue) && theValue.charAt(0) == '#') {
return theValue.substring(1);
@ -330,7 +400,7 @@ public abstract class BaseParser implements IParser {
String baseUrl = baseType.getValueAsString();
String idPart = res.getIdElement().getIdPart();
String resourceName = resDef.getName();
if (!baseUrl.startsWith("cid:") && !baseUrl.startsWith("urn:")) {
res.setId(new IdDt(baseUrl, resourceName, idPart, versionIdPart));
@ -376,15 +446,15 @@ public abstract class BaseParser implements IParser {
}
@Override
public BaseParser setParserErrorHandler(IParserErrorHandler theErrorHandler) {
Validate.notNull(theErrorHandler, "theErrorHandler must not be null");
myErrorHandler = theErrorHandler;
public BaseParser setOmitResourceId(boolean theOmitResourceId) {
myOmitResourceId = theOmitResourceId;
return this;
}
@Override
public BaseParser setOmitResourceId(boolean theOmitResourceId) {
myOmitResourceId = theOmitResourceId;
public BaseParser setParserErrorHandler(IParserErrorHandler theErrorHandler) {
Validate.notNull(theErrorHandler, "theErrorHandler must not be null");
myErrorHandler = theErrorHandler;
return this;
}

View File

@ -180,7 +180,7 @@ public class JsonParser extends BaseParser implements IParser {
}
@Override
public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException {
public void doEncodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException {
JsonGenerator eventWriter = createJsonGenerator(theWriter);
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
encodeBundleToWriterInDstu2Format(theBundle, eventWriter);
@ -777,9 +777,7 @@ public class JsonParser extends BaseParser implements IParser {
}
@Override
public void encodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException {
Validate.notNull(theResource, "Resource can not be null");
protected void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws IOException {
JsonGenerator eventWriter = createJsonGenerator(theWriter);
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);

View File

@ -84,14 +84,14 @@ import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.ElementUtil;
import ca.uhn.fhir.util.NonPrettyPrintWriterWrapper;
import ca.uhn.fhir.util.PrettyPrintWriterWrapper;
import ca.uhn.fhir.util.XmlUtil;
/**
* This class is the FHIR XML parser/encoder. Users should not interact with this class directly, but should use
* {@link FhirContext#newXmlParser()} to get an instance.
* This class is the FHIR XML parser/encoder. Users should not interact with this class directly, but should use {@link FhirContext#newXmlParser()} to get an instance.
*/
public class XmlParser extends BaseParser implements IParser {
@ -110,9 +110,9 @@ public class XmlParser extends BaseParser implements IParser {
private boolean myPrettyPrint;
/**
* Do not use this constructor, the recommended way to obtain a new instance of the XML parser is to invoke
* {@link FhirContext#newXmlParser()}.
* @param theParserErrorHandler
* Do not use this constructor, the recommended way to obtain a new instance of the XML parser is to invoke {@link FhirContext#newXmlParser()}.
*
* @param theParserErrorHandler
*/
public XmlParser(FhirContext theContext, IParserErrorHandler theParserErrorHandler) {
super(theContext, theParserErrorHandler);
@ -224,13 +224,17 @@ public class XmlParser extends BaseParser implements IParser {
@Override
public String encodeBundleToString(Bundle theBundle) throws DataFormatException {
StringWriter stringWriter = new StringWriter();
encodeBundleToWriter(theBundle, stringWriter);
try {
encodeBundleToWriter(theBundle, stringWriter);
} catch (IOException e) {
throw new InternalErrorException("IOException writing to StringWriter - Should not happen", e);
}
return stringWriter.toString();
}
@Override
public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws DataFormatException {
public void doEncodeBundleToWriter(Bundle theBundle, Writer theWriter) throws DataFormatException {
try {
XMLStreamWriter eventWriter = createXmlWriter(theWriter);
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
@ -442,7 +446,8 @@ public class XmlParser extends BaseParser implements IParser {
theEventWriter.close();
}
private void encodeChildElementToStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, IBase nextValue, String childName, BaseRuntimeElementDefinition<?> childDef, String theExtensionUrl, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
private void encodeChildElementToStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, IBase nextValue, String childName, BaseRuntimeElementDefinition<?> childDef,
String theExtensionUrl, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
if (nextValue == null || nextValue.isEmpty()) {
if (isChildContained(childDef, theIncludedResource)) {
// We still want to go in..
@ -497,10 +502,9 @@ public class XmlParser extends BaseParser implements IParser {
case CONTAINED_RESOURCE_LIST:
case CONTAINED_RESOURCES: {
/*
* Disable per #103 for (IResource next : value.getContainedResources()) { if
* (getContainedResources().getResourceId(next) != null) { continue; }
* theEventWriter.writeStartElement("contained"); encodeResourceToXmlStreamWriter(next, theEventWriter,
* true, fixContainedResourceId(next.getId().getValue())); theEventWriter.writeEndElement(); }
* Disable per #103 for (IResource next : value.getContainedResources()) { if (getContainedResources().getResourceId(next) != null) { continue; }
* theEventWriter.writeStartElement("contained"); encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(next.getId().getValue()));
* theEventWriter.writeEndElement(); }
*/
for (IBaseResource next : getContainedResources().getContainedResources()) {
IIdType resourceId = getContainedResources().getResourceId(next);
@ -544,7 +548,8 @@ public class XmlParser extends BaseParser implements IParser {
}
private void encodeCompositeElementChildrenToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, List<? extends BaseRuntimeChildDefinition> children, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
private void encodeCompositeElementChildrenToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, List<? extends BaseRuntimeChildDefinition> children,
boolean theIncludedResource) throws XMLStreamException, DataFormatException {
for (BaseRuntimeChildDefinition nextChild : children) {
if (nextChild.getElementName().equals("extension") || nextChild.getElementName().equals("modifierExtension")) {
continue;
@ -570,30 +575,30 @@ public class XmlParser extends BaseParser implements IParser {
}
} else {
// Narrative generation not currently supported for HL7org structures
// INarrative narr1 = ((IDomainResource) theResource).getText();
// BaseNarrativeDt<?> narr2 = null;
// if (gen != null && narr1.isEmpty()) {
// // TODO: need to implement this
// String resourceProfile = myContext.getResourceDefinition(theResource).getResourceProfile();
// gen.generateNarrative(resourceProfile, theResource, null);
// }
// if (narr2 != null) {
// RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
// String childName = nextChild.getChildNameByDatatype(child.getDatatype());
// BaseRuntimeElementDefinition<?> type = child.getChildByName(childName);
// encodeChildElementToStreamWriter(theResource, theEventWriter, narr2, childName, type, null, theIncludedResource);
// continue;
// }
// INarrative narr1 = ((IDomainResource) theResource).getText();
// BaseNarrativeDt<?> narr2 = null;
// if (gen != null && narr1.isEmpty()) {
// // TODO: need to implement this
// String resourceProfile = myContext.getResourceDefinition(theResource).getResourceProfile();
// gen.generateNarrative(resourceProfile, theResource, null);
// }
// if (narr2 != null) {
// RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
// String childName = nextChild.getChildNameByDatatype(child.getDatatype());
// BaseRuntimeElementDefinition<?> type = child.getChildByName(childName);
// encodeChildElementToStreamWriter(theResource, theEventWriter, narr2, childName, type, null, theIncludedResource);
// continue;
// }
}
}
if (nextChild instanceof RuntimeChildContainedResources) {
if (!theIncludedResource) {
encodeChildElementToStreamWriter(theResource, theEventWriter, null, nextChild.getChildNameByDatatype(null), nextChild.getChildElementDefinitionByDatatype(null), null, theIncludedResource);
encodeChildElementToStreamWriter(theResource, theEventWriter, null, nextChild.getChildNameByDatatype(null), nextChild.getChildElementDefinitionByDatatype(null), null,
theIncludedResource);
}
} else {
List<? extends IBase> values = nextChild.getAccessor().getValues(theElement);
if (values == null || values.isEmpty()) {
continue;
@ -636,7 +641,8 @@ public class XmlParser extends BaseParser implements IParser {
}
}
private void encodeCompositeElementToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> theElementDefinition, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
private void encodeCompositeElementToStreamWriter(IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> theElementDefinition,
boolean theIncludedResource) throws XMLStreamException, DataFormatException {
encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource);
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, theElementDefinition.getExtensions(), theIncludedResource);
encodeCompositeElementChildrenToStreamWriter(theResource, theElement, theEventWriter, theElementDefinition.getChildren(), theIncludedResource);
@ -659,9 +665,8 @@ public class XmlParser extends BaseParser implements IParser {
}
/**
* This is just to work around the fact that casting java.util.List<ca.uhn.fhir.model.api.ExtensionDt> to
* java.util.List<? extends org.hl7.fhir.instance.model.api.IBaseExtension<?, ?>> seems to be rejected by the compiler
* some of the time.
* This is just to work around the fact that casting java.util.List<ca.uhn.fhir.model.api.ExtensionDt> to java.util.List<? extends org.hl7.fhir.instance.model.api.IBaseExtension<?, ?>> seems to be
* rejected by the compiler some of the time.
*/
private <Q extends IBaseExtension<?, ?>> List<IBaseExtension<?, ?>> toBaseExtensionList(final List<Q> theList) {
List<IBaseExtension<?, ?>> retVal = new ArrayList<IBaseExtension<?, ?>>(theList.size());
@ -673,7 +678,7 @@ public class XmlParser extends BaseParser implements IParser {
String reference = determineReferenceText(theRef);
encodeExtensionsIfPresent(theResource, theEventWriter, theRef, theIncludedResource);
if (StringUtils.isNotBlank(reference)) {
theEventWriter.writeStartElement(RESREF_REFERENCE);
theEventWriter.writeAttribute("value", reference);
@ -686,10 +691,11 @@ public class XmlParser extends BaseParser implements IParser {
}
}
private void encodeResourceToStreamWriterInDstu2Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
private void encodeResourceToStreamWriterInDstu2Format(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theElement, XMLStreamWriter theEventWriter,
BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
/*
* DSTU2 requires extensions to come in a specific spot within the encoded content - This is a bit of a messy
* way to make that happen, but hopefully this won't matter as much once we use the HL7 structures
* DSTU2 requires extensions to come in a specific spot within the encoded content - This is a bit of a messy way to make that happen, but hopefully this won't matter as much once we use the
* HL7 structures
*/
List<BaseRuntimeChildDefinition> preExtensionChildren = new ArrayList<BaseRuntimeChildDefinition>();
@ -714,18 +720,7 @@ public class XmlParser extends BaseParser implements IParser {
}
@Override
public String encodeResourceToString(IBaseResource theResource) throws DataFormatException {
if (theResource == null) {
throw new NullPointerException("Resource can not be null");
}
Writer stringWriter = new StringWriter();
encodeResourceToWriter(theResource, stringWriter);
return stringWriter.toString();
}
@Override
public void encodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws DataFormatException {
public void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter) throws DataFormatException {
XMLStreamWriter eventWriter;
try {
eventWriter = createXmlWriter(theWriter);
@ -756,7 +751,7 @@ public class XmlParser extends BaseParser implements IParser {
if (isOmitResourceId() && !theIncludedResource) {
resourceId = null;
}
encodeResourceToXmlStreamWriter(theResource, theEventWriter, theIncludedResource, resourceId);
}
@ -774,7 +769,7 @@ public class XmlParser extends BaseParser implements IParser {
theEventWriter.writeDefaultNamespace(FHIR_NS);
if (theResource instanceof IAnyResource) {
// HL7.org Structures
writeOptionalTagWithValue(theEventWriter, "id", theResourceId);
encodeCompositeElementToStreamWriter(theResource, theResource, theEventWriter, resDef, theContainedResource);
@ -890,7 +885,8 @@ public class XmlParser extends BaseParser implements IParser {
}
}
private void encodeUndeclaredExtensions(IBaseResource theResource, XMLStreamWriter theWriter, List<? extends IBaseExtension<?, ?>> theExtensions, String tagName, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
private void encodeUndeclaredExtensions(IBaseResource theResource, XMLStreamWriter theWriter, List<? extends IBaseExtension<?, ?>> theExtensions, String tagName, boolean theIncludedResource)
throws XMLStreamException, DataFormatException {
for (IBaseExtension<?, ?> next : theExtensions) {
if (next == null || (ElementUtil.isEmpty(next.getValue()) && next.getExtension().isEmpty())) {
continue;

View File

@ -45,4 +45,4 @@ public enum ValidationModeEnum implements IBase {
public boolean isEmpty() {
return false;
}
}
}

View File

@ -756,7 +756,7 @@ public class RestfulServer extends HttpServlet {
public final void init() throws ServletException {
initialize();
try {
ourLog.info("Initializing HAPI FHIR restful server");
ourLog.info("Initializing HAPI FHIR restful server running in " + getFhirContext().getVersion().getVersion().name() + " mode");
ProvidedResourceScanner providedResourceScanner = new ProvidedResourceScanner(getFhirContext());
providedResourceScanner.scanForProvidedResources(this);

View File

@ -118,7 +118,7 @@ public class FhirTerser {
return retVal;
}
public <T extends IBase> List<ResourceReferenceInfo> getAllResourceReferences(final IBaseResource theResource) {
public List<ResourceReferenceInfo> getAllResourceReferences(final IBaseResource theResource) {
final ArrayList<ResourceReferenceInfo> retVal = new ArrayList<ResourceReferenceInfo>();
BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
visit(theResource, null, null, def, new IModelVisitor() {
@ -168,33 +168,44 @@ public class FhirTerser {
}
private List<Object> getValues(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, Object theCurrentObj, List<String> theSubList) {
@SuppressWarnings("unchecked")
private <T> List<T> getValues(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, Object theCurrentObj, List<String> theSubList, Class<T> theWantedClass) {
String name = theSubList.get(0);
BaseRuntimeChildDefinition nextDef = theCurrentDef.getChildByNameOrThrowDataFormatException(name);
List<? extends IBase> values = nextDef.getAccessor().getValues(theCurrentObj);
List<Object> retVal = new ArrayList<Object>();
List<T> retVal = new ArrayList<T>();
if (theSubList.size() == 1) {
if (nextDef instanceof RuntimeChildChoiceDefinition) {
for (IBase next : values) {
if (next != null) {
if (name.endsWith("[x]")) {
retVal.add(next);
if (theWantedClass == null || theWantedClass.isAssignableFrom(next.getClass())) {
retVal.add((T) next);
}
} else {
String childName = nextDef.getChildNameByDatatype(next.getClass());
if (theSubList.get(0).equals(childName)) {
retVal.add(next);
if (theWantedClass == null || theWantedClass.isAssignableFrom(next.getClass())) {
retVal.add((T) next);
}
}
}
}
}
} else {
retVal.addAll(values);
for (IBase next : values) {
if (next != null) {
if (theWantedClass == null || theWantedClass.isAssignableFrom(next.getClass())) {
retVal.add((T) next);
}
}
}
}
} else {
for (IBase nextElement : values) {
BaseRuntimeElementCompositeDefinition<?> nextChildDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(nextElement.getClass());
List<?> foundValues = getValues(nextChildDef, nextElement, theSubList.subList(1, theSubList.size()));
List<T> foundValues = getValues(nextChildDef, nextElement, theSubList.subList(1, theSubList.size()), theWantedClass);
retVal.addAll(foundValues);
}
}
@ -202,6 +213,13 @@ public class FhirTerser {
}
public List<Object> getValues(IBaseResource theResource, String thePath) {
Class<Object> wantedClass = Object.class;
return getValues(theResource, thePath, wantedClass);
}
public <T> List<T> getValues(IBaseResource theResource, String thePath, Class<T> theWantedClass) {
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
BaseRuntimeElementCompositeDefinition<?> currentDef = def;
@ -212,8 +230,7 @@ public class FhirTerser {
if (subList.size() < 1) {
throw new ConfigurationException("Invalid path: " + thePath);
}
return getValues(currentDef, currentObj, subList);
return getValues(currentDef, currentObj, subList, theWantedClass);
}
private List<String> addNameToList(List<String> theCurrentList, BaseRuntimeChildDefinition theChildDefinition) {
@ -469,6 +486,12 @@ public class FhirTerser {
}
public Object getSingleValueOrNull(IBase theTarget, String thePath) {
Class<Object> wantedType = Object.class;
return getSingleValueOrNull(theTarget, thePath, wantedType);
}
public <T> T getSingleValueOrNull(IBase theTarget, String thePath, Class<T> theWantedType) {
Validate.notNull(theTarget, "theTarget must not be null");
Validate.notBlank(thePath, "thePath must not be empty");
@ -481,7 +504,7 @@ public class FhirTerser {
Object currentObj = theTarget;
List<String> parts = Arrays.asList(thePath.split("\\."));
List<Object> retVal = getValues(currentDef, currentObj, parts);
List<T> retVal = getValues(currentDef, currentObj, parts, theWantedType);
if (retVal.isEmpty()) {
return null;
} else {
@ -489,4 +512,5 @@ public class FhirTerser {
}
}
}

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.jpa.dao;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
import ca.uhn.fhir.model.primitive.UriDt;

View File

@ -127,7 +127,8 @@ public class XmlParserDstu2Test {
assertThat(str, not(containsString("meta")));
assertThat(str, containsString("<length><value value=\"123\"/><units value=\"day\"/></length>"));
}
@Test
public void testEncodeAndParseBundleWithoutResourceIds() {
Organization org = new Organization();
@ -211,6 +212,7 @@ public class XmlParserDstu2Test {
}
@Test
public void testEncodeAndParseExtensionOnResourceReference() {
DataElement de = new DataElement();
@ -236,6 +238,7 @@ public class XmlParserDstu2Test {
}
@Test
public void testEncodeAndParseExtensions() throws Exception {
@ -381,6 +384,7 @@ public class XmlParserDstu2Test {
assertEquals(new Tag("scheme1", "term1", "label1"), tagList.get(0));
assertEquals(new Tag("scheme2", "term2", "label2"), tagList.get(1));
}
@Test
public void testEncodeAndParseMetaProfiles() {
@ -427,7 +431,7 @@ public class XmlParserDstu2Test {
assertEquals(new Tag("scheme1", "term1", "label1"), tagList.get(0));
assertEquals(new Tag("scheme2", "term2", "label2"), tagList.get(1));
}
@Test
public void testEncodeAndParseSecurityLabels() {
Patient p = new Patient();
@ -485,7 +489,7 @@ public class XmlParserDstu2Test {
assertEquals(false, label.getPrimary());
assertEquals("VERSION2", label.getVersion());
}
/**
* See #103
*/
@ -510,7 +514,7 @@ public class XmlParserDstu2Test {
parsed = parser.parseResource(Composition.class, string);
assertEquals(2, parsed.getContained().getContainedResources().size());
}
/**
* See #103
*/
@ -536,7 +540,6 @@ public class XmlParserDstu2Test {
assertEquals(2, parsed.getContained().getContainedResources().size());
}
@Test
public void testEncodeBinaryWithNoContentType() {
Binary b = new Binary();
@ -548,6 +551,97 @@ public class XmlParserDstu2Test {
assertEquals("<Binary xmlns=\"http://hl7.org/fhir\"><content value=\"AQIDBA==\"/></Binary>", output);
}
@Test
public void testEncodeBundleContainingResourceWithUuidBase() {
Patient p = new Patient();
p.setId(IdDt.newRandomUuid());
p.addName().addFamily("PATIENT");
ca.uhn.fhir.model.dstu2.resource.Bundle b = new ca.uhn.fhir.model.dstu2.resource.Bundle();
b.addEntry().setResource(p);
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(b);
ourLog.info(encoded);
assertThat(encoded, stringContainsInOrder("<Bundle", "<entry>", "<base value=\"urn:uuid:\"/>", "<Patient", "<id value="));
}
@Test
public void testEncodeBundleContainingResourceWithUuidBaseBundleBaseIsSet() {
Patient p = new Patient();
p.setId(IdDt.newRandomUuid());
p.addName().addFamily("PATIENT");
ca.uhn.fhir.model.dstu2.resource.Bundle b = new ca.uhn.fhir.model.dstu2.resource.Bundle();
b.setBase("urn:uuid:");
b.addEntry().setResource(p);
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(b);
ourLog.info(encoded);
assertThat(encoded, not(stringContainsInOrder("<Bundle", "<entry>", "<base value=\"urn:uuid:\"/>", "<Patient", "<id value=")));
assertThat(encoded, stringContainsInOrder("<Bundle", "<base value=\"urn:uuid:\"/>", "<entry>", "<Patient", "<id value="));
}
@Test
public void testEncodeBundleContainingResourceWithUuidBaseBundleBaseIsSetDifferently() {
Patient p = new Patient();
p.setId(IdDt.newRandomUuid());
p.addName().addFamily("PATIENT");
ca.uhn.fhir.model.dstu2.resource.Bundle b = new ca.uhn.fhir.model.dstu2.resource.Bundle();
b.setBase("urn:oid:");
b.addEntry().setResource(p);
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(b);
ourLog.info(encoded);
assertThat(encoded, stringContainsInOrder("<Bundle", "<base value=\"urn:oid:\"/>", "<entry>", "<base value=\"urn:uuid:\"/>", "<Patient", "<id value="));
}
@Test
public void testEncodeBundleOldStyleContainingResourceWithUuidBase() {
Patient p = new Patient();
p.setId(IdDt.newRandomUuid());
p.addName().addFamily("PATIENT");
Bundle b = new Bundle();
b.addEntry().setResource(p);
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeBundleToString(b);
ourLog.info(encoded);
assertThat(encoded, stringContainsInOrder("<Bundle", "<entry>", "<base value=\"urn:uuid:\"/>", "<Patient", "<id value="));
}
@Test
public void testEncodeBundleOldStyleContainingResourceWithUuidBaseBundleBaseIsSet() {
Patient p = new Patient();
p.setId(IdDt.newRandomUuid());
p.addName().addFamily("PATIENT");
Bundle b = new Bundle();
b.getLinkBase().setValue("urn:uuid:");
b.addEntry().setResource(p);
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeBundleToString(b);
ourLog.info(encoded);
assertThat(encoded, not(stringContainsInOrder("<Bundle", "<entry>", "<base value=\"urn:uuid:\"/>", "<Patient", "<id value=")));
assertThat(encoded, stringContainsInOrder("<Bundle", "<base value=\"urn:uuid:\"/>", "<entry>", "<Patient", "<id value="));
}
@Test
public void testEncodeBundleOldStyleContainingResourceWithUuidBaseBundleBaseIsSetDifferently() {
Patient p = new Patient();
p.setId(IdDt.newRandomUuid());
p.addName().addFamily("PATIENT");
Bundle b = new Bundle();
b.getLinkBase().setValue("urn:oid:");
b.addEntry().setResource(p);
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeBundleToString(b);
ourLog.info(encoded);
assertThat(encoded, stringContainsInOrder("<Bundle", "<base value=\"urn:oid:\"/>", "<entry>", "<base value=\"urn:uuid:\"/>", "<Patient", "<id value="));
}
/**
* See #113
*/

View File

@ -33,6 +33,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.math.BigDecimal;
import java.util.UUID;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
@ -43,6 +44,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.DataFormatException;
/**
@ -656,4 +658,12 @@ public final class IdType extends UriType implements IPrimitiveType<String>, IId
return theIdPart.toString();
}
/**
* Construct a new ID with with form "urn:uuid:[UUID]" where [UUID] is a new, randomly
* created UUID generated by {@link UUID#randomUUID()}
*/
public static IdDt newRandomUuid() {
return new IdDt("urn:uuid:" + UUID.randomUUID().toString());
}
}

View File

@ -87,7 +87,10 @@
<action type="fix">
When parsing Bundles, if Bundle.entry.base is set to "cid:" (for DSTU1)
or "urn:uuid:" / "urn:oid:" (for DSTU2) this is now correctly passed as
the base in resource.getId()
the base in resource.getId(). Conversely, when
encoding bundles, if a resource ID has a base defined,
and Bundle.entry.base is empty, it will now be
automatically set by the parser.
</action>
<action type="add">
Add fluent client method for validate operation, and support the

View File

@ -113,15 +113,20 @@ a[name]:before {
margin: auto;
}
DIV.main-body DIV.row DIV.span8 DIV.body-content {
top: -8px;
position: relative;
}
.section h2 {
border-bottom-width: 2px;
border-bottom-style: solid;
border-bottom-color: #CF4711;
background-color: #FF5721;
border-bottom: 2px solid #CF4711;
background-color: #FF7741;
color: #FFA;
font-size: 1.5em;
line-height: 1.4em;
border-radius: 6px;
padding-left: 5px;
padding-right: 5px;
}
.section h3 {
@ -195,4 +200,4 @@ a,a.externalLink,a:active,a:hover,a:link,a:visited {
DIV.sidebar-nav UL LI UL LI {
font-size: 0.9em;
}
*/
*/

View File

@ -21,6 +21,9 @@
<a href="https://github.com/jamesagnew/hapi-fhir/releases">GitHub Release Section</a>.
</p>
</section>
<section name="Maven Users">
<p>
To use HAPI in your application, at a minimum you need to include the HAPI-FHIR core
JAR <code>hapi-fhir-base-[version].jar</code>, as well as at least one "structures" JAR.
@ -49,7 +52,7 @@
<version>${hapi_stable_version}</version>
</dependency>]]></source>
<subsection name="Supporting DSTU2 Resources">
<subsection name="Supporting DSTU2 Resources - HAPI Structures">
<p>
HAPI also has a <code>hapi-fhir-structures-dstu2-[version].jar</code>, which
@ -72,24 +75,6 @@
</dependency>]]></source>
</subsection>
<subsection name="Gradle Users">
<p>
If you are using Gradle, you may use the following dependencies. Note that if
you are doing Android development, you may want to use our
<a href="./doc_android.html">Android build</a> instead.
</p>
<p>
DSTU1:
</p>
<source><![CDATA[compile 'ca.uhn.hapi.fhir:hapi-fhir-base:${hapi_stable_version}'
compile 'ca.uhn.hapi.fhir:hapi-fhir-structures-dstu:${hapi_stable_version}']]></source>
<p>
DSTU2:
</p>
<source><![CDATA[compile 'ca.uhn.hapi.fhir:hapi-fhir-base:${hapi_stable_version}'
compile 'ca.uhn.hapi.fhir:hapi-fhir-structures-dstu2:${hapi_stable_version}']]></source>
</subsection>
<subsection name="Using Reference Implementation Structures">
@ -105,7 +90,25 @@ compile 'ca.uhn.hapi.fhir:hapi-fhir-structures-dstu2:${hapi_stable_version}']]><
</dependency>]]></source>
</subsection>
</section>
<section name="Gradle Users">
<p>
If you are using Gradle, you may use the following dependencies. Note that if
you are doing Android development, you may want to use our
<a href="./doc_android.html">Android build</a> instead.
</p>
<p>
DSTU1:
</p>
<source><![CDATA[compile 'ca.uhn.hapi.fhir:hapi-fhir-base:${hapi_stable_version}'
compile 'ca.uhn.hapi.fhir:hapi-fhir-structures-dstu:${hapi_stable_version}']]></source>
<p>
DSTU2:
</p>
<source><![CDATA[compile 'ca.uhn.hapi.fhir:hapi-fhir-base:${hapi_stable_version}'
compile 'ca.uhn.hapi.fhir:hapi-fhir-structures-dstu2:${hapi_stable_version}']]></source>
</section>
<section name="Using Snapshot Builds">