Fix #402 - Don't overwrite narrative sections

This commit is contained in:
James Agnew 2019-09-30 20:14:36 -04:00
parent 731be21c9f
commit eaaf2768c3
9 changed files with 347 additions and 311 deletions

View File

@ -66,13 +66,17 @@ public abstract class BaseRuntimeChildDefinition {
}
public interface IAccessor {
List<IBase> getValues(Object theTarget);
List<IBase> getValues(IBase theTarget);
default IBase getFirstValueOrNull(IBase theTarget) {
return getValues(theTarget).stream().findFirst().orElse(null);
}
}
public interface IMutator {
void addValue(Object theTarget, IBase theValue);
void addValue(IBase theTarget, IBase theValue);
void setValue(Object theTarget, IBase theValue);
void setValue(IBase theTarget, IBase theValue);
}
BaseRuntimeElementDefinition<?> findResourceReferenceDefinition(Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {

View File

@ -20,34 +20,33 @@ package ca.uhn.fhir.context;
* #L%
*/
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.util.ValidateUtil;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.util.ValidateUtil;
public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChildDefinition {
private final IAccessor myAccessor;
private String myBindingValueSet;
private final String myElementName;
private final Field myField;
private final String myFormalDefinition;
private final int myMax;
private final int myMin;
private boolean myModifier;
private final IMutator myMutator;
private final String myShortDefinition;
private String myBindingValueSet;
private boolean myModifier;
private boolean mySummary;
BaseRuntimeDeclaredChildDefinition(Field theField, Child theChildAnnotation, Description theDescriptionAnnotation, String theElementName) throws ConfigurationException {
super();
Validate.notNull(theField, "No field speficied");
Validate.notNull(theField, "No field specified");
ValidateUtil.isGreaterThanOrEqualTo(theChildAnnotation.min(), 0, "Min must be >= 0");
Validate.isTrue(theChildAnnotation.max() == -1 || theChildAnnotation.max() >= theChildAnnotation.min(), "Max must be >= Min (unless it is -1 / unlimited)");
Validate.notBlank(theElementName, "Element name must not be blank");
@ -87,6 +86,10 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
return myBindingValueSet;
}
void setBindingValueSet(String theBindingValueSet) {
myBindingValueSet = theBindingValueSet;
}
@Override
public String getElementName() {
return myElementName;
@ -119,107 +122,99 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil
return myShortDefinition;
}
public BaseRuntimeElementDefinition<?> getSingleChildOrThrow() {
if (getValidChildNames().size() != 1) {
throw new IllegalStateException("This child has " + getValidChildNames().size() + " children, expected 1. This is a HAPI bug. Found: " + getValidChildNames());
}
return getChildByName(getValidChildNames().iterator().next());
}
public boolean isModifier() {
return myModifier;
}
protected void setModifier(boolean theModifier) {
myModifier = theModifier;
}
@Override
public boolean isSummary() {
return mySummary;
}
void setBindingValueSet(String theBindingValueSet) {
myBindingValueSet = theBindingValueSet;
}
protected void setModifier(boolean theModifier) {
myModifier = theModifier;
}
private final class FieldListAccessor implements IAccessor {
@SuppressWarnings("unchecked")
@Override
public List<IBase> getValues(Object theTarget) {
List<IBase> retVal;
try {
retVal = (List<IBase>) myField.get(theTarget);
} catch (Exception e) {
throw new ConfigurationException("Failed to get value", e);
}
public List<IBase> getValues(IBase theTarget) {
List<IBase> retVal = (List<IBase>) getFieldValue(theTarget, myField);
if (retVal == null) {
retVal = Collections.emptyList();
}
return retVal;
}
}
protected final class FieldListMutator implements IMutator {
@Override
public void addValue(Object theTarget, IBase theValue) {
public void addValue(IBase theTarget, IBase theValue) {
addValue(theTarget, theValue, false);
}
private void addValue(Object theTarget, IBase theValue, boolean theClear) {
try {
@SuppressWarnings("unchecked")
List<IBase> existingList = (List<IBase>) myField.get(theTarget);
if (existingList == null) {
existingList = new ArrayList<IBase>(2);
myField.set(theTarget, existingList);
}
if (theClear) {
existingList.clear();
}
existingList.add(theValue);
} catch (Exception e) {
throw new ConfigurationException("Failed to set value", e);
private void addValue(IBase theTarget, IBase theValue, boolean theClear) {
@SuppressWarnings("unchecked")
List<IBase> existingList = (List<IBase>) getFieldValue(theTarget, myField);
if (existingList == null) {
existingList = new ArrayList<>(2);
setFieldValue(theTarget, existingList, myField);
}
if (theClear) {
existingList.clear();
}
existingList.add(theValue);
}
@Override
public void setValue(Object theTarget, IBase theValue) {
public void setValue(IBase theTarget, IBase theValue) {
addValue(theTarget, theValue, true);
}
}
private final class FieldPlainAccessor implements IAccessor {
@Override
public List<IBase> getValues(Object theTarget) {
try {
Object values = myField.get(theTarget);
if (values == null) {
return Collections.emptyList();
}
List<IBase> retVal = Collections.singletonList((IBase) values);
return retVal;
} catch (Exception e) {
throw new ConfigurationException("Failed to get value", e);
public List<IBase> getValues(IBase theTarget) {
Object values = getFieldValue(theTarget, myField);
if (values == null) {
return Collections.emptyList();
}
return Collections.singletonList((IBase) values);
}
@Override
public IBase getFirstValueOrNull(IBase theTarget) {
return (IBase) getFieldValue(theTarget, myField);
}
}
protected final class FieldPlainMutator implements IMutator {
@Override
public void addValue(Object theTarget, IBase theValue) {
try {
myField.set(theTarget, theValue);
} catch (Exception e) {
throw new ConfigurationException("Failed to set value", e);
}
public void addValue(IBase theTarget, IBase theValue) {
setFieldValue(theTarget, theValue, myField);
}
@Override
public void setValue(Object theTarget, IBase theValue) {
public void setValue(IBase theTarget, IBase theValue) {
addValue(theTarget, theValue);
}
}
private static void setFieldValue(IBase theTarget, Object theValue, Field theField) {
try {
theField.set(theTarget, theValue);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to set value", e);
}
}
private static Object getFieldValue(IBase theTarget, Field theField) {
try {
return theField.get(theTarget);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to get value", e);
}
}
}

View File

@ -68,7 +68,7 @@ public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildD
public IAccessor getAccessor() {
return new IAccessor() {
@Override
public List<IBase> getValues(Object theTarget) {
public List<IBase> getValues(IBase theTarget) {
ExtensionDt target = (ExtensionDt) theTarget;
if (target.getValue() != null) {
return Collections.singletonList((IBase) target.getValue());
@ -76,6 +76,7 @@ public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildD
ArrayList<IBase> retVal = new ArrayList<IBase>(target.getUndeclaredExtensions());
return retVal;
}
};
}
@ -113,7 +114,7 @@ public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildD
public IMutator getMutator() {
return new IMutator() {
@Override
public void addValue(Object theTarget, IBase theValue) {
public void addValue(IBase theTarget, IBase theValue) {
ExtensionDt target = (ExtensionDt) theTarget;
if (theValue instanceof IDatatype) {
target.setValue((IDatatype) theTarget);
@ -123,7 +124,7 @@ public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildD
}
@Override
public void setValue(Object theTarget, IBase theValue) {
public void setValue(IBase theTarget, IBase theValue) {
ExtensionDt target = (ExtensionDt) theTarget;
if (theValue instanceof IDatatype) {
target.setValue((IDatatype) theTarget);

View File

@ -19,17 +19,6 @@ package ca.uhn.fhir.parser;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import java.util.*;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
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.api.*;
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition.IMutator;
@ -37,21 +26,32 @@ import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.base.resource.ResourceMetadataMap;
import ca.uhn.fhir.model.primitive.*;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.parser.json.JsonLikeValue.ScalarType;
import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
import ca.uhn.fhir.util.*;
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.api.*;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.*;
class ParserState<T> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ParserState.class);
private List<String> myComments = new ArrayList<String>(2);
private final FhirContext myContext;
private final IParserErrorHandler myErrorHandler;
private final boolean myJsonMode;
private T myObject;
private final IParser myParser;
private List<String> myComments = new ArrayList<String>(2);
private T myObject;
private IBase myPreviousElement;
private BaseState myState;
@ -152,38 +152,6 @@ class ParserState<T> {
}
}
/**
* @param theResourceType
* May be null
*/
static <T extends IBaseResource> ParserState<T> getPreResourceInstance(IParser theParser, Class<T> theResourceType, FhirContext theContext, boolean theJsonMode, IParserErrorHandler theErrorHandler)
throws DataFormatException {
ParserState<T> retVal = new ParserState<T>(theParser, theContext, theJsonMode, theErrorHandler);
if (theResourceType == null) {
if (theContext.getVersion().getVersion().isRi()) {
retVal.push(retVal.new PreResourceStateHl7Org(theResourceType));
} else {
retVal.push(retVal.new PreResourceStateHapi(theResourceType));
}
} else {
if (IResource.class.isAssignableFrom(theResourceType)) {
retVal.push(retVal.new PreResourceStateHapi(theResourceType));
} else {
retVal.push(retVal.new PreResourceStateHl7Org(theResourceType));
}
}
return retVal;
}
static ParserState<TagList> getPreTagListInstance(IParser theParser, FhirContext theContext, boolean theJsonMode, IParserErrorHandler theErrorHandler) {
ParserState<TagList> retVal = new ParserState<TagList>(theParser, theContext, theJsonMode, theErrorHandler);
retVal.push(retVal.new PreTagListState());
return retVal;
}
private abstract class BaseState {
private PreResourceState myPreResourceState;
@ -195,8 +163,7 @@ class ParserState<T> {
}
/**
* @param theValue
* The attribute value
* @param theValue The attribute value
*/
public void attributeValue(String theName, String theValue) throws DataFormatException {
myErrorHandler.unknownAttribute(null, theName);
@ -211,8 +178,7 @@ class ParserState<T> {
}
/**
* @param theNamespaceUri
* The XML namespace (if XML) or null
* @param theNamespaceUri The XML namespace (if XML) or null
*/
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
myErrorHandler.unknownElement(null, theLocalPart);
@ -275,8 +241,7 @@ class ParserState<T> {
}
/**
* @param theData
* The string value
* @param theData The string value
*/
public void string(String theData) {
// ignore by default
@ -287,8 +252,7 @@ class ParserState<T> {
}
/**
* @param theNextEvent
* The XML event
* @param theNextEvent The XML event
*/
public void xmlEvent(XMLEvent theNextEvent) {
// ignore
@ -414,30 +378,30 @@ class ParserState<T> {
}
switch (target.getChildType()) {
case COMPOSITE_DATATYPE: {
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
ICompositeType newChildInstance = (ICompositeType) compositeTarget.newInstance(myDefinition.getInstanceConstructorArguments());
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
ElementCompositeState newState = new ElementCompositeState(myPreResourceState, theLocalPart, compositeTarget, newChildInstance);
push(newState);
return;
}
case ID_DATATYPE:
case PRIMITIVE_DATATYPE: {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance = primitiveTarget.newInstance(myDefinition.getInstanceConstructorArguments());
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
push(newState);
return;
}
case PRIMITIVE_XHTML:
case RESOURCE:
case RESOURCE_BLOCK:
case UNDECL_EXT:
case EXTENSION_DECLARED:
default:
break;
case COMPOSITE_DATATYPE: {
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
ICompositeType newChildInstance = (ICompositeType) compositeTarget.newInstance(myDefinition.getInstanceConstructorArguments());
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
ElementCompositeState newState = new ElementCompositeState(myPreResourceState, theLocalPart, compositeTarget, newChildInstance);
push(newState);
return;
}
case ID_DATATYPE:
case PRIMITIVE_DATATYPE: {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance = primitiveTarget.newInstance(myDefinition.getInstanceConstructorArguments());
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
push(newState);
return;
}
case PRIMITIVE_XHTML:
case RESOURCE:
case RESOURCE_BLOCK:
case UNDECL_EXT:
case EXTENSION_DECLARED:
default:
break;
}
}
@ -545,81 +509,81 @@ class ParserState<T> {
}
switch (target.getChildType()) {
case COMPOSITE_DATATYPE: {
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
ICompositeType newChildInstance = (ICompositeType) compositeTarget.newInstance(child.getInstanceConstructorArguments());
child.getMutator().addValue(myInstance, newChildInstance);
ParserState<T>.ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), theChildName, compositeTarget, newChildInstance);
push(newState);
return;
}
case ID_DATATYPE:
case PRIMITIVE_DATATYPE: {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance;
newChildInstance = primitiveTarget.newInstance(child.getInstanceConstructorArguments());
child.getMutator().addValue(myInstance, newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
push(newState);
return;
}
case RESOURCE_BLOCK: {
RuntimeResourceBlockDefinition blockTarget = (RuntimeResourceBlockDefinition) target;
IBase newBlockInstance = blockTarget.newInstance();
child.getMutator().addValue(myInstance, newBlockInstance);
ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), theChildName, blockTarget, newBlockInstance);
push(newState);
return;
}
case PRIMITIVE_XHTML: {
RuntimePrimitiveDatatypeNarrativeDefinition xhtmlTarget = (RuntimePrimitiveDatatypeNarrativeDefinition) target;
XhtmlDt newDt = xhtmlTarget.newInstance();
child.getMutator().addValue(myInstance, newDt);
XhtmlState state = new XhtmlState(getPreResourceState(), newDt, true);
push(state);
return;
}
case PRIMITIVE_XHTML_HL7ORG: {
RuntimePrimitiveDatatypeXhtmlHl7OrgDefinition xhtmlTarget = (RuntimePrimitiveDatatypeXhtmlHl7OrgDefinition) target;
IBaseXhtml newDt = xhtmlTarget.newInstance();
child.getMutator().addValue(myInstance, newDt);
XhtmlStateHl7Org state = new XhtmlStateHl7Org(getPreResourceState(), newDt);
push(state);
return;
}
case CONTAINED_RESOURCES: {
List<? extends IBase> values = child.getAccessor().getValues(myInstance);
Object newDt;
if (values == null || values.isEmpty() || values.get(0) == null) {
newDt = newContainedDt((IResource) getPreResourceState().myInstance);
child.getMutator().addValue(myInstance, (IBase) newDt);
} else {
newDt = values.get(0);
case COMPOSITE_DATATYPE: {
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
ICompositeType newChildInstance = (ICompositeType) compositeTarget.newInstance(child.getInstanceConstructorArguments());
child.getMutator().addValue(myInstance, newChildInstance);
ParserState<T>.ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), theChildName, compositeTarget, newChildInstance);
push(newState);
return;
}
ContainedResourcesStateHapi state = new ContainedResourcesStateHapi(getPreResourceState());
push(state);
return;
}
case CONTAINED_RESOURCE_LIST: {
ContainedResourcesStateHl7Org state = new ContainedResourcesStateHl7Org(getPreResourceState());
push(state);
return;
}
case RESOURCE: {
if (myInstance instanceof IAnyResource || myInstance instanceof IBaseBackboneElement || myInstance instanceof IBaseElement) {
ParserState<T>.PreResourceStateHl7Org state = new PreResourceStateHl7Org(myInstance, child.getMutator(), null);
push(state);
} else {
ParserState<T>.PreResourceStateHapi state = new PreResourceStateHapi(myInstance, child.getMutator(), null);
push(state);
case ID_DATATYPE:
case PRIMITIVE_DATATYPE: {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance;
newChildInstance = primitiveTarget.newInstance(child.getInstanceConstructorArguments());
child.getMutator().addValue(myInstance, newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
push(newState);
return;
}
case RESOURCE_BLOCK: {
RuntimeResourceBlockDefinition blockTarget = (RuntimeResourceBlockDefinition) target;
IBase newBlockInstance = blockTarget.newInstance();
child.getMutator().addValue(myInstance, newBlockInstance);
ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), theChildName, blockTarget, newBlockInstance);
push(newState);
return;
}
case PRIMITIVE_XHTML: {
RuntimePrimitiveDatatypeNarrativeDefinition xhtmlTarget = (RuntimePrimitiveDatatypeNarrativeDefinition) target;
XhtmlDt newDt = xhtmlTarget.newInstance();
child.getMutator().addValue(myInstance, newDt);
XhtmlState state = new XhtmlState(getPreResourceState(), newDt, true);
push(state);
return;
}
case PRIMITIVE_XHTML_HL7ORG: {
RuntimePrimitiveDatatypeXhtmlHl7OrgDefinition xhtmlTarget = (RuntimePrimitiveDatatypeXhtmlHl7OrgDefinition) target;
IBaseXhtml newDt = xhtmlTarget.newInstance();
child.getMutator().addValue(myInstance, newDt);
XhtmlStateHl7Org state = new XhtmlStateHl7Org(getPreResourceState(), newDt);
push(state);
return;
}
case CONTAINED_RESOURCES: {
List<? extends IBase> values = child.getAccessor().getValues(myInstance);
Object newDt;
if (values == null || values.isEmpty() || values.get(0) == null) {
newDt = newContainedDt((IResource) getPreResourceState().myInstance);
child.getMutator().addValue(myInstance, (IBase) newDt);
} else {
newDt = values.get(0);
}
ContainedResourcesStateHapi state = new ContainedResourcesStateHapi(getPreResourceState());
push(state);
return;
}
case CONTAINED_RESOURCE_LIST: {
ContainedResourcesStateHl7Org state = new ContainedResourcesStateHl7Org(getPreResourceState());
push(state);
return;
}
case RESOURCE: {
if (myInstance instanceof IAnyResource || myInstance instanceof IBaseBackboneElement || myInstance instanceof IBaseElement) {
ParserState<T>.PreResourceStateHl7Org state = new PreResourceStateHl7Org(myInstance, child.getMutator(), null);
push(state);
} else {
ParserState<T>.PreResourceStateHapi state = new PreResourceStateHapi(myInstance, child.getMutator(), null);
push(state);
}
return;
}
case UNDECL_EXT:
case EXTENSION_DECLARED: {
// Throw an exception because this shouldn't happen here
break;
}
return;
}
case UNDECL_EXT:
case EXTENSION_DECLARED: {
// Throw an exception because this shouldn't happen here
break;
}
}
throw new DataFormatException("Illegal resource position: " + target.getChildType());
@ -716,32 +680,32 @@ class ParserState<T> {
if (target != null) {
switch (target.getChildType()) {
case COMPOSITE_DATATYPE: {
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
ICompositeType newChildInstance = (ICompositeType) compositeTarget.newInstance();
myExtension.setValue(newChildInstance);
ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), theLocalPart, compositeTarget, newChildInstance);
push(newState);
return;
}
case ID_DATATYPE:
case PRIMITIVE_DATATYPE: {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance = primitiveTarget.newInstance();
myExtension.setValue(newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
push(newState);
return;
}
case CONTAINED_RESOURCES:
case CONTAINED_RESOURCE_LIST:
case EXTENSION_DECLARED:
case PRIMITIVE_XHTML:
case PRIMITIVE_XHTML_HL7ORG:
case RESOURCE:
case RESOURCE_BLOCK:
case UNDECL_EXT:
break;
case COMPOSITE_DATATYPE: {
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
ICompositeType newChildInstance = (ICompositeType) compositeTarget.newInstance();
myExtension.setValue(newChildInstance);
ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), theLocalPart, compositeTarget, newChildInstance);
push(newState);
return;
}
case ID_DATATYPE:
case PRIMITIVE_DATATYPE: {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance = primitiveTarget.newInstance();
myExtension.setValue(newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
push(newState);
return;
}
case CONTAINED_RESOURCES:
case CONTAINED_RESOURCE_LIST:
case EXTENSION_DECLARED:
case PRIMITIVE_XHTML:
case PRIMITIVE_XHTML_HL7ORG:
case RESOURCE:
case RESOURCE_BLOCK:
case UNDECL_EXT:
break;
}
}
@ -890,7 +854,6 @@ class ParserState<T> {
}
private abstract class PreResourceState extends BaseState {
private Map<String, IBaseResource> myContainedResources;
@ -1008,14 +971,14 @@ class ParserState<T> {
if (wantedProfileType != null && !wantedProfileType.equals(myInstance.getClass())) {
if (myResourceType == null || myResourceType.isAssignableFrom(wantedProfileType)) {
ourLog.debug("Converting resource of type {} to type defined for profile \"{}\": {}", new Object[] { myInstance.getClass().getName(), usedProfile, wantedProfileType });
ourLog.debug("Converting resource of type {} to type defined for profile \"{}\": {}", new Object[]{myInstance.getClass().getName(), usedProfile, wantedProfileType});
/*
* This isn't the most efficient thing really.. If we want a specific
* type we just re-parse into that type. The problem is that we don't know
* until we've parsed the resource which type we want to use because the
* profile declarations are in the text of the resource itself.
*
*
* At some point it would be good to write code which can present a view
* of one type backed by another type and use that.
*/
@ -1094,7 +1057,7 @@ class ParserState<T> {
@Override
public void acceptElement(IBaseResource theResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition,
BaseRuntimeElementDefinition<?> theDefinition) {
BaseRuntimeElementDefinition<?> theDefinition) {
if (theElement instanceof BaseResourceReferenceDt) {
BaseResourceReferenceDt nextRef = (BaseResourceReferenceDt) theElement;
String ref = nextRef.getReference().getValue();
@ -1137,7 +1100,7 @@ class ParserState<T> {
private class PreResourceStateHapi extends PreResourceState {
private IMutator myMutator;
private Object myTarget;
private IBase myTarget;
public PreResourceStateHapi(Class<? extends IBaseResource> theResourceType) {
@ -1145,7 +1108,7 @@ class ParserState<T> {
assert theResourceType == null || IResource.class.isAssignableFrom(theResourceType);
}
public PreResourceStateHapi(Object theTarget, IMutator theMutator, Class<? extends IBaseResource> theResourceType) {
public PreResourceStateHapi(IBase theTarget, IMutator theMutator, Class<? extends IBaseResource> theResourceType) {
super(theResourceType);
myTarget = theTarget;
myMutator = theMutator;
@ -1175,18 +1138,18 @@ class ParserState<T> {
String resourceName = myContext.getResourceDefinition(nextResource).getName();
String bundleIdPart = nextResource.getId().getIdPart();
if (isNotBlank(bundleIdPart)) {
// if (isNotBlank(entryBaseUrl)) {
// nextResource.setId(new IdDt(entryBaseUrl, resourceName, bundleIdPart, version));
// } else {
IdDt previousId = nextResource.getId();
nextResource.setId(new IdDt(null, resourceName, bundleIdPart, version));
// Copy extensions
if (!previousId.getAllUndeclaredExtensions().isEmpty()) {
for (final ExtensionDt ext : previousId.getAllUndeclaredExtensions()) {
nextResource.getId().addUndeclaredExtension(ext);
}
}
// }
// if (isNotBlank(entryBaseUrl)) {
// nextResource.setId(new IdDt(entryBaseUrl, resourceName, bundleIdPart, version));
// } else {
IdDt previousId = nextResource.getId();
nextResource.setId(new IdDt(null, resourceName, bundleIdPart, version));
// Copy extensions
if (!previousId.getAllUndeclaredExtensions().isEmpty()) {
for (final ExtensionDt ext : previousId.getAllUndeclaredExtensions()) {
nextResource.getId().addUndeclaredExtension(ext);
}
}
// }
}
}
@ -1195,13 +1158,13 @@ class ParserState<T> {
private class PreResourceStateHl7Org extends PreResourceState {
private IMutator myMutator;
private Object myTarget;
private IBase myTarget;
public PreResourceStateHl7Org(Class<? extends IBaseResource> theResourceType) {
super(theResourceType);
}
public PreResourceStateHl7Org(Object theTarget, IMutator theMutator, Class<? extends IBaseResource> theResourceType) {
public PreResourceStateHl7Org(IBase theTarget, IMutator theMutator, Class<? extends IBaseResource> theResourceType) {
super(theResourceType);
myMutator = theMutator;
myTarget = theTarget;
@ -1463,21 +1426,21 @@ class ParserState<T> {
String value = defaultIfBlank(theValue, null);
switch (mySubState) {
case TERM:
myTerm = (value);
break;
case LABEL:
myLabel = (value);
break;
case SCHEME:
myScheme = (value);
break;
case NONE:
// This handles JSON encoding, which is a bit weird
enteringNewElement(null, theName);
attributeValue(null, value);
endingElement();
break;
case TERM:
myTerm = (value);
break;
case LABEL:
myLabel = (value);
break;
case SCHEME:
myScheme = (value);
break;
case NONE:
// This handles JSON encoding, which is a bit weird
enteringNewElement(null, theName);
attributeValue(null, value);
endingElement();
break;
}
}
@ -1604,4 +1567,32 @@ class ParserState<T> {
}
/**
* @param theResourceType May be null
*/
static <T extends IBaseResource> ParserState<T> getPreResourceInstance(IParser theParser, Class<T> theResourceType, FhirContext theContext, boolean theJsonMode, IParserErrorHandler theErrorHandler)
throws DataFormatException {
ParserState<T> retVal = new ParserState<T>(theParser, theContext, theJsonMode, theErrorHandler);
if (theResourceType == null) {
if (theContext.getVersion().getVersion().isRi()) {
retVal.push(retVal.new PreResourceStateHl7Org(theResourceType));
} else {
retVal.push(retVal.new PreResourceStateHapi(theResourceType));
}
} else {
if (IResource.class.isAssignableFrom(theResourceType)) {
retVal.push(retVal.new PreResourceStateHapi(theResourceType));
} else {
retVal.push(retVal.new PreResourceStateHl7Org(theResourceType));
}
}
return retVal;
}
static ParserState<TagList> getPreTagListInstance(IParser theParser, FhirContext theContext, boolean theJsonMode, IParserErrorHandler theErrorHandler) {
ParserState<TagList> retVal = new ParserState<TagList>(theParser, theContext, theJsonMode, theErrorHandler);
retVal.push(retVal.new PreTagListState());
return retVal;
}
}

View File

@ -354,17 +354,10 @@ public class XmlParser extends BaseParser {
}
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
INarrative narr = (INarrative) nextChild.getAccessor().getFirstValueOrNull(theElement);
INarrativeGenerator gen = myContext.getNarrativeGenerator();
INarrative narr;
if (theResource instanceof IResource) {
narr = ((IResource) theResource).getText();
} else if (theResource instanceof IDomainResource) {
narr = ((IDomainResource) theResource).getText();
} else {
narr = null;
}
// FIXME potential null access on narr see line 623
if (gen != null && narr.isEmpty()) {
if (gen != null && (narr == null || narr.isEmpty())) {
gen.populateResourceNarrative(myContext, theResource);
}
if (narr != null && narr.isEmpty() == false) {

View File

@ -216,12 +216,12 @@ public class FhirTerser {
}
public Object getSingleValueOrNull(IBase theTarget, String thePath) {
Class<Object> wantedType = Object.class;
Class<IBase> wantedType = IBase.class;
return getSingleValueOrNull(theTarget, thePath, wantedType);
}
public <T> T getSingleValueOrNull(IBase theTarget, String thePath, Class<T> theWantedType) {
public <T extends IBase> 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");
@ -241,12 +241,12 @@ public class FhirTerser {
return retVal.get(0);
}
private <T> List<T> getValues(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, Object theCurrentObj, List<String> theSubList, Class<T> theWantedClass) {
private <T extends IBase> List<T> getValues(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, IBase theCurrentObj, List<String> theSubList, Class<T> theWantedClass) {
return getValues(theCurrentDef, theCurrentObj, theSubList, theWantedClass, false, false);
}
@SuppressWarnings("unchecked")
private <T> List<T> getValues(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, Object theCurrentObj, List<String> theSubList, Class<T> theWantedClass, boolean theCreate, boolean theAddExtension) {
private <T extends IBase> List<T> getValues(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, IBase theCurrentObj, List<String> theSubList, Class<T> theWantedClass, boolean theCreate, boolean theAddExtension) {
String name = theSubList.get(0);
List<T> retVal = new ArrayList<>();
@ -325,7 +325,7 @@ public class FhirTerser {
List<T> values = retVal;
retVal = new ArrayList<>();
for (T nextElement : values) {
BaseRuntimeElementCompositeDefinition<?> nextChildDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition((Class<? extends IBase>) nextElement.getClass());
BaseRuntimeElementCompositeDefinition<?> nextChildDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(nextElement.getClass());
List<T> foundValues = getValues(nextChildDef, nextElement, theSubList.subList(1, theSubList.size()), theWantedClass, theCreate, theAddExtension);
retVal.addAll(foundValues);
}
@ -410,7 +410,7 @@ public class FhirTerser {
List<T> values = retVal;
retVal = new ArrayList<>();
for (T nextElement : values) {
BaseRuntimeElementCompositeDefinition<?> nextChildDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition((Class<? extends IBase>) nextElement.getClass());
BaseRuntimeElementCompositeDefinition<?> nextChildDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(nextElement.getClass());
List<T> foundValues = getValues(nextChildDef, nextElement, theSubList.subList(1, theSubList.size()), theWantedClass, theCreate, theAddExtension);
retVal.addAll(foundValues);
}
@ -476,9 +476,10 @@ public class FhirTerser {
* @return A list of values of type {@link Object}.
*/
public List<Object> getValues(IBaseResource theResource, String thePath) {
Class<Object> wantedClass = Object.class;
Class<IBase> wantedClass = IBase.class;
return getValues(theResource, thePath, wantedClass);
List values = getValues(theResource, thePath, wantedClass);
return values;
}
/**
@ -491,9 +492,10 @@ public class FhirTerser {
* @return A list of values of type {@link Object}.
*/
public List<Object> getValues(IBaseResource theResource, String thePath, boolean theCreate) {
Class<Object> wantedClass = Object.class;
Class<IBase> wantedClass = IBase.class;
return getValues(theResource, thePath, wantedClass, theCreate);
List retVal = getValues(theResource, thePath, wantedClass, theCreate);
return retVal;
}
/**
@ -507,9 +509,10 @@ public class FhirTerser {
* @return A list of values of type {@link Object}.
*/
public List<Object> getValues(IBaseResource theResource, String thePath, boolean theCreate, boolean theAddExtension) {
Class<Object> wantedClass = Object.class;
Class<IBase> wantedClass = IBase.class;
return getValues(theResource, thePath, wantedClass, theCreate, theAddExtension);
List retVal = getValues(theResource, thePath, wantedClass, theCreate, theAddExtension);
return retVal;
}
/**
@ -522,7 +525,7 @@ public class FhirTerser {
* @param <T> Type declared by <code>theWantedClass</code>
* @return A list of values of type <code>theWantedClass</code>.
*/
public <T> List<T> getValues(IBaseResource theResource, String thePath, Class<T> theWantedClass) {
public <T extends IBase> List<T> getValues(IBaseResource theResource, String thePath, Class<T> theWantedClass) {
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
List<String> parts = parsePath(def, thePath);
return getValues(def, theResource, parts, theWantedClass);
@ -539,7 +542,7 @@ public class FhirTerser {
* @param <T> Type declared by <code>theWantedClass</code>
* @return A list of values of type <code>theWantedClass</code>.
*/
public <T> List<T> getValues(IBaseResource theResource, String thePath, Class<T> theWantedClass, boolean theCreate) {
public <T extends IBase> List<T> getValues(IBaseResource theResource, String thePath, Class<T> theWantedClass, boolean theCreate) {
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
List<String> parts = parsePath(def, thePath);
return getValues(def, theResource, parts, theWantedClass, theCreate, false);
@ -557,7 +560,7 @@ public class FhirTerser {
* @param <T> Type declared by <code>theWantedClass</code>
* @return A list of values of type <code>theWantedClass</code>.
*/
public <T> List<T> getValues(IBaseResource theResource, String thePath, Class<T> theWantedClass, boolean theCreate, boolean theAddExtension) {
public <T extends IBase> List<T> getValues(IBaseResource theResource, String thePath, Class<T> theWantedClass, boolean theCreate, boolean theAddExtension) {
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
List<String> parts = parsePath(def, thePath);
return getValues(def, theResource, parts, theWantedClass, theCreate, theAddExtension);

View File

@ -319,6 +319,29 @@ public class JsonParserDstu3Test {
}
/**
* See #402
*/
@Test
public void testEncodeCompositionDoesntOverwriteNarrative() {
FhirContext ctx = FhirContext.forDstu3();
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
Composition composition = new Composition();
composition.getText().setDivAsString("<div>root</div>");
composition.addSection().getText().setDivAsString("<div>section0</div>");
composition.addSection().getText().setDivAsString("<div>section1</div>");
String output = ctx.newJsonParser().setPrettyPrint(true).encodeResourceToString(composition);
ourLog.info(output);
assertThat(output, containsString("<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">root</div>"));
assertThat(output, containsString("<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">section0</div>"));
assertThat(output, containsString("<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">section1</div>"));
}
@Test
public void testEncodeAndParseMetaProfileAndTags() {
Patient p = new Patient();

View File

@ -1070,6 +1070,28 @@ public class XmlParserDstu3Test {
assertEquals("grandparent", gp.getName());
}
/**
* See #402
*/
@Test
public void testEncodeCompositionDoesntOverwriteNarrative() {
FhirContext ctx = FhirContext.forDstu3();
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
Composition composition = new Composition();
composition.getText().setDivAsString("<div>root</div>");
composition.addSection().getText().setDivAsString("<div>section0</div>");
composition.addSection().getText().setDivAsString("<div>section1</div>");
String output = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(composition);
ourLog.info(output);
assertThat(output, containsString("<div xmlns=\"http://www.w3.org/1999/xhtml\">root</div>"));
assertThat(output, containsString("<div xmlns=\"http://www.w3.org/1999/xhtml\">section0</div>"));
assertThat(output, containsString("<div xmlns=\"http://www.w3.org/1999/xhtml\">section1</div>"));
}
/**
* See #326
*/

View File

@ -287,6 +287,10 @@
conditional creates can now be authorized even if they are happening inside a FHIR
transaction.
</action>
<action type="fix" issue="402">
When encoding a Composition resource in XML, the section narrative blocks were incorrectly
replaced by the main resource narrative. Thanks to Mirjam Baltus for reporting!
</action>
</release>
<release version="4.0.3" date="2019-09-03" description="Igloo (Point Release)">
<action type="fix">