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