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      boolean complex = false;
077      if (m != null) {
078        String[] maps = (m.getMap() == null ? "" : m.getMap()).split("\\,");
079        if (maps.length == 1) {
080          renderMap(div, maps[0]);
081        } else {
082          complex = true;
083          XhtmlNode ul = div.ul();
084          for (String s : maps) {
085            renderMap(ul.li(), s);
086          }
087        }
088        if (m.hasComment()) {
089          if (!complex) {
090            div.br();
091          }
092          div.i().tx(m.getComment());
093        }
094      }
095    }
096  }
097
098  private String processMap(String s) {
099    if (s.contains(":")) {
100      String l = s.substring(0, s.indexOf(":"));
101      return l;
102    } else {
103      try {
104        ExpressionNode exp = fpe.parse(s);
105        stripFunctions(exp);
106        return exp.toString();
107      } catch (Exception e) {
108        return null;
109      }
110    }
111  }
112
113  private void renderElementLink(XhtmlNode div, ElementDefinition ed) {
114    div.ah(ref()+"#"+ed.getId()).tx(ed.getPath());
115  }
116
117  private void renderMap(XhtmlNode x, String s) {
118    // the approved syntax is id: fhirPath, or it's just rendered directly
119    if (s.contains(":")) {
120      String l = s.substring(0, s.indexOf(":"));
121      String r = s.substring(s.indexOf(":")+1);
122      if (dest != null && dest.getSnapshot().getElementById(l) != null) {
123        x.ah(ref()+"#"+l, l).tx(r);
124      } else {
125        x.tx(r);        
126      }
127    } else {
128      try {
129        ExpressionNode exp = fpe.parse(s);
130        stripFunctions(exp);
131        String p = exp.toString();
132        if (dest.getSnapshot().getElementById(p) != null) {
133          x.ah(ref()+"#"+p, p).tx(s);
134        } else {
135          x.tx(s);        
136        }
137      } catch (Exception e) {
138        x.tx(s);
139      }
140    }
141  }
142
143  private void stripFunctions(ExpressionNode exp) {
144    while (exp.getInner() != null && exp.getInner().getKind() == Kind.Function) {
145      exp.setInner(exp.getInner().getInner());
146    }
147    if (exp.getInner() != null) {
148      stripFunctions(exp.getInner());
149    }
150  }
151}