Merge remote-tracking branch 'remotes/origin/master' into ks-20200120-near-search

This commit is contained in:
Ken Stevens 2020-01-22 15:06:52 -05:00
commit f2e8037c90
5 changed files with 143 additions and 85 deletions

View File

@ -22,16 +22,25 @@ package ca.uhn.fhir.parser;
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition.IMutator;
import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IIdentifiableElement;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.base.resource.ResourceMetadataMap;
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 ca.uhn.fhir.util.BundleUtil;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.ReflectionUtil;
import ca.uhn.fhir.util.XmlUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.tuple.Pair;
@ -39,9 +48,17 @@ import org.hl7.fhir.instance.model.api.*;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
class ParserState<T> {
@ -54,6 +71,8 @@ class ParserState<T> {
private T myObject;
private IBase myPreviousElement;
private BaseState myState;
private List<IBaseResource> myGlobalResources = new ArrayList<>();
private List<IBaseReference> myGlobalReferences = new ArrayList<>();
private ParserState(IParser theParser, FhirContext theContext, boolean theJsonMode, IParserErrorHandler theErrorHandler) {
myParser = theParser;
@ -130,7 +149,6 @@ class ParserState<T> {
}
}
public void string(String theData) {
myState.string(theData);
}
@ -145,6 +163,60 @@ class ParserState<T> {
}
}
public IBase newInstance(RuntimeChildDeclaredExtensionDefinition theDefinition) {
return theDefinition.newInstance();
}
public ICompositeType newCompositeInstance(PreResourceState thePreResourceState, BaseRuntimeChildDefinition theChild, BaseRuntimeElementCompositeDefinition<?> theCompositeTarget) {
ICompositeType retVal = (ICompositeType) theCompositeTarget.newInstance(theChild.getInstanceConstructorArguments());
if (retVal instanceof IBaseReference) {
IBaseReference ref = (IBaseReference) retVal;
myGlobalReferences.add(ref);
thePreResourceState.getLocalReferences().add(ref);
}
return retVal;
}
public ICompositeType newCompositeTypeInstance(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theCompositeTarget) {
ICompositeType retVal = (ICompositeType) theCompositeTarget.newInstance();
if (retVal instanceof IBaseReference) {
IBaseReference ref = (IBaseReference) retVal;
myGlobalReferences.add(ref);
thePreResourceState.getLocalReferences().add(ref);
}
return retVal;
}
public IPrimitiveType<?> newPrimitiveInstance(RuntimeChildDeclaredExtensionDefinition theDefinition, RuntimePrimitiveDatatypeDefinition thePrimitiveTarget) {
return thePrimitiveTarget.newInstance(theDefinition.getInstanceConstructorArguments());
}
public IPrimitiveType<?> getPrimitiveInstance(BaseRuntimeChildDefinition theChild, RuntimePrimitiveDatatypeDefinition thePrimitiveTarget) {
return thePrimitiveTarget.newInstance(theChild.getInstanceConstructorArguments());
}
public IBaseXhtml newInstance(RuntimePrimitiveDatatypeXhtmlHl7OrgDefinition theXhtmlTarget) {
return theXhtmlTarget.newInstance();
}
public XhtmlDt newInstance(RuntimePrimitiveDatatypeNarrativeDefinition theXhtmlTarget) {
return theXhtmlTarget.newInstance();
}
public IPrimitiveType<?> newInstance(RuntimePrimitiveDatatypeDefinition thePrimitiveTarget) {
return thePrimitiveTarget.newInstance();
}
public IBaseResource newInstance(RuntimeResourceDefinition theDef) {
IBaseResource retVal = theDef.newInstance();
myGlobalResources.add(retVal);
return retVal;
}
public IBase newInstance(RuntimeResourceBlockDefinition theBlockTarget) {
return theBlockTarget.newInstance();
}
private abstract class BaseState {
private PreResourceState myPreResourceState;
@ -256,7 +328,7 @@ class ParserState<T> {
private class ContainedResourcesStateHapi extends PreResourceState {
public ContainedResourcesStateHapi(PreResourceState thePreResourcesState) {
super(thePreResourcesState, ((IResource) thePreResourcesState.myInstance).getStructureFhirVersionEnum());
super(thePreResourcesState, thePreResourcesState.myInstance.getStructureFhirVersionEnum());
}
@Override
@ -290,7 +362,6 @@ class ParserState<T> {
@SuppressWarnings("unchecked")
List<IResource> containedResources = (List<IResource>) preResCurrentElement.getContained().getContainedResources();
containedResources.add(res);
}
}
@ -374,7 +445,7 @@ class ParserState<T> {
switch (target.getChildType()) {
case COMPOSITE_DATATYPE: {
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
ICompositeType newChildInstance = (ICompositeType) compositeTarget.newInstance(myDefinition.getInstanceConstructorArguments());
ICompositeType newChildInstance = newCompositeInstance(getPreResourceState(), myDefinition, compositeTarget);
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
ElementCompositeState newState = new ElementCompositeState(myPreResourceState, theLocalPart, compositeTarget, newChildInstance);
push(newState);
@ -383,7 +454,7 @@ class ParserState<T> {
case ID_DATATYPE:
case PRIMITIVE_DATATYPE: {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance = primitiveTarget.newInstance(myDefinition.getInstanceConstructorArguments());
IPrimitiveType<?> newChildInstance = newPrimitiveInstance(myDefinition, primitiveTarget);
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
push(newState);
@ -404,7 +475,7 @@ class ParserState<T> {
RuntimeChildDeclaredExtensionDefinition declaredExtension = myDefinition.getChildExtensionForUrl(theUrlAttr);
if (declaredExtension != null) {
if (myChildInstance == null) {
myChildInstance = myDefinition.newInstance();
myChildInstance = newInstance(myDefinition);
myDefinition.getMutator().addValue(myParentInstance, myChildInstance);
}
BaseState newState = new DeclaredExtensionState(getPreResourceState(), declaredExtension, myChildInstance);
@ -414,6 +485,7 @@ class ParserState<T> {
}
}
@Override
protected IBase getCurrentElement() {
return myParentInstance;
@ -501,7 +573,7 @@ class ParserState<T> {
switch (target.getChildType()) {
case COMPOSITE_DATATYPE: {
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
ICompositeType newChildInstance = (ICompositeType) compositeTarget.newInstance(child.getInstanceConstructorArguments());
ICompositeType newChildInstance = newCompositeInstance(getPreResourceState(), child, compositeTarget);
child.getMutator().addValue(myInstance, newChildInstance);
ParserState<T>.ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), theChildName, compositeTarget, newChildInstance);
push(newState);
@ -511,7 +583,7 @@ class ParserState<T> {
case PRIMITIVE_DATATYPE: {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance;
newChildInstance = primitiveTarget.newInstance(child.getInstanceConstructorArguments());
newChildInstance = getPrimitiveInstance(child, primitiveTarget);
child.getMutator().addValue(myInstance, newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
push(newState);
@ -519,7 +591,7 @@ class ParserState<T> {
}
case RESOURCE_BLOCK: {
RuntimeResourceBlockDefinition blockTarget = (RuntimeResourceBlockDefinition) target;
IBase newBlockInstance = blockTarget.newInstance();
IBase newBlockInstance = newInstance(blockTarget);
child.getMutator().addValue(myInstance, newBlockInstance);
ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), theChildName, blockTarget, newBlockInstance);
push(newState);
@ -527,7 +599,7 @@ class ParserState<T> {
}
case PRIMITIVE_XHTML: {
RuntimePrimitiveDatatypeNarrativeDefinition xhtmlTarget = (RuntimePrimitiveDatatypeNarrativeDefinition) target;
XhtmlDt newDt = xhtmlTarget.newInstance();
XhtmlDt newDt = newInstance(xhtmlTarget);
child.getMutator().addValue(myInstance, newDt);
XhtmlState state = new XhtmlState(getPreResourceState(), newDt, true);
push(state);
@ -535,7 +607,7 @@ class ParserState<T> {
}
case PRIMITIVE_XHTML_HL7ORG: {
RuntimePrimitiveDatatypeXhtmlHl7OrgDefinition xhtmlTarget = (RuntimePrimitiveDatatypeXhtmlHl7OrgDefinition) target;
IBaseXhtml newDt = xhtmlTarget.newInstance();
IBaseXhtml newDt = newInstance(xhtmlTarget);
child.getMutator().addValue(myInstance, newDt);
XhtmlStateHl7Org state = new XhtmlStateHl7Org(getPreResourceState(), newDt);
push(state);
@ -669,7 +741,7 @@ class ParserState<T> {
switch (target.getChildType()) {
case COMPOSITE_DATATYPE: {
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
ICompositeType newChildInstance = (ICompositeType) compositeTarget.newInstance();
ICompositeType newChildInstance = newCompositeTypeInstance(getPreResourceState(), compositeTarget);
myExtension.setValue(newChildInstance);
ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), theLocalPart, compositeTarget, newChildInstance);
push(newState);
@ -678,7 +750,7 @@ class ParserState<T> {
case ID_DATATYPE:
case PRIMITIVE_DATATYPE: {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance = primitiveTarget.newInstance();
IPrimitiveType<?> newChildInstance = newInstance(primitiveTarget);
myExtension.setValue(newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
push(newState);
@ -847,10 +919,10 @@ class ParserState<T> {
private abstract class PreResourceState extends BaseState {
private Map<String, IBaseResource> myContainedResources;
private List<IBaseReference> myLocalReferences = new ArrayList<>();
private IBaseResource myInstance;
private FhirVersionEnum myParentVersion;
private Class<? extends IBaseResource> myResourceType;
PreResourceState(Class<? extends IBaseResource> theResourceType) {
super(null);
myResourceType = theResourceType;
@ -869,6 +941,10 @@ class ParserState<T> {
myContainedResources = thePreResourcesState.getContainedResources();
}
public List<IBaseReference> getLocalReferences() {
return myLocalReferences;
}
@Override
public void endingElement() throws DataFormatException {
stitchBundleCrossReferences();
@ -905,10 +981,10 @@ class ParserState<T> {
if (!definition.getName().equals(theLocalPart) && definition.getName().equalsIgnoreCase(theLocalPart)) {
throw new DataFormatException("Unknown resource type '" + theLocalPart + "': Resource names are case sensitive, found similar name: '" + definition.getName() + "'");
}
myInstance = def.newInstance();
myInstance = newInstance(def);
if (myInstance instanceof IResource) {
push(new ResourceStateHapi(getRootPreResourceState(), def, (IResource) myInstance));
push(new ResourceStateHapi(getRootPreResourceState(), def, (IResource) myInstance, myContainedResources));
} else {
push(new ResourceStateHl7Org(getRootPreResourceState(), def, myInstance));
}
@ -996,8 +1072,7 @@ class ParserState<T> {
/*
* Stitch together resource references
*/
List<IBaseResource> resources = t.getAllPopulatedChildElementsOfType(myInstance, IBaseResource.class);
for (IBaseResource next : resources) {
for (IBaseResource next : myGlobalResources) {
IIdType id = next.getIdElement();
if (id != null && id.isEmpty() == false) {
String resName = myContext.getResourceDefinition(next).getName();
@ -1006,15 +1081,12 @@ class ParserState<T> {
}
}
for (IBaseResource next : resources) {
List<IBaseReference> refs = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class);
for (IBaseReference nextRef : refs) {
if (nextRef.isEmpty() == false && nextRef.getReferenceElement() != null) {
IIdType unqualifiedVersionless = nextRef.getReferenceElement().toUnqualifiedVersionless();
IBaseResource target = idToResource.get(unqualifiedVersionless.getValueAsString());
if (target != null) {
nextRef.setResource(target);
}
for (IBaseReference nextRef : myGlobalReferences) {
if (nextRef.isEmpty() == false && nextRef.getReferenceElement() != null) {
IIdType unqualifiedVersionless = nextRef.getReferenceElement().toUnqualifiedVersionless();
IBaseResource target = idToResource.get(unqualifiedVersionless.getValueAsString());
if (target != null) {
nextRef.setResource(target);
}
}
}
@ -1035,43 +1107,21 @@ class ParserState<T> {
}
void weaveContainedResources() {
FhirTerser terser = myContext.newTerser();
terser.visit(myInstance, new IModelVisitor() {
@Override
public void acceptElement(IBaseResource theResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition,
BaseRuntimeElementDefinition<?> theDefinition) {
if (theElement instanceof BaseResourceReferenceDt) {
BaseResourceReferenceDt nextRef = (BaseResourceReferenceDt) theElement;
String ref = nextRef.getReference().getValue();
if (isNotBlank(ref)) {
if (ref.startsWith("#")) {
IResource target = (IResource) myContainedResources.get(ref);
if (target != null) {
ourLog.debug("Resource contains local ref {} in field {}", ref, thePathToElement);
nextRef.setResource(target);
} else {
myErrorHandler.unknownReference(null, ref);
}
}
}
} else if (theElement instanceof IBaseReference) {
IBaseReference nextRef = (IBaseReference) theElement;
String ref = nextRef.getReferenceElement().getValue();
if (isNotBlank(ref)) {
if (ref.startsWith("#")) {
IBaseResource target = myContainedResources.get(ref);
if (target != null) {
ourLog.debug("Resource contains local ref {} in field {}", ref, thePathToElement);
nextRef.setResource(target);
} else {
myErrorHandler.unknownReference(null, ref);
}
}
for (IBaseReference nextRef : myLocalReferences) {
String ref = nextRef.getReferenceElement().getValue();
if (isNotBlank(ref)) {
if (ref.startsWith("#")) {
IBaseResource target = myContainedResources.get(ref);
if (target != null) {
ourLog.debug("Resource contains local ref {}", ref);
nextRef.setResource(target);
} else {
myErrorHandler.unknownReference(null, ref);
}
}
}
});
}
}
@Override
@ -1284,7 +1334,7 @@ class ParserState<T> {
private IResource myInstance;
public ResourceStateHapi(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, IResource theInstance) {
public ResourceStateHapi(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, IResource theInstance, Map<String, IBaseResource> theContainedResources) {
super(thePreResourceState, theDef.getName(), theDef, theInstance);
myInstance = theInstance;
}

View File

@ -181,9 +181,6 @@ public class XmlParser extends BaseParser {
heldComments.clear();
}
parserState.endingElement();
// if (parserState.isComplete()) {
// return parserState.getObject();
// }
break;
}
case XMLStreamConstants.CHARACTERS: {
@ -707,12 +704,12 @@ public class XmlParser extends BaseParser {
theEventWriter.writeStartElement(prefix, se.getName().getLocalPart(), namespaceURI);
theEventWriter.writeNamespace(prefix, namespaceURI);
}
for (Iterator<Attribute> iter= se.getAttributes(); iter.hasNext(); ) {
Attribute next = iter.next();
if ("lang".equals(next.getName().getLocalPart())) {
theEventWriter.writeAttribute("", "", next.getName().getLocalPart(), next.getValue());
}
}
// for (Iterator<Attribute> iter= se.getAttributes(); iter.hasNext(); ) {
// Attribute next = iter.next();
// if ("lang".equals(next.getName().getLocalPart())) {
// theEventWriter.writeAttribute("", "", next.getName().getLocalPart(), next.getValue());
// }
// }
firstElement = false;
} else {
if (isBlank(se.getName().getPrefix())) {
@ -729,10 +726,10 @@ public class XmlParser extends BaseParser {
} else {
theEventWriter.writeStartElement(se.getName().getPrefix(), se.getName().getLocalPart(), se.getName().getNamespaceURI());
}
for (Iterator<?> attrIter = se.getAttributes(); attrIter.hasNext(); ) {
Attribute next = (Attribute) attrIter.next();
theEventWriter.writeAttribute(next.getName().getLocalPart(), next.getValue());
}
}
for (Iterator<?> attrIter = se.getAttributes(); attrIter.hasNext(); ) {
Attribute next = (Attribute) attrIter.next();
theEventWriter.writeAttribute(next.getName().getLocalPart(), next.getValue());
}
break;
case XMLStreamConstants.DTD:

View File

@ -160,7 +160,7 @@ public class FhirTerser {
public <T extends IBase> List<T> getAllPopulatedChildElementsOfType(IBaseResource theResource, final Class<T> theType) {
final ArrayList<T> retVal = new ArrayList<>();
BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
visit(new IdentityHashMap<>(), theResource, theResource, null, null, def, new IModelVisitor() {
visit(newMap(), theResource, theResource, null, null, def, new IModelVisitor() {
@SuppressWarnings("unchecked")
@Override
public void acceptElement(IBaseResource theOuterResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) {
@ -179,7 +179,7 @@ public class FhirTerser {
public List<ResourceReferenceInfo> getAllResourceReferences(final IBaseResource theResource) {
final ArrayList<ResourceReferenceInfo> retVal = new ArrayList<>();
BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
visit(new IdentityHashMap<>(), theResource, theResource, null, null, def, new IModelVisitor() {
visit(newMap(), theResource, theResource, null, null, def, new IModelVisitor() {
@Override
public void acceptElement(IBaseResource theOuterResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) {
if (theElement == null || theElement.isEmpty()) {
@ -793,7 +793,11 @@ public class FhirTerser {
*/
public void visit(IBaseResource theResource, IModelVisitor theVisitor) {
BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
visit(new IdentityHashMap<>(), theResource, theResource, null, null, def, theVisitor);
visit(newMap(), theResource, theResource, null, null, def, theVisitor);
}
public Map<Object, Object> newMap() {
return new IdentityHashMap<>();
}
/**
@ -814,7 +818,7 @@ public class FhirTerser {
visit(theResource, null, def, theVisitor, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
}
private void visit(IdentityHashMap<Object, Object> theStack, IBaseResource theResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition,
private void visit(Map<Object, Object> theStack, IBaseResource theResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition,
BaseRuntimeElementDefinition<?> theDefinition, IModelVisitor theCallback) {
List<String> pathToElement = addNameToList(thePathToElement, theChildDefinition);

View File

@ -5,6 +5,7 @@
(dependent HAPI modules listed in brackets):
<ul>
<li>Jetty (CLI): 9.4.14.v20181114 -&gt; 9.4.23.v20191118</li>
<li>Spring (JPA, Testpage): 5.2.1 -&gt; 5.2.3 (addresses CVE-2020-5398 and CVE-2020-5397)</li>
</ul>"
- item:
issue: "1583"
@ -64,6 +65,12 @@
issue: "1649"
type: "add"
title: "Support for LOINC 2.67 file format changes has been added to the JPA Server LOINC uploader. Thanks to Dan Vreeman for reporting!"
- item:
issue: "1660"
type: "perf"
title: "A significant performance improvement was added to the Json and XML parsers when parsing large Bundle
resources. Throughput for parsing these resources has been improved by roughly 50%. Thanks to Rok Bertoncelj
and Bogdan Solga for providing analysis and insight that triggered this change."
- item:
issue: "1658"
type: "fix"

View File

@ -655,7 +655,7 @@
<servicemix_saxon_version>9.5.1-5_1</servicemix_saxon_version>
<servicemix_xmlresolver_version>1.2_5</servicemix_xmlresolver_version>
<slf4j_version>1.7.28</slf4j_version>
<spring_version>5.2.1.RELEASE</spring_version>
<spring_version>5.2.3.RELEASE</spring_version>
<!-- FYI: Spring Data JPA 2.1.9 causes test failures due to unexpected cascading deletes -->
<spring_data_version>2.2.0.RELEASE</spring_data_version>
<spring_boot_version>2.2.0.RELEASE</spring_boot_version>