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