001package org.hl7.fhir.r5.renderers;
002
003import java.io.IOException;
004import java.io.UnsupportedEncodingException;
005import java.util.List;
006
007import org.apache.commons.codec.binary.Base64;
008import org.hl7.fhir.exceptions.DefinitionException;
009import org.hl7.fhir.exceptions.FHIRException;
010import org.hl7.fhir.exceptions.FHIRFormatError;
011import org.hl7.fhir.r5.model.CanonicalResource;
012import org.hl7.fhir.r5.model.Resource;
013import org.hl7.fhir.r5.model.ValueSet;
014import org.hl7.fhir.r5.renderers.utils.RenderingContext;
015import org.hl7.fhir.r5.renderers.utils.ResourceWrapper;
016import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules;
017import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType;
018import org.hl7.fhir.r5.utils.EOperationOutcome;
019import org.hl7.fhir.r5.utils.ToolingExtensions;
020import org.hl7.fhir.r5.utils.UserDataNames;
021import org.hl7.fhir.r5.utils.sql.Column;
022import org.hl7.fhir.r5.utils.sql.ColumnKind;
023import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
024import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage;
025import org.hl7.fhir.utilities.Utilities;
026import org.hl7.fhir.utilities.VersionUtilities;
027import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator;
028import org.hl7.fhir.utilities.xhtml.NodeType;
029import org.hl7.fhir.utilities.xhtml.XhtmlNode;
030import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell;
031import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece;
032import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row;
033import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel;
034import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Title;
035
036@MarkedToMoveToAdjunctPackage
037public class ViewDefinitionRenderer extends ResourceRenderer {
038  
039  public ViewDefinitionRenderer(RenderingContext context) { 
040    super(context); 
041  } 
042
043  @Override
044  public boolean renderingUsesValidation() {
045    return true;
046  }
047  
048  @Override
049  public String buildSummary(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
050    return canonicalTitle(r);
051  }
052
053  @Override
054  public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper vd) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
055    renderResourceTechDetails(vd, x);
056    genSummaryTable(status, x, vd);
057    
058    XhtmlNode p = x.para();
059    p.tx("This view acts on the "+vd.primitiveValue("resource")+" resource");
060    var vers = vd.children("fhirVersion");
061    for (int i = 0; i <  vers.size(); i++) {
062      if (i == 0) {
063        p.tx(" for version"+(vers.size() == 1 ? "" : "s")+" ");
064      } else if (i == vers.size() - 1) {
065        p.tx(" and ");
066      } else {
067        p.tx(", ");
068      }
069      String ver = vers.get(i).primitiveValue();
070      p.ah(VersionUtilities.getSpecUrl(ver)).tx(VersionUtilities.getNameForVersion(ver));
071    }
072    if (vd.has("resourceProfile")) {
073      p.tx(" using profile ?");
074    }    
075    if (vd.has("name")) {
076      p.tx(" to produce a table named \"");
077      p.code().tx(vd.primitiveValue("name"));
078      p.tx("\"");      
079    } else {
080      p.tx(" to produce a unnamed table");
081    }
082    if (vd.has("where")) {
083      List<ResourceWrapper> wheres = vd.children("where");
084      if (wheres.size() == 1) {
085        p.tx(" where ");
086        p.code().tx(wheres.get(0).primitiveValue("path"));
087        if (wheres.get(0).has("description")) {
088          p.tx(" ("+wheres.get(0).primitiveValue("description")+")");
089        }
090        p.tx(".");
091      } else {
092        p.tx(" where:");
093        XhtmlNode ul = x.ul();
094        for (ResourceWrapper w : wheres) {
095          XhtmlNode li = ul.li();
096          li.code().tx(wheres.get(0).primitiveValue("path"));
097          if (wheres.get(0).has("description")) {
098            li.tx(" ("+wheres.get(0).primitiveValue("description")+")");
099          }
100        }
101      }
102    } else {
103      p.tx(".");
104    }
105    
106    if (vd.hasUserData(UserDataNames.db_columns)) {
107      x.para().tx("The table contains the following columns:");
108      List<Column> cols = (List<Column>) vd.getUserData(UserDataNames.db_columns);
109      boolean hasNotes = false;
110      for (Column col : cols) {
111        hasNotes = hasNotes || !Utilities.noString(col.getNotes());
112      }
113      
114      XhtmlNode t2 = x.table("grid", false);
115      XhtmlNode tr = t2.tr();
116      tr.th().tx("Name");
117      tr.th().tx("Fhir Type");
118      tr.th().tx("SQL Type");
119      tr.th().tx("Collection");
120      if (hasNotes) {
121        tr.th().tx("Notes");
122      }
123      
124      for (Column col : cols) {
125        tr = t2.tr();
126        tr.td().tx(col.getName());
127        tr.td().tx(col.getType());
128        tr.td().tx(col.getKind().name());
129        tr.td().tx(col.isColl() == null ? "" : col.isColl() ? "Y" : "N");
130        if (hasNotes) {
131          tr.td().tx(col.getNotes());
132        }
133      }
134    }
135    if (vd.has("constant")) {
136      x.para().tx("Constants:");
137
138      XhtmlNode t2 = x.table("grid", false);
139      XhtmlNode tr = t2.tr();
140      tr.th().tx("Name");
141      tr.th().tx("Value");
142      
143      for (ResourceWrapper cnst : vd.children("constant")) {
144        tr = t2.tr();
145        tr.td().tx(cnst.primitiveValue("name"));
146        tr.td().tx(cnst.primitiveValue("value"));
147      }
148    }
149    // row 4: select tree
150    x.para().tx("Selection Rules:");
151    
152    HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, context.getDestDir(), context.isInlineGraphics(), true, ""); 
153    TableModel model = gen.new TableModel("vd="+vd.getId(), context.getRules() == GenerationRules.IG_PUBLISHER);     
154    model.setAlternating(true); 
155    if (context.getRules() == GenerationRules.VALID_RESOURCE || context.isInlineGraphics()) { 
156      model.setDocoImg(HierarchicalTableGenerator.help16AsData());     
157    } else { 
158      model.setDocoImg(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "help16.png")); 
159    }  
160    model.getTitles().add(gen.new Title(null, model.getDocoRef(), ("Item"), (context.formatPhrase(RenderingContext.QUEST_LINK)), null, 0)); 
161    model.getTitles().add(gen.new Title(null, model.getDocoRef(), ("Coll"), (context.formatPhrase(RenderingContext.QUEST_TEXTFOR)), null, 0)); 
162    model.getTitles().add(gen.new Title(null, model.getDocoRef(), ("Type"), (context.formatPhrase(RenderingContext.QUEST_TIMES)), null, 0)); 
163    model.getTitles().add(gen.new Title(null, model.getDocoRef(), ("Description"), (context.formatPhrase(RenderingContext.QUEST_TYPE_ITEM)), null, 0)); 
164 
165    // first we add a root for the questionaire itself 
166    Row row = addViewRoot(gen, model.getRows(), vd); 
167    for (ResourceWrapper select : vd.children("select")) { 
168      renderSelect(status, gen, row.getSubRows(), vd, select); 
169    } 
170    XhtmlNode xn = gen.generate(model, context.getLocalPrefix(), 1, null); 
171    x.addChildNode(xn); 
172  }
173
174  private void renderSelect(RenderingStatus status, HierarchicalTableGenerator gen, List<Row> rows, ResourceWrapper vd, ResourceWrapper select) {
175    Row r = gen.new Row(); 
176    rows.add(r); 
177    
178    r.setIcon("icon_vd_select.png", "Select"); 
179    Cell c1 = gen.new Cell(null, null, "Select", null, null); 
180    r.getCells().add(c1); 
181    r.getCells().add(gen.new Cell(null, null, null, null, null)); 
182    r.getCells().add(gen.new Cell(null, null, null, null, null));
183    Cell cell = gen.new Cell(null, null, null, null, null);
184    if (select.has("forEach")) {
185      addFHIRPath(cell.getPieces().get(0), "for each ", select.primitiveValue("forEach"), null);     
186    } else if (select.has("forEachOrNull")) {
187      addFHIRPath(cell.getPieces().get(0), "for each ", select.primitiveValue("forEachOrNull"), ", or null");        
188    } else {
189    }
190    r.getCells().add(cell);
191
192    for (ResourceWrapper column : select.children("column")) { 
193      renderColumn(status, gen, r.getSubRows(), vd, select, column); 
194    }      
195
196    for (ResourceWrapper child : select.children("select")) { 
197      renderSelect(status, gen, r.getSubRows(), vd, child); 
198    }      
199
200  }
201
202  private void renderColumn(RenderingStatus status, HierarchicalTableGenerator gen, List<Row> rows, ResourceWrapper vd, ResourceWrapper select, ResourceWrapper column) {
203    Row r = gen.new Row(); 
204    rows.add(r); 
205    
206    r.setIcon("icon_vd_col.png", "Column"); 
207    Cell c1 = gen.new Cell(null, null, column.primitiveValue("name"), null, null); 
208    r.getCells().add(c1); 
209    String coll = column.has("collection") ? "true".equals(column.primitiveValue("collection")) ? "Y" : "N" : "";
210    r.getCells().add(gen.new Cell(null, null, coll, null, null)); 
211    r.getCells().add(gen.new Cell(null, null, column.primitiveValue("type"), null, null));
212    Cell cell = gen.new Cell(null, null, null, null, null);
213    addFHIRPath(cell.getPieces().get(0), null, column.primitiveValue("path"), null); 
214    if (column.has("description")) {
215      cell.addPiece(gen.new Piece("br")); 
216      cell.addPiece(gen.new Piece(null, column.primitiveValue("description"), null)); 
217    }
218    for (ResourceWrapper tag : column.children("tag")) {
219      cell.addPiece(gen.new Piece("br")); 
220      cell.addPiece(gen.new Piece(null, tag.primitiveValue("name")+"="+tag.primitiveValue("value"), null)); 
221    }
222    r.getCells().add(cell);  
223  }
224
225  private void addFHIRPath(Piece p, String pfx, String expr, String sfx) {
226    XhtmlNode x = new XhtmlNode(NodeType.Element, "span").style("font-size: 11px"); 
227    p.addHtml(x); 
228    if (pfx != null) {
229      x.tx(pfx);
230    }
231    x.code(expr); 
232    if (sfx != null) {
233      x.tx(sfx);
234    }
235  }
236
237  private Row addViewRoot(HierarchicalTableGenerator gen, List<Row> rows, ResourceWrapper vd) throws IOException { 
238    Row r = gen.new Row(); 
239    rows.add(r); 
240
241    r.setIcon("icon_vd_view.png", context.formatPhrase(RenderingContext.QUEST_ROOT)); 
242    r.getCells().add(gen.new Cell(null, null, vd.primitiveValue("name"), null, null)); 
243    r.getCells().add(gen.new Cell(null, null, "", null, null)); 
244    r.getCells().add(gen.new Cell(null, null, vd.primitiveValue("resource"), null, null)); 
245    r.getCells().add(gen.new Cell(null, null, vd.primitiveValue("description"), null, null)); 
246    return r;     
247  } 
248
249}