
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.StructureDefinition; 014import org.hl7.fhir.r5.model.ValueSet; 015import org.hl7.fhir.r5.renderers.Renderer.RenderingStatus; 016import org.hl7.fhir.r5.renderers.utils.RenderingContext; 017import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; 018import org.hl7.fhir.r5.terminologies.utilities.ValidationResult; 019import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; 020import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; 021import org.hl7.fhir.r5.utils.EOperationOutcome; 022import org.hl7.fhir.r5.utils.ToolingExtensions; 023import org.hl7.fhir.r5.utils.UserDataNames; 024import org.hl7.fhir.r5.utils.sql.Column; 025import org.hl7.fhir.r5.utils.sql.ColumnKind; 026import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 027import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 028import org.hl7.fhir.utilities.Utilities; 029import org.hl7.fhir.utilities.VersionUtilities; 030import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; 031import org.hl7.fhir.utilities.xhtml.NodeType; 032import org.hl7.fhir.utilities.xhtml.XhtmlNode; 033import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; 034import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece; 035import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row; 036import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel; 037import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Title; 038 039@MarkedToMoveToAdjunctPackage 040public class WebTemplateRenderer extends ResourceRenderer { 041 042 public WebTemplateRenderer(RenderingContext context) { 043 super(context); 044 } 045 046 @Override 047 public boolean renderingUsesValidation() { 048 return true; 049 } 050 051 @Override 052 public String buildSummary(ResourceWrapper r) throws UnsupportedEncodingException, IOException { 053 return canonicalTitle(r); 054 } 055 056 @Override 057 public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper wt) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { 058 renderResourceTechDetails(wt, x); 059 060 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, context.getDestDir(), context.isInlineGraphics(), true, ""); 061 TableModel model = gen.new TableModel("wt="+wt.getId(), context.getRules() == GenerationRules.IG_PUBLISHER); 062 model.setAlternating(true); 063 if (context.getRules() == GenerationRules.VALID_RESOURCE || context.isInlineGraphics()) { 064 model.setDocoImg(HierarchicalTableGenerator.help16AsData()); 065 } else { 066 model.setDocoImg(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "help16.png")); 067 } 068 model.getTitles().add(gen.new Title(null, model.getDocoRef(), ("Name"), (context.formatPhrase(RenderingContext.QUEST_LINK)), null, 0)); 069 model.getTitles().add(gen.new Title(null, model.getDocoRef(), ("Card."), (context.formatPhrase(RenderingContext.QUEST_TEXTFOR)), null, 0)); 070 model.getTitles().add(gen.new Title(null, model.getDocoRef(), ("Definition"), (context.formatPhrase(RenderingContext.QUEST_TIMES)), null, 0)); 071 model.getTitles().add(gen.new Title(null, model.getDocoRef(), ("Type"), (context.formatPhrase(RenderingContext.QUEST_TIMES)), null, 0)); 072 model.getTitles().add(gen.new Title(null, model.getDocoRef(), ("Inputs"), (context.formatPhrase(RenderingContext.QUEST_TYPE_ITEM)), null, 0)); 073 074 // first we add a root for the WebTemplate itself 075 Row row = addItem(gen, model.getRows(), wt.child("tree")); 076// for (ResourceWrapper select : vd.children("select")) { 077// renderSelect(status, gen, row.getSubRows(), vd, select); 078// } 079 XhtmlNode xn = gen.generate(model, context.getLocalPrefix(), 1, null); 080 x.addChildNode(xn); 081 082 083 } 084 085 private Row addItem(HierarchicalTableGenerator gen, List<Row> rows, ResourceWrapper item) { 086 Row r = gen.new Row(); 087 rows.add(r); 088 089 r.setIcon("icon_vd_view.png", context.formatPhrase(RenderingContext.QUEST_ROOT)); 090 r.getCells().add(gen.new Cell(null, null, item.primitiveValue("name"), null, null)); 091 092 r.getCells().add(gen.new Cell(null, null, item.primitiveValue("min")+".."+("-1".equals(item.primitiveValue("max")) ? "*" : item.primitiveValue("max")), null, null)); 093 094 String def = (item.primitiveValue("archetype_id") != null ? item.primitiveValue("archetype_id")+"/" : "")+(item.primitiveValue("nodeId") != null ?item.primitiveValue("nodeId") : ""); 095 Cell cell = gen.new Cell(null, null, Utilities.noString(def) ? "--" : def, item.primitiveValue("aqlPath"), null); 096 r.getCells().add(cell); 097 addTermBindings(gen, cell, item); 098 099 100 r.getCells().add(gen.new Cell(null, linkForType(item.primitiveValue("rmType")), item.primitiveValue("rmType"), null, null)); 101 102 cell = gen.new Cell(null, null, null, null, null); 103 r.getCells().add(cell); 104 boolean first = true; 105 for (ResourceWrapper input : item.children("inputs")) { 106 if (first) { 107 first = false; 108 } else { 109 cell.getPieces().add(gen.new Piece("br")); 110 } 111 addInput(gen, cell, input); 112 } 113 114 115 for (ResourceWrapper child : item.children("children")) { 116 addItem(gen, r.getSubRows(), child); 117 } 118 return r; 119 } 120 121 private void addInput(HierarchicalTableGenerator gen, Cell cell, ResourceWrapper input) { 122 if (input.has("suffix")) { 123 cell.getPieces().add(gen.new Piece(null, input.primitiveValue("suffix"), null)); 124 cell.getPieces().add(gen.new Piece(null, " ", null)); 125 } 126 cell.getPieces().add(gen.new Piece(null, input.primitiveValue("type"), null).addStyle("font-weight: bold")); 127 if (input.has("defaultValue")) { 128 cell.getPieces().add(gen.new Piece(null, "(=", null)); 129 cell.getPieces().add(gen.new Piece(null, input.primitiveValue("defaultValue"), null)); 130 cell.getPieces().add(gen.new Piece(null, ")", null)); 131 } 132 if (input.has("validation")) { 133 addValidation(gen, cell, input.child("validation")); 134 } 135 if (input.has("list")) { 136 addList(gen, cell, input.children("list")); 137 } 138 } 139 140 private void addValidation(HierarchicalTableGenerator gen, Cell cell, ResourceWrapper validation) { 141 cell.getPieces().add(gen.new Piece(null, ": ", null)); 142 143 if (validation.has("range") && !validation.has("precision")) { 144 addRange(gen, cell, validation.child("range")); 145 } else { 146 boolean first = true; 147 if (validation.has("range")) { 148 cell.getPieces().add(gen.new Piece(null, "range: ", null)); 149 addRange(gen, cell, validation.child("range")); 150 first = false; 151 } 152 if (validation.has("precision")) { 153 if (!first) { 154 cell.getPieces().add(gen.new Piece(null, "; ", null)); 155 } 156 cell.getPieces().add(gen.new Piece(null, "precision: ", null)); 157 addRange(gen, cell, validation.child("precision")); 158 } 159 } 160 } 161 162 private void addRange(HierarchicalTableGenerator gen, Cell cell, ResourceWrapper range) { 163 String min = range.primitiveValue("min"); 164 String minOp = range.primitiveValue("minOp"); 165 String max = range.primitiveValue("max"); 166 String maxOp = range.primitiveValue("maxOp"); 167 String summ; 168 if ("0".equals(min) && "0".equals(max)) { 169 summ = "0"; 170 } else { 171 summ = minOp+min; 172 if (!Utilities.noString(max)) { 173 summ = summ+","+maxOp+max; 174 } 175 } 176 cell.getPieces().add(gen.new Piece(null, summ, null)); 177 178 } 179 180 private void addList(HierarchicalTableGenerator gen, Cell cell, List<ResourceWrapper> list) { 181 for (ResourceWrapper item : list) { 182 cell.getPieces().add(gen.new Piece("br")); 183 cell.getPieces().add(gen.new Piece(null, "? ", null)); 184 cell.getPieces().add(gen.new Piece(null, item.primitiveValue("label"), item.primitiveValue("value"))); 185 } 186 187 } 188 189 private void addTermBindings(HierarchicalTableGenerator gen, Cell cell, ResourceWrapper item) { 190 for (ResourceWrapper tb : item.children("termBindings")) { 191 String code = tb.primitiveValue("code"); 192 ResourceWrapper v = tb.child("value"); 193 String value = v.primitiveValue("value"); 194 if (value.contains("::")) { 195 value = value.substring(value.indexOf("::")+2).replace("]", ""); 196 } 197 String tid = v.primitiveValue("terminologyId"); 198 String pfx = ""; 199 String link = null; 200 String hint = null; 201 switch (code) { 202 case "SNOMED-CT" : 203 pfx = "SCT:"; 204 link = getLinkForCode("http://snomed.info/sct", null, value); 205 ValidationResult vr = context.getContext().validateCode(context.getTerminologyServiceOptions(), "http://snomed.info/sct", null, value, null); 206 if (vr.isOk()) { 207 hint = "SNOMED CT "+value+": "+vr.getDisplay(); 208 } 209 break; 210 case "LOINC" : 211 pfx = "LN:"; 212 link = getLinkForCode("http://loinc.org", null, value); 213 vr = context.getContext().validateCode(context.getTerminologyServiceOptions(), "http://loinc.org", null, value, null); 214 if (vr.isOk()) { 215 hint = "LOINC "+value+": "+vr.getDisplay(); 216 } 217 break; 218 case "LNC205" : 219 // what is this? 220 break; 221 default: 222 System.out.println("?"); 223 } 224 cell.addPiece(gen.new Piece(null, " ", null)); 225 cell.addPiece(gen.new Piece(link, pfx+value, hint)); 226 } 227 } 228 229 private String linkForType(String t) { 230 if (t == null) { 231 return null; 232 } 233 StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, "http://openehr.org/fhir/StructureDefinition/"+t); 234 if (sd == null) { 235 sd = context.getContext().fetchTypeDefinition(t); 236 } 237 if (sd != null) { 238 return sd.getWebPath(); 239 } 240 return null; 241 } 242 243 protected XhtmlNode renderResourceTechDetails(ResourceWrapper r, XhtmlNode x) throws UnsupportedEncodingException, FHIRException, IOException { 244 return renderResourceTechDetails(r, x, (context.isContained() && r.getId() != null ? "#"+r.getId() : r.getId())); 245 } 246 247 protected XhtmlNode renderResourceTechDetails(ResourceWrapper r, XhtmlNode x, String desc) throws UnsupportedEncodingException, FHIRException, IOException { 248 XhtmlNode p = x.para().attribute("class", "res-header-id"); 249 if (desc == null) { 250 p.b().tx(context.formatPhrase(context.isTechnicalMode() && !isInner() ? RenderingContext.PROF_DRIV_GEN_NARR_TECH : RenderingContext.PROF_DRIV_GEN_NARR, "WebTemplate", "")); 251 } else { 252 p.b().tx(context.formatPhrase(context.isTechnicalMode() && !isInner() ? RenderingContext.PROF_DRIV_GEN_NARR_TECH : RenderingContext.PROF_DRIV_GEN_NARR, "WebTemplate", desc)); 253 } 254 255 // first thing we do is lay down the resource anchors. 256 String tid = r.primitiveValue("templateId"); 257 if (!Utilities.noString(tid)) { 258 String sid = "hc"+tid; 259 if (!context.hasAnchor(sid)) { 260 context.addAnchor(sid); 261 x.an(context.prefixAnchor(sid)); 262 } 263 } 264 265 if (context.isTechnicalMode()) { 266 RenderingStatus status = new RenderingStatus(); 267 268 String lang = r.primitiveValue("defaultLanguage"); 269 ResourceWrapper versionId = r.child("semver"); 270 271 if (lang != null || versionId != null) { 272 XhtmlNode div = x.div().style("display: inline-block").style("background-color: #d9e0e7").style("padding: 6px") 273 .style("margin: 4px").style("border: 1px solid #8da1b4") 274 .style("border-radius: 5px").style("line-height: 60%"); 275 276 boolean sfirst = true; 277 p = plateStyle(div.para()); 278 if (tid != null) { 279 p.tx(context.formatPhrase(RenderingContext.RES_REND_TEMPLATE_ID, tid)); 280 sfirst = false; 281 } 282 if (versionId != null) { 283 p.tx(context.formatPhrase(RenderingContext.RES_REND_VER, versionId.primitiveValue())); 284 sfirst = false; 285 } 286 if (lang != null) { 287 if (!sfirst) { 288 p.tx("; "); 289 } 290 p.tx(context.formatPhrase(RenderingContext.RES_REND_LANGUAGE, lang)); 291 sfirst = false; 292 } 293 } 294 } 295 return null; 296 } 297 298}