001package org.hl7.fhir.dstu2016may.metamodel; 002 003import java.io.InputStream; 004import java.io.OutputStream; 005import java.util.ArrayList; 006import java.util.List; 007 008import org.hl7.fhir.dstu2016may.formats.FormatUtilities; 009import org.hl7.fhir.dstu2016may.formats.IParser.OutputStyle; 010import org.hl7.fhir.dstu2016may.model.ElementDefinition; 011import org.hl7.fhir.dstu2016may.model.ElementDefinition.PropertyRepresentation; 012import org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent; 013import org.hl7.fhir.dstu2016may.model.StructureDefinition; 014import org.hl7.fhir.dstu2016may.utils.IWorkerContext; 015import org.hl7.fhir.dstu2016may.utils.ProfileUtilities; 016import org.hl7.fhir.dstu2016may.utils.ToolingExtensions; 017import org.hl7.fhir.utilities.validation.ValidationMessage; 018import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 019import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; 020import org.hl7.fhir.utilities.validation.ValidationMessage.Source; 021import org.hl7.fhir.exceptions.DefinitionException; 022import org.hl7.fhir.exceptions.FHIRFormatError; 023import org.hl7.fhir.utilities.Utilities; 024 025public abstract class ParserBase { 026 027 protected IWorkerContext context; 028 protected ValidationPolicy policy; 029 protected List<ValidationMessage> errors; 030 031 public ParserBase(IWorkerContext context) { 032 super(); 033 this.context = context; 034 policy = ValidationPolicy.NONE; 035 } 036 037 public abstract void compose(Element e, OutputStream destination, OutputStyle style, String base) throws Exception; 038 039 protected List<Property> getChildProperties(Property property, String elementName, String statedType) throws DefinitionException { 040 ElementDefinition ed = property.getDefinition(); 041 StructureDefinition sd = property.getStructure(); 042 List<ElementDefinition> children = ProfileUtilities.getChildMap(sd, ed); 043 if (children.isEmpty()) { 044 // ok, find the right definitions 045 String t = null; 046 if (ed.getType().size() == 1) 047 t = ed.getType().get(0).getCode(); 048 else if (ed.getType().size() == 0) 049 throw new Error("types == 0, and no children found"); 050 else { 051 t = ed.getType().get(0).getCode(); 052 boolean all = true; 053 for (TypeRefComponent tr : ed.getType()) { 054 if (!tr.getCode().equals(t)) { 055 all = false; 056 break; 057 } 058 } 059 if (!all) { 060 // ok, it's polymorphic 061 if (ed.hasRepresentation(PropertyRepresentation.TYPEATTR)) { 062 t = statedType; 063 if (t == null && ToolingExtensions.hasExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaultype")) 064 t = ToolingExtensions.readStringExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaultype"); 065 boolean ok = false; 066 for (TypeRefComponent tr : ed.getType()) 067 if (tr.getCode().equals(t)) 068 ok = true; 069 if (!ok) 070 throw new DefinitionException("Type '"+t+"' is not an acceptable type for '"+elementName+"' on property "+property.getDefinition().getPath()); 071 072 } else { 073 t = elementName.substring(tail(ed.getPath()).length() - 3); 074 if (isPrimitive(lowFirst(t))) 075 t = lowFirst(t); 076 } 077 } 078 } 079 if (!"xhtml".equals(t)) { 080 sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+t); 081 if (sd == null) 082 throw new DefinitionException("Unable to find class '"+t+"' for name '"+elementName+"' on property "+property.getDefinition().getPath()); 083 children = ProfileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0)); 084 } 085 } 086 List<Property> properties = new ArrayList<Property>(); 087 for (ElementDefinition child : children) { 088 properties.add(new Property(context, child, sd)); 089 } 090 return properties; 091 } 092 093 protected StructureDefinition getDefinition(int line, int col, String name) throws FHIRFormatError { 094 if (name == null) { 095 logError(line, col, name, IssueType.STRUCTURE, "This cannot be parsed as a FHIR object (no name)", IssueSeverity.FATAL); 096 return null; 097 } 098 for (StructureDefinition sd : context.allStructures()) { 099 if (name.equals(sd.getIdElement().getIdPart())) { 100 return sd; 101 } 102 } 103 logError(line, col, name, IssueType.STRUCTURE, "This does not appear to be a FHIR resource (unknown name '"+name+"')", IssueSeverity.FATAL); 104 return null; 105 } 106 107 protected StructureDefinition getDefinition(int line, int col, String ns, String name) throws FHIRFormatError { 108 if (ns == null) { 109 logError(line, col, name, IssueType.STRUCTURE, "This cannot be parsed as a FHIR object (no namespace)", IssueSeverity.FATAL); 110 return null; 111 } 112 if (name == null) { 113 logError(line, col, name, IssueType.STRUCTURE, "This cannot be parsed as a FHIR object (no name)", IssueSeverity.FATAL); 114 return null; 115 } 116 for (StructureDefinition sd : context.allStructures()) { 117 if (name.equals(sd.getIdElement().getIdPart())) { 118 if((ns == null || ns.equals(FormatUtilities.FHIR_NS)) && !ToolingExtensions.hasExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")) 119 return sd; 120 String sns = ToolingExtensions.readStringExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"); 121 if (ns != null && ns.equals(sns)) 122 return sd; 123 } 124 } 125 logError(line, col, name, IssueType.STRUCTURE, "This does not appear to be a FHIR resource (unknown namespace/name '"+ns+"::"+name+"')", IssueSeverity.FATAL); 126 return null; 127 } 128 129 public void logError(int line, int col, String path, IssueType type, String message, IssueSeverity level) throws FHIRFormatError { 130 if (policy == ValidationPolicy.EVERYTHING) { 131 ValidationMessage msg = new ValidationMessage(Source.InstanceValidator, type, line, col, path, message, level); 132 errors.add(msg); 133 } else if (level == IssueSeverity.FATAL || (level == IssueSeverity.ERROR && policy == ValidationPolicy.QUICK)) 134 throw new FHIRFormatError(message+String.format(" at line %d col %d", line, col)); 135 } 136 137 private String lowFirst(String t) { 138 return t.substring(0, 1).toLowerCase()+t.substring(1); 139 } 140 141 public abstract Element parse(InputStream stream) throws Exception; 142 143 public void setupValidation(ValidationPolicy policy, List<ValidationMessage> errors) { 144 this.policy = policy; 145 this.errors = errors; 146 } 147 148 private String tail(String path) { 149 return path.contains(".") ? path.substring(path.lastIndexOf(".")+1) : path; 150 } 151 152 public static boolean isPrimitive(String code) { 153 return Utilities.existsInList(code, 154 "xhtml", "boolean", "integer", "string", "decimal", "uri", "base64Binary", "instant", "date", "dateTime", 155 "time", "code", "oid", "id", "markdown", "unsignedInt", "positiveInt", "xhtml", "base64Binary"); 156 } 157 158 interface IErrorNotifier { 159 160 } 161 162 public enum ValidationPolicy { NONE, QUICK, EVERYTHING } 163 164}