
001package org.hl7.fhir.r5.renderers; 002 003import java.io.IOException; 004import java.io.UnsupportedEncodingException; 005import java.util.ArrayList; 006import java.util.List; 007 008import javax.annotation.Nonnull; 009 010import org.hl7.fhir.exceptions.DefinitionException; 011import org.hl7.fhir.exceptions.FHIRException; 012import org.hl7.fhir.exceptions.FHIRFormatError; 013import org.hl7.fhir.r5.context.ContextUtilities; 014import org.hl7.fhir.r5.model.CanonicalResource; 015import org.hl7.fhir.r5.model.Resource; 016import org.hl7.fhir.r5.model.StructureDefinition; 017import org.hl7.fhir.r5.model.ValueSet; 018import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; 019import org.hl7.fhir.r5.renderers.utils.RenderingContext; 020import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; 021import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; 022import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; 023import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; 024import org.hl7.fhir.r5.utils.EOperationOutcome; 025import org.hl7.fhir.r5.utils.ToolingExtensions; 026import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 027import org.hl7.fhir.utilities.Utilities; 028import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; 029import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; 030import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece; 031import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row; 032import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel; 033import org.hl7.fhir.utilities.xhtml.NodeType; 034import org.hl7.fhir.utilities.xhtml.XhtmlNode; 035 036@MarkedToMoveToAdjunctPackage 037public class QuestionnaireRenderer extends TerminologyRenderer { 038 039 public QuestionnaireRenderer(RenderingContext context) { 040 super(context); 041 } 042 043 @Override 044 public String buildSummary(ResourceWrapper r) throws UnsupportedEncodingException, IOException { 045 return canonicalTitle(r); 046 } 047 048 @Override 049 public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper q) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { 050 renderResourceTechDetails(q, x); 051 genSummaryTable(status, x, (CanonicalResource) q.getResourceNative()); 052 switch (context.getQuestionnaireMode()) { 053 case FORM: 054 renderForm(status, x, q); 055 break; 056 case LINKS: 057 renderLinks(status, x, q); 058 break; 059 case LOGIC: 060 renderLogic(status, x, q); 061 break; 062 case DEFNS: 063 renderDefns(status, x, q); 064 break; 065 case TREE: 066 renderTree(status, x, q); 067 break; 068 default: 069 throw new Error("Unknown questionnaire Renderer Mode"); 070 } 071 072 boolean first = true; 073 for (ResourceWrapper cont : q.children("contained")) { 074 if (first) { 075 x.h2().tx("Contained Resources"); 076 first = false; 077 } 078 x.hr(); 079 RendererFactory.factory(cont, context.forContained()).setInner(true).buildNarrative(status, x, cont); 080 } 081 } 082 083 public void renderTree(RenderingStatus status, XhtmlNode x, ResourceWrapper q) throws UnsupportedEncodingException, IOException { 084 boolean hasFlags = checkForFlags(q.children("item")); 085 boolean doOpts = context.getDefinitionsTarget() == null && hasAnyOptions(q.children("item")); 086 087 if (doOpts) { 088 x.b().tx(context.formatPhrase(RenderingContext.QUEST_STRUCT)); 089 } 090 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, context.getDestDir(), context.isInlineGraphics(), true, ""); 091 TableModel model = gen.new TableModel("qtree="+q.getId(), context.getRules() == GenerationRules.IG_PUBLISHER); 092 model.setAlternating(true); 093 if (context.getRules() == GenerationRules.VALID_RESOURCE || context.isInlineGraphics()) { 094 model.setDocoImg(HierarchicalTableGenerator.help16AsData()); 095 } else { 096 model.setDocoImg(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "help16.png")); 097 } 098 model.setDocoRef(context.getLink(KnownLinkType.SPEC)+"formats.html#table"); 099 model.getTitles().add(gen.new Title(null, model.getDocoRef(), (context.formatPhrase(RenderingContext.QUEST_LINKID)), (context.formatPhrase(RenderingContext.QUEST_LINK)), null, 0)); 100 model.getTitles().add(gen.new Title(null, model.getDocoRef(), (context.formatPhrase(RenderingContext.QUEST_TEXT)), (context.formatPhrase(RenderingContext.QUEST_TEXTFOR)), null, 0)); 101 model.getTitles().add(gen.new Title(null, model.getDocoRef(), (context.formatPhrase(RenderingContext.GENERAL_CARDINALITY)), (context.formatPhrase(RenderingContext.QUEST_TIMES)), null, 0)); 102 model.getTitles().add(gen.new Title(null, model.getDocoRef(), (context.formatPhrase(RenderingContext.GENERAL_TYPE)), (context.formatPhrase(RenderingContext.QUEST_TYPE_ITEM)), null, 0)); 103 if (hasFlags) { 104 model.getTitles().add(gen.new Title(null, model.getDocoRef(), (context.formatPhrase(RenderingContext.GENERAL_FLAGS)), (context.formatPhrase(RenderingContext.QUEST_ATTRIBUTES)), null, 0)); 105 } 106 model.getTitles().add(gen.new Title(null, model.getDocoRef(), (context.formatPhrase(RenderingContext.GENERAL_DESC_CONST)), (context.formatPhrase(RenderingContext.QUEST_ADD_INFO)), null, 0)); 107 108 // first we add a root for the questionaire itself 109 Row row = addTreeRoot(gen, model.getRows(), q, hasFlags); 110 for (ResourceWrapper i : q.children("item")) { 111 renderTreeItem(status, gen, row.getSubRows(), q, i, hasFlags); 112 } 113 XhtmlNode xn = gen.generate(model, context.getLocalPrefix(), 1, null); 114 x.addChildNode(xn); 115 if (doOpts) { 116 renderOptions(q, x); 117 } 118 } 119 120 private void renderOptions(ResourceWrapper q, XhtmlNode x) { 121 if (hasAnyOptions(q.children("item"))) { 122 x.hr(); 123 x.para().b().tx(context.formatPhrase(RenderingContext.QUEST_OPT)); 124 renderOptions(q.children("item"), x); 125 } 126 } 127 128 private void renderOptions(List<ResourceWrapper> items, XhtmlNode x) { 129 for (ResourceWrapper i : items) { 130 renderItemOptionsList(x, i); 131 renderOptions(i.children("item"), x); 132 } 133 } 134 135 public void renderItemOptions(XhtmlNode x, ResourceWrapper i) { 136 if (i.has("answerOption")) { 137 for (ResourceWrapper opt : i.children("answerOption")) { 138 String value = "??"; 139 String text = "??"; 140 ResourceWrapper v = opt.child("value"); 141 if (v.isPrimitive()) { 142 value = v.primitiveValue(); 143 text = v.primitiveValue(); 144 } else if (v.fhirType().equals("Coding")) { 145 if (v.has("system")) { 146 value = v.primitiveValue("system")+"#"+v.primitiveValue("code"); 147 } else { 148 value = v.primitiveValue("code"); 149 } 150 if (v.has("display")) { 151 text = v.primitiveValue("display"); 152 } else { 153 text = v.primitiveValue("code"); 154 } 155 } 156 if (value == null) { 157 value = "??"; 158 } 159 if (text == null) { 160 text = "??"; 161 } 162 boolean selected = "true".equals(opt.primitiveValue("initialSelected")); 163 x.option(value, text, selected); 164 } 165 } 166 } 167 168 public void renderItemOptionsList(XhtmlNode x, ResourceWrapper i) { 169 if (i.has("answerOption")) { 170 x.an(context.prefixAnchor("opt-item."+i.primitiveValue("linkId"))); 171 x.para().b().tx(context.formatPhrase(RenderingContext.QUEST_ANSW, i.primitiveValue("linkId"))+" "); 172 XhtmlNode ul = x.ul(); 173 for (ResourceWrapper opt : i.children("answerOption")) { 174 XhtmlNode li = ul.li(); 175 li.style("font-size: 11px"); 176 ResourceWrapper v = opt.child("value"); 177 if (v.isPrimitive()) { 178 li.tx(v.primitiveValue()); 179 } else if (v.fhirType().equals("Coding")) { 180 String link = v.has("system") ? new ContextUtilities(context.getWorker()).getLinkForUrl(context.getLink(KnownLinkType.SPEC), v.primitiveValue("system")) : null; 181 if (link == null) { 182 li.tx(v.primitiveValue("system")+"#"+v.primitiveValue("code")); 183 } else { 184 li.ah(link).tx(displaySystem(v.primitiveValue("system"))); 185 li.tx(": "+v.primitiveValue("code")); 186 } 187 if (v.has("display")) { 188 li.tx(" (\""+v.primitiveValue("display")+"\")"); 189 } 190 } else { 191 li.tx("??"); 192 } 193 } 194 } 195 } 196 197 private boolean hasAnyOptions(List<ResourceWrapper> items) { 198 for (ResourceWrapper i : items) { 199 if (i.has("answerOption")) { 200 return true; 201 } 202 if (hasAnyOptions(i.children("item"))) { 203 return true; 204 } 205 } 206 return false; 207 } 208 209 private boolean checkForFlags(List<ResourceWrapper> items) { 210 for (ResourceWrapper i : items) { 211 if (checkForFlags(i)) { 212 return true; 213 } 214 } 215 return false; 216 } 217 218 private boolean checkForFlags(ResourceWrapper i) { 219 if (i.has("readOnly")) { 220 return true; 221 } 222 if ("true".equals(i.extensionString(ToolingExtensions.EXT_Q_IS_SUBJ))) { 223 return true; 224 } 225 if ("true".equals(i.extensionString(ToolingExtensions.EXT_Q_HIDDEN))) { 226 return true; 227 } 228 if ("true".equals(i.extensionString(ToolingExtensions.EXT_Q_OTP_DISP))) { 229 return true; 230 } 231 if (i.hasExtension(ToolingExtensions.EXT_O_LINK_PERIOD)) { 232 return true; 233 } 234 if (i.hasExtension(ToolingExtensions.EXT_Q_CHOICE_ORIENT)) { 235 return true; 236 } 237 if (i.hasExtension(ToolingExtensions.EXT_Q_DISPLAY_CAT)) { 238 return true; 239 } 240 return checkForFlags(i.children("item")); 241 } 242 243 244 245 private Row addTreeRoot(HierarchicalTableGenerator gen, List<Row> rows, ResourceWrapper q, boolean hasFlags) throws IOException { 246 Row r = gen.new Row(); 247 rows.add(r); 248 249 r.setIcon("icon_q_root.gif", context.formatPhrase(RenderingContext.QUEST_ROOT)); 250 r.getCells().add(gen.new Cell(null, null, q.primitiveValue("name"), null, null)); 251 r.getCells().add(gen.new Cell(null, null, q.primitiveValue("description"), null, null)); 252 r.getCells().add(gen.new Cell(null, null, "", null, null)); 253 r.getCells().add(gen.new Cell(null, null, context.formatPhrase(RenderingContext.QUEST_QUEST), null, null)); 254 if (hasFlags) { 255 r.getCells().add(gen.new Cell(null, null, "", null, null)); 256 } 257 r.getCells().add(gen.new Cell(null, null, q.has("url") ? q.has("version") ? q.primitiveValue("url")+"#"+q.primitiveValue("version") : q.primitiveValue("url") : "", null, null)); 258 return r; 259 } 260 261 private String getSpecLink(String path) { 262 return Utilities.pathURL(context.getLink(KnownLinkType.SPEC), path); 263 } 264 265 private String getSDCLink(String url, String path) { 266 StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, url); 267 if (sd == null) { 268 sd = context.getContext().fetchResource(StructureDefinition.class, path); 269 } 270 if (sd != null && sd.hasWebPath()) { 271 return sd.getWebPath(); 272 } else if (Utilities.isAbsoluteUrl(path)) { 273 return path.replace("StructureDefinition/", "StructureDefinition-")+".html"; 274 } else if ("http://hl7.org/fhir/uv/sdc".equals(context.getPkp().getCanonicalForDefaultContext())) { 275 return Utilities.pathURL(path); // for now? 276 } else { 277 return Utilities.pathURL("http://hl7.org/fhir/uv/sdc", path); // for now? 278 } 279 } 280 281 private void renderTreeItem(RenderingStatus status, HierarchicalTableGenerator gen, List<Row> rows, ResourceWrapper q, ResourceWrapper i, boolean hasFlags) throws IOException { 282 Row r = gen.new Row(); 283 rows.add(r); 284 String type = i.primitiveValue("type"); 285 286 r.setIcon("icon-q-"+type.toLowerCase()+".png", type); 287 Cell c1 = gen.new Cell(null, context.getDefinitionsTarget() == null ? "" : context.getDefinitionsTarget()+"#item."+i.primitiveValue("linkId"), i.primitiveValue("linkId"), null, null); 288 c1.setId("item."+i.primitiveValue("linkId")); 289 r.getCells().add(c1); 290 String txt = (i.has("prefix") ? i.primitiveValue("prefix") + ". " : "") + i.primitiveValue("text"); 291 r.getCells().add(gen.new Cell(null, null, txt, null, null)); 292 r.getCells().add(gen.new Cell(null, null, ("true".equals(i.primitiveValue("required")) ? "1" : "0")+".."+("true".equals(i.primitiveValue("repeats")) ? "*" : "1"), null, null)); 293 if (i.child("type").hasExtension(ToolingExtensions.EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL)) { 294 status.setExtensions(true); 295 String t = i.child("type").extensionString(ToolingExtensions.EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL); 296 r.getCells().add(gen.new Cell(null, context.getLink(KnownLinkType.SPEC)+"codesystem-item-type.html#item-type-"+t, t, null, null)); 297 } else { 298 r.getCells().add(gen.new Cell(null, context.getLink(KnownLinkType.SPEC)+"codesystem-item-type.html#item-type-"+type, type, null, null)); 299 } 300 301 if (hasFlags) { 302 // flags: 303 Cell flags = gen.new Cell(); 304 r.getCells().add(flags); 305 if ("true".equals(i.primitiveValue("readOnly"))) { 306 flags.addPiece(gen.new Piece(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "questionnaire-definitions.html#Questionnaire.item.readOnly"), null, context.formatPhrase(RenderingContext.QUEST_READONLY)).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-readonly.png")))); 307 } 308 if ("true".equals(i.extensionString("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-isSubject"))) { 309 status.setExtensions(true); 310 flags.addPiece(gen.new Piece(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-isSubject", "StructureDefinition-sdc-questionnaire-isSubject.html"), null, context.formatPhrase(RenderingContext.QUEST_SUBJECT)).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-subject.png")))); 311 } 312 if ("true".equals(i.extensionString(ToolingExtensions.EXT_Q_HIDDEN))) { 313 status.setExtensions(true); 314 flags.addPiece(gen.new Piece(getSpecLink("extension-questionnaire-hidden.html"), null, context.formatPhrase(RenderingContext.QUEST_HIDDEN)).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-hidden.png")))); 315 } 316 if ("true".equals(i.extensionString(ToolingExtensions.EXT_Q_OTP_DISP))) { 317 status.setExtensions(true); 318 flags.addPiece(gen.new Piece(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-optionalDisplay", "StructureDefinition-sdc-questionnaire-optionalDisplay.html"), null, context.formatPhrase(RenderingContext.QUEST_DISPLAY)).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-optional.png")))); 319 } 320 if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-observationLinkPeriod")) { 321 status.setExtensions(true); 322 flags.addPiece(gen.new Piece(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-observationLinkPeriod", "StructureDefinition-sdc-questionnaire-observationLinkPeriod.html"), null, context.formatPhrase(RenderingContext.QUEST_LINKED)).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-observation.png")))); 323 } 324 if (i.hasExtension(ToolingExtensions.EXT_Q_CHOICE_ORIENT)) { 325 status.setExtensions(true); 326 String code = i.extensionString(ToolingExtensions.EXT_Q_CHOICE_ORIENT); 327 flags.addPiece(gen.new Piece(getSpecLink("extension-questionnaire-choiceorientation.html"), null, context.formatPhrase(RenderingContext.QUEST_ORIENTATION, code)+" ").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-" + code + ".png")))); 328 } 329 if (i.hasExtension(ToolingExtensions.EXT_Q_DISPLAY_CAT)) { 330 status.setExtensions(true); 331 ResourceWrapper cc = i.extensionValue(ToolingExtensions.EXT_Q_DISPLAY_CAT); 332 String code = getCodeFromCC(cc, "http://hl7.org/fhir/questionnaire-display-category"); 333 flags.addPiece(gen.new Piece("https://hl7.org/fhir/R4/extension-questionnaire-displayCategory.html", null, context.formatPhrase(RenderingContext.QUEST_CAT, code)+" ").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-" + code + ".png")))); 334 } 335 } 336 Cell defn = gen.new Cell(); 337 r.getCells().add(defn); 338 339 if (i.has("maxLength")) { 340 defn.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.GENERAL_MAX_LENGTH)+" "), null)); 341 defn.getPieces().add(gen.new Piece(null, i.primitiveValue("maxLength"), null)); 342 } 343 if (i.has("definition")) { 344 if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); 345 defn.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.GENERAL_DEFINITION_COLON)+" "), null)); 346 genDefinitionLink(gen, i, defn, q); 347 } 348 if (i.has("enableWhen")) { 349 if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); 350 Piece p = gen.new Piece(null, (context.formatPhrase(RenderingContext.QUEST_ENABLE)+" "), null); 351 defn.getPieces().add(p); 352 if (i.children("enableWhen").size() == 1) { 353 XhtmlNode x = new XhtmlNode(NodeType.Element, "span"); 354 p.getChildren().add(x); 355 renderEnableWhen(x, i.firstChild("enableWhen")); 356 } else { 357 XhtmlNode x = new XhtmlNode(NodeType.Element, "ul"); 358 p.getChildren().add(x); 359 for (ResourceWrapper qi : i.children("enableWhen")) { 360 renderEnableWhen(x.li(), qi); 361 } 362 } 363 } 364 if (i.has("answerValueSet")) { 365 if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); 366 defn.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.QUEST_VALUE)+" "), null)); 367 if (i.hasPrimitiveValue("answerValueSet") && i.primitiveValue("answerValueSet").startsWith("#")) { 368 ResourceWrapper vs = q.getContained(i.primitiveValue("answerValueSet").substring(1)); 369 if (vs == null) { 370 defn.getPieces().add(gen.new Piece(null, i.primitiveValue("answerValueSet"), null)); 371 } else { 372 defn.getPieces().add(gen.new Piece(vs.getWebPath(), RendererFactory.factory(vs, context.forContained()).buildSummary(vs), null)); 373 } 374 } else { 375 ValueSet vs = context.getWorker().findTxResource(ValueSet.class, i.primitiveValue("answerValueSet")); 376 if (vs == null || !vs.hasWebPath()) { 377 defn.getPieces().add(gen.new Piece(null, i.primitiveValue("answerValueSet"), null)); 378 } else { 379 defn.getPieces().add(gen.new Piece(vs.getWebPath(), vs.present(), null)); 380 } 381 } 382 } 383 if (i.has("answerOption")) { 384 if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); 385 defn.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.QUEST_OPTIONS)+" "), null)); 386 if (context.getDefinitionsTarget() == null) { 387 // if we don't have a definitions target, we'll add them below. 388 defn.getPieces().add(gen.new Piece("#"+context.prefixAnchor("opt-item."+i.primitiveValue("linkId")), Integer.toString(i.children("answerOption").size())+" "+Utilities.pluralize("option", i.children("answerOption").size()), null)); 389 } else { 390 defn.getPieces().add(gen.new Piece(context.getDefinitionsTarget()+"#item."+i.primitiveValue("linkId"), Integer.toString(i.children("answerOption").size())+" "+Utilities.pluralize("option", i.children("answerOption").size()), null)); 391 } 392 } 393 if (i.has("initial")) { 394 for (ResourceWrapper v : i.children("initial")) { 395 ResourceWrapper vv = v.child("value"); 396 if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); 397 defn.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.QUEST_INITIAL)+" "), null)); 398 defn.getPieces().add(gen.new Piece(null, vv.fhirType(), null)); 399 defn.getPieces().add(gen.new Piece(null, " = ", null)); 400 if (vv.isPrimitive()) { 401 defn.getPieces().add(gen.new Piece(null, vv.primitiveValue(), null)); 402 } else if (vv.fhirType().equals("Coding")) { 403 renderCoding(gen, defn.getPieces(), vv); 404 } else if (vv.fhirType().equals("Quantity")) { 405 renderQuantity(gen, defn.getPieces(), vv, false); 406 } else if (vv.fhirType().equals("Reference")) { 407 renderReference(q, gen, defn.getPieces(), vv, true); 408 } else if (vv.fhirType().equals("Attachment")) { 409 // renderAttachment(gen, defn.getPieces(), vv); 410 } 411 } 412 } 413 // still todo 414 415 // 416 //http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-choiceColumn 417 // 418 //http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-width 419 //http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-observationLinkPeriod 420 //http://hl7.org/fhir/StructureDefinition/Questionnaire-itemControl 421 //http://hl7.org/fhir/StructureDefinition/Questionnaire-sliderStepValue 422 423 if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-enableWhenExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-itemContext") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-calculatedExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-contextExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-candidateExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-initialExpression")) { 424 if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); 425 defn.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.QUEST_EXP)+" "), null)); 426 Piece p = gen.new Piece("ul"); 427 defn.getPieces().add(p); 428 for (ResourceWrapper e : i.extensions("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-initialExpression")) { 429 addExpression(p, e.child("value"), context.formatPhrase(RenderingContext.QUEST_INT), "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-initialExpression"); 430 } 431 for (ResourceWrapper e : i.extensions("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-contextExpression")) { 432 addExpression(p, e.child("value"), context.formatPhrase(RenderingContext.QUEST_CONT), "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-contextExpression"); 433 } 434 for (ResourceWrapper e : i.extensions("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-itemContext")) { 435 addExpression(p, e.child("value"), context.formatPhrase(RenderingContext.QUEST_ITEM_CONT), "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-itemContext"); 436 } 437 for (ResourceWrapper e : i.extensions("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-enableWhenExpression")) { 438 addExpression(p, e.child("value"), context.formatPhrase(RenderingContext.QUEST_EN), "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-enableWhenExpression"); 439 } 440 for (ResourceWrapper e : i.extensions("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-calculatedExpression")) { 441 addExpression(p, e.child("value"), context.formatPhrase(RenderingContext.QUEST_CALC), "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-calculatedExpression"); 442 } 443 for (ResourceWrapper e : i.extensions("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-candidateExpression")) { 444 addExpression(p, e.child("value"), context.formatPhrase(RenderingContext.QUEST_CAND), "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-candidateExpression"); 445 } 446 } 447 448 for (ResourceWrapper c : i.children("item")) { 449 renderTreeItem(status, gen, r.getSubRows(), q, c, hasFlags); 450 } 451 } 452 453 private String getCodeFromCC(ResourceWrapper cc, String system) { 454 for (ResourceWrapper coding : cc.children("coding")) { 455 if (system.equals(coding.primitiveValue("system"))) { 456 return coding.primitiveValue("code"); 457 } 458 } 459 return null; 460 461 } 462 463 public void genDefinitionLink(HierarchicalTableGenerator gen, ResourceWrapper i, Cell defn, ResourceWrapper q) { 464 // can we resolve the definition? 465 String path = null; 466 String d = i.primitiveValue("definition"); 467 if (d.contains("#")) { 468 path = d.substring(d.indexOf("#")+1); 469 d = d.substring(0, d.indexOf("#")); 470 } 471 StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, d, q.getResourceNative()); 472 if (sd != null) { 473 String url = sd.getWebPath(); 474 if (url != null) { 475 defn.getPieces().add(gen.new Piece(url+"#"+path, path, null)); 476 } else { 477 defn.getPieces().add(gen.new Piece(null, i.primitiveValue("definition"), null)); 478 } 479 } else { 480 defn.getPieces().add(gen.new Piece(null, i.primitiveValue("definition"), null)); 481 } 482 } 483 484 public void genDefinitionLink(XhtmlNode x, ResourceWrapper i, ResourceWrapper q) { 485 // can we resolve the definition? 486 String path = null; 487 String d = i.primitiveValue("definition"); 488 if (d.contains("#")) { 489 path = d.substring(d.indexOf("#")+1); 490 d = d.substring(0, d.indexOf("#")); 491 } 492 StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, d, q.getResourceNative()); 493 if (sd != null) { 494 String url = sd.getWebPath(); 495 if (url != null) { 496 x.ah(url+"#"+path).tx(path); 497 } else { 498 x.tx(i.primitiveValue("definition")); 499 } 500 } else { 501 x.tx(i.primitiveValue("definition")); 502 } 503 } 504 505 private void addExpression(Piece p, ResourceWrapper exp, String label, String url) { 506 XhtmlNode x = new XhtmlNode(NodeType.Element, "li").style("font-size: 11px"); 507 p.addHtml(x); 508 CanonicalResource cr = (CanonicalResource) context.getContext().fetchResource(Resource.class, url); 509 if (cr != null && cr.hasWebPath()) { 510 x.ah(cr.getWebPath()).tx(label); 511 } else { 512 x.ah(url).tx(label); 513 } 514 x.tx(": "); 515 x.code(exp.primitiveValue("expression")); 516 } 517 518 private void renderLogic(RenderingStatus status, XhtmlNode x, ResourceWrapper q) throws FHIRException, IOException { 519 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, context.getDestDir(), context.isInlineGraphics(), true, ""); 520 TableModel model = gen.new TableModel("qtree="+q.getId(), true); 521 model.setAlternating(true); 522 if (context.getRules() == GenerationRules.VALID_RESOURCE || context.isInlineGraphics()) { 523 model.setDocoImg(HierarchicalTableGenerator.help16AsData()); 524 } else { 525 model.setDocoImg(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "help16.png")); 526 } 527 model.setDocoRef(context.getLink(KnownLinkType.SPEC)+"formats.html#table"); 528 model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingContext.QUEST_LINKID), context.formatPhrase(RenderingContext.QUEST_LINK), null, 0)); 529 model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingContext.GENERAL_DESC_CONST), context.formatPhrase(RenderingContext.QUEST_ADD_INFO), null, 0)); 530 531 if (!q.has("item")) { 532 gen.emptyRow(model, 2); 533 } else { 534 for (ResourceWrapper i : q.children("item")) { 535 renderLogicItem(status, gen, model.getRows(), q, i); 536 } 537 } 538 XhtmlNode xn = gen.generate(model, context.getLocalPrefix(), 1, null); 539 x.addChildNode(xn); 540 } 541 542 private void renderLogicItem(RenderingStatus status, HierarchicalTableGenerator gen, List<Row> rows, ResourceWrapper q, ResourceWrapper i) throws IOException { 543 Row r = gen.new Row(); 544 rows.add(r); 545 String type = i.primitiveValue("type"); 546 547 r.setIcon("icon-q-"+type.toLowerCase()+".png", type); 548 r.getCells().add(gen.new Cell(null, context.getDefinitionsTarget() == null ? "" : context.getDefinitionsTarget()+"#item."+i.primitiveValue("linkId"), i.primitiveValue("linkId"), null, null)); 549 Cell defn = gen.new Cell(); 550 r.getCells().add(defn); 551 552 if (i.has("maxLength")) { 553 defn.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.GENERAL_MAX_LENGTH)+" "), null)); 554 defn.getPieces().add(gen.new Piece(null, i.primitiveValue("maxLength"), null)); 555 } 556 if (i.has("definition")) { 557 if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); 558 defn.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.GENERAL_DEFINITION_COLON)+" "), null)); 559 genDefinitionLink(gen, i, defn, q); 560 } 561 if (i.has("enableWhen")) { 562 if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); 563 defn.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.QUEST_ENABLE)+" "), null)); 564 defn.getPieces().add(gen.new Piece(null, context.formatPhrase(RenderingContext.GENERAL_TODO), null)); 565 } 566 if (i.has("answerValueSet")) { 567 if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); 568 defn.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.QUEST_VALUE)+" "), null)); 569 if (Utilities.noString(i.primitiveValue("answerValueSet")) && i.primitiveValue("answerValueSet").startsWith("#")) { 570 ResourceWrapper vs = q.getContained(i.primitiveValue("answerValueSet").substring(1)); 571 if (vs == null) { 572 defn.getPieces().add(gen.new Piece(null, i.primitiveValue("answerValueSet"), null)); 573 } else { 574 defn.getPieces().add(gen.new Piece(vs.getWebPath(), RendererFactory.factory(vs, context.forContained()).buildSummary(vs), null)); 575 } 576 } else { 577 ValueSet vs = context.getWorker().findTxResource(ValueSet.class, i.primitiveValue("answerValueSet"), q.getResourceNative()); 578 if (vs == null || !vs.hasWebPath()) { 579 defn.getPieces().add(gen.new Piece(null, i.primitiveValue("answerValueSet"), null)); 580 } else { 581 defn.getPieces().add(gen.new Piece(vs.getWebPath(), vs.present(), null)); 582 } 583 } 584 } 585 if (i.has("answerOption")) { 586 if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); 587 defn.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.QUEST_OPTIONS)+" "), null)); 588 defn.getPieces().add(gen.new Piece(context.getDefinitionsTarget()+"#item."+i.primitiveValue("linkId"), Integer.toString(i.children("answerOption").size())+" "+Utilities.pluralize("option", i.children("answerOption").size()), null)); 589 } 590 if (i.has("initial")) { 591 for (ResourceWrapper v : i.children("initial")) { 592 if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); 593 ResourceWrapper vv = v.child("value"); 594 defn.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.QUEST_INITIAL)+" "), null)); 595 defn.getPieces().add(gen.new Piece(null, vv.fhirType(), null)); 596 defn.getPieces().add(gen.new Piece(null, " = ", null)); 597 if (vv.isPrimitive()) { 598 defn.getPieces().add(gen.new Piece(null, vv.primitiveValue(), null)); 599 } else if (vv.fhirType().equals("Coding")) { 600 renderCoding(gen, defn.getPieces(), vv); 601 } else if (vv.fhirType().equals("Coding")) { 602 renderQuantity(gen, defn.getPieces(), vv, false); 603 } else if (vv.fhirType().equals("Coding")) { 604 renderReference(q, gen, defn.getPieces(), vv, false); 605 // } else if (v.hasValueAttachment()) { 606 // renderAttachment(gen, defn.getPieces(), v.getValueAttachment()); 607 } 608 } 609 } 610 // still todo 611 612 // 613 //http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-choiceColumn 614 // 615 //http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-width 616 //http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-observationLinkPeriod 617 //http://hl7.org/fhir/StructureDefinition/Questionnaire-itemControl 618 //http://hl7.org/fhir/StructureDefinition/Questionnaire-sliderStepValue 619 620 if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-enableWhenExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-itemContext") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-calculatedExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-contextExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-candidateExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-initialExpression")) { 621 if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); 622 defn.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.QUEST_EXP)+" "), null)); 623 Piece p = gen.new Piece("ul"); 624 defn.getPieces().add(p); 625 for (ResourceWrapper e : i.extensions("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-initialExpression")) { 626 addExpression(p, e.child("value"), context.formatPhrase(RenderingContext.QUEST_INT), "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-initialExpression"); 627 } 628 for (ResourceWrapper e : i.extensions("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-contextExpression")) { 629 addExpression(p, e.child("value"), context.formatPhrase(RenderingContext.QUEST_CONT), "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-contextExpression"); 630 } 631 for (ResourceWrapper e : i.extensions("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-itemContext")) { 632 addExpression(p, e.child("value"), context.formatPhrase(RenderingContext.QUEST_ITEM_CONT), "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-itemContext"); 633 } 634 for (ResourceWrapper e : i.extensions("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-enableWhenExpression")) { 635 addExpression(p, e.child("value"), context.formatPhrase(RenderingContext.QUEST_EN), "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-enableWhenExpression"); 636 } 637 for (ResourceWrapper e : i.extensions("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-calculatedExpression")) { 638 addExpression(p, e.child("value"), context.formatPhrase(RenderingContext.QUEST_CALC), "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-calculatedExpression"); 639 } 640 for (ResourceWrapper e : i.extensions("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-candidateExpression")) { 641 addExpression(p, e.child("value"), context.formatPhrase(RenderingContext.QUEST_CAND), "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-candidateExpression"); 642 } 643 } 644 645 for (ResourceWrapper c : i.children("item")) { 646 renderLogicItem(status, gen, r.getSubRows(), q, c); 647 } 648 649 } 650 651 652 public void renderForm(RenderingStatus status, XhtmlNode x, ResourceWrapper q) throws UnsupportedEncodingException, IOException { 653 XhtmlNode d = x.div(); 654 boolean hasPrefix = false; 655 for (ResourceWrapper c : q.children("item")) { 656 hasPrefix = hasPrefix || doesItemHavePrefix(c); 657 } 658 int i = 1; 659 for (ResourceWrapper c : q.children("item")) { 660 renderFormItem(status, d, q, c, hasPrefix ? null : Integer.toString(i), 0); 661 i++; 662 } 663 } 664 665 private boolean doesItemHavePrefix(ResourceWrapper i) { 666 if (i.has("prefix")) { 667 return true; 668 } 669 for (ResourceWrapper c : i.children("item")) { 670 if (doesItemHavePrefix(c)) { 671 return true; 672 } 673 } 674 return false; 675 } 676 677 private void renderFormItem(RenderingStatus status, XhtmlNode x, ResourceWrapper q, ResourceWrapper i, String pfx, int indent) throws IOException { 678 boolean hasExt = false; 679 XhtmlNode d = x.div().style("width: "+Integer.toString(900-indent*10)+"px; border-top: 1px #eeeeee solid"); 680 if (indent > 0) { 681 d.style("margin-left: "+Integer.toString(10*indent)+"px"); 682 } 683 XhtmlNode display = d.div().style("display: inline-block; width: "+Integer.toString(500-indent*10)+"px"); 684 XhtmlNode details = d.div().style("border: 1px #ccccff solid; padding: 2px; display: inline-block; background-color: #fefce7; width: 380px"); 685 XhtmlNode p = display.para(); 686 687 String type = i.primitiveValue("type"); 688 String typeT = getTranslatedCode(i.child("type")); 689 if ("group".equals(type)) { 690 p = p.b(); 691 } 692 if (i.has("prefix")) { 693 p.tx(i.primitiveValue("prefix")); 694 p.tx(": "); 695 } 696 p.span(null, "linkId: "+i.primitiveValue("linkId")).tx(i.primitiveValue("text")); 697 if ("true".equals(i.primitiveValue("required"))) { 698 p.span("color: red", context.formatPhrase(RenderingContext.QUEST_MAND)).tx("*"); 699 } 700 701 XhtmlNode input = null; 702 switch (type) { 703 case "string": 704 p.tx(" "); 705 input = p.input(i.primitiveValue("linkId"), "text", typeT, 60); 706 break; 707 case "attachment": 708 break; 709 case "boolean": 710 p.tx(" "); 711 input = p.input(i.primitiveValue("linkId"), "checkbox", typeT, 1); 712 break; 713 case "coding": 714 input = p.select(i.primitiveValue("linkId")); 715 listOptions(q, i, input); 716 break; 717 case "date": 718 p.tx(" "); 719 input = p.input(i.primitiveValue("linkId"), "date", typeT, 10); 720 break; 721 case "dateTime": 722 p.tx(" "); 723 input = p.input(i.primitiveValue("linkId"), "datetime-local", typeT, 25); 724 break; 725 case "decimal": 726 p.tx(" "); 727 input = p.input(i.primitiveValue("linkId"), "number", typeT, 15); 728 break; 729 case "display": 730 break; 731 case "group": 732 break; 733 case "integer": 734 p.tx(" "); 735 input = p.input(i.primitiveValue("linkId"), "number", typeT, 10); 736 break; 737 case "qantity": 738 p.tx(" "); 739 input = p.input(i.primitiveValue("linkId"), "number", "value", 15); 740 p.tx(" "); 741 input = p.input(i.primitiveValue("linkId"), "unit", "unit", 10); 742 break; 743 case "question": 744 break; 745 case "reference": 746 break; 747 case "text": 748 break; 749 case "time": 750 break; 751 case "url": 752 break; 753 default: 754 break; 755 } 756 if (input != null) { 757 if ("true".equals(i.primitiveValue("readOnly"))) { 758 input.attribute("readonly", "1"); 759 input.style("background-color: #eeeeee"); 760 } 761 } 762 763 // if (i.hasExtension(ToolingExtensions.EXT_Q_CHOICE_ORIENT)) { 764 // String code = ToolingExtensions.readStringExtension(i, ToolingExtensions.EXT_Q_CHOICE_ORIENT); 765 // flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-observationLinkPeriod", null, "Orientation: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-"+code+".png")))); 766 //} 767 768 769 XhtmlNode ul = details.ul(); 770 boolean hasFlag = false; 771 XhtmlNode flags = item(ul, "Flags"); 772 item(ul, "linkId", i.primitiveValue("linkId")); 773 774 if ("true".equals(i.extensionString("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-isSubject"))) { 775 hasFlag = true; 776 flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-isSubject", "StructureDefinition-sdc-questionnaire-isSubject.html"), context.formatPhrase(RenderingContext.QUEST_SUBJECT)).img(getImgPath("icon-qi-subject.png"), "icon"); 777 } 778 if ("true".equals(i.extensionString(ToolingExtensions.EXT_Q_HIDDEN))) { 779 hasFlag = true; 780 flags.ah(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "extension-questionnaire-hidden.html"), context.formatPhrase(RenderingContext.QUEST_HIDDEN)).img(getImgPath("icon-qi-hidden.png"), "icon"); 781 d.style("background-color: #eeeeee"); 782 } 783 if ("true".equals(i.extensionString(ToolingExtensions.EXT_Q_OTP_DISP))) { 784 hasFlag = true; 785 flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-optionalDisplay", "StructureDefinition-sdc-questionnaire-optionalDisplay.html"), context.formatPhrase(RenderingContext.QUEST_DISPLAY)).img(getImgPath("icon-qi-optional.png"), "icon"); 786 } 787 if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-observationLinkPeriod")) { 788 hasFlag = true; 789 flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-observationLinkPeriod", "StructureDefinition-sdc-questionnaire-observationLinkPeriod.html"), context.formatPhrase(RenderingContext.QUEST_LINKED)).img(getImgPath("icon-qi-observation.png"), "icon"); 790 } 791 if (i.hasExtension(ToolingExtensions.EXT_Q_DISPLAY_CAT)) { 792 ResourceWrapper cc = i.extension(ToolingExtensions.EXT_Q_DISPLAY_CAT).child("value"); 793 String code = getCodeFromCC(cc, "http://hl7.org/fhir/questionnaire-display-category"); 794 hasFlag = true; 795 flags.ah("https://hl7.org/fhir/R4/extension-questionnaire-displayCategory.html", (context.formatPhrase(RenderingContext.QUEST_CAT, code)+" ")).img(getImgPath("icon-qi-" + code + ".png"), "icon"); 796 } 797 798 if (i.has("maxLength")) { 799 item(ul, context.formatPhrase(RenderingContext.GENERAL_MAX_LENGTH), i.primitiveValue("maxLength")); 800 } 801 if (i.has("definition")) { 802 genDefinitionLink(item(ul, context.formatPhrase(RenderingContext.GENERAL_DEFINITION_COLON)), i, q); 803 } 804 if (i.has("enableWhen")) { 805 item(ul, context.formatPhrase(RenderingContext.QUEST_EN), "todo"); 806 } 807 if (i.has("answerValueSet")) { 808 XhtmlNode ans = item(ul, context.formatPhrase(RenderingContext.QUEST_ANSWERS)); 809 if (!Utilities.noString(i.primitiveValue("answerValueSet")) && i.primitiveValue("answerValueSet").startsWith("#")) { 810 ResourceWrapper vs = q.getContained(i.primitiveValue("answerValueSet").substring(1)); 811 if (vs == null) { 812 ans.tx(i.primitiveValue("answerValueSet")); 813 } else if (vs.getWebPath() == null) { 814 ans.ah(context.prefixLocalHref("#hc"+vs.getScopedId())).tx(RendererFactory.factory(vs, context.forContained()).buildSummary(vs)); 815 } else { 816 ans.ah(context.prefixLocalHref(vs.getWebPath())).tx(RendererFactory.factory(vs, context.forContained()).buildSummary(vs)); 817 } 818 } else { 819 ValueSet vs = context.getWorker().findTxResource(ValueSet.class, i.primitiveValue("answerValueSet"), q.getResourceNative()); 820 if (vs == null || !vs.hasWebPath()) { 821 ans.tx(i.primitiveValue("answerValueSet")); 822 } else { 823 ans.ah(vs.getWebPath()).tx(vs.present()); 824 } 825 } 826 } 827 if (i.has("answerOption")) { 828 item(ul, context.formatPhrase(RenderingContext.QUEST_ANSWERS), Integer.toString(i.children("answerOption").size())+" "+Utilities.pluralize("option", i.children("answerOption").size()), context.getDefinitionsTarget()+"#item."+i.primitiveValue("linkId")); 829 } 830 if (i.has("initial")) { 831 XhtmlNode vi = item(ul, context.formatPhrase(RenderingContext.QUEST_INT)); 832 boolean first = true; 833 for (ResourceWrapper v : i.children("initial")) { 834 if (first) first = false; else vi.tx(", "); 835 ResourceWrapper vv = v.child("value"); 836 if (vv.isPrimitive()) { 837 vi.tx(vv.primitiveValue()); 838 } else if (vv.fhirType().equals("Coding")) { 839 renderCoding(status, vi, vv); 840 } else if (vv.fhirType().equals("Reference")) { 841 renderReference(status, vi, vv); 842 } else if (vv.fhirType().equals("Quantity")) { 843 renderQuantity(status, vi, vv); 844 // } else if (v.hasValueAttachment()) { 845 // renderAttachment(vi, v.getValueAttachment()); 846 } 847 } 848 } 849 if (!hasFlag) { 850 ul.remove(flags); 851 } 852 // if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-enableWhenExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-itemContext") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-calculatedExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-contextExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-candidateExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-initialExpression")) { 853 // if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); 854 // defn.getPieces().add(gen.new Piece(null, "Expressions: ", null)); 855 // Piece p = gen.new Piece("ul"); 856 // defn.getPieces().add(p); 857 // for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-initialExpression")) { 858 // addExpression(p, e.getValueExpression(), "Initial Value", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-initialExpression"); 859 // } 860 // for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-contextExpression")) { 861 // addExpression(p, e.getValueExpression(), "Context", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-contextExpression"); 862 // } 863 // for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-itemContext")) { 864 // addExpression(p, e.getValueExpression(), "Item Context", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-itemContext"); 865 // } 866 // for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-enableWhenExpression")) { 867 // addExpression(p, e.getValueExpression(), "Enable When", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-enableWhenExpression"); 868 // } 869 // for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-calculatedExpression")) { 870 // addExpression(p, e.getValueExpression(), "Calculated Value", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-calculatedExpression"); 871 // } 872 // for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-candidateExpression")) { 873 // addExpression(p, e.getValueExpression(), "Candidates", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-candidateExpression"); 874 // } 875 // } 876 // 877 878 int t = 1; 879 for (ResourceWrapper c : i.children("item")) { 880 renderFormItem(status, x, q, c, pfx == null ? null : pfx+"."+Integer.toString(t), indent+1); 881 t++; 882 } 883 } 884 885 @Nonnull 886 private String getImgPath(String code) throws IOException { 887 return context.getLocalPrefix().length() > 0 888 ? Utilities.path(context.getLocalPrefix(), code) 889 : Utilities.path(code); 890 } 891 892 private void item(XhtmlNode ul, String name, String value, String valueLink) { 893 if (!Utilities.noString(value)) { 894 ul.li().style("font-size: 10px").ah(context.prefixLocalHref(valueLink)).tx(name+": "+value); 895 } 896 } 897 898 private void item(XhtmlNode ul, String name, String value) { 899 if (!Utilities.noString(value)) { 900 ul.li().style("font-size: 10px").tx(name+": "+value); 901 } 902 } 903 private XhtmlNode item(XhtmlNode ul, String name) { 904 XhtmlNode li = ul.li(); 905 li.style("font-size: 10px").tx(name+": "); 906 return li; 907 } 908 909 910 private void listOptions(ResourceWrapper q, ResourceWrapper i, XhtmlNode select) { 911 if (i.has("answerValueSet")) { 912 ValueSet vs = null; 913 if (!Utilities.noString(i.primitiveValue("answerValueSet")) && i.primitiveValue("answerValueSet").startsWith("#")) { 914 ResourceWrapper contained = q.getContained(i.primitiveValue("answerValueSet").substring(1)); 915 vs = contained == null ? null : (ValueSet) contained.getResourceNative(); 916 if (vs != null && !vs.hasUrl()) { 917 vs = vs.copy(); 918 vs.setUrl(q.primitiveValue("url")+"--"+contained); 919 } 920 } else { 921 vs = context.getContext().findTxResource(ValueSet.class, i.primitiveValue("answerValueSet"), q.getResourceNative()); 922 } 923 if (vs != null) { 924 ValueSetExpansionOutcome exp = context.getContext().expandVS(vs, true, false); 925 if (exp.getValueset() != null) { 926 for (ValueSetExpansionContainsComponent cc : exp.getValueset().getExpansion().getContains()) { 927 select.option(cc.getCode(), cc.hasDisplay() ? cc.getDisplay() : cc.getCode(), false); 928 } 929 return; 930 } 931 } 932 } else if (i.has("answerOption")) { 933 renderItemOptions(select, i); 934 } 935 select.option("a", "??", false); 936 } 937 938 private void renderLinks(RenderingStatus status, XhtmlNode x, ResourceWrapper q) { 939 x.para().tx(context.formatPhrase(RenderingContext.QUEST_TRY)); 940 XhtmlNode ul = x.ul(); 941 ul.li().ah("http://todo.nlm.gov/path?mode=ig&src="+Utilities.pathURL(context.getLink(KnownLinkType.SELF), "package.tgz")+"&q="+q.getId()+".json").tx(context.formatPhrase(RenderingContext.QUEST_NLM)); 942 } 943 944 private void renderDefns(RenderingStatus status, XhtmlNode x, ResourceWrapper q) throws IOException { 945 XhtmlNode tbl = x.table("dict", false); 946 renderRootDefinition(status, tbl, q, new ArrayList<>()); 947 for (ResourceWrapper qi : q.children("item")) { 948 renderDefinition(status, tbl, q, qi, new ArrayList<>()); 949 } 950 } 951 952 private void renderRootDefinition(RenderingStatus status, XhtmlNode tbl, ResourceWrapper q, List<ResourceWrapper> parents) throws IOException { 953 boolean ext = false; 954 XhtmlNode td = tbl.tr().td("structure").colspan("2").span(null, null).attribute("class", "self-link-parent"); 955 td.an(context.prefixAnchor(q.getId())); 956 td.img(getImgPath("icon_q_root.gif"), "icon"); 957 td.tx(" "+(context.formatPhrase(RenderingContext.QUEST_QUEST)+" ")); 958 td.b().tx(q.getId()); 959 960 // general information 961 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_URL), q.primitiveValue("url")); 962 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_VER), q.primitiveValue("version")); 963 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_NAME), q.primitiveValue("name")); 964 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_TITLE), q.primitiveValue("title")); 965 if (q.has("derivedFrom")) { 966 td = defn(tbl, context.formatPhrase(RenderingContext.QUEST_DERIVED)); 967 boolean first = true; 968 for (ResourceWrapper c : q.children("derivedFrom")) { 969 if (first) first = false; else td.tx(", "); 970 td.tx(c.primitiveValue()); // todo: make these a reference 971 } 972 } 973 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_STATUS), q.primitiveValue("status")); 974 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_EXPER), q.primitiveValue("experimental")); 975 defn(tbl, context.formatPhrase(RenderingContext.QUEST_PUB), q.primitiveValue("date")); 976 defn(tbl, context.formatPhrase(RenderingContext.QUEST_APP), q.primitiveValue("approvalDate")); 977 defn(tbl, context.formatPhrase(RenderingContext.QUEST_REV_DATE), q.primitiveValue("lastReviewDate")); 978 if (q.has("effectivePeriod")) { 979 renderPeriod(status, defn(tbl, context.formatPhrase(RenderingContext.QUEST_EFF_PERIOD)), q.child("effectivePeriod")); 980 } 981 982 if (q.has("subjectType")) { 983 td = defn(tbl, context.formatPhrase(RenderingContext.QUEST_SUB_TYPE)); 984 boolean first = true; 985 for (ResourceWrapper c : q.children("subjectType")) { 986 if (first) first = false; else td.tx(", "); 987 td.tx(c.primitiveValue()); 988 } 989 } 990 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_DESC), q.primitiveValue("description")); 991 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_PURPOSE), q.primitiveValue("purpose")); 992 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_COPYRIGHT), q.primitiveValue("copyright")); 993 if (q.has("code")) { 994 td = defn(tbl, Utilities.pluralize("Code", q.children("code").size())); 995 boolean first = true; 996 for (ResourceWrapper c : q.children("code")) { 997 if (first) first = false; else td.tx(", "); 998 renderCodingWithDetails(status, td, c); 999 } 1000 } 1001 } 1002 1003 private void renderDefinition(RenderingStatus status, XhtmlNode tbl, ResourceWrapper q, ResourceWrapper qi, List<ResourceWrapper> parents) throws IOException { 1004 XhtmlNode td = tbl.tr().td("structure").colspan("2").span(null, null).attribute("class", "self-link-parent"); 1005 td.an(context.prefixAnchor("item."+qi.primitiveValue("linkId"))); 1006 for (ResourceWrapper p : parents) { 1007 td.ah(context.prefixLocalHref("#item."+p.primitiveValue("linkId"))).img(getImgPath("icon_q_item.png"), "icon"); 1008 td.tx(" > "); 1009 } 1010 td.img(getImgPath("icon_q_item.png"), "icon"); 1011 td.tx(" Item "); 1012 td.b().tx(qi.primitiveValue("linkId")); 1013 String type = qi.primitiveValue("type"); 1014 1015 // general information 1016 defn(tbl, context.formatPhrase(RenderingContext.QUEST_ID), qi.primitiveValue("linkId")); 1017 defn(tbl, context.formatPhrase(RenderingContext.QUEST_PREFIX), qi.primitiveValue("prefix")); 1018 defn(tbl, context.formatPhrase(RenderingContext.QUEST_TEXT), qi.primitiveValue("text")); 1019 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_TYPE), type); 1020 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_REQUIRED), qi.primitiveValue("required")); 1021 defn(tbl, context.formatPhrase(RenderingContext.QUEST_REP), qi.primitiveValue("repeats")); 1022 defn(tbl, context.formatPhrase(RenderingContext.QUEST_READ_ONLY), qi.primitiveValue("readOnly")); 1023 if ("true".equals(qi.extensionString("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-isSubject"))) { 1024 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_SUBJ), "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-isSubject", "This element changes who the subject of the question is", null); 1025 } 1026 1027 // content control 1028 defn(tbl, context.formatPhrase(RenderingContext.QUEST_MAX_LENGTH), qi.primitiveValue("maxLength")); 1029 if (qi.has("answerValueSet")) { 1030 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_VALUESET), qi.primitiveValue("definition"), context.getWorker().findTxResource(ValueSet.class, qi.primitiveValue("answerValueSet"), q.getResourceNative())); 1031 } 1032 if (qi.has("answerOption")) { 1033 XhtmlNode tr = tbl.tr(); 1034 tr.td().tx(context.formatPhrase(RenderingContext.QUEST_ALLOWED)); 1035 XhtmlNode ul = tr.td().ul(); 1036 for (ResourceWrapper ans : qi.children("answerOption")) { 1037 XhtmlNode li = ul.li(); 1038 renderDataType(status, li, ans.child("value")); 1039 if ("true".equals(ans.primitiveValue("initialSelected"))) { 1040 li.tx(" "+(context.formatPhrase(RenderingContext.QUEST_INITIALLY))); 1041 } 1042 } 1043 } 1044 if (qi.has("initial")) { 1045 XhtmlNode tr = tbl.tr(); 1046 tr.td().tx(Utilities.pluralize((context.formatPhrase(RenderingContext.QUEST_INITIAL_ANSWER)), qi.children("initial").size())); 1047 if (qi.children("initial").size() == 1) { 1048 renderDataType(status, tr.td(), qi.firstChild("initial").child("value")); 1049 } else { 1050 XhtmlNode ul = tr.td().ul(); 1051 for (ResourceWrapper ans : qi.children("initial")) { 1052 XhtmlNode li = ul.li(); 1053 renderDataType(status, li, ans.child("value")); 1054 } 1055 } 1056 } 1057 1058 // appearance 1059 if (qi.hasExtension(ToolingExtensions.EXT_Q_DISPLAY_CAT)) { 1060 XhtmlNode tr = tbl.tr(); 1061 tr.td().ah(ToolingExtensions.EXT_Q_DISPLAY_CAT).tx("Display Category"); 1062 renderDataType(status, tr.td(), qi.extension(ToolingExtensions.EXT_Q_DISPLAY_CAT).child("value")); 1063 } 1064 if ("true".equals(qi.extensionString(ToolingExtensions.EXT_Q_HIDDEN))) { 1065 defn(tbl, context.formatPhrase(RenderingContext.QUEST_HIDDEN_ITEM), ToolingExtensions.EXT_Q_DISPLAY_CAT, "This item is a hidden question", null); 1066 } 1067 if ("true".equals(qi.extensionString(ToolingExtensions.EXT_Q_OTP_DISP))) { 1068 defn(tbl, context.formatPhrase(RenderingContext.QUEST_HIDDEN_ITEM), ToolingExtensions.EXT_Q_OTP_DISP, "This item is optional to display", null); 1069 } 1070 1071 // formal definitions 1072 if (qi.has("definition")) { 1073 genDefinitionLink(defn(tbl, context.formatPhrase(RenderingContext.GENERAL_DEFINITION)), qi, q); 1074 } 1075 1076 if (qi.has("code")) { 1077 XhtmlNode tr = tbl.tr(); 1078 tr.td().tx(Utilities.pluralize(context.formatPhrase(RenderingContext.GENERAL_CODE), qi.children("code").size())); 1079 XhtmlNode ul = tr.td().ul(); 1080 for (ResourceWrapper c : qi.children("code")) { 1081 renderCodingWithDetails(status, ul.li(), c); 1082 } 1083 } 1084 if (qi.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-observationLinkPeriod")) { 1085 XhtmlNode tr = tbl.tr(); 1086 StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, ToolingExtensions.EXT_O_LINK_PERIOD); 1087 if (sd != null && sd.hasWebPath()) { 1088 tr.td().ah(sd.getWebPath()).tx(context.formatPhrase(RenderingContext.QUEST_OBSERVATION)); 1089 } else { 1090 tr.td().ah("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-observationLinkPeriod").tx(context.formatPhrase(RenderingContext.QUEST_OBSERVATION)); 1091 } 1092 renderDataType(status, tr.td(), qi.extension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-Questionnaire-observationLinkPeriod").child("value")); 1093 } 1094 1095 // dynamic management 1096 if (qi.has("enableWhen")) { 1097 XhtmlNode tr = tbl.tr(); 1098 tr.td().tx(context.formatPhrase(RenderingContext.QUEST_EN)); 1099 td = tr.td(); 1100 if (qi.children("enableWhen").size() == 1) { 1101 renderEnableWhen(td, qi.children("enableWhen").get(0)); 1102 } else { 1103 if (qi.has("enableBehavior")) { 1104 td.tx(qi.primitiveValue("enableBehavior")+" "+(context.formatPhrase(RenderingContext.QUEST_TRUE))); 1105 } else { 1106 td.tx(context.formatPhrase(RenderingContext.QUEST_ARE_TRUE)); 1107 } 1108 XhtmlNode ul = td.ul(); 1109 for (ResourceWrapper ew : qi.children("enableWhen")) { 1110 renderEnableWhen(ul.li(), ew); 1111 } 1112 } 1113 } 1114 1115 1116 // other stuff 1117 1118 1119 1120 List<ResourceWrapper> curr = new ArrayList<>(); 1121 curr.addAll(parents); 1122 curr.add(qi); 1123 for (ResourceWrapper qic : qi.children("item")) { 1124 renderDefinition(status, tbl, q, qic, curr); 1125 } 1126 } 1127 1128 private void defn(XhtmlNode tbl, String name, String url, Resource res) throws UnsupportedEncodingException, IOException { 1129 if (res != null && res.hasWebPath()) { 1130 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_DEFINITION), RendererFactory.factory(res, context.forContained()).buildSummary(wrap(res)), res.getWebPath()); 1131 } else if (Utilities.isAbsoluteUrlLinkable(url)) { 1132 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_DEFINITION), url, url); 1133 } { 1134 defn(tbl, context.formatPhrase(RenderingContext.GENERAL_DEFINITION), url); 1135 } 1136 1137 } 1138 1139 private void renderEnableWhen(XhtmlNode x, ResourceWrapper ew) { 1140 x.ah(context.prefixLocalHref("#item."+ew.primitiveValue("question"))).tx(ew.primitiveValue("question")); 1141 x.tx(" "); 1142 x.tx(ew.primitiveValue("operator")); 1143 x.tx(" "); 1144 x.tx(displayDataType(ew.child("Answer"))); 1145 } 1146 1147 private XhtmlNode defn(XhtmlNode tbl, String name) { 1148 XhtmlNode tr = tbl.tr(); 1149 tr.td().tx(name); 1150 return tr.td(); 1151 } 1152 1153 private void defn(XhtmlNode tbl, String name, int value) { 1154 if (value > 0) { 1155 XhtmlNode tr = tbl.tr(); 1156 tr.td().tx(name); 1157 tr.td().tx(value); 1158 } 1159 } 1160 1161 1162 private void defn(XhtmlNode tbl, String name, boolean value) { 1163 XhtmlNode tr = tbl.tr(); 1164 tr.td().tx(name); 1165 tr.td().tx(Boolean.toString(value)); 1166 } 1167 1168 private void defn(XhtmlNode tbl, String name, String value) { 1169 if (!Utilities.noString(value)) { 1170 XhtmlNode tr = tbl.tr(); 1171 tr.td().tx(name); 1172 tr.td().tx(value); 1173 } 1174 } 1175 1176 private void defn(XhtmlNode tbl, String name, String value, String url) { 1177 if (!Utilities.noString(value)) { 1178 XhtmlNode tr = tbl.tr(); 1179 tr.td().tx(name); 1180 tr.td().ah(context.prefixLocalHref(url)).tx(value); 1181 } 1182 } 1183 1184 private void defn(XhtmlNode tbl, String name, String nurl, String value, String url) { 1185 if (!Utilities.noString(value)) { 1186 XhtmlNode tr = tbl.tr(); 1187 tr.td().ah(context.prefixLocalHref(nurl)).tx(name); 1188 if (url != null) { 1189 tr.td().ah(context.prefixLocalHref(url)).tx(value); 1190 } else { 1191 tr.td().tx(value); 1192 } 1193 } 1194 } 1195 1196 private void defn(XhtmlNode tbl, String name, boolean value, boolean ifFalse) { 1197 if (ifFalse || value) { 1198 XhtmlNode tr = tbl.tr(); 1199 tr.td().tx(name); 1200 tr.td().tx(Boolean.toString(value)); 1201 } 1202 } 1203 1204}