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