Performance improvements for IG Publisher

This commit is contained in:
Grahame Grieve 2023-09-15 10:13:53 -07:00
parent a59a9619d9
commit d40006ba8c
4 changed files with 116 additions and 72 deletions

View File

@ -911,8 +911,10 @@ public class ProfileUtilities extends TranslatingUtilities {
derived.clearUserData("profileutils.snapshot.generating");
snapshotStack.remove(derived.getUrl());
}
derived.setUserData("profileutils.snapshot.generated", true); // used by the publisher
}
private XVerExtensionManager makeXVer() {
if (xver == null) {
xver = new XVerExtensionManager(context);
@ -4478,4 +4480,14 @@ public class ProfileUtilities extends TranslatingUtilities {
ProfileUtilities.suppressIgnorableExceptions = suppressIgnorableExceptions;
}
public void setMessages(List<ValidationMessage> messages) {
this.messages = messages;
}
private Map<String, List<Property>> propertyCache = new HashMap<>();
public Map<String, List<Property>> getCachedPropertyList() {
return propertyCache;
}
}

View File

@ -86,17 +86,20 @@ public class JsonParser extends ParserBase {
private ProfileUtilities profileUtilities;
private Element baseElement;
private ContextUtilities contextUtilities;
public JsonParser(IWorkerContext context, ProfileUtilities utilities) {
super(context);
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 {
@ -118,6 +121,7 @@ public class JsonParser extends ParserBase {
@Override
public List<NamedElement> parse(InputStream inStream) throws IOException, FHIRException {
// long start = System.currentTimeMillis();
byte[] content = TextFile.streamToBytes(inStream);
NamedElement ctxt = new NamedElement("focus", "json", content);
@ -126,6 +130,7 @@ public class JsonParser extends ParserBase {
// if we're parsing at this point, then we're going to use the custom parser
String source = TextFile.streamToString(stream);
JsonObject obj = null;
if (policy == ValidationPolicy.EVERYTHING) {
try {
obj = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(source, true, true);
@ -135,10 +140,13 @@ public class JsonParser extends ParserBase {
} else {
obj = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(source, true, true);
}
ctxt.setElement(parse(ctxt.getErrors(), obj));
List<NamedElement> res = new ArrayList<>();
res.add(ctxt);
// long t=System.currentTimeMillis()-start;
// System.out.println("json parser: "+(t)+": "+content.length+(t == 0 ? "" : " @ "+(content.length / t)));
return res;
}
@ -172,7 +180,7 @@ public class JsonParser extends ParserBase {
baseElement.markLocation(line(object), col(object));
baseElement.setType(name);
baseElement.setPath(baseElement.fhirTypeRoot());
parseChildren(errors, path, object, baseElement, true);
parseChildren(errors, path, object, baseElement, true, null);
baseElement.numberChildren();
return baseElement;
}
@ -200,13 +208,58 @@ public class JsonParser extends ParserBase {
}
}
private void parseChildren(List<ValidationMessage> errors, String path, JsonObject object, Element element, boolean hasResourceType) throws FHIRException {
reapComments(object, element);
List<Property> properties = element.getProperty().getChildProperties(element.getName(), null);
Set<String> processed = new HashSet<String>();
if (hasResourceType) {
processed.add("resourceType");
private List<Property> parseChildren(List<ValidationMessage> errors, String path, JsonObject object, Element element, boolean hasResourceType, List<Property> properties) throws FHIRException {
if (properties == null) {
// save time refetching these if we're in a loop
properties = element.getProperty().getChildProperties(element.getName(), null);
}
Map<String, JsonProperty> recognisedChildren = processChildren(errors, path, object);
// note that we do not trouble ourselves to maintain the wire format order here
// first pass: process the properties
for (Property property : properties) {
parseChildItem(errors, path, recognisedChildren, element, property);
}
// second pass: check for things not processed
checkNotProcessed(errors, path, element, hasResourceType, recognisedChildren);
if (object.isExtraComma()) {
logError(errors, "2022-11-26", object.getEnd().getLine(), object.getEnd().getCol(), path, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_COMMA_EXTRA, "Object"), IssueSeverity.ERROR);
}
return properties;
}
private void checkNotProcessed(List<ValidationMessage> errors, String path, Element element, boolean hasResourceType, Map<String, JsonProperty> recognisedChildren) {
if (policy != ValidationPolicy.NONE) {
for (Entry<String, JsonProperty> e : recognisedChildren.entrySet()) {
if (e.getValue().getTag() == 0) {
StructureDefinition sd = element.getProperty().isLogical() ? contextUtilities.fetchByJsonName(e.getKey()) : null;
if (sd != null) {
Property property = new Property(context, sd.getSnapshot().getElementFirstRep(), sd, element.getProperty().getUtils());
parseChildItem(errors, path, recognisedChildren, element, property);
} else if ("fhir_comments".equals(e.getKey()) && (VersionUtilities.isR2BVer(context.getVersion()) || VersionUtilities.isR2Ver(context.getVersion()))) {
if (!e.getValue().getValue().isJsonArray()) {
logError(errors, "2022-12-17", line(e.getValue().getValue()), col(e.getValue().getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.ILLEGAL_COMMENT_TYPE, e.getValue().getValue().type().toName()), IssueSeverity.ERROR);
} else {
for (JsonElement c : e.getValue().getValue().asJsonArray()) {
if (!c.isJsonString()) {
logError(errors, "2022-12-17", line(e.getValue().getValue()), col(e.getValue().getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.ILLEGAL_COMMENT_TYPE, c.type().toName()), IssueSeverity.ERROR);
} else {
element.getComments().add(c.asString());
}
}
}
} else if (hasResourceType && "resourceType".equals(e.getKey())) {
// nothing
} else {
logError(errors, ValidationMessage.NO_RULE_DATE, line(e.getValue().getValue()), col(e.getValue().getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_, e.getKey()), IssueSeverity.ERROR);
}
}
}
}
}
private Map<String, JsonProperty> processChildren(List<ValidationMessage> errors, String path, JsonObject object) {
Map<String, JsonProperty> recognisedChildren = new HashMap<>();
Set<String> unique = new HashSet<>();
for (JsonProperty p : object.getProperties()) {
@ -223,57 +276,22 @@ public class JsonParser extends ParserBase {
recognisedChildren.put(p.getName(), p);
}
}
// note that we do not trouble ourselves to maintain the wire format order here - we don't even know what it was anyway
// first pass: process the properties
for (Property property : properties) {
parseChildItem(errors, path, recognisedChildren, element, processed, property);
}
// second pass: check for things not processed
if (policy != ValidationPolicy.NONE) {
for (Entry<String, JsonProperty> e : recognisedChildren.entrySet()) {
if (!processed.contains(e.getKey())) {
StructureDefinition sd = element.getProperty().isLogical() ? new ContextUtilities(context).fetchByJsonName(e.getKey()) : null;
if (sd != null) {
Property property = new Property(context, sd.getSnapshot().getElementFirstRep(), sd, element.getProperty().getUtils());
parseChildItem(errors, path, recognisedChildren, element, null, property);
} else if ("fhir_comments".equals(e.getKey()) && (VersionUtilities.isR2BVer(context.getVersion()) || VersionUtilities.isR2Ver(context.getVersion()))) {
if (!e.getValue().getValue().isJsonArray()) {
logError(errors, "2022-12-17", line(e.getValue().getValue()), col(e.getValue().getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.ILLEGAL_COMMENT_TYPE, e.getValue().getValue().type().toName()), IssueSeverity.ERROR);
} else {
for (JsonElement c : e.getValue().getValue().asJsonArray()) {
if (!c.isJsonString()) {
logError(errors, "2022-12-17", line(e.getValue().getValue()), col(e.getValue().getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.ILLEGAL_COMMENT_TYPE, c.type().toName()), IssueSeverity.ERROR);
} else {
element.getComments().add(c.asString());
}
}
}
} else {
logError(errors, ValidationMessage.NO_RULE_DATE, line(e.getValue().getValue()), col(e.getValue().getValue()), path, IssueType.STRUCTURE, context.formatMessage(I18nConstants.UNRECOGNISED_PROPERTY_, e.getKey()), IssueSeverity.ERROR);
}
}
}
}
if (object.isExtraComma()) {
logError(errors, "2022-11-26", object.getEnd().getLine(), object.getEnd().getCol(), path, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_COMMA_EXTRA, "Object"), IssueSeverity.ERROR);
}
return recognisedChildren;
}
public void parseChildItem(List<ValidationMessage> errors, String path, Map<String, JsonProperty> children, Element context, Set<String> processed, Property property) {
public void parseChildItem(List<ValidationMessage> errors, String path, Map<String, JsonProperty> children, Element context, Property property) {
if (property.isChoice() || property.getDefinition().getPath().endsWith("data[x]")) {
if (property.isJsonPrimitiveChoice()) {
if (children.containsKey(property.getJsonName())) {
JsonElement je = children.get(property.getJsonName()).getValue();
if (processed != null) processed.add(property.getJsonName());
JsonProperty jp = children.get(property.getJsonName());
if (jp != null) {
jp.setTag(1);
JsonElement je = jp.getValue();
String type = getTypeFromJsonType(je);
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);
parseChildPrimitive(errors, children, context, processed, np, path, property.getName(), false);
parseChildPrimitive(errors, 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);
}
@ -282,18 +300,18 @@ public class JsonParser extends ParserBase {
for (TypeRefComponent type : property.getDefinition().getType()) {
String eName = property.getJsonName().substring(0, property.getName().length()-3) + Utilities.capitalize(type.getWorkingCode());
if (!isPrimitive(type.getWorkingCode()) && children.containsKey(eName)) {
parseChildComplex(errors, path, children, context, processed, property, eName, false);
parseChildComplex(errors, path, children, context, property, eName, false);
break;
} else if (isPrimitive(type.getWorkingCode()) && (children.containsKey(eName) || children.containsKey("_"+eName))) {
parseChildPrimitive(errors, children, context, processed, property, path, eName, false);
parseChildPrimitive(errors, children, context, property, path, eName, false);
break;
}
}
}
} else if (property.isPrimitive(property.getType(null))) {
parseChildPrimitive(errors, children, context, processed, property, path, property.getJsonName(), property.hasJsonName());
parseChildPrimitive(errors, children, context, property, path, property.getJsonName(), property.hasJsonName());
} else if (children.containsKey(property.getJsonName())) {
parseChildComplex(errors, path, children, context, processed, property, property.getJsonName(), property.hasJsonName());
parseChildComplex(errors, path, children, context, property, property.getJsonName(), property.hasJsonName());
}
}
@ -317,13 +335,11 @@ public class JsonParser extends ParserBase {
}
}
private void parseChildComplex(List<ValidationMessage> errors, String path, Map<String, JsonProperty> children, Element element, Set<String> processed, Property property, String name, boolean isJsonName) throws FHIRException {
if (processed != null) {
processed.add(name);
}
private void parseChildComplex(List<ValidationMessage> errors, String path, Map<String, JsonProperty> children, Element element, Property property, String name, boolean isJsonName) throws FHIRException {
String npath = path+"."+property.getName();
String fpath = element.getPath()+"."+property.getName();
JsonProperty p = children.get(name);
if (p != null) { p.setTag(1); }
JsonElement e = p == null ? null : p.getValue();
if (property.isList() && !property.isJsonKeyArray() && (e instanceof JsonArray)) {
JsonArray arr = (JsonArray) e;
@ -338,8 +354,9 @@ public class JsonParser extends ParserBase {
}
}
int c = 0;
List<Property> properties = null;
for (JsonElement am : arr) {
parseChildComplexInstance(errors, npath+"["+c+"]", fpath+"["+c+"]", element, property, name, am, c == 0 ? arr : null, path);
properties = parseChildComplexInstance(errors, npath+"["+c+"]", fpath+"["+c+"]", element, property, name, am, c == 0 ? arr : null, path, properties);
c++;
}
} else if (property.isJsonKeyArray()) {
@ -373,6 +390,7 @@ public class JsonParser extends ParserBase {
logError(errors, "2022-11-26", line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.DUPLICATE_JSON_PROPERTY_KEY, pv.getName()), IssueSeverity.ERROR);
} else {
names.add(pv.getName());
pv.setTag(1);
}
// create an array entry
String npathArr = path+"."+property.getName()+"["+i+"]";
@ -411,7 +429,7 @@ public class JsonParser extends ParserBase {
if (propV.isPrimitive(pvl.getType(null))) {
parseChildPrimitiveInstance(errors, n, pvl, pvl.getName(), false, npathV, fpathV, pv.getValue(), null);
} else if (pv.getValue() instanceof JsonObject || pv.getValue() instanceof JsonNull) {
parseChildComplexInstance(errors, npathV, fpathV, n, pvl, pvl.getName(), pv.getValue(), null, null);
parseChildComplexInstance(errors, npathV, fpathV, n, pvl, pvl.getName(), pv.getValue(), null, null, null);
} else {
logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_OBJECT_NOT_, describe(pv.getValue())), IssueSeverity.ERROR);
}
@ -424,7 +442,7 @@ public class JsonParser extends ParserBase {
if (property.isList()) {
logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE_AN_ARRAY_NOT_, describe(e), name, path), IssueSeverity.ERROR);
}
parseChildComplexInstance(errors, npath, fpath, element, property, name, e, null, null);
parseChildComplexInstance(errors, npath, fpath, element, property, name, e, null, null, null);
}
}
@ -436,7 +454,7 @@ public class JsonParser extends ParserBase {
return b.toString();
}
private void parseChildComplexInstance(List<ValidationMessage> errors, String npath, String fpath, Element element, Property property, String name, JsonElement e, JsonElement commentContext, String commentPath) throws FHIRException {
private List<Property> parseChildComplexInstance(List<ValidationMessage> errors, String npath, String fpath, Element element, Property property, String name, JsonElement e, JsonElement commentContext, String commentPath, List<Property> properties) throws FHIRException {
if (property.hasTypeSpecifier()) {
FHIRPathEngine fpe = new FHIRPathEngine(context);
String type = null;
@ -477,7 +495,7 @@ public class JsonParser extends ParserBase {
if (property.isResource()) {
parseResource(errors, npath, child, n, property);
} else {
parseChildren(errors, npath, child, n, false);
return parseChildren(errors, npath, child, n, false, properties);
}
} else if (property.isNullable() && e instanceof JsonNull) {
// we create an element marked as a null element so we know something was present
@ -492,6 +510,7 @@ public class JsonParser extends ParserBase {
} else {
logError(errors, ValidationMessage.NO_RULE_DATE, line(e), col(e), npath, IssueType.INVALID, context.formatMessage(I18nConstants.THIS_PROPERTY_MUST_BE__NOT_, (property.isList() ? "an Array" : "an Object"), describe(e), name, npath), IssueSeverity.ERROR);
}
return null;
}
private String describe(JsonElement e) {
@ -514,13 +533,14 @@ public class JsonParser extends ParserBase {
return e.type().toName();
}
private void parseChildPrimitive(List<ValidationMessage> errors, Map<String, JsonProperty> children, Element element, Set<String> processed, Property property, String path, String name, boolean isJsonName) throws FHIRException {
private void parseChildPrimitive(List<ValidationMessage> errors, Map<String, JsonProperty> children, Element element, Property property, String path, String name, boolean isJsonName) throws FHIRException {
String npath = path+"."+property.getName();
String fpath = element.getPath()+"."+property.getName();
processed.add(name);
processed.add("_"+name);
JsonProperty main = children.containsKey(name) ? children.get(name) : null;
JsonProperty fork = children.containsKey("_"+name) ? children.get("_"+name) : null;
if (main != null) { main.setTag(1); }
if (fork != null) { fork.setTag(1); }
if (main != null && main.getValue().isJsonString() && main.isUnquotedValue()) {
logError(errors, "2022-11-26", line(main.getValue()), col(main.getValue()), path, IssueType.INVALID, context.formatMessage(I18nConstants.JSON_PROPERTY_VALUE_NO_QUOTES, main.getName(), main.getValue().asString()), IssueSeverity.ERROR);
}
@ -617,7 +637,7 @@ public class JsonParser extends ParserBase {
if (fork != null) {
JsonObject child = (JsonObject) fork;
checkObject(errors, child, n, npath);
parseChildren(errors, npath, child, n, false);
parseChildren(errors, npath, child, n, false, null);
}
}
}
@ -637,7 +657,7 @@ public class JsonParser extends ParserBase {
} else {
parent.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, this.profileUtilities), SpecialElement.fromProperty(parent.getProperty()), elementProperty);
parent.setType(name);
parseChildren(errors, npath, res, parent, true);
parseChildren(errors, npath, res, parent, true, null);
}
}
if (res.isExtraComma()) {
@ -646,10 +666,6 @@ public class JsonParser extends ParserBase {
}
private void reapComments(JsonObject object, Element context) {
// todo
}
private int line(JsonElement e) {
return e.getStart().getLine();
}

View File

@ -341,6 +341,11 @@ public class Property {
public List<Property> getChildProperties(String elementName, String statedType) throws FHIRException {
String cacheKey = structure.getVUrl()+"#"+definition.getPath()+":"+elementName+"/"+statedType;
List<Property> cached = profileUtilities.getCachedPropertyList().get(cacheKey);
if (cached != null) {
return cached;
}
ElementDefinition ed = definition;
StructureDefinition sd = structure;
SourcedChildDefinitions children = profileUtilities.getChildMap(sd, ed);
@ -415,6 +420,7 @@ public class Property {
for (ElementDefinition child : children.getList()) {
properties.add(new Property(context, child, sd, this.profileUtilities));
}
profileUtilities.getCachedPropertyList().put(cacheKey, properties);
return properties;
}

View File

@ -8,6 +8,8 @@ public class JsonProperty {
boolean unquotedName;
boolean unquotedValue;
private int tag;
public JsonProperty(String name, JsonElement value) {
super();
this.name = name;
@ -54,5 +56,13 @@ public class JsonProperty {
public String toString() {
return "\""+name+"\" : "+value.toString();
}
public int getTag() {
return tag;
}
public void setTag(int tag) {
this.tag = tag;
}
}