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}