001package org.hl7.fhir.r5.elementmodel;
002
003import java.io.IOException;
004import java.io.InputStream;
005import java.io.OutputStream;
006import java.util.List;
007
008import org.apache.commons.lang3.NotImplementedException;
009import org.hl7.fhir.exceptions.DefinitionException;
010import org.hl7.fhir.exceptions.FHIRException;
011import org.hl7.fhir.exceptions.FHIRFormatError;
012import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
013import org.hl7.fhir.r5.context.IWorkerContext;
014import org.hl7.fhir.r5.elementmodel.Element.SpecialElement;
015import org.hl7.fhir.r5.formats.IParser.OutputStyle;
016import org.hl7.fhir.r5.model.Base;
017import org.hl7.fhir.r5.model.Resource;
018import org.hl7.fhir.r5.model.StructureDefinition;
019import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage;
020import org.hl7.fhir.utilities.Utilities;
021import org.hl7.fhir.utilities.i18n.I18nConstants;
022import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
023
024@MarkedToMoveToAdjunctPackage
025public class ResourceParser extends ParserBase {
026
027  public ResourceParser(IWorkerContext context) {
028    super(context);
029  }
030
031  @Override
032  public List<ValidatedFragment> parse(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException {
033    throw new NotImplementedException("parse(InputStream stream)"); // doesns't make sense
034  }
035
036  @Override
037  public void compose(Element e, OutputStream destination, OutputStyle style, String base)
038      throws FHIRException, IOException {
039    throw new NotImplementedException("compose(Element e, OutputStream destination, OutputStyle style, String base)"); // doesns't make sense    
040  }
041
042  /**
043   * It's possible to get an element model from an resource by writing it to a stream, and reading it, 
044   * but this loads it directly, and links to the element model from the resource model
045   * 
046   * @param resource
047   * @return
048   * @throws IOException 
049   */
050  public Element parse(Resource resource) {
051    StructureDefinition sd = context.fetchTypeDefinition(resource.fhirType());
052    if (sd == null) {
053      throw new FHIRException("No definition exists for "+resource.fhirType());
054    }
055    Property p = new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities());
056    String path = resource.fhirType();
057    
058    Element e = new Element(resource.fhirType(), p);
059    e.setPath(path);
060    e.setType(resource.fhirType());
061    parseChildren(path, resource, e);
062    e.numberChildren();
063    return e;
064  }
065
066  private void parseChildren(String path, Base src, Element dst) {
067    dst.setSource(src);
068    dst.copyFormatComments(src);
069    List<Property> properties = dst.getProperty().getChildProperties(dst.getName(), null);
070    for (org.hl7.fhir.r5.model.Property c : src.children()) {
071      if (c.hasValues()) {
072        Property p = getPropertyByName(properties, c.getName());
073        if (p == null) {
074          throw new FHIRException("Unable to find property for "+path+"."+c.getName());
075        }
076        int i = 0;
077        for (Base v : c.getValues()) {
078          String npath = path+"."+c.getName()+(p.isList() ? "["+i+"]" : "");
079          i++;
080          String name = c.getName();
081          if (name.endsWith("[x]")) {
082            name = name.substring(0, name.length()-3)+Utilities.capitalize(v.fhirType());
083          }
084          Element n = new Element(name, p);
085          dst.getChildren().add(n);
086          if (v.isPrimitive()) {
087            if (v.fhirType().equals("xhtml")) {
088              n.setXhtml(v.getXhtml());
089              try {
090                n.setValue(new XhtmlComposer(true).compose(n.getXhtml()));
091              } catch (Exception e) {
092                // won't happen here
093              }
094            } else {
095              n.setValue(v.primitiveValue());
096            }
097//            if (!n.getProperty().isChoice() && n.getType().equals("xhtml")) {
098//                try {
099//                  n.setXhtml(new XhtmlParser().setValidatorMode(policy == ValidationPolicy.EVERYTHING).parse(n.getValue(), null).getDocumentElement());
100//                } catch (Exception e) {
101//                  logError(line(main), col(main), npath, IssueType.INVALID, context.formatMessage(I18nConstants.ERROR_PARSING_XHTML_, e.getMessage()), IssueSeverity.ERROR);
102//                }
103//              }
104//            }
105          }      
106          n.setPath(npath);
107          if (p.isResource()) {
108            StructureDefinition sd = context.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(v.fhirType(), null));
109            if (sd == null)
110              throw new FHIRFormatError(context.formatMessage(I18nConstants.CONTAINED_RESOURCE_DOES_NOT_APPEAR_TO_BE_A_FHIR_RESOURCE_UNKNOWN_NAME_, v.fhirType()));
111            n.updateProperty(new Property(context, sd.getSnapshot().getElement().get(0), sd, getProfileUtilities(), getContextUtilities()), SpecialElement.fromProperty(n.getProperty()), p);
112            n.setType(v.fhirType());
113            parseChildren(npath, v, n);
114          } else {
115            parseChildren(npath, v, n);
116          }
117        }       
118      }
119    }
120
121  }
122
123  private Property getPropertyByName(List<Property> properties, String name) {
124    for (Property p : properties) {
125      if (p.getName().equals(name)) {
126        return p;
127      }
128    }
129    return null;
130
131  }
132}
133