Performance improvements for type handling, and rename NamedElement to ValidatedFragment

This commit is contained in:
Grahame Grieve 2023-09-19 09:58:36 +10:00
parent 2b2ded9321
commit 46c12d1a22
30 changed files with 293 additions and 187 deletions

View File

@ -141,7 +141,7 @@ import com.google.gson.JsonObject;
import javax.annotation.Nonnull;
public abstract class BaseWorkerContext extends I18nBase implements IWorkerContext{
public abstract class BaseWorkerContext extends I18nBase implements IWorkerContext {
private static final boolean QA_CHECK_REFERENCE_SOURCE = false; // see comments below
@ -223,6 +223,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
private CanonicalResourceManager<ConceptMap> maps = new CanonicalResourceManager<ConceptMap>(false, minimalMemory);
protected CanonicalResourceManager<StructureMap> transforms = new CanonicalResourceManager<StructureMap>(false, minimalMemory);
private CanonicalResourceManager<StructureDefinition> structures = new CanonicalResourceManager<StructureDefinition>(false, minimalMemory);
private TypeManager typeManager = new TypeManager(structures);
private final CanonicalResourceManager<Measure> measures = new CanonicalResourceManager<Measure>(false, minimalMemory);
private final CanonicalResourceManager<Library> libraries = new CanonicalResourceManager<Library>(false, minimalMemory);
private CanonicalResourceManager<ImplementationGuide> guides = new CanonicalResourceManager<ImplementationGuide>(false, minimalMemory);
@ -279,6 +280,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
this.valueSets = valueSets;
this.maps = maps;
this.structures = profiles;
this.typeManager = new TypeManager(structures);
this.guides = guides;
clock = new TimeTracker();
}
@ -292,6 +294,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
maps.copy(other.maps);
transforms.copy(other.transforms);
structures.copy(other.structures);
typeManager = new TypeManager(structures);
searchParameters.copy(other.searchParameters);
plans.copy(other.plans);
questionnaires.copy(other.questionnaires);
@ -370,6 +373,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
fixOldSD(sd);
}
structures.register(r, packageInfo);
typeManager.see(r);
break;
case "ValueSet":
valueSets.register(r, packageInfo);
@ -462,6 +466,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
fixOldSD(sd);
}
structures.see(sd, packageInfo);
typeManager.see(sd);
} else if (r instanceof ValueSet) {
valueSets.see((ValueSet) m, packageInfo);
} else if (r instanceof CodeSystem) {
@ -2337,6 +2342,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (fhirType.equals("StructureDefinition")) {
structures.drop(id);
typeManager.reload();
} else if (fhirType.equals("ImplementationGuide")) {
guides.drop(id);
} else if (fhirType.equals("CapabilityStatement")) {
@ -2463,34 +2469,23 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (Utilities.isAbsoluteUrl(typeName)) {
return fetchResource(StructureDefinition.class, typeName);
} else {
Set<StructureDefinition> types = new HashSet<>();
types.addAll(fetchTypeDefinitions(typeName));
types.removeIf(sd -> sd.getDerivation() == TypeDerivationRule.CONSTRAINT);
if (types.size() == 0) {
return null; // throw new FHIRException("Unresolved type "+typeName+" (0)");
} else if (types.size() == 1) {
return types.iterator().next();
} else {
types.removeIf(sd -> !sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/"));
if (types.size() == 0) {
return null;
} else if (types.size() != 1) {
throw new FHIRException("Ambiguous type "+typeName+" ("+types.toString()+") (contact Grahame Grieve for investigation)");
} else {
return types.iterator().next();
}
}
return typeManager.fetchTypeDefinition(typeName);
}
}
@Override
public List<StructureDefinition> fetchTypeDefinitions(String typeName) {
List<StructureDefinition> res = new ArrayList<>();
structures.listAll(res);
res.removeIf(sd -> !sd.hasType() || !(sd.getType().equals(typeName) || sd.getTypeTail().equals(typeName)));
return res;
return typeManager.getDefinitions(typeName);
}
public boolean isPrimitiveType(String type) {
return typeManager.isPrimitive(type);
}
public boolean isDataType(String type) {
return typeManager.isDataType(type);
}
public boolean isTlogging() {
return tlogging;
}
@ -2627,6 +2622,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
maps.setVersion(version);
transforms.setVersion(version);
structures.setVersion(version);
typeManager.reload();
measures.setVersion(version);
libraries.setVersion(version);
guides.setVersion(version);

View File

@ -31,10 +31,11 @@ public class CanonicalResourceManager<T extends CanonicalResource> {
private String url;
private String version;
private String supplements;
private String derivation;
private CanonicalResource resource;
private boolean hacked;
public CanonicalResourceProxy(String type, String id, String url, String version, String supplements) {
public CanonicalResourceProxy(String type, String id, String url, String version, String supplements, String derivation) {
super();
this.type = type;
this.id = id;
@ -75,6 +76,14 @@ public class CanonicalResourceManager<T extends CanonicalResource> {
return supplements;
}
public String getDerivation() {
return derivation;
}
public void setDerivation(String derivation) {
this.derivation = derivation;
}
public CanonicalResource getResource() throws FHIRException {
if (resource == null) {
resource = loadResource();

View File

@ -310,8 +310,7 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
@Override
public boolean isPrimitiveType(String type) {
StructureDefinition sd = context.fetchTypeDefinition(type);
return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
return context.isPrimitiveType(type);
}
@Override
@ -364,11 +363,6 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
return false;
}
public boolean isPrimitiveDatatype(String type) {
StructureDefinition sd = context.fetchTypeDefinition(type);
return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
}
public StructureDefinition fetchByJsonName(String key) {
for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
ElementDefinition ed = sd.getSnapshot().getElementFirstRep();

View File

@ -938,7 +938,20 @@ public interface IWorkerContext {
*/
public List<StructureDefinition> fetchTypeDefinitions(String n);
/**
* return whether type is primitive type. This is called a lot, and needs a high performance implementation
* @param type
* @return
*/
public boolean isPrimitiveType(String type);
/**
* return whether type is data type. This is called a lot, and needs a high performance implementation
* @param type
* @return
*/
public boolean isDataType(String type);
/**
* Returns a set of keys that can be used to get binaries from this context.
* The binaries come from the loaded packages (mostly the pubpack)
@ -1020,4 +1033,6 @@ public interface IWorkerContext {
public boolean isForPublication();
public void setForPublication(boolean value);
}

View File

@ -99,7 +99,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
private final IContextResourceLoader loader;
public PackageResourceLoader(PackageResourceInformation pri, IContextResourceLoader loader) {
super(pri.getResourceType(), pri.getId(), loader == null ? pri.getUrl() :loader.patchUrl(pri.getUrl(), pri.getResourceType()), pri.getVersion(), pri.getSupplements());
super(pri.getResourceType(), pri.getId(), loader == null ? pri.getUrl() :loader.patchUrl(pri.getUrl(), pri.getResourceType()), pri.getVersion(), pri.getSupplements(), pri.getDerivation());
this.filename = pri.getFilename();
this.loader = loader;
}

View File

@ -0,0 +1,119 @@
package org.hl7.fhir.r5.context;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.utilities.Utilities;
public class TypeManager {
private CanonicalResourceManager<StructureDefinition> structures;
private Map<String, List<StructureDefinition>> typeDefinitions = new HashMap<>();
private Map<String, List<StructureDefinition>> fhirTypeDefinitions = new HashMap<>();
private Set<String> primitiveNames = new HashSet<>();
private Set<String> dataTypeNames = new HashSet<>();
public TypeManager(CanonicalResourceManager<StructureDefinition> structures) {
super();
this.structures = structures;
reload();
}
public void reload() {
typeDefinitions.clear();
primitiveNames.clear();
dataTypeNames.clear();
for (StructureDefinition sd : structures.getList()) {
see(sd);
}
}
public void see(CanonicalResourceProxy r) {
if (!"constraint".equals(r.getDerivation())) {
see((StructureDefinition) r.getResource());
}
}
public void see(StructureDefinition sd) {
if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
String type = sd.getType();
List<StructureDefinition> types = typeDefinitions.get(type);
if (types == null) {
types = new ArrayList<>();
typeDefinitions.put(type, types);
}
types.add(sd);
if (sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/")) {
types = fhirTypeDefinitions.get(type);
if (types == null) {
types = new ArrayList<>();
fhirTypeDefinitions.put(type, types);
}
types.add(sd);
}
if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) {
primitiveNames.add(sd.getType());
} else if (sd.getKind() == StructureDefinitionKind.COMPLEXTYPE) {
dataTypeNames.add(sd.getType());
}
}
}
public List<StructureDefinition> getDefinitions(String typeName) {
List<StructureDefinition> list = new ArrayList<>();
List<StructureDefinition> defined = typeDefinitions.get(typeName);
if (defined != null) {
list.addAll(defined);
}
return list;
}
public StructureDefinition fetchTypeDefinition(String typeName) {
List<StructureDefinition> types = typeDefinitions.get(typeName);
if (types == null) {
return null; // throw new FHIRException("Unresolved type "+typeName+" (0)");
} else if (types.size() == 1) {
return types.get(0);
} else {
types = fhirTypeDefinitions.get(typeName);
if (types == null) {
return null;
} else if (types.size() != 1) {
throw new FHIRException("Ambiguous type "+typeName+" ("+types.toString()+") (contact Grahame Grieve for investigation)");
} else {
return types.get(0);
}
}
}
public boolean isPrimitive(String type) {
if (primitiveNames.contains(type) || Utilities.existsInList(type, "boolean", "integer", "integer64", "string", "decimal", "uri", "base64Binary", "instant", "date", "dateTime", "time", "code", "oid", "id", "markdown", "unsignedInt", "positiveInt", "uuid", "xhtml", "url", "canonical")) {
return true;
} else {
StructureDefinition sd = structures.get(type);
return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
}
}
public boolean isDataType(String type) {
if (dataTypeNames.contains(type) || Utilities.existsInList(type, "Address", "Age", "Annotation", "Attachment", "CodeableConcept", "Coding", "ContactPoint", "Count", "Distance", "Duration", "HumanName", "Identifier", "Money", "Period", "Quantity", "Range", "Ratio", "Reference", "SampledData", "Signature", "Timing",
"ContactDetail", "Contributor", "DataRequirement", "Expression", "ParameterDefinition", "RelatedArtifact", "TriggerDefinition", "UsageContext")) {
return true;
} else {
StructureDefinition sd = structures.get(type);
return sd != null && sd.getKind() == StructureDefinitionKind.COMPLEXTYPE;
}
}
}

View File

@ -556,7 +556,7 @@ public class Element extends Base {
return ne;
} else if (p.getDefinition().isChoice() && name.startsWith(p.getName().replace("[x]", ""))) {
String type = name.substring(p.getName().length()-3);
if (new ContextUtilities(property.getContext()).isPrimitiveDatatype(Utilities.uncapitalize(type))) {
if (property.getContext().isPrimitiveType(Utilities.uncapitalize(type))) {
type = Utilities.uncapitalize(type);
}
Element ne = new Element(name, p);

View File

@ -45,12 +45,12 @@ public class FmlParser extends ParserBase {
}
@Override
public List<NamedElement> parse(InputStream inStream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
public List<ValidatedFragment> parse(InputStream inStream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
byte[] content = TextFile.streamToBytes(inStream);
ByteArrayInputStream stream = new ByteArrayInputStream(content);
String text = TextFile.streamToString(stream);
List<NamedElement> result = new ArrayList<>();
NamedElement ctxt = new NamedElement("focus", "fml", content);
List<ValidatedFragment> result = new ArrayList<>();
ValidatedFragment ctxt = new ValidatedFragment("focus", "fml", content);
ctxt.setElement(parse(ctxt.getErrors(), text));
result.add(ctxt);
return result;

View File

@ -52,7 +52,6 @@ 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.elementmodel.Element.SpecialElement;
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.formats.JsonCreator;
import org.hl7.fhir.r5.formats.JsonCreatorCanonical;
@ -120,10 +119,10 @@ public class JsonParser extends ParserBase {
@Override
public List<NamedElement> parse(InputStream inStream) throws IOException, FHIRException {
public List<ValidatedFragment> parse(InputStream inStream) throws IOException, FHIRException {
// long start = System.currentTimeMillis();
byte[] content = TextFile.streamToBytes(inStream);
NamedElement ctxt = new NamedElement("focus", "json", content);
ValidatedFragment ctxt = new ValidatedFragment("focus", "json", content);
ByteArrayInputStream stream = new ByteArrayInputStream(content);
@ -142,7 +141,7 @@ public class JsonParser extends ParserBase {
}
ctxt.setElement(parse(ctxt.getErrors(), obj));
List<NamedElement> res = new ArrayList<>();
List<ValidatedFragment> res = new ArrayList<>();
res.add(ctxt);
// long t=System.currentTimeMillis()-start;

View File

@ -41,7 +41,6 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.model.StructureDefinition;
@ -106,7 +105,7 @@ public class Manager {
}
}
public static List<NamedElement> parse(IWorkerContext context, InputStream source, FhirFormat inputFormat) throws FHIRFormatError, DefinitionException, IOException, FHIRException {
public static List<ValidatedFragment> parse(IWorkerContext context, InputStream source, FhirFormat inputFormat) throws FHIRFormatError, DefinitionException, IOException, FHIRException {
return makeParser(context, inputFormat).parse(source);
}

View File

@ -39,7 +39,6 @@ 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.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.CodeableConcept;
@ -72,7 +71,7 @@ public class ObjectConverter {
org.hl7.fhir.r5.formats.JsonParser jp = new org.hl7.fhir.r5.formats.JsonParser();
jp.compose(bs, ig);
ByteArrayInputStream bi = new ByteArrayInputStream(bs.toByteArray());
List<NamedElement> list = new JsonParser(context).parse(bi);
List<ValidatedFragment> list = new JsonParser(context).parse(bi);
if (list.size() != 1) {
throw new FHIRException("Unable to convert because the source contains multieple resources");
}

View File

@ -35,7 +35,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.ArrayList;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
@ -69,54 +68,6 @@ public abstract class ParserBase {
}
}
public class NamedElement {
private String name;
private String extension;
private Element element;
private byte[] content;
private List<ValidationMessage> errors = new ArrayList<>();
public NamedElement(String name, String extension, Element element, byte[] content) {
super();
this.name = name;
this.element = element;
this.content = content;
this.extension = extension;
}
public NamedElement(String name, String extension, byte[] content) {
super();
this.name = name;
this.content = content;
this.extension = extension;
}
public String getName() {
return name;
}
public Element getElement() {
return element;
}
public byte[] getContent() {
return content;
}
public List<ValidationMessage> getErrors() {
return errors;
}
public void setElement(Element element) {
this.element = element;
}
public String getFilename() {
return name+"."+extension;
}
}
public interface ILinkResolver {
String resolveType(String type);
String resolveProperty(Property property);
@ -126,13 +77,7 @@ public abstract class ParserBase {
public enum ValidationPolicy { NONE, QUICK, EVERYTHING }
public boolean isPrimitive(String code) {
StructureDefinition sd = context.fetchTypeDefinition(code);
if (sd != null) {
return sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
}
return Utilities.existsInList(code, "boolean", "integer", "integer64", "string", "decimal", "uri", "base64Binary", "instant", "date", "dateTime", "time", "code", "oid", "id", "markdown", "unsignedInt", "positiveInt", "uuid", "xhtml", "url", "canonical");
return context.isPrimitiveType(code);
}
protected IWorkerContext context;
@ -152,20 +97,20 @@ public abstract class ParserBase {
this.policy = policy;
}
public abstract List<NamedElement> parse(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException;
public abstract List<ValidatedFragment> parse(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException;
public Element parseSingle(InputStream stream, List<ValidationMessage> errors) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
List<NamedElement> res = parse(stream);
List<ValidatedFragment> res = parse(stream);
if (res.size() != 1) {
throw new FHIRException("Parsing FHIR content returned multiple elements in a context where only one element is allowed");
}
var resE = res.get(0);
if (resE.getElement() == null) {
throw new FHIRException("Parsing FHIR content failed: "+errorSummary(resE.errors));
throw new FHIRException("Parsing FHIR content failed: "+errorSummary(resE.getErrors()));
} else if (res.size() == 0) {
throw new FHIRException("Parsing FHIR content returned no elements in a context where one element is required because: "+errorSummary(resE.errors));
throw new FHIRException("Parsing FHIR content returned no elements in a context where one element is required because: "+errorSummary(resE.getErrors()));
}
if (errors != null) {
errors.addAll(resE.getErrors());

View File

@ -249,12 +249,7 @@ public class Property {
* @param E.g. "integer"
*/
public boolean isPrimitive(String code) {
if (Utilities.isAbsoluteUrl(code)) {
StructureDefinition sd = context.fetchTypeDefinition(code);
return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
} else {
return TypesUtilities.isPrimitive(code);
}
return context.isPrimitiveType(code);
}
public boolean isPrimitive() {

View File

@ -27,7 +27,7 @@ public class ResourceParser extends ParserBase {
}
@Override
public List<NamedElement> parse(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
public List<ValidatedFragment> parse(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
throw new NotImplementedException("parse(InputStream stream)"); // doesns't make sense
}

View File

@ -24,7 +24,6 @@ import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
import org.hl7.fhir.r5.elementmodel.SHCParser.SHCSignedJWT;
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.utilities.TextFile;
@ -80,11 +79,11 @@ public class SHCParser extends ParserBase {
jsonParser = new JsonParser(context);
}
public List<NamedElement> parse(InputStream inStream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
public List<ValidatedFragment> parse(InputStream inStream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
byte[] content = TextFile.streamToBytes(inStream);
ByteArrayInputStream stream = new ByteArrayInputStream(content);
List<NamedElement> res = new ArrayList<>();
NamedElement shc = new NamedElement("shc", "json", content);
List<ValidatedFragment> res = new ArrayList<>();
ValidatedFragment shc = new ValidatedFragment("shc", "json", content);
res.add(shc);
String src = TextFile.streamToString(stream).trim();
@ -166,7 +165,7 @@ public class SHCParser extends ParserBase {
return res;
}
// ok. all checks passed, we can now validate the bundle
NamedElement bnd = new NamedElement(path, "json", org.hl7.fhir.utilities.json.parser.JsonParser.composeBytes(cs.getJsonObject("fhirBundle")));
ValidatedFragment bnd = new ValidatedFragment(path, "json", org.hl7.fhir.utilities.json.parser.JsonParser.composeBytes(cs.getJsonObject("fhirBundle")));
res.add(bnd);
bnd.setElement(jsonParser.parse(bnd.getErrors(), cs.getJsonObject("fhirBundle")));
}

View File

@ -72,11 +72,11 @@ public class SHLParser extends ParserBase {
super(context);
}
public List<NamedElement> parse(InputStream inStream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
public List<ValidatedFragment> parse(InputStream inStream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
byte[] content = TextFile.streamToBytes(inStream);
List<NamedElement> res = new ArrayList<>();
NamedElement shl = addNamedElement(res, "shl", "txt", content);
List<ValidatedFragment> res = new ArrayList<>();
ValidatedFragment shl = addNamedElement(res, "shl", "txt", content);
String src = TextFile.bytesToString(content);
if (src.startsWith("shlink:/")) {
@ -93,7 +93,7 @@ public class SHLParser extends ParserBase {
}
if (src != null) {
byte[] cntin = Base64.getUrlDecoder().decode(src);
NamedElement json = addNamedElement(res, "json", "json", cntin);
ValidatedFragment json = addNamedElement(res, "json", "json", cntin);
JsonObject j = null;
try {
j = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(cntin);
@ -145,8 +145,8 @@ public class SHLParser extends ParserBase {
}
private void checkManifest(List<NamedElement> res, HTTPResult cnt) throws IOException {
NamedElement manifest = addNamedElement(res, "manifest", "json", cnt.getContent());
private void checkManifest(List<ValidatedFragment> res, HTTPResult cnt) throws IOException {
ValidatedFragment manifest = addNamedElement(res, "manifest", "json", cnt.getContent());
if (!cnt.getContentType().equals("application/json")) {
logError(manifest.getErrors(), "202-08-31", 1, 1, "manifest", IssueType.STRUCTURE, "The mime type should be application/json not "+cnt.getContentType(), IssueSeverity.ERROR);
@ -188,7 +188,7 @@ public class SHLParser extends ParserBase {
}
}
private void processManifestEntry(List<NamedElement> res, List<ValidationMessage> errors, JsonObject j, String path, String name) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
private void processManifestEntry(List<ValidatedFragment> res, List<ValidationMessage> errors, JsonObject j, String path, String name) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
for (JsonProperty p : j.getProperties()) {
if (!Utilities.existsInList(p.getName(), "contentType", "location", "embedded")) {
logError(errors, "202-08-31", p.getValue().getStart().getLine(), p.getValue().getStart().getCol(), "manifest."+p.getName(),
@ -242,8 +242,8 @@ public class SHLParser extends ParserBase {
}
}
private void processContent(List<NamedElement> res, List<ValidationMessage> errors, String path, String name, String jose, String ct) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
NamedElement bin = addNamedElement(res, "encrypted", "jose", TextFile.stringToBytes(jose, false));
private void processContent(List<ValidatedFragment> res, List<ValidationMessage> errors, String path, String name, String jose, String ct) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
ValidatedFragment bin = addNamedElement(res, "encrypted", "jose", TextFile.stringToBytes(jose, false));
byte[] cnt = null;
JWEObject jwe;
try {
@ -261,7 +261,7 @@ public class SHLParser extends ParserBase {
res.addAll(shc.parse(new ByteArrayInputStream(cnt)));
break;
case "application/fhir+json":
NamedElement doc = addNamedElement(res, name, "json", cnt);
ValidatedFragment doc = addNamedElement(res, name, "json", cnt);
// a JSON file containing any FHIR resource (e.g., an individual resource or a Bundle of resources). Generally this format may not be tamper-proof.
logError(doc.getErrors(), "202-08-31", 1, 1, name, IssueType.STRUCTURE, "Processing content of type 'application/smart-api-access' is not done yet", IssueSeverity.INFORMATION);
break;
@ -279,8 +279,8 @@ public class SHLParser extends ParserBase {
}
}
private NamedElement addNamedElement(List<NamedElement> res, String name, String type, byte[] content) {
NamedElement result = new NamedElement(name, type, content);
private ValidatedFragment addNamedElement(List<ValidatedFragment> res, String name, String type, byte[] content) {
ValidatedFragment result = new ValidatedFragment(name, type, content);
res.add(result);
return result;
}

View File

@ -45,7 +45,6 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.Element.SpecialElement;
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.StructureDefinition;
@ -81,9 +80,9 @@ public class TurtleParser extends ParserBase {
super(context);
}
@Override
public List<NamedElement> parse(InputStream inStream) throws IOException, FHIRException {
public List<ValidatedFragment> parse(InputStream inStream) throws IOException, FHIRException {
byte[] content = TextFile.streamToBytes(inStream);
NamedElement ctxt = new NamedElement("focus", "ttl", content);
ValidatedFragment ctxt = new ValidatedFragment("focus", "ttl", content);
ByteArrayInputStream stream = new ByteArrayInputStream(content);
Turtle src = new Turtle();
@ -99,7 +98,7 @@ public class TurtleParser extends ParserBase {
src.parse(TextFile.streamToString(stream));
ctxt.setElement(parse(ctxt.getErrors(), src));
}
List<NamedElement> res = new ArrayList<>();
List<ValidatedFragment> res = new ArrayList<>();
res.add(ctxt);
return res;
}

View File

@ -0,0 +1,54 @@
package org.hl7.fhir.r5.elementmodel;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.utilities.validation.ValidationMessage;
public class ValidatedFragment {
private String name;
private String extension;
private Element element;
private byte[] content;
private List<ValidationMessage> errors = new ArrayList<>();
public ValidatedFragment(String name, String extension, Element element, byte[] content) {
super();
this.name = name;
this.element = element;
this.content = content;
this.extension = extension;
}
public ValidatedFragment(String name, String extension, byte[] content) {
super();
this.name = name;
this.content = content;
this.extension = extension;
}
public String getName() {
return name;
}
public Element getElement() {
return element;
}
public byte[] getContent() {
return content;
}
public List<ValidationMessage> getErrors() {
return errors;
}
public void setElement(Element element) {
this.element = element;
}
public String getFilename() {
return name+"."+extension;
}
}

View File

@ -454,7 +454,7 @@ public class VerticalBarParser extends ParserBase {
private Delimiters delimiters = new Delimiters();
@Override
public List<NamedElement> parse(InputStream inStream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
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));
byte[] content = TextFile.streamToBytes(inStream);
@ -464,8 +464,8 @@ public class VerticalBarParser extends ParserBase {
preDecode(reader);
while (!reader.isFinished()) // && (getOptions().getSegmentLimit() == 0 || getOptions().getSegmentLimit() > message.getSegments().size()))
readSegment(message, reader);
List<NamedElement> res = new ArrayList<>();
res.add(new NamedElement("focus", "hl7", message, content));
List<ValidatedFragment> res = new ArrayList<>();
res.add(new ValidatedFragment("focus", "hl7", message, content));
return res;
}

View File

@ -111,10 +111,10 @@ public class XmlParser extends ParserBase {
this.allowXsiLocation = allowXsiLocation;
}
public List<NamedElement> parse(InputStream inStream) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
public List<ValidatedFragment> parse(InputStream inStream) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
byte[] content = TextFile.streamToBytes(inStream);
NamedElement context = new NamedElement("focus", "xml", content);
ValidatedFragment context = new ValidatedFragment("focus", "xml", content);
ByteArrayInputStream stream = new ByteArrayInputStream(content);
Document doc = null;
@ -177,7 +177,7 @@ public class XmlParser extends ParserBase {
if (doc != null) {
context.setElement(parse(context.getErrors(), doc));
}
List<NamedElement> res = new ArrayList<>();
List<ValidatedFragment> res = new ArrayList<>();
res.add(context);
return res;
}

View File

@ -323,7 +323,7 @@ public class PEBuilder {
if (passElementPropsCheck(defn) && !Utilities.existsInList(defn.getName(), omitList)) {
PEDefinitionElement pe = new PEDefinitionElement(this, profile, defn, parent.path());
pe.setRecursing(definition == defn || (profile.getDerivation() == TypeDerivationRule.SPECIALIZATION && profile.getType().equals("Extension")));
if (cu.isPrimitiveDatatype(definition.getTypeFirstRep().getWorkingCode()) && "value".equals(pe.name())) {
if (context.isPrimitiveType(definition.getTypeFirstRep().getWorkingCode()) && "value".equals(pe.name())) {
pe.setMustHaveValue(definition.getMustHaveValue());
}
pe.setInFixedValue(definition.hasFixed() || definition.hasPattern() || parent.isInFixedValue());

View File

@ -801,7 +801,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
}
} else if (hasDef && element.getType().get(0).getWorkingCode() != null && element.getType().get(0).getWorkingCode().startsWith("@")) {
row.setIcon("icon_reuse.png", HierarchicalTableGenerator.TEXT_ICON_REUSE);
} else if (hasDef && isPrimitive(element.getType().get(0).getWorkingCode())) {
} else if (hasDef && context.getContext().isPrimitiveType(element.getType().get(0).getWorkingCode())) {
if (keyRows.contains(element.getId())) {
row.setIcon("icon-key.png", HierarchicalTableGenerator.TEXT_ICON_KEY);
} else {
@ -809,7 +809,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
}
} else if (hasDef && element.getType().get(0).hasTarget()) {
row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE);
} else if (hasDef && isDataType(element.getType().get(0).getWorkingCode())) {
} else if (hasDef && context.getContext().isDataType(element.getType().get(0).getWorkingCode())) {
row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE);
} else if (hasDef && element.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) {
row.setIcon("icon-object-box.png", HierarchicalTableGenerator.TEXT_ICON_OBJECT_BOX);
@ -2122,7 +2122,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
if (!pattern) {
c.addPiece(gen.new Piece(null, "0..0", null));
row.setIcon("icon_fixed.gif", "Fixed Value" /*HierarchicalTableGenerator.TEXT_ICON_FIXED*/);
} else if (isPrimitive(t.getTypeCode())) {
} else if (context.getContext().isPrimitiveType(t.getTypeCode())) {
row.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE);
c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null));
} else if (isReference(t.getTypeCode())) {
@ -2450,28 +2450,6 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
return path;
}
protected boolean isPrimitive(String value) {
StructureDefinition sd = context.getWorker().fetchTypeDefinition(value);
if (sd == null) // might be running before all SDs are available
return Utilities.existsInList(value, "base64Binary", "boolean", "canonical", "code", "date", "dateTime", "decimal", "id", "instant", "integer", "integer64", "markdown", "oid", "positiveInt", "string", "time", "unsignedInt", "uri", "url", "uuid");
else
return sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
}
private boolean isDataType(String value) {
StructureDefinition sd = context.getWorker().fetchTypeDefinition(value);
if (sd == null) // might be running before all SDs are available
return Utilities.existsInList(value, "Address", "Age", "Annotation", "Attachment", "CodeableConcept", "Coding", "ContactPoint", "Count", "Distance", "Duration", "HumanName", "Identifier", "Money", "Period", "Quantity", "Range", "Ratio", "Reference", "SampledData", "Signature", "Timing",
"ContactDetail", "Contributor", "DataRequirement", "Expression", "ParameterDefinition", "RelatedArtifact", "TriggerDefinition", "UsageContext");
else
return sd.getKind() == StructureDefinitionKind.COMPLEXTYPE && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION;
}
private boolean slicesExist(List<ElementDefinition> elements, ElementDefinition element) {
if (elements == null) {
return true;
@ -3827,7 +3805,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
private boolean hasPrimitiveTypes(ElementDefinition d) {
for (TypeRefComponent tr : d.getType()) {
if (isPrimitive(tr.getCode())) {
if (context.getContext().isPrimitiveType(tr.getCode())) {
return true;
}
}

View File

@ -92,7 +92,7 @@ public class ResourceLanguageFileBuilder {
private boolean isTranslatable(Property p, Base b, String id) {
if (new ContextUtilities(context).isPrimitiveDatatype(b.fhirType())) { // never any translations for non-primitives
if (context.isPrimitiveType(b.fhirType())) { // never any translations for non-primitives
ElementDefinition ed = null;
for (ElementDefinition t : profile.getSnapshot().getElement()) {
if (t.getId().equals(id)) {

View File

@ -19,7 +19,7 @@ public class CanonicalResourceManagerTests {
private CanonicalResource resource;
public DeferredLoadTestResource(CanonicalResource resource) {
super(resource.fhirType(), resource.getId(), resource.getUrl(), resource.getVersion(), resource instanceof CodeSystem ? ((CodeSystem) resource).getSupplements() : null);
super(resource.fhirType(), resource.getId(), resource.getUrl(), resource.getVersion(), resource instanceof CodeSystem ? ((CodeSystem) resource).getSupplements() : null, null);
this.resource = resource;
}

View File

@ -13,7 +13,7 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.PathEngineException;
import org.hl7.fhir.r5.elementmodel.Manager;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
import org.hl7.fhir.r5.elementmodel.ValidatedFragment;
import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.formats.XmlParser;
import org.hl7.fhir.r5.model.*;
@ -209,7 +209,7 @@ public class FHIRPathTests {
if (node != null) {
try {
if ("element".equals(test.getAttribute("mode"))) {
List<NamedElement> e = Manager.parse(fp.getWorker(), TestingUtilities.loadTestResourceStream("r5", input), input.endsWith(".json") ? FhirFormat.JSON : FhirFormat.XML);
List<ValidatedFragment> e = Manager.parse(fp.getWorker(), TestingUtilities.loadTestResourceStream("r5", input), input.endsWith(".json") ? FhirFormat.JSON : FhirFormat.XML);
outcome = fp.evaluate(e.get(0).getElement(), node);
} else {
outcome = fp.evaluate(res, node);

View File

@ -113,6 +113,7 @@ public class NpmPackage {
private String filename;
private String supplements;
private String stype;
private String derivation;
public PackageResourceInformation(String root, JsonObject fi) throws IOException {
super();
@ -123,6 +124,7 @@ public class NpmPackage {
filename = Utilities.path(root, fi.asString("filename"));
supplements = fi.asString("supplements");
stype = fi.asString("type");
derivation = fi.asString("derivation");
}
public String getId() {
return id;
@ -148,6 +150,9 @@ public class NpmPackage {
public boolean hasId() {
return !Utilities.noString(id);
}
public String getDerivation() {
return derivation;
}
}
public class IndexVersionSorter implements Comparator<JsonObject> {

View File

@ -55,9 +55,11 @@ public class NpmPackageIndexBuilder {
"Type nvarchar NULL,\r\n"+
"Supplements nvarchar NULL,\r\n"+
"Content nvarchar NULL,\r\n"+
"ValueSet nvarchar NULL,\r\n"+
"Derivation nvarchar NULL,\r\n"+
"PRIMARY KEY (FileName))\r\n");
psql = conn.prepareStatement("Insert into ResourceList (FileName, ResourceType, Id, Url, Version, Kind, Type, Supplements, Content) values (?, ?, ?, ?, ?, ?, ?, ?, ?)");
psql = conn.prepareStatement("Insert into ResourceList (FileName, ResourceType, Id, Url, Version, Kind, Type, Supplements, Content, ValueSet) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
} catch (Exception e) {
conn = null;
}
@ -95,6 +97,12 @@ public class NpmPackageIndexBuilder {
if (json.hasPrimitive("content")) {
fi.add("content", json.asString("content"));
}
if (json.hasPrimitive("valueSet")) {
fi.add("valueSet", json.asString("valueSet"));
}
if (json.hasPrimitive("derivation")) {
fi.add("valueSet", json.asString("derivation"));
}
if (psql != null) {
psql.setString(1, name); // FileName);
psql.setString(2, json.asString("resourceType")); // ResourceType");
@ -105,6 +113,8 @@ public class NpmPackageIndexBuilder {
psql.setString(7, json.asString("type")); // Type");
psql.setString(8, json.asString("supplements")); // Supplements");
psql.setString(9, json.asString("content")); // Content");
psql.setString(10, json.asString("valueSet")); // ValueSet");
psql.setString(10, json.asString("derivation")); // ValueSet");
psql.execute();
}
}

View File

@ -77,9 +77,9 @@ import org.hl7.fhir.r5.elementmodel.Manager;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r5.elementmodel.ObjectConverter;
import org.hl7.fhir.r5.elementmodel.ParserBase;
import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
import org.hl7.fhir.r5.elementmodel.ParserBase.ValidationPolicy;
import org.hl7.fhir.r5.elementmodel.ResourceParser;
import org.hl7.fhir.r5.elementmodel.ValidatedFragment;
import org.hl7.fhir.r5.elementmodel.XmlParser;
import org.hl7.fhir.r5.formats.FormatUtilities;
import org.hl7.fhir.r5.model.Address;
@ -517,7 +517,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private Map<String, CanonicalResourceLookupResult> crLookups = new HashMap<>();
private boolean logProgress;
private CodingsObserver codingObserver;
public List<NamedElement> validatedContent;
public List<ValidatedFragment> validatedContent;
public boolean testMode;
public InstanceValidator(@Nonnull IWorkerContext theContext, @Nonnull IEvaluationContext hostServices, @Nonnull XVerExtensionManager xverManager) {
@ -747,7 +747,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (validatedContent != null && !validatedContent.isEmpty()) {
if (SAVE_INTERMEDIARIES) {
int index = 0;
for (NamedElement ne : validatedContent) {
for (ValidatedFragment ne : validatedContent) {
index++;
saveValidatedContent(ne, index);
}
@ -761,7 +761,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
profiles.add(sd);
}
}
for (NamedElement ne : validatedContent) {
for (ValidatedFragment ne : validatedContent) {
if (ne.getElement() != null) {
validate(appContext, ne.getErrors(), validatedContent.size() > 1 ? ne.getName() : null, ne.getElement(), profiles);
}
@ -771,7 +771,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return (validatedContent == null || validatedContent.isEmpty()) ? null : validatedContent.get(0).getElement(); // todo: this is broken, but fixing it really complicates things elsewhere, so we do this for now
}
private void saveValidatedContent(NamedElement ne, int index) {
private void saveValidatedContent(ValidatedFragment ne, int index) {
String tgt = null;
try {
tgt = Utilities.path("[tmp]", "validator", "content");
@ -4309,8 +4309,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
public boolean isPrimitiveType(String code) {
StructureDefinition sd = context.fetchTypeDefinition(code);
return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
return context.isPrimitiveType(code);
}
private String getErrorMessage(String message) {

View File

@ -481,7 +481,7 @@ public class StructureDefinitionValidator extends BaseValidator {
if (v != null) {
ok = rule(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), typeCodes.contains(v.fhirType()), I18nConstants.SD_VALUE_TYPE_IILEGAL, element.getIdBase(), "fixed", v.fhirType(), typeCodes) && ok;
hint(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), !repeating, I18nConstants.SD_VALUE_TYPE_REPEAT_HINT, element.getIdBase(), "fixed");
if (isPrimitiveType(v.fhirType())) {
if (context.isPrimitiveType(v.fhirType())) {
warning(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), !repeating, I18nConstants.SD_VALUE_TYPE_REPEAT_WARNING_DOTNET, element.getIdBase(), "fixed");
} else {
warning(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), false, I18nConstants.SD_VALUE_COMPLEX_FIXED, v.fhirType());
@ -491,7 +491,7 @@ public class StructureDefinitionValidator extends BaseValidator {
if (v != null) {
ok = rule(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), typeCodes.contains(v.fhirType()), I18nConstants.SD_VALUE_TYPE_IILEGAL, element.getIdBase(), "pattern", v.fhirType(), typeCodes) && ok;
hint(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), !repeating, I18nConstants.SD_VALUE_TYPE_REPEAT_HINT, element.getIdBase(), "pattern");
if (isPrimitiveType(v.fhirType())) {
if (context.isPrimitiveType(v.fhirType())) {
warning(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), !repeating, I18nConstants.SD_VALUE_TYPE_REPEAT_WARNING_DOTNET, element.getIdBase(), "pattern");
}
}
@ -737,10 +737,6 @@ public class StructureDefinitionValidator extends BaseValidator {
return true;
}
private boolean isPrimitiveType(String fhirType) {
StructureDefinition sd = context.fetchTypeDefinition(fhirType);
return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
}
private String boundType(Set<String> typeCodes) {
for (String tc : typeCodes) {

View File

@ -119,7 +119,7 @@ public class NodeStack {
if (en.endsWith("[x]")) {
en = en.substring(0, en.length() - 3);
String t = n.substring(en.length());
if (isPrimitiveType(Utilities.uncapitalize(t)))
if (context.isPrimitiveType(Utilities.uncapitalize(t)))
t = Utilities.uncapitalize(t);
res.literalPath = res.literalPath.substring(0, res.literalPath.lastIndexOf(".")) + "." + en + ".ofType(" + t + ")";
} else {
@ -194,10 +194,6 @@ public class NodeStack {
return path.substring(path.lastIndexOf(".") + 1);
}
public boolean isPrimitiveType(String code) {
StructureDefinition sd = context.fetchTypeDefinition(code);
return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
}
public String getWorkingLang() {
return workingLang;