001package org.hl7.fhir.r5.renderers.mappings;
002
003import java.util.ArrayList;
004import java.util.List;
005
006import org.hl7.fhir.r5.fhirpath.ExpressionNode;
007import org.hl7.fhir.r5.fhirpath.FHIRPathEngine;
008import org.hl7.fhir.r5.fhirpath.TypeDetails;
009import org.hl7.fhir.r5.fhirpath.ExpressionNode.Function;
010import org.hl7.fhir.r5.fhirpath.ExpressionNode.Kind;
011import org.hl7.fhir.r5.fhirpath.ExpressionNode.Operation;
012import org.hl7.fhir.r5.model.Base;
013import org.hl7.fhir.r5.model.ElementDefinition;
014import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent;
015import org.hl7.fhir.r5.model.StructureDefinition;
016import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionMappingComponent;
017import org.hl7.fhir.r5.renderers.StructureDefinitionRenderer.Column;
018import org.hl7.fhir.r5.renderers.utils.RenderingContext;
019import org.hl7.fhir.utilities.SourceLocation;
020import org.hl7.fhir.utilities.xhtml.XhtmlNode;
021
022public class StructureDefinitionMappingProvider extends ModelMappingProvider {
023
024  private StructureDefinition sd;
025  private StructureDefinitionMappingComponent map;
026  private FHIRPathEngine fpe;
027
028  public StructureDefinitionMappingProvider(RenderingContext context, StructureDefinition dest, boolean reverse, StructureDefinition sd, StructureDefinitionMappingComponent map, FHIRPathEngine fpe) {
029    super(context, dest, reverse);
030    this.sd = sd;
031    this.map = map;
032    this.fpe = fpe;
033  }
034
035  @Override
036  public Column makeColumn(String id) {
037    return new Column(id, map.getName(), dest == null ? "??" : dest.present(), null);
038  }
039
040  @Override
041  public void render(ElementDefinition element, XhtmlNode div) {
042    if (reverse) {
043      List<ElementDefinition> sources = new ArrayList<>();
044      for (ElementDefinition ed : dest.getSnapshot().getElement()) {
045        ElementDefinitionMappingComponent m = null;
046        for (ElementDefinitionMappingComponent t : ed.getMapping()) {
047          if (t.hasIdentity() && t.getIdentity().equals(map.getIdentity())) {
048            m = t;
049          }
050        }
051        if (m != null) {
052          String[] maps = (m.getMap() == null ? "" : m.getMap()).split("\\,");
053          for (String s : maps) {
054            String tgt = processMap(s);
055            if (tgt != null && tgt.equals(element.getId()) ||  tgt.equals(element.getPath())) {
056              sources.add(ed);
057            }
058          }
059        }
060      }
061      if (sources.size() == 1) {
062        renderElementLink(div, sources.get(0));
063      } else {
064        XhtmlNode ul = div.ul();
065        for (ElementDefinition ed : sources) {
066          renderElementLink(ul.li(), ed);
067        }
068      }
069    } else {
070      ElementDefinitionMappingComponent m = null;
071      for (ElementDefinitionMappingComponent t : element.getMapping()) {
072        if (t.hasIdentity() && t.getIdentity().equals(map.getIdentity())) {
073          m = t;
074        }
075      }
076      if (m != null) {
077        String[] maps = (m.getMap() == null ? "" : m.getMap()).split("\\,");
078        if (maps.length == 1) {
079          renderMap(div, maps[0]);
080        } else {
081          XhtmlNode ul = div.ul();
082          for (String s : maps) {
083            renderMap(ul.li(), s);
084          }
085        }
086        if (m.hasComment()) {
087          div.i().tx(m.getComment());
088        }
089      }
090    }
091  }
092
093  private String processMap(String s) {
094    if (s.contains(":")) {
095      String l = s.substring(0, s.indexOf(":"));
096      return l;
097    } else {
098      try {
099        ExpressionNode exp = fpe.parse(s);
100        stripFunctions(exp);
101        return exp.toString();
102      } catch (Exception e) {
103        return null;
104      }
105    }
106  }
107
108  private void renderElementLink(XhtmlNode div, ElementDefinition ed) {
109    div.ah(ref()+"#"+ed.getId()).tx(ed.getPath());
110  }
111
112  private void renderMap(XhtmlNode x, String s) {
113    // the approved syntax is id: fhirPath, or it's just rendered directly
114    if (s.contains(":")) {
115      String l = s.substring(0, s.indexOf(":"));
116      String r = s.substring(s.indexOf(":")+1);
117      if (dest != null && dest.getSnapshot().getElementById(l) != null) {
118        x.ah(ref()+"#"+l, l).tx(r);
119      } else {
120        x.tx(r);        
121      }
122    } else {
123      try {
124        ExpressionNode exp = fpe.parse(s);
125        stripFunctions(exp);
126        String p = exp.toString();
127        if (dest.getSnapshot().getElementById(p) != null) {
128          x.ah(ref()+"#"+p, p).tx(s);
129        } else {
130          x.tx(s);        
131        }
132      } catch (Exception e) {
133        x.tx(s);
134      }
135    }
136  }
137
138  private void stripFunctions(ExpressionNode exp) {
139    while (exp.getInner() != null && exp.getInner().getKind() == Kind.Function) {
140      exp.setInner(exp.getInner().getInner());
141    }
142    if (exp.getInner() != null) {
143      stripFunctions(exp.getInner());
144    }
145  }
146}