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