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