001package org.hl7.fhir.r5.renderers;
002
003import java.io.IOException;
004import java.io.UnsupportedEncodingException;
005import java.util.List;
006
007import org.hl7.fhir.exceptions.DefinitionException;
008import org.hl7.fhir.exceptions.FHIRException;
009import org.hl7.fhir.exceptions.FHIRFormatError;
010import org.hl7.fhir.r5.model.Expression;
011import org.hl7.fhir.r5.model.Questionnaire;
012import org.hl7.fhir.r5.model.QuestionnaireResponse;
013import org.hl7.fhir.r5.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent;
014import org.hl7.fhir.r5.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
015import org.hl7.fhir.r5.model.Resource;
016import org.hl7.fhir.r5.model.StructureDefinition;
017import org.hl7.fhir.r5.renderers.utils.RenderingContext;
018import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules;
019import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType;
020import org.hl7.fhir.r5.renderers.utils.ResourceWrapper;
021import org.hl7.fhir.r5.utils.EOperationOutcome;
022import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage;
023import org.hl7.fhir.utilities.Utilities;
024import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator;
025import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell;
026import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece;
027import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row;
028import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel;
029import org.hl7.fhir.utilities.xhtml.NodeType;
030import org.hl7.fhir.utilities.xhtml.XhtmlNode;
031
032@MarkedToMoveToAdjunctPackage
033public class QuestionnaireResponseRenderer extends ResourceRenderer {
034
035  public QuestionnaireResponseRenderer(RenderingContext context) { 
036    super(context); 
037  } 
038  
039  @Override
040  public String buildSummary(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
041    ResourceWrapper q = r.child("questionnaire");
042    String qd = q == null ? context.formatPhrase(RenderingContext.QUEST_UNSPECIFIED_QUESTIONNAIRE) : displayCanonical(q);
043    ResourceWrapper s = r.child("subject");
044    String sd = s == null ? context.formatPhrase(RenderingContext.QUEST_UNSPECIFIED_SUBJECT) : displayReference(s);
045    return context.formatPhrase(RenderingContext.QUEST_SUMMARY, qd, sd);
046  }
047
048  @Override
049  public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper qr) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
050    renderResourceTechDetails(qr, x);
051
052    switch (context.getQuestionnaireMode()) {
053    case FORM:
054      renderTree(status, x, qr);
055      break;
056    case LINKS: 
057      renderLinks(status, x, qr);
058      break;
059//    case LOGIC: return renderLogic(x, q);
060//    case DEFNS: return renderDefns(x, q);
061    case TREE:  
062      renderTree(status, x, qr);
063      break;
064    default:
065      throw new Error(context.formatPhrase(RenderingContext.QUEST_UNKNOWN_MODE));
066    }
067  }
068  
069  public void renderTree(RenderingStatus status, XhtmlNode x, ResourceWrapper qr) throws UnsupportedEncodingException, IOException {
070    HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, context.getDestDir(), context.isInlineGraphics(), true, "");
071    TableModel model = gen.new TableModel("qtree="+qr.getId(), false);    
072    model.setAlternating(true);
073    if (context.getRules() == GenerationRules.VALID_RESOURCE || context.isInlineGraphics()) {
074      model.setDocoImg(HierarchicalTableGenerator.help16AsData());   
075    } else {
076      model.setDocoImg(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "help16.png"));
077    }
078    model.setDocoRef(context.getLink(KnownLinkType.SPEC)+"formats.html#table");
079    model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingContext.QUEST_LINKID), context.formatPhrase(RenderingContext.QUEST_LINK), null, 0));
080    model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingContext.QUEST_TEXT), context.formatPhrase(RenderingContext.QUEST_TEXTFOR), null, 0));
081    model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingContext.GENERAL_DEFINITION), context.formatPhrase(RenderingContext.QUEST_TIMES), null, 0));
082    model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingContext.QUEST_ANSWER), context.formatPhrase(RenderingContext.QUEST_TYPE_ITEM), null, 0));
083
084    // first we add a root for the questionaire itself
085    Row row = addTreeRoot(gen, model.getRows(), qr);
086    List<ResourceWrapper> items = qr.children("item");
087    for (ResourceWrapper i : items) {
088      renderTreeItem(status, gen, row.getSubRows(), qr, i);
089    }
090    XhtmlNode xn = gen.generate(model, context.getLocalPrefix(), 1, null);
091    x.addChildNode(xn);
092  }
093
094  private Row addTreeRoot(HierarchicalTableGenerator gen, List<Row> rows, ResourceWrapper qr) throws IOException {
095    Row r = gen.new Row();
096    rows.add(r);
097
098    ResourceWrapper b = qr.child("questionnaire");
099    String ref = b == null ? null : b.primitiveValue();
100    Questionnaire q = context.getContext().fetchResource(Questionnaire.class, ref);
101    
102    r.setIcon("icon_q_root.gif", context.formatPhrase(RenderingContext.QUEST_RESP_ROOT));
103    r.getCells().add(gen.new Cell(null, null, qr.getId(), null, null));
104    r.getCells().add(gen.new Cell(null, null, "", null, null));
105    if (ref == null ) {
106      r.getCells().add(gen.new Cell(null, null, "", null, null));
107      r.getCells().add(gen.new Cell(context.formatPhrase(RenderingContext.QUEST_QUESTION), null, context.formatPhrase(RenderingContext.QUEST_NONE_SPEC), null, null));
108    } else if (q == null || !q.hasWebPath()) {
109      r.getCells().add(gen.new Cell(null, null, "", null, null));
110      r.getCells().add(gen.new Cell(context.formatPhrase(RenderingContext.QUEST_QUESTION), null, ref, null, null));
111    } else{
112      r.getCells().add(gen.new Cell(null, null, "", null, null));
113      r.getCells().add(gen.new Cell(context.formatPhrase(RenderingContext.QUEST_QUESTION), q.getWebPath(), q.present(), null, null));
114    }
115    return r;    
116  }
117
118  private boolean renderTreeItem(RenderingStatus status, HierarchicalTableGenerator gen, List<Row> rows, ResourceWrapper qr, ResourceWrapper i) throws IOException {
119    Row r = gen.new Row();
120    rows.add(r);
121    boolean hasExt = false;
122
123    List<ResourceWrapper> items = i.children("item");
124    List<ResourceWrapper> answers = i.children("answer");
125    boolean hasItem = items != null && !items.isEmpty();
126    if (answers != null) {
127      for (ResourceWrapper a : answers) {
128        hasItem = a.has("item");
129      }
130    }
131    if (hasItem) {
132      r.setIcon("icon-q-group.png", context.formatPhrase(RenderingContext.QUEST_GROUP));
133    } else {
134      r.setIcon("icon-q-string.png", context.formatPhrase(RenderingContext.QUEST_ITEM));
135    }
136    String linkId = i.has("linkId") ? i.primitiveValue("linkId") : "??";
137    String text = i.has("text") ? i.primitiveValue("text") : "";
138    r.getCells().add(gen.new Cell(null, context.getDefinitionsTarget() == null ? "" : context.getDefinitionsTarget()+"#item."+linkId, linkId, null, null));
139    r.getCells().add(gen.new Cell(null, null, text, null, null));
140    r.getCells().add(gen.new Cell(null, null, null, null, null));
141    if (answers == null ||  answers.size() == 0) {
142      r.getCells().add(gen.new Cell(null, null, null, null, null));
143      if (items != null) {
144        for (ResourceWrapper si : items) {
145          renderTreeItem(status, gen, r.getSubRows(), qr, si);
146        }
147      }
148    } else if (answers.size() == 1) {
149      ResourceWrapper ans = answers.get(0);
150      renderAnswer(status, gen, qr, r, ans);
151    } else {
152      r.getCells().add(gen.new Cell(null, null, null, null, null));          
153      for (ResourceWrapper ans : answers) {
154        Row ar = gen.new Row();
155        ar.setIcon("icon-q-string.png", "Item");
156        ar.getSubRows().add(ar);
157        ar.getCells().add(gen.new Cell(null, null, null, null, null));
158        ar.getCells().add(gen.new Cell(null, null, text, null, null));
159        ar.getCells().add(gen.new Cell(null, null, null, null, null));
160        renderAnswer(status, gen, qr, ar, ans);
161      }
162    }
163
164    return hasExt;    
165  }
166
167  public void renderAnswer(RenderingStatus status, HierarchicalTableGenerator gen, ResourceWrapper qr, Row r, ResourceWrapper ans) throws UnsupportedEncodingException, IOException {
168    ResourceWrapper b = ans.child("value[x]");
169    if (b == null) {
170      r.getCells().add(gen.new Cell(null, null, "null!", null, null));
171    } else if (b.isPrimitive()) {
172      r.getCells().add(gen.new Cell(null, null, b.primitiveValue(), null, null));
173    } else {
174      XhtmlNode x = new XhtmlNode(NodeType.Element, "span");
175      Cell cell = gen.new Cell(null, null, null, null, null);
176      Piece p = gen.new Piece("span");
177      p.getChildren().add(x);
178      cell.addPiece(p);
179      renderDataType(status, x, b);
180      r.getCells().add(cell);
181    }
182    for (ResourceWrapper si : ans.children("item")) {
183      renderTreeItem(status, gen, r.getSubRows(), qr, si);
184    }
185  }
186  
187  private boolean renderTreeItem(HierarchicalTableGenerator gen, List<Row> rows, QuestionnaireResponse q, QuestionnaireResponseItemComponent i) throws IOException {
188    Row r = gen.new Row();
189    rows.add(r);
190    boolean hasExt = false;
191
192    boolean hasItem = i.hasItem();
193    for (QuestionnaireResponseItemAnswerComponent a : i.getAnswer()) {
194      hasItem = a.hasItem();
195    }
196    if (hasItem) {
197      r.setIcon("icon-q-group.png", context.formatPhrase(RenderingContext.QUEST_GROUP));
198    } else {
199      r.setIcon("icon-q-string.png", context.formatPhrase(RenderingContext.QUEST_ITEM));
200    }
201    r.getCells().add(gen.new Cell(null, context.getDefinitionsTarget() == null ? "" : context.getDefinitionsTarget()+"#item."+i.getLinkId(), i.getLinkId(), null, null));
202    r.getCells().add(gen.new Cell(null, null, i.getText(), null, null));
203    r.getCells().add(gen.new Cell(null, null, null, null, null));
204    r.getCells().add(gen.new Cell(null, null, null, null, null));
205
206    return hasExt;    
207  }
208
209  public void genDefinitionLink(HierarchicalTableGenerator gen, QuestionnaireResponseItemComponent i, Cell defn, Resource src) {
210    // can we resolve the definition? 
211    String path = null;
212    String d = i.getDefinition();
213    if (d.contains("#")) {
214      path = d.substring(d.indexOf("#")+1);
215      d = d.substring(0, d.indexOf("#"));
216    }
217    StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, d, src);
218    if (sd != null) {
219      String url = sd.getWebPath();
220      if (url != null) {
221        defn.getPieces().add(gen.new Piece(url+"#"+path, path, null));          
222      } else {
223        defn.getPieces().add(gen.new Piece(null, i.getDefinition(), null));
224      }
225    } else {
226      defn.getPieces().add(gen.new Piece(null, i.getDefinition(), null));
227    }
228  }
229
230  public void genDefinitionLink(XhtmlNode x, QuestionnaireResponseItemComponent i, Resource src) {
231    // can we resolve the definition? 
232    String path = null;
233    String d = i.getDefinition();
234    if (d.contains("#")) {
235      path = d.substring(d.indexOf("#")+1);
236      d = d.substring(0, d.indexOf("#"));
237    }
238    StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, d, src);
239    if (sd != null) {
240      String url = sd.getWebPath();
241      if (url != null) {
242        x.ah(context.prefixLocalHref(url+"#"+path)).tx(path);          
243      } else {
244        x.tx(i.getDefinition());
245      }
246    } else {
247      x.tx(i.getDefinition());
248    }
249  }
250
251  private void addExpression(Piece p, Expression exp, String label, String url) {
252    XhtmlNode x = new XhtmlNode(NodeType.Element, "li").style("font-size: 11px");
253    p.addHtml(x);
254    x.ah(context.prefixLocalHref(url)).tx(label);
255    x.tx(": ");
256    x.code(exp.getExpression());
257  }
258
259  public boolean renderForm(XhtmlNode x, QuestionnaireResponse q) throws UnsupportedEncodingException, IOException {
260    boolean hasExt = false;
261//    XhtmlNode d = x.div();
262//    boolean hasPrefix = false;
263//    for (QuestionnaireItemComponent c : q.getItem()) {
264//      hasPrefix = hasPrefix || doesItemHavePrefix(c);
265//    }
266//    int i = 1;
267//    for (QuestionnaireItemComponent c : q.getItem()) {
268//      hasExt = renderFormItem(d, q, c, hasPrefix ? null : Integer.toString(i), 0) || hasExt;
269//      i++;
270//    }
271//    return hasExt; 
272//  }
273//
274//  private boolean doesItemHavePrefix(QuestionnaireItemComponent i) {
275//    if (i.hasPrefix()) {
276//      return true;
277//    }
278//    for (QuestionnaireItemComponent c : i.getItem()) {
279//      if (doesItemHavePrefix(c)) {
280//        return true;
281//      }
282//    }
283    return false;
284  }
285
286  public boolean renderForm(XhtmlNode x, ResourceWrapper q) throws UnsupportedEncodingException, IOException {
287    boolean hasExt = false;
288    XhtmlNode d = x.div();
289    d.tx(context.formatPhrase(RenderingContext.GENERAL_TODO));
290//    boolean hasPrefix = false;
291//    for (QuestionnaireItemComponent c : q.getItem()) {
292//      hasPrefix = hasPrefix || doesItemHavePrefix(c);
293//    }
294//    int i = 1;
295//    for (QuestionnaireItemComponent c : q.getItem()) {
296//      hasExt = renderFormItem(d, q, c, hasPrefix ? null : Integer.toString(i), 0) || hasExt;
297//      i++;
298//    }
299//    return hasExt; 
300//  }
301//
302//  private boolean doesItemHavePrefix(QuestionnaireItemComponent i) {
303//    if (i.hasPrefix()) {
304//      return true;
305//    }
306//    for (QuestionnaireItemComponent c : i.getItem()) {
307//      if (doesItemHavePrefix(c)) {
308//        return true;
309//      }
310//    }
311    return hasExt;
312  }
313
314//  private boolean renderFormItem(XhtmlNode x, QuestionnaireResponse q, QuestionnaireItemComponent i, String pfx, int indent) throws IOException {
315//    boolean hasExt = false;
316//    XhtmlNode d = x.div().style("width: "+Integer.toString(900-indent*10)+"px; border-top: 1px #eeeeee solid");
317//    if (indent > 0) {
318//      d.style("margin-left: "+Integer.toString(10*indent)+"px");
319//    }
320//    XhtmlNode display = d.div().style("display: inline-block; width: "+Integer.toString(500-indent*10)+"px");
321//    XhtmlNode details = d.div().style("border: 1px #ccccff solid; padding: 2px; display: inline-block; background-color: #fefce7; width: 380px");
322//    XhtmlNode p = display.para();
323//    if (i.getType() == QuestionnaireItemType.GROUP) {
324//      p = p.b();
325//    }
326//    if (i.hasPrefix()) {
327//      p.tx(i.getPrefix());
328//      p.tx(": ");
329//    }
330//    p.span(null, "linkId: "+i.getLinkId()).tx(i.getText());
331//    if (i.getRequired()) {
332//      p.span("color: red", context.formatPhrase(RenderingContext.QUEST_MAND)).tx("*");
333//    }
334//
335//    XhtmlNode input = null;
336//    switch (i.getType()) {
337//    case STRING:
338//      p.tx(" ");
339//      input = p.input(i.getLinkId(), "text", i.getType().getDisplay(), 60);
340//      break;
341//    case ATTACHMENT:
342//      break;
343//    case BOOLEAN:
344//      p.tx(" ");
345//      input = p.input(i.getLinkId(), "checkbox", i.getType().getDisplay(), 1);
346//      break;
347//    case CHOICE:
348//      input = p.select(i.getLinkId());
349//      listOptions(q, i, input);
350//      break;
351//    case DATE:
352//      p.tx(" ");
353//      input = p.input(i.getLinkId(), "date", i.getType().getDisplay(), 10);
354//      break;
355//    case DATETIME:
356//      p.tx(" ");
357//      input = p.input(i.getLinkId(), "datetime-local", i.getType().getDisplay(), 25);
358//      break;
359//    case DECIMAL:
360//      p.tx(" ");
361//      input = p.input(i.getLinkId(), "number", i.getType().getDisplay(), 15);
362//      break;
363//    case DISPLAY:
364//      break;
365//    case GROUP:
366//      
367//      break;
368//    case INTEGER:
369//      p.tx(" ");
370//      input = p.input(i.getLinkId(), "number", i.getType().getDisplay(), 10);
371//      break;
372//    case OPENCHOICE:
373//      break;
374//    case QUANTITY:
375//      p.tx(" ");
376//      input = p.input(i.getLinkId(), "number", "value", 15);
377//      p.tx(" ");
378//      input = p.input(i.getLinkId(), "unit", "unit", 10);
379//      break;
380//    case QUESTION:
381//      break;
382//    case REFERENCE:
383//      break;
384//    case TEXT:
385//      break;
386//    case TIME:
387//      break;
388//    case URL:
389//      break;
390//    default:
391//      break;
392//    }
393//    if (input != null) {
394//      if (i.getReadOnly()) {
395//        input.attribute("readonly", "1");
396//        input.style("background-color: #eeeeee");
397//      }
398//    }
399//    
400////  if (i.hasExtension(" http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse-choiceOrientation")) {
401////  String code = ToolingExtensions.readStringExtension(i,  "http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse-choiceOrientation");
402////  flags.addPiece(gen.new Piece("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-observationLinkPeriod", null, "Orientation: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-"+code+".png"))));
403////}
404//
405//    
406//    XhtmlNode ul = details.ul();
407//    boolean hasFlag = false; 
408//    XhtmlNode flags = item(ul, "Flags");
409//    item(ul, "linkId", i.getLinkId());
410//    
411//    if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-isSubject")) {
412//      hasFlag = true;
413//      flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-isSubject", "Can change the subject of the QuestionnaireResponse").img(Utilities.path(context.getLocalPrefix(), "icon-qi-subject.png"));
414//    }
415//    if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse-hidden")) {
416//      hasFlag = true;
417//      flags.ah(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "extension-QuestionnaireResponse-hidden.html"), "Is a hidden item").img(Utilities.path(context.getLocalPrefix(), "icon-qi-hidden.png"));
418//      d.style("background-color: #eeeeee");
419//    }
420//    if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-optionalDisplay")) {
421//      hasFlag = true;
422//      flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-optionalDisplay", "Is optional to display").img(Utilities.path(context.getLocalPrefix(), "icon-qi-optional.png"));
423//    }
424//    if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-observationLinkPeriod")) {
425//      hasFlag = true;
426//      flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-observationLinkPeriod", "Is linked to an observation").img(Utilities.path(context.getLocalPrefix(), "icon-qi-observation.png"));
427//    }
428//    if (i.hasExtension(" http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse-displayCategory")) {
429//      CodeableConcept cc = i.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse-displayCategory").getValueCodeableConcept();
430//      String code = cc.getCode("http://hl7.org/fhir/QuestionnaireResponse-display-category");
431//      hasFlag = true;
432//      flags.ah("https://hl7.org/fhir/R4/extension-questionnaire-displayCategory.html", "Category: "+code).img(Utilities.path(context.getLocalPrefix(), "icon-qi-"+code+".png"));
433//    }
434//
435//    if (i.hasMaxLength()) {
436//      item(ul, "Max Length", Integer.toString(i.getMaxLength()));
437//    }
438//    if (i.hasDefinition()) {
439//      genDefinitionLink(item(ul, "Definition"), i);      
440//    }
441//    if (i.hasEnableWhen()) {
442//      item(ul, "Enable When", "todo");
443//    }
444//    if (i.hasAnswerValueSet()) {
445//      XhtmlNode ans = item(ul, "Answers");
446//      if (i.getAnswerValueSet().startsWith("#")) {
447//        ValueSet vs = (ValueSet) q.getContained(i.getAnswerValueSet().substring(1));
448//        if (vs == null) {
449//          ans.tx(i.getAnswerValueSet());                    
450//        } else {
451//          ans.ah(vs.getWebPath()).tx(vs.present());                              
452//        }
453//      } else {
454//        ValueSet vs = context.getWorker().findTxResource(ValueSet.class, i.getAnswerValueSet());
455//        if (vs == null  || !vs.hasWebPath()) {
456//          ans.tx(i.getAnswerValueSet());                    
457//        } else {
458//          ans.ah(vs.getWebPath()).tx(vs.present());                              
459//        }             
460//      }
461//    }
462//    if (i.hasAnswerOption()) {
463//      item(ul, "Answers", Integer.toString(i.getAnswerOption().size())+" "+Utilities.pluralize("option", i.getAnswerOption().size()), context.getDefinitionsTarget()+"#item."+i.getLinkId());
464//    }
465//    if (i.hasInitial()) {
466//      XhtmlNode vi = item(ul, "Initial Values");
467//      boolean first = true;
468//      for (QuestionnaireItemInitialComponent v : i.getInitial()) {
469//        if (first) first = false; else vi.tx(", ");
470//        if (v.getValue().isPrimitive()) {
471//          vi.tx(v.getValue().primitiveValue());
472//        } else {
473//          vi.tx("{todo}");          
474//        }
475//      }
476//    }
477//    if (!hasFlag) {
478//      ul.remove(flags);
479//    }
480////    if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-enableWhenExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-itemContext") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-calculatedExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-contextExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-candidateExpression") || i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-initialExpression")) {
481////      if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br"));
482////      defn.getPieces().add(gen.new Piece(null, "Expressions: ", null));
483////      Piece p = gen.new Piece("ul");
484////      defn.getPieces().add(p);
485////      for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-initialExpression")) {
486////        addExpression(p, e.getValueExpression(), "Initial Value", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-initialExpression");
487////      }
488////      for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-contextExpression")) {
489////        addExpression(p, e.getValueExpression(), "Context", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-contextExpression");
490////      }
491////      for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-itemContext")) {
492////        addExpression(p, e.getValueExpression(), "Item Context", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-itemContext");
493////      }
494////      for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-enableWhenExpression")) {
495////        addExpression(p, e.getValueExpression(), "Enable When", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-enableWhenExpression");
496////      }
497////      for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-calculatedExpression")) {
498////        addExpression(p, e.getValueExpression(), "Calculated Value", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-calculatedExpression");
499////      }
500////      for (Extension e : i.getExtensionsByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-candidateExpression")) {
501////        addExpression(p, e.getValueExpression(), "Candidates", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-candidateExpression");
502////      } 
503////    }
504////
505//
506//    int t = 1;
507//    for (QuestionnaireItemComponent c : i.getItem()) {
508//      hasExt = renderFormItem(x, q, c, pfx == null ? null : pfx+"."+Integer.toString(t), indent+1) || hasExt;
509//      t++;
510//    }
511//    return hasExt; 
512//  }
513//
514//  private void item(XhtmlNode ul, String name, String value, String valueLink) {
515//    if (!Utilities.noString(value)) {
516//      ul.li().style("font-size: 10px").ah(valueLink).tx(name+": "+value);
517//    }
518//  }
519//
520//  private void item(XhtmlNode ul, String name, String value) {
521//    if (!Utilities.noString(value)) {
522//      ul.li().style("font-size: 10px").tx(name+": "+value);
523//    }
524//  }
525//  private XhtmlNode item(XhtmlNode ul, String name) {
526//    XhtmlNode li = ul.li();
527//    li.style("font-size: 10px").tx(name+": ");
528//    return li;
529//  }
530//
531//
532//  private void listOptions(QuestionnaireResponse q, QuestionnaireItemComponent i, XhtmlNode select) {
533//    if (i.hasAnswerValueSet()) {
534//      ValueSet vs = null;
535//      if (i.getAnswerValueSet().startsWith("#")) {
536//        vs = (ValueSet) q.getContained(i.getAnswerValueSet().substring(1)).copy();
537//        if (vs != null && !vs.hasUrl()) {
538//          vs.setUrl("urn:uuid:"+UUID.randomUUID().toString().toLowerCase());
539//        }
540//      } else {
541//        vs = context.getContext().findTxResource(ValueSet.class, i.getAnswerValueSet());
542//      }
543//      if (vs != null) {
544//        ValueSetExpansionOutcome exp = context.getContext().expandVS(vs, true, false);
545//        if (exp.getValueset() != null) {
546//          for (ValueSetExpansionContainsComponent cc : exp.getValueset().getExpansion().getContains()) {
547//            select.option(cc.getCode(), cc.hasDisplay() ? cc.getDisplay() : cc.getCode(), false);    
548//          }
549//          return;
550//        }
551//      }
552//    } else if (i.hasAnswerOption()) {
553//      
554//    } 
555//    select.option("a", "??", false);    
556//  }
557//
558//  public String display(Resource dr) throws UnsupportedEncodingException, IOException {
559//    return display((QuestionnaireResponse) dr);
560//  }
561//
562//  public String display(QuestionnaireResponse q) throws UnsupportedEncodingException, IOException {
563//    return "QuestionnaireResponse "+q.present();
564//  }
565// 
566  private boolean renderLinks(RenderingStatus status, XhtmlNode x, ResourceWrapper q) {
567    x.para().tx(context.formatPhrase(RenderingContext.QUEST_TRY_QUEST));
568    XhtmlNode ul = x.ul();
569    ul.li().ah("http://todo.nlm.gov/path?mode=ig&src="+Utilities.pathURL(context.getLink(KnownLinkType.SELF), "package.tgz")+"&q="+q.getId()+".json").tx(context.formatPhrase(RenderingContext.QUEST_NLM));
570    return false;
571  }
572
573//  private boolean renderDefns(XhtmlNode x, QuestionnaireResponse q) throws IOException {
574//    XhtmlNode tbl = x.table("dict");
575//    boolean ext = false;
576//    ext = renderRootDefinition(tbl, q, new ArrayList<>()) || ext;
577//    for (QuestionnaireItemComponent qi : q.getItem()) {
578//      ext = renderDefinition(tbl, q, qi, new ArrayList<>()) || ext;
579//    }
580//    return ext;
581//  }
582//
583//  private boolean renderRootDefinition(XhtmlNode tbl, QuestionnaireResponse q, List<QuestionnaireItemComponent> parents) throws IOException {
584//    boolean ext = false;
585//    XhtmlNode td = tbl.tr().td("structure").colspan("2").span(null, null).attribute("class", "self-link-parent");
586//    td.an(q.getId());
587//    td.img(Utilities.path(context.getLocalPrefix(), "icon_q_root.gif"));
588//    td.tx(" QuestionnaireResponse ");
589//    td.b().tx(q.getId());
590//    
591//    // general information
592//    defn(tbl, "URL", q.getUrl());
593//    defn(tbl, "Version", q.getVersion());
594//    defn(tbl, "Name", q.getName());
595//    defn(tbl, "Title", q.getTitle());
596//    if (q.hasDerivedFrom()) {
597//      td = defn(tbl, "Derived From");
598//      boolean first = true;
599//      for (CanonicalType c : q.getDerivedFrom()) {
600//        if (first) first = false; else td.tx(", ");
601//        td.tx(c.asStringValue()); // todo: make these a reference
602//      }
603//    }
604//    defn(tbl, "Status", q.getStatus().getDisplay());
605//    defn(tbl, "Experimental", q.getExperimental());
606//    defn(tbl, "Publication Date", q.getDateElement().primitiveValue());
607//    defn(tbl, "Approval Date", q.getApprovalDateElement().primitiveValue());
608//    defn(tbl, "Last Review Date", q.getLastReviewDateElement().primitiveValue());
609//    if (q.hasEffectivePeriod()) {
610//      renderPeriod(defn(tbl, "Effective Period"), q.getEffectivePeriod());
611//    }
612//    
613//    if (q.hasSubjectType()) {
614//      td = defn(tbl, "Subject Type");
615//      boolean first = true;
616//      for (CodeType c : q.getSubjectType()) {
617//        if (first) first = false; else td.tx(", ");
618//        td.tx(c.asStringValue());
619//      }
620//    }
621//    defn(tbl, "Description", q.getDescription());
622//    defn(tbl, "Purpose", q.getPurpose());
623//    defn(tbl, "Copyright", q.getCopyright());
624//    if (q.hasCode()) {
625//      td = defn(tbl, Utilities.pluralize("Code", q.getCode().size()));
626//      boolean first = true;
627//      for (Coding c : q.getCode()) {
628//        if (first) first = false; else td.tx(", ");
629//        renderCodingWithDetails(td,  c);
630//      }
631//    }
632//    return false;
633//  }
634  
635//  private boolean renderDefinition(XhtmlNode tbl, QuestionnaireResponse q, QuestionnaireItemComponent qi, List<QuestionnaireItemComponent> parents) throws IOException {
636//    boolean ext = false;
637//    XhtmlNode td = tbl.tr().td("structure").colspan("2").span(null, null).attribute("class", "self-link-parent");
638//    td.an("item."+qi.getLinkId());
639//    for (QuestionnaireItemComponent p : parents) {
640//      td.ah("#item."+p.getLinkId()).img(Utilities.path(context.getLocalPrefix(), "icon_q_item.png"));
641//      td.tx(" > ");
642//    }
643//    td.img(Utilities.path(context.getLocalPrefix(), "icon_q_item.png"));
644//    td.tx(" Item ");
645//    td.b().tx(qi.getLinkId());
646//    
647//    // general information
648//    defn(tbl, "Link Id", qi.getLinkId());
649//    defn(tbl, "Prefix", qi.getPrefix());
650//    defn(tbl, "Text", qi.getText());
651//    defn(tbl, "Type", qi.getType().getDisplay());
652//    defn(tbl, "Required", qi.getRequired(), true);
653//    defn(tbl, "Repeats", qi.getRepeats(), true);
654//    defn(tbl, "Read Only", qi.getReadOnly(), false);
655//    if (ToolingExtensions.readBoolExtension(qi, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-isSubject")) {
656//      defn(tbl, "Subject", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-isSubject", "This element changes who the subject of the question is", null);
657//    }
658//    
659//    // content control
660//    defn(tbl, "Max Length", qi.getMaxLength());
661//    if (qi.hasAnswerValueSet()) {
662//      defn(tbl, "Value Set", qi.getDefinition(), context.getWorker().findTxResource(ValueSet.class,  qi.getAnswerValueSet()));
663//    }
664//    if (qi.hasAnswerOption()) {
665//      XhtmlNode tr = tbl.tr();
666//      tr.td().tx(context.formatPhrase(RenderingContext.QUEST_ALLOWED));
667//      XhtmlNode ul = tr.td().ul();
668//      for (QuestionnaireItemAnswerOptionComponent ans : qi.getAnswerOption()) {
669//        XhtmlNode li = ul.li();
670//        render(li, ans.getValue());
671//        if (ans.getInitialSelected()) {
672//          li.tx(context.formatPhrase(RenderingContext.QUEST_INITIALLY));
673//        }
674//      }      
675//    }
676//    if (qi.hasInitial()) {
677//      XhtmlNode tr = tbl.tr();
678//      tr.td().tx(Utilities.pluralize("Initial Answer", qi.getInitial().size()));
679//      if (qi.getInitial().size() == 1) {
680//        render(tr.td(), qi.getInitialFirstRep().getValue());
681//      } else {
682//        XhtmlNode ul = tr.td().ul();
683//        for (QuestionnaireItemInitialComponent ans : qi.getInitial()) {
684//          XhtmlNode li = ul.li();
685//          render(li, ans.getValue());
686//        }
687//      }      
688//    }
689//
690//    // appearance 
691//    if (qi.hasExtension(" http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse-displayCategory")) {
692//      XhtmlNode tr = tbl.tr();
693//      tr.td().ah("http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse-displayCategory").tx(context.formatPhrase(RenderingContext.QUEST_DISPLAY_CAT));
694//      render(tr.td(), qi.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse-displayCategory").getValue());
695//    }
696//    if (ToolingExtensions.readBoolExtension(qi, "http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse-hidden")) {
697//      defn(tbl, "Hidden Item", "https://hl7.org/fhir/R4/extension-questionnaire-displayCategory.html", "This item is a hidden question", null);
698//    }
699//    if (ToolingExtensions.readBoolExtension(qi, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-optionalDisplay")) {
700//      defn(tbl, "Hidden Item", "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-optionalDisplay", "This item is optional to display", null);
701//    }
702//    
703//    // formal definitions
704//    if (qi.hasDefinition()) {
705//      genDefinitionLink(defn(tbl, "Definition"), qi);
706//    }
707//      
708//    if (qi.hasCode()) {
709//      XhtmlNode tr = tbl.tr();
710//      tr.td().tx(Utilities.pluralize("Code", qi.getCode().size()));
711//      XhtmlNode ul = tr.td().ul();
712//      for (Coding c : qi.getCode()) {
713//        renderCodingWithDetails(ul.li(), c);
714//      }
715//    }
716//    if (qi.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-observationLinkPeriod")) {
717//      XhtmlNode tr = tbl.tr();
718//      tr.td().ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-observationLinkPeriod").tx(context.formatPhrase(RenderingContext.QUEST_OBSERVATION));
719//      render(tr.td(), qi.getExtensionByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-observationLinkPeriod").getValue());
720//    }
721//    
722//    // dynamic management
723//    if (qi.hasEnableWhen()) {
724//      XhtmlNode tr = tbl.tr();
725//      tr.td().tx(context.formatPhrase(RenderingContext.QUEST_ENABLE));
726//      td = tr.td();
727//      if (qi.getEnableWhen().size() == 1) {
728//        renderEnableWhen(td, qi.getEnableWhen().get(0));
729//      } else {
730//        td.tx(qi.getEnableBehavior().getDisplay()+" are true:");
731//        XhtmlNode ul = td.ul();
732//        for (QuestionnaireItemEnableWhenComponent ew : qi.getEnableWhen()) {
733//          renderEnableWhen(ul.li(), ew);
734//        }
735//      }      
736//    }
737//    
738//    
739//    // other stuff
740//    
741//
742//    
743//    List<QuestionnaireItemComponent> curr = new ArrayList<>();
744//    curr.addAll(parents);
745//    curr.add(qi);
746//    for (QuestionnaireItemComponent qic : qi.getItem()) {
747//      ext = renderDefinition(tbl, q, qic, curr) || ext;
748//    }
749//    return ext;
750//  }
751//
752//  private void defn(XhtmlNode tbl, String name, String url, Resource res) throws UnsupportedEncodingException, IOException {
753//    if (res != null && res.hasWebPath()) {
754//      defn(tbl, "Definition", RendererFactory.factory(res, context).display(res), res.getWebPath());
755//    } else if (Utilities.isAbsoluteUrl(url)) {
756//      defn(tbl, "Definition", url, url);
757//    } {
758//      defn(tbl, "Definition", url);
759//    }
760// 
761//  }
762//
763//  private void renderEnableWhen(XhtmlNode x, QuestionnaireItemEnableWhenComponent ew) {
764//    x.ah("#item."+ew.getQuestion()).tx(ew.getQuestion());
765//    x.tx(" ");
766//    x.tx(ew.getOperator().toCode());
767//    x.tx(" ");
768//    x.tx(display(ew.getAnswer()));
769//  }
770//
771//  private XhtmlNode defn(XhtmlNode tbl, String name) {
772//    XhtmlNode tr = tbl.tr();
773//    tr.td().tx(name);
774//    return tr.td();
775//  }
776//  
777//  private void defn(XhtmlNode tbl, String name, int value) {
778//    if (value > 0) {
779//      XhtmlNode tr = tbl.tr();
780//      tr.td().tx(name);
781//      tr.td().tx(value);
782//    }    
783//  }
784// 
785//  
786//  private void defn(XhtmlNode tbl, String name, boolean value) {
787//    XhtmlNode tr = tbl.tr();
788//    tr.td().tx(name);
789//    tr.td().tx(Boolean.toString(value));
790//  }
791// 
792//  private void defn(XhtmlNode tbl, String name, String value) {
793//    if (!Utilities.noString(value)) {
794//      XhtmlNode tr = tbl.tr();
795//      tr.td().tx(name);
796//      tr.td().tx(value);
797//    }    
798//  }
799//  
800//  private void defn(XhtmlNode tbl, String name, String value, String url) {
801//    if (!Utilities.noString(value)) {
802//      XhtmlNode tr = tbl.tr();
803//      tr.td().tx(name);
804//      tr.td().ah(url).tx(value);
805//    }    
806//  }
807//
808//  private void defn(XhtmlNode tbl, String name, String nurl, String value, String url) {
809//    if (!Utilities.noString(value)) {
810//      XhtmlNode tr = tbl.tr();
811//      tr.td().ah(nurl).tx(name);
812//      if (url != null) {
813//        tr.td().ah(url).tx(value);
814//      } else {
815//        tr.td().tx(value);
816//      }
817//    }    
818//  }
819//
820//  private void defn(XhtmlNode tbl, String name, boolean value, boolean ifFalse) {
821//    if (ifFalse || value) {
822//      XhtmlNode tr = tbl.tr();
823//      tr.td().tx(name);
824//      tr.td().tx(Boolean.toString(value));
825//    }    
826//  }
827
828
829}