001package org.hl7.fhir.r5.elementmodel;
002
003/*
004  Copyright (c) 2011+, HL7, Inc.
005  All rights reserved.
006  
007  Redistribution and use in source and binary forms, with or without modification, 
008  are permitted provided that the following conditions are met:
009    
010   * Redistributions of source code must retain the above copyright notice, this 
011     list of conditions and the following disclaimer.
012   * Redistributions in binary form must reproduce the above copyright notice, 
013     this list of conditions and the following disclaimer in the documentation 
014     and/or other materials provided with the distribution.
015   * Neither the name of HL7 nor the names of its contributors may be used to 
016     endorse or promote products derived from this software without specific 
017     prior written permission.
018  
019  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
020  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
021  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
022  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
023  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
024  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
025  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
026  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
027  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
028  POSSIBILITY OF SUCH DAMAGE.
029  
030 */
031
032
033
034import java.io.IOException;
035import java.io.InputStream;
036import java.io.OutputStream;
037import java.util.List;
038
039import org.hl7.fhir.exceptions.DefinitionException;
040import org.hl7.fhir.exceptions.FHIRException;
041import org.hl7.fhir.exceptions.FHIRFormatError;
042import org.hl7.fhir.r5.context.IWorkerContext;
043import org.hl7.fhir.r5.elementmodel.ParserBase.NamedElement;
044import org.hl7.fhir.r5.formats.FormatUtilities;
045import org.hl7.fhir.r5.formats.IParser.OutputStyle;
046import org.hl7.fhir.r5.model.StructureDefinition;
047import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
048import org.hl7.fhir.r5.utils.ToolingExtensions;
049import org.hl7.fhir.utilities.Utilities;
050import org.hl7.fhir.utilities.i18n.I18nConstants;
051import org.hl7.fhir.utilities.validation.ValidationMessage;
052import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
053import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
054import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
055
056public abstract class ParserBase {
057
058  public class NamedElement {
059    private String name;
060    private Element element;
061    public NamedElement(String name, Element element) {
062      super();
063      this.name = name;
064      this.element = element;
065    }
066    public String getName() {
067      return name;
068    }
069    public Element getElement() {
070      return element;
071    }
072    
073  }
074
075  public interface ILinkResolver {
076    String resolveType(String type);
077    String resolveProperty(Property property);
078    String resolvePage(String string);
079  }
080  
081  public enum ValidationPolicy { NONE, QUICK, EVERYTHING }
082
083  public boolean isPrimitive(String code) {
084    return Utilities.existsInList(code, "boolean", "integer", "integer64", "string", "decimal", "uri", "base64Binary", "instant", "date", "dateTime", "time", "code", "oid", "id", "markdown", "unsignedInt", "positiveInt", "xhtml", "url", "canonical");
085    
086//    StructureDefinition sd = context.fetchTypeDefinition(code);
087//    return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
088        }
089
090        protected IWorkerContext context;
091        protected ValidationPolicy policy;
092  protected List<ValidationMessage> errors;
093  protected ILinkResolver linkResolver;
094  protected boolean showDecorations;
095  
096        public ParserBase(IWorkerContext context) {
097                super();
098                this.context = context;
099                policy = ValidationPolicy.NONE;
100        }
101
102        public void setupValidation(ValidationPolicy policy, List<ValidationMessage> errors) {
103          this.policy = policy;
104          this.errors = errors;
105        }
106
107  public abstract List<NamedElement> parse(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException;
108  
109  public Element parseSingle(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
110    List<NamedElement> res = parse(stream);
111    if (res.size() != 1) {
112      throw new FHIRException("Parsing FHIR content returned multiple elements in a context where only one element is allowed");
113    }
114    return res.get(0).getElement();
115  }
116
117        public abstract void compose(Element e, OutputStream destination, OutputStyle style, String base)  throws FHIRException, IOException;
118
119        //FIXME: i18n should be done here
120        public void logError(int line, int col, String path, IssueType type, String message, IssueSeverity level) throws FHIRFormatError {
121          if (errors != null) {
122            if (policy == ValidationPolicy.EVERYTHING) {
123              ValidationMessage msg = new ValidationMessage(Source.InstanceValidator, type, line, col, path, message, level);
124              errors.add(msg);
125            } else if (level == IssueSeverity.FATAL || (level == IssueSeverity.ERROR && policy == ValidationPolicy.QUICK))
126              throw new FHIRFormatError(message+String.format(" at line %d col %d", line, col));
127          }
128        }
129        
130        
131        protected StructureDefinition getDefinition(int line, int col, String ns, String name) throws FHIRFormatError {
132    if (ns == null) {
133      logError(line, col, name, IssueType.STRUCTURE, context.formatMessage(I18nConstants.THIS__CANNOT_BE_PARSED_AS_A_FHIR_OBJECT_NO_NAMESPACE, name), IssueSeverity.FATAL);
134      return null;
135    }
136    if (name == null) {
137      logError(line, col, name, IssueType.STRUCTURE, context.formatMessage(I18nConstants.THIS_CANNOT_BE_PARSED_AS_A_FHIR_OBJECT_NO_NAME), IssueSeverity.FATAL);
138      return null;
139        }
140          for (StructureDefinition sd : context.allStructures()) {
141            if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && !sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/de-")) {
142              if(name.equals(sd.getType()) && (ns == null || ns.equals(FormatUtilities.FHIR_NS)) && !ToolingExtensions.hasExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))
143                return sd;
144              String sns = ToolingExtensions.readStringExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace");
145              if ((name.equals(sd.getType()) || name.equals(sd.getName())) && ns != null && ns.equals(sns))
146                return sd;
147            }
148          }
149          logError(line, col, name, IssueType.STRUCTURE, context.formatMessage(I18nConstants.THIS_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAMESPACENAME_, ns, name), IssueSeverity.FATAL);
150          return null;
151  }
152
153  protected StructureDefinition getDefinition(int line, int col, String name) throws FHIRFormatError {
154    if (name == null) {
155      logError(line, col, name, IssueType.STRUCTURE, context.formatMessage(I18nConstants.THIS_CANNOT_BE_PARSED_AS_A_FHIR_OBJECT_NO_NAME), IssueSeverity.FATAL);
156      return null;
157        }
158    // first pass: only look at base definitions
159          for (StructureDefinition sd : context.getStructures()) {
160            if (sd.getUrl().equals("http://hl7.org/fhir/StructureDefinition/"+name)) {
161              context.generateSnapshot(sd); 
162              return sd;
163            }
164          }
165    for (StructureDefinition sd : context.getStructures()) {
166      if (name.equals(sd.getType()) && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
167        context.generateSnapshot(sd); 
168        return sd;
169      }
170    }
171          logError(line, col, name, IssueType.STRUCTURE, context.formatMessage(I18nConstants.THIS_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_, name), IssueSeverity.FATAL);
172          return null;
173  }
174
175  public ILinkResolver getLinkResolver() {
176    return linkResolver;
177  }
178
179  public ParserBase setLinkResolver(ILinkResolver linkResolver) {
180    this.linkResolver = linkResolver;
181    return this;
182  }
183
184  public boolean isShowDecorations() {
185    return showDecorations;
186  }
187
188  public void setShowDecorations(boolean showDecorations) {
189    this.showDecorations = showDecorations;
190  }
191
192  public String getImpliedProfile() {
193    return null;
194  }
195
196
197}