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