001package org.hl7.fhir.r5.renderers.utils;
002
003import java.io.IOException;
004import java.io.UnsupportedEncodingException;
005import java.util.ArrayList;
006import java.util.List;
007
008import org.hl7.fhir.exceptions.FHIRException;
009import org.hl7.fhir.exceptions.FHIRFormatError;
010import org.hl7.fhir.r5.conformance.ProfileUtilities;
011import org.hl7.fhir.r5.model.Base;
012import org.hl7.fhir.r5.model.ElementDefinition;
013import org.hl7.fhir.r5.model.StructureDefinition;
014import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
015import org.hl7.fhir.r5.renderers.ResourceRenderer;
016import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper;
017import org.hl7.fhir.r5.renderers.utils.BaseWrappers.PropertyWrapper;
018import org.hl7.fhir.r5.renderers.utils.BaseWrappers.RendererWrapperImpl;
019import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
020import org.hl7.fhir.r5.renderers.utils.BaseWrappers.WrapperBaseImpl;
021import org.hl7.fhir.r5.utils.NarrativeGenerator;
022import org.hl7.fhir.utilities.Utilities;
023import org.hl7.fhir.utilities.xhtml.XhtmlNode;
024import org.hl7.fhir.utilities.xhtml.XhtmlParser;
025import org.hl7.fhir.utilities.xml.XMLUtil;
026import org.hl7.fhir.utilities.xml.XmlGenerator;
027import org.w3c.dom.Element;
028
029public class DOMWrappers {
030
031
032  public static class BaseWrapperElement extends WrapperBaseImpl implements BaseWrapper {
033    private Element element;
034    private String type;
035    private StructureDefinition structure;
036    private ElementDefinition definition;
037    private List<ElementDefinition> children;
038    private List<PropertyWrapper> list;
039
040    public BaseWrapperElement(ResourceRenderer renderer, Element element, String type, StructureDefinition structure, ElementDefinition definition) {
041      super(renderer);
042      this.element = element;
043      this.type = type;
044      this.structure = structure;
045      this.definition = definition;
046    }
047
048    @Override
049    public Base getBase() throws UnsupportedEncodingException, IOException, FHIRException {
050      if (type == null || type.equals("Resource") || type.equals("BackboneElement") || type.equals("Element"))
051        return null;
052
053      String xml;
054      try {
055        xml = new XmlGenerator().generate(element);
056      } catch (org.hl7.fhir.exceptions.FHIRException e) {
057        throw new FHIRException(e.getMessage(), e);
058      }
059      return renderer.parseType(xml, type);
060    }
061
062    @Override
063    public List<PropertyWrapper> children() {
064      if (list == null) {
065        children = renderer.getContext().getProfileUtilities().getChildList(structure, definition);
066        list = new ArrayList<PropertyWrapper>();
067        for (ElementDefinition child : children) {
068          List<Element> elements = new ArrayList<Element>();
069          XMLUtil.getNamedChildrenWithWildcard(element, tail(child.getPath()), elements);
070          list.add(new PropertyWrapperElement(renderer, structure, child, elements));
071        }
072      }
073      return list;
074    }
075
076    @Override
077    public PropertyWrapper getChildByName(String name) {
078      for (PropertyWrapper p : children())
079        if (p.getName().equals(name))
080          return p;
081      return null;
082    }
083
084  }
085
086  public static class PropertyWrapperElement extends RendererWrapperImpl implements PropertyWrapper {
087
088    private StructureDefinition structure;
089    private ElementDefinition definition;
090    private List<Element> values;
091    private List<BaseWrapper> list;
092
093    public PropertyWrapperElement(ResourceRenderer renderer, StructureDefinition structure, ElementDefinition definition, List<Element> values) {
094      super(renderer);
095      this.structure = structure;
096      this.definition = definition;
097      this.values = values;
098    }
099
100    @Override
101    public String getName() {
102      return tail(definition.getPath());
103    }
104
105    @Override
106    public boolean hasValues() {
107      return values.size() > 0;
108    }
109
110    @Override
111    public List<BaseWrapper> getValues() {
112      if (list == null) {
113        list = new ArrayList<BaseWrapper>();
114        for (Element e : values)
115          list.add(new BaseWrapperElement(renderer, e, determineType(e), structure, definition));
116      }
117      return list;
118    }
119    private String determineType(Element e) {
120      if (definition.getType().isEmpty())
121        return null;
122      if (definition.getType().size() == 1) {
123        if (definition.getType().get(0).getWorkingCode().equals("Element") || definition.getType().get(0).getWorkingCode().equals("BackboneElement"))
124          return null;
125        return definition.getType().get(0).getWorkingCode();
126      }
127      String t = e.getNodeName().substring(tail(definition.getPath()).length()-3);
128
129      if (isPrimitive(Utilities.uncapitalize(t)))
130        return Utilities.uncapitalize(t);
131      else
132        return t;
133    }
134
135    private boolean isPrimitive(String code) {
136      StructureDefinition sd = renderer.getContext().getWorker().fetchTypeDefinition(code);
137      return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
138    }
139
140    @Override
141    public String getTypeCode() {
142      if (definition == null || definition.getType().size() != 1)
143        throw new Error("not handled");
144      return definition.getType().get(0).getWorkingCode();
145    }
146
147    @Override
148    public String getDefinition() {
149      if (definition == null)
150        throw new Error("not handled");
151      return definition.getDefinition();
152    }
153
154    @Override
155    public int getMinCardinality() {
156      if (definition == null)
157        throw new Error("not handled");
158      return definition.getMin();
159    }
160
161    @Override
162    public int getMaxCardinality() {
163      if (definition == null)
164        throw new Error("not handled");
165      return definition.getMax().equals("*") ? Integer.MAX_VALUE : Integer.parseInt(definition.getMax());
166    }
167
168    @Override
169    public StructureDefinition getStructure() {
170      return structure;
171    }
172
173    @Override
174    public BaseWrapper value() {
175      if (getValues().size() != 1)
176        throw new Error("Access single value, but value count is "+getValues().size());
177      return getValues().get(0);
178    }
179
180  }
181
182  public static class ResourceWrapperElement extends WrapperBaseImpl implements ResourceWrapper {
183
184    private Element wrapped;
185    private StructureDefinition definition;
186    private List<ResourceWrapper> list;
187    private List<PropertyWrapper> list2;
188
189    public ResourceWrapperElement(ResourceRenderer renderer, Element wrapped, StructureDefinition definition) {
190      super(renderer);
191      this.wrapped = wrapped;
192      this.definition = definition;
193    }
194
195    @Override
196    public List<ResourceWrapper> getContained() {
197      if (list == null) {
198        List<Element> children = new ArrayList<Element>();
199        XMLUtil.getNamedChildren(wrapped, "contained", children);
200        list = new ArrayList<ResourceWrapper>();
201        for (Element e : children) {
202          Element c = XMLUtil.getFirstChild(e);
203          list.add(new ResourceWrapperElement(renderer, c, renderer.getContext().getWorker().fetchTypeDefinition(c.getNodeName())));
204        }
205      }
206      return list;
207    }
208
209    @Override
210    public String getId() {
211      return XMLUtil.getNamedChildValue(wrapped, "id");
212    }
213
214    @Override
215    public XhtmlNode getNarrative() throws FHIRFormatError, IOException, FHIRException {
216      Element txt = XMLUtil.getNamedChild(wrapped, "text");
217      if (txt == null)
218        return null;
219      Element div = XMLUtil.getNamedChild(txt, "div");
220      if (div == null)
221        return null;
222      try {
223        return new XhtmlParser().parse(new XmlGenerator().generate(div), "div");
224      } catch (org.hl7.fhir.exceptions.FHIRFormatError e) {
225        throw new FHIRFormatError(e.getMessage(), e);
226      } catch (org.hl7.fhir.exceptions.FHIRException e) {
227        throw new FHIRException(e.getMessage(), e);
228      }
229    }
230
231    @Override
232    public String getName() {
233      return wrapped.getNodeName();
234    }
235
236    @Override
237    public List<PropertyWrapper> children() {
238      if (list2 == null) {
239        List<ElementDefinition> children = renderer.getContext().getProfileUtilities().getChildList(definition, definition.getSnapshot().getElement().get(0));
240        list2 = new ArrayList<PropertyWrapper>();
241        for (ElementDefinition child : children) {
242          List<Element> elements = new ArrayList<Element>();
243          XMLUtil.getNamedChildrenWithWildcard(wrapped, tail(child.getPath()), elements);
244          list2.add(new PropertyWrapperElement(renderer, definition, child, elements));
245        }
246      }
247      return list2;
248    }
249
250
251
252    @Override
253    public void describe(XhtmlNode x) {
254      throw new Error("Not done yet");      
255    }
256  }
257
258}