package org.hl7.fhir.dstu2016may.metamodel; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import org.hl7.fhir.dstu2016may.formats.FormatUtilities; import org.hl7.fhir.dstu2016may.formats.IParser.OutputStyle; import org.hl7.fhir.dstu2016may.model.ElementDefinition; import org.hl7.fhir.dstu2016may.model.ElementDefinition.PropertyRepresentation; import org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent; import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueSeverity; import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueType; import org.hl7.fhir.dstu2016may.model.StructureDefinition; import org.hl7.fhir.dstu2016may.utils.IWorkerContext; import org.hl7.fhir.dstu2016may.utils.ProfileUtilities; import org.hl7.fhir.dstu2016may.utils.ToolingExtensions; import org.hl7.fhir.dstu2016may.validation.ValidationMessage; import org.hl7.fhir.dstu2016may.validation.ValidationMessage.Source; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.utilities.Utilities; public abstract class ParserBase { interface IErrorNotifier { } public enum ValidationPolicy { NONE, QUICK, EVERYTHING } public static boolean isPrimitive(String code) { return Utilities.existsInList(code, "xhtml", "boolean", "integer", "string", "decimal", "uri", "base64Binary", "instant", "date", "dateTime", "time", "code", "oid", "id", "markdown", "unsignedInt", "positiveInt", "xhtml", "base64Binary"); } protected IWorkerContext context; protected ValidationPolicy policy; protected List<ValidationMessage> errors; public ParserBase(IWorkerContext context) { super(); this.context = context; policy = ValidationPolicy.NONE; } public void setupValidation(ValidationPolicy policy, List<ValidationMessage> errors) { this.policy = policy; this.errors = errors; } public abstract Element parse(InputStream stream) throws Exception; public abstract void compose(Element e, OutputStream destination, OutputStyle style, String base) throws Exception; public void logError(int line, int col, String path, IssueType type, String message, IssueSeverity level) throws FHIRFormatError { if (policy == ValidationPolicy.EVERYTHING) { ValidationMessage msg = new ValidationMessage(Source.InstanceValidator, type, line, col, path, message, level); errors.add(msg); } else if (level == IssueSeverity.FATAL || (level == IssueSeverity.ERROR && policy == ValidationPolicy.QUICK)) throw new FHIRFormatError(message+String.format(" at line %d col %d", line, col)); } protected StructureDefinition getDefinition(int line, int col, String ns, String name) throws FHIRFormatError { if (ns == null) { logError(line, col, name, IssueType.STRUCTURE, "This cannot be parsed as a FHIR object (no namespace)", IssueSeverity.FATAL); return null; } if (name == null) { logError(line, col, name, IssueType.STRUCTURE, "This cannot be parsed as a FHIR object (no name)", IssueSeverity.FATAL); return null; } for (StructureDefinition sd : context.allStructures()) { if (name.equals(sd.getIdElement().getIdPart())) { if((ns == null || ns.equals(FormatUtilities.FHIR_NS)) && !ToolingExtensions.hasExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")) return sd; String sns = ToolingExtensions.readStringExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"); if (ns != null && ns.equals(sns)) return sd; } } logError(line, col, name, IssueType.STRUCTURE, "This does not appear to be a FHIR resource (unknown namespace/name '"+ns+"::"+name+"')", IssueSeverity.FATAL); return null; } protected StructureDefinition getDefinition(int line, int col, String name) throws FHIRFormatError { if (name == null) { logError(line, col, name, IssueType.STRUCTURE, "This cannot be parsed as a FHIR object (no name)", IssueSeverity.FATAL); return null; } for (StructureDefinition sd : context.allStructures()) { if (name.equals(sd.getIdElement().getIdPart())) { return sd; } } logError(line, col, name, IssueType.STRUCTURE, "This does not appear to be a FHIR resource (unknown name '"+name+"')", IssueSeverity.FATAL); return null; } protected List<Property> getChildProperties(Property property, String elementName, String statedType) throws DefinitionException { ElementDefinition ed = property.getDefinition(); StructureDefinition sd = property.getStructure(); List<ElementDefinition> children = ProfileUtilities.getChildMap(sd, ed); if (children.isEmpty()) { // ok, find the right definitions String t = null; if (ed.getType().size() == 1) t = ed.getType().get(0).getCode(); else if (ed.getType().size() == 0) throw new Error("types == 0, and no children found"); else { t = ed.getType().get(0).getCode(); boolean all = true; for (TypeRefComponent tr : ed.getType()) { if (!tr.getCode().equals(t)) { all = false; break; } } if (!all) { // ok, it's polymorphic if (ed.hasRepresentation(PropertyRepresentation.TYPEATTR)) { t = statedType; if (t == null && ToolingExtensions.hasExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaultype")) t = ToolingExtensions.readStringExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaultype"); boolean ok = false; for (TypeRefComponent tr : ed.getType()) if (tr.getCode().equals(t)) ok = true; if (!ok) throw new DefinitionException("Type '"+t+"' is not an acceptable type for '"+elementName+"' on property "+property.getDefinition().getPath()); } else { t = elementName.substring(tail(ed.getPath()).length() - 3); if (isPrimitive(lowFirst(t))) t = lowFirst(t); } } } if (!"xhtml".equals(t)) { sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+t); if (sd == null) throw new DefinitionException("Unable to find class '"+t+"' for name '"+elementName+"' on property "+property.getDefinition().getPath()); children = ProfileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0)); } } List<Property> properties = new ArrayList<Property>(); for (ElementDefinition child : children) { properties.add(new Property(context, child, sd)); } return properties; } private String lowFirst(String t) { return t.substring(0, 1).toLowerCase()+t.substring(1); } private String tail(String path) { return path.contains(".") ? path.substring(path.lastIndexOf(".")+1) : path; } }