performance improvement for element model parsing - thanks Brian Postlethwaite

This commit is contained in:
Grahame Grieve 2024-02-22 17:52:49 +11:00
parent 4fdaee16a5
commit d5972caeb1
10 changed files with 86 additions and 42 deletions

View File

@ -47,6 +47,7 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
private List<StructureDefinition> allStructuresList = new ArrayList<StructureDefinition>();
private List<String> canonicalResourceNames;
private List<String> concreteResourceNames;
private Set<String> concreteResourceNameSet;
public ContextUtilities(IWorkerContext context) {
super();
@ -317,6 +318,9 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
@Override
public boolean isResource(String t) {
if (getConcreteResourceSet().contains(t)) {
return true;
}
StructureDefinition sd;
try {
sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+t);
@ -370,16 +374,22 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
return null;
}
public List<String> getConcreteResources() {
if (concreteResourceNames == null) {
concreteResourceNames = new ArrayList<>();
public Set<String> getConcreteResourceSet() {
if (concreteResourceNameSet == null) {
concreteResourceNameSet = new HashSet<>();
Set<String> names = new HashSet<>();
for (StructureDefinition sd : allStructures()) {
if (sd.getKind() == StructureDefinitionKind.RESOURCE && !sd.getAbstract()) {
names.add(sd.getType());
}
}
concreteResourceNames.addAll(Utilities.sorted(names));
}
return concreteResourceNameSet;
}
public List<String> getConcreteResources() {
if (concreteResourceNames == null) {
concreteResourceNames.addAll(Utilities.sorted(concreteResourceNameSet));
}
return concreteResourceNames;
}

View File

@ -514,7 +514,7 @@ public class Element extends Base implements NamedItem {
}
private boolean isDataType(Base v) {
return v instanceof DataType && new ContextUtilities(property.getContext()).getTypeNames().contains(v.fhirType());
return v instanceof DataType && property.getContextUtils().getTypeNames().contains(v.fhirType());
}
@Override

View File

@ -134,7 +134,7 @@ public class FmlParser extends ParserBase {
lexer.token("conceptmap");
Element map = structureMap.makeElement("contained");
StructureDefinition sd = context.fetchTypeDefinition("ConceptMap");
map.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd), SpecialElement.fromProperty(map.getElementProperty() != null ? map.getElementProperty() : map.getProperty()), map.getProperty());
map.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities()), SpecialElement.fromProperty(map.getElementProperty() != null ? map.getElementProperty() : map.getProperty()), map.getProperty());
map.setType("ConceptMap");
Element eid = map.makeElement("id").markLocation(lexer.getCurrentLocation());
String id = lexer.readConstant("map id");
@ -225,6 +225,8 @@ public class FmlParser extends ParserBase {
String token = lexer.take();
if (token.equals("-"))
return ConceptMapRelationship.RELATEDTO;
if (token.equals("=")) // temporary
return ConceptMapRelationship.RELATEDTO;
if (token.equals("=="))
return ConceptMapRelationship.EQUIVALENT;
if (token.equals("!="))

View File

@ -86,22 +86,15 @@ public class JsonParser extends ParserBase {
private JsonCreator json;
private boolean allowComments;
private ProfileUtilities profileUtilities;
private Element baseElement;
private ContextUtilities contextUtilities;
public JsonParser(IWorkerContext context, ProfileUtilities utilities) {
super(context);
super(context, utilities);
this.profileUtilities = utilities;
contextUtilities = new ContextUtilities(context);
}
public JsonParser(IWorkerContext context) {
super(context);
this.profileUtilities = new ProfileUtilities(this.context, null, null, new FHIRPathEngine(context));
contextUtilities = new ContextUtilities(context);
}
public Element parse(String source, String type) throws Exception {
@ -128,7 +121,7 @@ public class JsonParser extends ParserBase {
nEd.addType().setCode(type);
nEd.setMax(obj.getProperties().get(0).getValue().isJsonArray() ? "*" : "1");
}
Element result = new Element(type, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities)).setFormat(FhirFormat.JSON);
Element result = new Element(type, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.getProfileUtilities(), this.getContextUtilities())).setFormat(FhirFormat.JSON);
result.setPath(type);
checkObject(focusFragment.getErrors(), obj, result, path);
result.setType(type);
@ -199,7 +192,7 @@ public class JsonParser extends ParserBase {
name = sd.getType();
path = sd.getTypeTail();
}
baseElement = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities)).setFormat(FhirFormat.JSON);
baseElement = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd, this.getProfileUtilities(), this.getContextUtilities())).setFormat(FhirFormat.JSON);
checkObject(errors, object, baseElement, path);
baseElement.markLocation(line(object), col(object));
baseElement.setType(name);
@ -259,9 +252,9 @@ public class JsonParser extends ParserBase {
if (policy != ValidationPolicy.NONE) {
for (JsonProperty e : children) {
if (e.getTag() == 0) {
StructureDefinition sd = element.getProperty().isLogical() ? contextUtilities.fetchByJsonName(e.getName()) : null;
StructureDefinition sd = element.getProperty().isLogical() ? getContextUtilities().fetchByJsonName(e.getName()) : null;
if (sd != null) {
Property property = new Property(context, sd.getSnapshot().getElementFirstRep(), sd, element.getProperty().getUtils());
Property property = new Property(context, sd.getSnapshot().getElementFirstRep(), sd, element.getProperty().getUtils(), element.getProperty().getContextUtils());
parseChildItem(errors, path, children, element, property);
} else if ("fhir_comments".equals(e.getName()) && (VersionUtilities.isR2BVer(context.getVersion()) || VersionUtilities.isR2Ver(context.getVersion()))) {
if (!e.getValue().isJsonArray()) {
@ -341,7 +334,7 @@ public class JsonParser extends ParserBase {
if (type == null) {
logError(errors, ValidationMessage.NO_RULE_DATE, line(je), col(je), path, IssueType.STRUCTURE, this.context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_TYPE, describeType(je), property.getName(), property.typeSummary()), IssueSeverity.ERROR);
} else if (property.hasType(type)) {
Property np = new Property(property.getContext(), property.getDefinition(), property.getStructure(), property.getUtils(), type);
Property np = new Property(property.getContext(), property.getDefinition(), property.getStructure(), property.getUtils(), property.getContextUtils(), type);
parseChildPrimitive(errors, jp, getJsonPropertyByName("_"+property.getJsonName(), children), context, np, path, property.getName(), false);
} else {
logError(errors, ValidationMessage.NO_RULE_DATE, line(je), col(je), path, IssueType.STRUCTURE, this.context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_TYPE_WRONG, describeType(je), property.getName(), type, property.typeSummary()), IssueSeverity.ERROR);
@ -474,7 +467,7 @@ public class JsonParser extends ParserBase {
if (type == null) {
logError(errors, ValidationMessage.NO_RULE_DATE, line(pv.getValue()), col(pv.getValue()), path, IssueType.STRUCTURE, this.context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_TYPE, describeType(pv.getValue()), propV.getName(), propV.typeSummary()), IssueSeverity.ERROR);
} else if (propV.hasType(type)) {
pvl = new Property(propV.getContext(), propV.getDefinition(), propV.getStructure(), propV.getUtils(), type);
pvl = new Property(propV.getContext(), propV.getDefinition(), propV.getStructure(), propV.getUtils(), propV.getContextUtils(), type);
ok = true;
} else {
logError(errors, ValidationMessage.NO_RULE_DATE, line(pv.getValue()), col(pv.getValue()), path, IssueType.STRUCTURE, this.context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_TYPE_WRONG, describeType(pv.getValue()), propV.getName(), type, propV.typeSummary()), IssueSeverity.ERROR);
@ -713,7 +706,7 @@ public class JsonParser extends ParserBase {
if (sd == null) {
logError(errors, ValidationMessage.NO_RULE_DATE, line(res), col(res), npath, IssueType.INVALID, context.formatMessage(I18nConstants.CONTAINED_RESOURCE_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_, name), IssueSeverity.FATAL);
} else {
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities), SpecialElement.fromProperty(parent.getProperty()), elementProperty);
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, this.getProfileUtilities(), this.getContextUtilities()), SpecialElement.fromProperty(parent.getProperty()), elementProperty);
parent.setType(name);
parseChildren(errors, npath, res, parent, true, null);
}

View File

@ -39,8 +39,10 @@ import java.util.List;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
import org.hl7.fhir.r5.context.ContextUtilities;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.fhirpath.FHIRPathEngine;
import org.hl7.fhir.r5.formats.FormatUtilities;
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.model.StructureDefinition;
@ -89,14 +91,26 @@ public abstract class ParserBase {
protected IdRenderingPolicy idPolicy = IdRenderingPolicy.All;
protected StructureDefinition logical;
protected IDigitalSignatureServices signatureServices;
public ParserBase(IWorkerContext context) {
private ProfileUtilities profileUtilities;
private ContextUtilities contextUtilities;
public ParserBase(IWorkerContext context, ProfileUtilities utilities) {
super();
this.context = context;
this.profileUtilities = utilities;
contextUtilities = new ContextUtilities(context);
policy = ValidationPolicy.NONE;
}
public void setupValidation(ValidationPolicy policy) {
public ParserBase(IWorkerContext context) {
super();
this.context = context;
this.profileUtilities = new ProfileUtilities(context, null, null, new FHIRPathEngine(context));
contextUtilities = new ContextUtilities(context);
policy = ValidationPolicy.NONE;
}
public void setupValidation(ValidationPolicy policy) {
this.policy = policy;
}
@ -215,19 +229,19 @@ public abstract class ParserBase {
// first pass: only look at base definitions
for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
if (sd.getUrl().equals("http://hl7.org/fhir/StructureDefinition/"+name)) {
new ContextUtilities(context).generateSnapshot(sd);
contextUtilities.generateSnapshot(sd);
return sd;
}
}
for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
if (name.equals(sd.getTypeName()) && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
new ContextUtilities(context).generateSnapshot(sd);
contextUtilities.generateSnapshot(sd);
return sd;
}
}
for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
if (name.equals(sd.getUrl()) && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
new ContextUtilities(context).generateSnapshot(sd);
contextUtilities.generateSnapshot(sd);
return sd;
}
}
@ -304,4 +318,22 @@ public abstract class ParserBase {
return element.getNamedChildValue("reference");
}
}
public IWorkerContext getContext() {
return context;
}
public ValidationPolicy getPolicy() {
return policy;
}
public ProfileUtilities getProfileUtilities() {
return profileUtilities;
}
public ContextUtilities getContextUtilities() {
return contextUtilities;
}
}

View File

@ -38,6 +38,7 @@ import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.SourcedChildDefinitions;
import org.hl7.fhir.r5.context.ContextUtilities;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.fhirpath.TypeDetails;
import org.hl7.fhir.r5.formats.FormatUtilities;
@ -60,21 +61,24 @@ public class Property {
private ElementDefinition definition;
private StructureDefinition structure;
private ProfileUtilities profileUtilities;
private ContextUtilities utils;
private TypeRefComponent type;
public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure, ProfileUtilities profileUtilities) {
public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure, ProfileUtilities profileUtilities, ContextUtilities utils) {
this.context = context;
this.definition = definition;
this.structure = structure;
this.utils = utils;
this.profileUtilities = profileUtilities;
}
public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure, ProfileUtilities profileUtilities, String type) {
public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure, ProfileUtilities profileUtilities, ContextUtilities utils, String type) {
this.context = context;
this.definition = definition;
this.structure = structure;
this.profileUtilities = profileUtilities;
this.utils = utils;
for (TypeRefComponent tr : definition.getType()) {
if (tr.getWorkingCode().equals(type)) {
this.type = tr;
@ -83,7 +87,7 @@ public class Property {
}
public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure) {
this(context, definition, structure, new ProfileUtilities(context, null, null));
this(context, definition, structure, new ProfileUtilities(context, null, null), new ContextUtilities(context));
}
public String getName() {
@ -265,10 +269,10 @@ public class Property {
public boolean isResource() {
if (type != null) {
String tc = type.getCode();
return (("Resource".equals(tc) || "DomainResource".equals(tc)) || Utilities.existsInList(tc, context.getResourceNames()));
return (("Resource".equals(tc) || "DomainResource".equals(tc)) || utils.isResource(tc));
} else if (definition.getType().size() > 0) {
String tc = definition.getType().get(0).getCode();
return definition.getType().size() == 1 && (("Resource".equals(tc) || "DomainResource".equals(tc)) || Utilities.existsInList(tc, context.getResourceNames()));
return definition.getType().size() == 1 && (("Resource".equals(tc) || "DomainResource".equals(tc)) || utils.isResource(tc));
}
else {
return !definition.getPath().contains(".") && (structure.getKind() == StructureDefinitionKind.RESOURCE);
@ -425,7 +429,7 @@ public class Property {
}
List<Property> properties = new ArrayList<Property>();
for (ElementDefinition child : children.getList()) {
properties.add(new Property(context, child, sd, this.profileUtilities));
properties.add(new Property(context, child, sd, this.profileUtilities, this.utils));
}
profileUtilities.getCachedPropertyList().put(cacheKey, properties);
return properties;
@ -485,7 +489,7 @@ public class Property {
}
List<Property> properties = new ArrayList<Property>();
for (ElementDefinition child : children.getList()) {
properties.add(new Property(context, child, sd, this.profileUtilities));
properties.add(new Property(context, child, sd, this.profileUtilities, this.utils));
}
return properties;
}
@ -613,6 +617,9 @@ public class Property {
public ProfileUtilities getUtils() {
return profileUtilities;
}
public ContextUtilities getContextUtils() {
return utils;
}
public boolean isJsonPrimitiveChoice() {
return ToolingExtensions.readBoolExtension(definition, ToolingExtensions.EXT_JSON_PRIMITIVE_CHOICE);

View File

@ -50,7 +50,7 @@ public class ResourceParser extends ParserBase {
if (sd == null) {
throw new FHIRException("No definition exists for "+resource.fhirType());
}
Property p = new Property(context, sd.getSnapshot().getElement().get(0), sd, new ProfileUtilities(context, null, null));
Property p = new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities());
String path = resource.fhirType();
Element e = new Element(resource.fhirType(), p);
@ -106,7 +106,7 @@ public class ResourceParser extends ParserBase {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(v.fhirType(), null));
if (sd == null)
throw new FHIRFormatError(context.formatMessage(I18nConstants.CONTAINED_RESOURCE_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_, v.fhirType()));
n.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd), SpecialElement.fromProperty(n.getProperty()), p);
n.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities()), SpecialElement.fromProperty(n.getProperty()), p);
n.setType(v.fhirType());
parseChildren(npath, v, n);
} else {

View File

@ -151,7 +151,7 @@ public class TurtleParser extends ParserBase {
if (sd == null)
return null;
Element result = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd)).setFormat(FhirFormat.TURTLE);
Element result = new Element(name, new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities())).setFormat(FhirFormat.TURTLE);
result.markLocation(cmp.getLine(), cmp.getCol());
result.setType(name);
parseChildren(errors, src, path, cmp, result, false);
@ -279,7 +279,7 @@ public class TurtleParser extends ParserBase {
Element n = new Element(tail(name), property).markLocation(object.getLine(), object.getCol()).setFormat(FhirFormat.TURTLE);
element.getChildren().add(n);
n.updateProperty(new Property(this.context, sd.getSnapshot().getElement().get(0), sd), SpecialElement.fromProperty(n.getProperty()), property);
n.updateProperty(new Property(this.context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities()), SpecialElement.fromProperty(n.getProperty()), property);
n.setType(rt);
parseChildren(errors, src, npath, obj, n, false);
}

View File

@ -456,7 +456,7 @@ public class VerticalBarParser extends ParserBase {
@Override
public List<ValidatedFragment> parse(InputStream inStream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/v2/StructureDefinition/Message");
Element message = new Element("Message", new Property(context, sd.getSnapshot().getElementFirstRep(), sd));
Element message = new Element("Message", new Property(context, sd.getSnapshot().getElementFirstRep(), sd, getProfileUtilities(), getContextUtilities()));
byte[] content = TextFile.streamToBytes(inStream);
ByteArrayInputStream stream = new ByteArrayInputStream(content);
VerticalBarParserReader reader = new VerticalBarParserReader(new BufferedInputStream(stream), charset);

View File

@ -231,7 +231,7 @@ public class XmlParser extends ParserBase {
if (sd == null)
return null;
Element result = new Element(element.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)).setFormat(FhirFormat.XML);
Element result = new Element(element.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities())).setFormat(FhirFormat.XML);
result.setPath(element.getLocalName());
checkElement(errors, element, result, path, result.getProperty(), false);
result.markLocation(line(element, false), col(element, false));
@ -321,7 +321,7 @@ public class XmlParser extends ParserBase {
public Element parse(List<ValidationMessage> errors, org.w3c.dom.Element base, String type) throws Exception {
StructureDefinition sd = getDefinition(errors, 0, 0, FormatUtilities.FHIR_NS, type);
Element result = new Element(base.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd)).setFormat(FhirFormat.XML).setNativeObject(base);
Element result = new Element(base.getLocalName(), new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities())).setFormat(FhirFormat.XML).setNativeObject(base);
result.setPath(base.getLocalName());
String path = "/"+pathPrefix(base.getNamespaceURI())+base.getLocalName();
checkElement(errors, base, result, path, result.getProperty(), false);
@ -662,7 +662,7 @@ public class XmlParser extends ParserBase {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, null));
if (sd == null)
throw new FHIRFormatError(context.formatMessage(I18nConstants.CONTAINED_RESOURCE_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_, res.getLocalName()));
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd), SpecialElement.fromProperty(parent.getProperty()), elementProperty);
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities()), SpecialElement.fromProperty(parent.getProperty()), elementProperty);
parent.setType(name);
parseChildren(errors, res.getLocalName(), res, parent);
}