001package org.hl7.fhir.r5.renderers;
002
003import java.io.IOException;
004import java.io.UnsupportedEncodingException;
005import java.util.ArrayList;
006import java.util.HashMap;
007import java.util.List;
008import java.util.Map;
009
010import javax.annotation.Nullable;
011
012import org.hl7.fhir.exceptions.DefinitionException;
013import org.hl7.fhir.exceptions.FHIRException;
014import org.hl7.fhir.exceptions.FHIRFormatError;
015import org.hl7.fhir.r5.extensions.ExtensionDefinitions;
016import org.hl7.fhir.r5.extensions.ExtensionUtilities;
017import org.hl7.fhir.r5.model.CanonicalType;
018import org.hl7.fhir.r5.model.CapabilityStatement;
019import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent;
020import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
021import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceOperationComponent;
022import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent;
023import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementDocumentComponent;
024import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementMessagingComponent;
025import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementMessagingSupportedMessageComponent;
026import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementMessagingEndpointComponent;
027import org.hl7.fhir.r5.model.CapabilityStatement.ReferenceHandlingPolicy;
028import org.hl7.fhir.r5.model.CapabilityStatement.ResourceInteractionComponent;
029import org.hl7.fhir.r5.model.CapabilityStatement.SystemInteractionComponent;
030import org.hl7.fhir.r5.model.CapabilityStatement.SystemRestfulInteraction;
031import org.hl7.fhir.r5.model.CapabilityStatement.TypeRestfulInteraction;
032import org.hl7.fhir.r5.model.CodeType;
033import org.hl7.fhir.r5.model.CodeableConcept;
034import org.hl7.fhir.r5.model.Element;
035import org.hl7.fhir.r5.model.Enumeration;
036import org.hl7.fhir.r5.model.Enumerations.FHIRVersion;
037import org.hl7.fhir.r5.model.Extension;
038import org.hl7.fhir.r5.model.Resource;
039import org.hl7.fhir.r5.model.StringType;
040import org.hl7.fhir.r5.model.StructureDefinition;
041import org.hl7.fhir.r5.renderers.utils.RenderingContext;
042import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules;
043import org.hl7.fhir.r5.renderers.utils.ResourceWrapper;
044import org.hl7.fhir.r5.utils.EOperationOutcome;
045
046import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage;
047import org.hl7.fhir.utilities.Utilities;
048import org.hl7.fhir.utilities.xhtml.XhtmlNode;
049
050
051@MarkedToMoveToAdjunctPackage
052public class CapabilityStatementRenderer extends ResourceRenderer {
053
054  public CapabilityStatementRenderer(RenderingContext context) { 
055    super(context); 
056  } 
057 
058  @Override
059  public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome {
060    if (r.isDirect()) {
061      renderResourceTechDetails(r, x);
062      render(status, x, (CapabilityStatement) r.getBase(), r);      
063    } else {
064      // the intention is to change this in the future
065      x.para().tx("CapabilityStatementRenderer only renders native resources directly");
066    }
067  }
068  
069  @Override
070  public String buildSummary(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
071    return canonicalTitle(r);
072  }
073
074  private static final String EXPECTATION = "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation";
075  private static final String COMBINED = "http://hl7.org/fhir/StructureDefinition/capabilitystatement-search-parameter-combination";
076  private static final String SP_BASE = "http://hl7.org/fhir/searchparameter/";
077  private static final String FHIR_BASE = "http://hl7.org/fhir/";
078  private static final String VERS_DEF_PREFIX = "FHIR Release ";
079
080  private String currentFhirBase = "";
081  private String collapseClass = "panel-collapse in";
082
083  private boolean multExpectationsPresent = false;
084  
085  //Private classes for driving the rendering
086
087  private class CombinedSearchParamSet {
088    private Map<Boolean, List<SingleParam>> params;
089    String expectation = "";
090
091    CombinedSearchParamSet(String expectation) {
092      params = new HashMap<Boolean, List<SingleParam>>();
093      params.put(true, new ArrayList<SingleParam>());
094      params.put(false, new ArrayList<SingleParam>());
095      if (!Utilities.noString(expectation)) this.expectation = expectation;
096    }
097 
098    public Map<Boolean, List<SingleParam>> getParams() {
099        return params;
100    }
101
102    public String getExpectation() {
103        return expectation;
104    }
105
106    public void addParam(boolean required, SingleParam param) {
107      params.get(required).add(param);
108    }
109  }
110  private class SingleParam {
111    private String name = "";
112    private String definition = "";
113    private String type = "";
114    private String documentation = "";
115    private String expectation = "";
116    private String hostResource = "";
117
118    public SingleParam(String name) {
119      if (!Utilities.noString(name)) this.name=name;
120    }
121 
122    public SingleParam(String name, String definition) {
123      if (!Utilities.noString(name)) this.name=name;
124      if (!Utilities.noString(definition)) this.definition=definition;
125    }
126
127    public SingleParam(String name, String definition, String type) {
128      if (!Utilities.noString(name)) this.name=name;
129      if (!Utilities.noString(definition)) this.definition=definition;
130      if (!Utilities.noString(type)) this.type=type;
131    }
132
133    public SingleParam(String name, String definition, String type, String documentation) {
134      if (!Utilities.noString(name)) this.name=name;
135      if (!Utilities.noString(definition)) this.definition=definition;
136      if (!Utilities.noString(type)) this.type=type;
137      if (!Utilities.noString(documentation)) this.documentation=documentation;
138    }
139 
140    public SingleParam(String name, String definition, String type, String documentation, String expectation, String hostResource) {
141      if (!Utilities.noString(name)) this.name=name;
142      if (!Utilities.noString(definition)) this.definition=definition;
143      if (!Utilities.noString(type)) this.type=type;
144      if (!Utilities.noString(documentation)) this.documentation=documentation;
145      if (!Utilities.noString(expectation)) this.expectation=expectation;
146      if (!Utilities.noString(hostResource)) this.hostResource = hostResource;
147    }
148 
149    public String getName() {
150        return name;
151    }
152
153    public String getDefinition() {
154        return definition;
155    }
156
157    public String getDocumentation() {
158        return documentation;
159    }
160 
161    public String getType() {
162        return type;
163    }
164
165    public String getExpectation() {
166        return expectation;
167    }
168
169    public String getHostResource() {
170      return hostResource;
171    }
172  }
173  private class ResourceSearchParams {
174    private Map<String, List<CombinedSearchParamSet>> combinedParams;
175    private Map<String, SingleParam> individualParamsByName;
176    private Map<String, List<SingleParam>> individualParamsByExp;
177
178    public ResourceSearchParams() {
179      combinedParams = new HashMap<String, List<CombinedSearchParamSet>>();
180      combinedParams.put("SHALL", new ArrayList<CombinedSearchParamSet>());
181      combinedParams.put("SHOULD", new ArrayList<CombinedSearchParamSet>());
182      combinedParams.put("SHOULD-NOT", new ArrayList<CombinedSearchParamSet>());
183      combinedParams.put("MAY", new ArrayList<CombinedSearchParamSet>());
184      combinedParams.put("supported", new ArrayList<CombinedSearchParamSet>());
185      individualParamsByName  = new HashMap<String, SingleParam>();
186      individualParamsByExp = new HashMap<String, List<SingleParam>>();
187      individualParamsByExp.put("SHALL", new ArrayList<SingleParam>());
188      individualParamsByExp.put("SHOULD", new ArrayList<SingleParam>());
189      individualParamsByExp.put("MAY", new ArrayList<SingleParam>());
190      individualParamsByExp.put("SHOULD-NOT", new ArrayList<SingleParam>());
191      individualParamsByExp.put("supported", new ArrayList<SingleParam>());
192    }
193
194    public Map<String, List<CombinedSearchParamSet>> getCombined() {
195      return combinedParams;
196    }
197
198    public Map<String, SingleParam> getInd() {
199        return individualParamsByName;
200    }
201
202    public Map<String, SingleParam> getIndbyName() {
203      return individualParamsByName;
204    }
205    
206    public Map<String, List<SingleParam>> getIndbyExp() {
207      return individualParamsByExp;
208    }
209
210    public void addCombinedParamSet(String exp, CombinedSearchParamSet params) {
211      combinedParams.get(exp).add(params);
212    }
213 
214    public void addIndividualbyName(String name, SingleParam param) {
215      individualParamsByName.put(name, param);
216    }
217 
218    public void addIndividualbyExp(String exp, SingleParam param) {
219      individualParamsByExp.get(exp).add(param);
220    }
221 
222  }
223 
224  private class SingleOperation {
225    private String name = "";
226    private String definition = "";
227    private String documentation = "";
228    private String expectation = "";
229
230    public SingleOperation(String name) {
231      if (!Utilities.noString(name)) this.name=name;
232    }
233
234    public SingleOperation(String name, String definition) {
235      if (!Utilities.noString(name)) this.name=name;
236      if (!Utilities.noString(definition)) this.definition=definition;
237    }
238
239    public SingleOperation(String name, String definition, String documentation) {
240      if (!Utilities.noString(name)) this.name=name;
241      if (!Utilities.noString(definition)) this.definition=definition;
242
243      if (!Utilities.noString(documentation)) this.documentation=documentation;
244    }
245
246    public SingleOperation(String name, String definition, String documentation, String expectation) {
247      if (!Utilities.noString(name)) this.name=name;
248      if (!Utilities.noString(definition)) this.definition=definition;
249      if (!Utilities.noString(documentation)) this.documentation=documentation;
250      if (!Utilities.noString(expectation)) this.expectation=expectation;
251    }
252 
253    public String getName() {
254        return name;
255    }
256
257    public String getDefinition() {
258        return definition;
259    }
260
261    public String getDocumentation() {
262        return documentation;
263     }
264
265    public String getExpectation() {
266      return expectation;
267    }
268  }
269 
270  private class ResourceOperations {
271    private Map<String, List<SingleOperation>> operationsByExp;
272
273    public ResourceOperations() {
274
275      operationsByExp = new HashMap<String, List<SingleOperation>>();
276      operationsByExp.put("SHALL", new ArrayList<SingleOperation>());
277      operationsByExp.put("SHOULD", new ArrayList<SingleOperation>());
278      operationsByExp.put("MAY", new ArrayList<SingleOperation>());
279      operationsByExp.put("SHOULD-NOT", new ArrayList<SingleOperation>());
280      operationsByExp.put("supported", new ArrayList<SingleOperation>());
281    }
282 
283    public Map<String, List<SingleOperation>> getOperations() {
284      return operationsByExp;
285    }
286    public void addOperation(String exp, SingleOperation op) {
287      operationsByExp.get(exp).add(op);
288    }
289
290  }
291
292  private class ResourceInteraction {
293    private String codeString;
294    private String documentation;
295    public ResourceInteraction(String code, String markdown) {
296      codeString = code;
297      if (!Utilities.noString(markdown)) {
298        documentation = markdown;
299      }
300      else {
301        documentation = null;
302      }
303    }
304
305    public String getDocumentation() {
306      return documentation;
307    }
308
309    public String getInteraction() {
310      return codeString;
311    }
312  }
313
314
315
316  public void render(RenderingStatus status, XhtmlNode x, CapabilityStatement conf, ResourceWrapper res) throws FHIRFormatError, DefinitionException, IOException {
317    status.setExtensions(true);
318    boolean igRenderingMode = (context.getRules() == GenerationRules.IG_PUBLISHER);
319    FHIRVersion currentVersion = conf.getFhirVersion();
320    String versionPathComponent = getVersionPathComponent(currentVersion.getDefinition());
321    if (!Utilities.noString(versionPathComponent)) {
322      currentFhirBase = FHIR_BASE + versionPathComponent + "/";
323    }
324    else {
325      currentFhirBase = FHIR_BASE;
326    }
327    
328    String igVersion = conf.getVersion();
329
330    x.h(2,context.prefixAnchor("title")).addText(conf.getTitle());
331    XhtmlNode uList = x.ul();
332    uList.li().addText(context.formatPhrase(RenderingContext.CAPABILITY_IMP_VER, igVersion) + " ");
333    uList.li().addText(context.formatPhrase(RenderingContext.CAPABILITY_FHIR_VER, currentVersion.toCode()) + " ");
334    addSupportedFormats(uList, conf);
335    
336    uList.li().addText(context.formatPhrase(RenderingContext.CAPABILITY_PUB_ON, displayDateTime(wrapWC(res, conf.getDateElement())) + " "));
337    uList.li().addText(context.formatPhrase(RenderingContext.CAPABILITY_PUB_BY, conf.getPublisherElement().asStringValue()) + " ");
338
339
340    XhtmlNode block = x.addTag("blockquote").attribute("class","impl-note");
341    block.addTag("p").addTag("strong").addText(context.formatPhrase(RenderingContext.CAPABILITY_NOTE_CAP));
342    block.addTag("p").addText(context.formatPhrase(RenderingContext.CAPABILTY_ALLOW_CAP));
343
344
345    addSupportedCSs(status, x, conf, res);
346    addSupportedIGs(x, conf);
347
348    int restNum = conf.getRest().size();
349    int nextLevel = 3;
350    if (restNum > 0) {
351      x.h(2,context.prefixAnchor("rest")).addText((context.formatPhrase(RenderingContext.CAPABILITY_REST_CAPS)));
352      int count=1;
353      for (CapabilityStatementRestComponent rest : conf.getRest()) {
354        if (restNum > 1) {
355          x.h(3,context.prefixAnchor("rest"+Integer.toString(count))).addText(context.formatPhrase(RenderingContext.CAPABILITY_REST_CONFIG, Integer.toString(count)) + " ");
356          nextLevel = 4;
357        }
358        addRestConfigPanel(x, rest, nextLevel, count);
359        
360        boolean hasVRead = false;
361        boolean hasPatch = false;
362        boolean hasDelete = false;
363        boolean hasHistory = false;
364        boolean hasUpdates = false;
365        for (CapabilityStatementRestResourceComponent r : rest.getResource()) {
366          hasVRead = hasVRead || hasOp(r, TypeRestfulInteraction.VREAD);
367          hasPatch = hasPatch || hasOp(r, TypeRestfulInteraction.PATCH);
368          hasDelete = hasDelete || hasOp(r, TypeRestfulInteraction.DELETE);
369          hasHistory = hasHistory || hasOp(r, TypeRestfulInteraction.HISTORYTYPE);
370          hasUpdates = hasUpdates || hasOp(r, TypeRestfulInteraction.HISTORYINSTANCE);
371        }
372        if (rest.getResource().size() >0) {
373          x.h(nextLevel,context.prefixAnchor("resourcesCap" + Integer.toString(count))).addText(context.formatPhrase(RenderingContext.CAPABILITY_RES_PRO));
374          x.h(nextLevel+1,context.prefixAnchor("resourcesSummary" + Integer.toString(count))).addText(context.formatPhrase(RenderingContext.GENERAL_SUMM));
375          addSummaryIntro(x);
376          addSummaryTable(status, res, x, rest, hasVRead, hasPatch, hasDelete, hasHistory, hasUpdates, count);
377          x.addTag("hr");
378          //Third time for individual resources
379          int resCount = 1;
380          for (CapabilityStatementRestResourceComponent r : rest.getResource()) {
381            addResourceConfigPanel(status, res, x, r, nextLevel+1, count, resCount, igRenderingMode);
382            resCount++;
383          }
384        }
385        if (rest.getOperation().size() > 0) {
386          //TODO Figure out what should come out of this
387          x.h(nextLevel,context.prefixAnchor("operationsCap" + Integer.toString(count))).addText(context.formatPhrase(RenderingContext.CAPABILITY_OP));
388          x.h(nextLevel+1,context.prefixAnchor("operationsSummary" + Integer.toString(count))).addText(context.formatPhrase(RenderingContext.OP_DEF_USE));
389        }
390        count++;
391      }
392    }
393
394    int messagingNum = conf.getMessaging().size();
395    nextLevel = 3;
396    if (messagingNum > 0) {
397      x.h(2,context.prefixAnchor("messaging")).addText((context.formatPhrase(RenderingContext.CAPABILITY_MESSAGING_CAPS)));
398      int count=1;
399      for (CapabilityStatementMessagingComponent msg : conf.getMessaging()) 
400      {
401        addMessagingPanel(status, res, x, msg, nextLevel, count, messagingNum);
402        count++;
403      }
404
405    }
406
407    int documentNum = conf.getDocument().size();
408    nextLevel = 3;
409    if (documentNum > 0) {
410      x.h(2,context.prefixAnchor("document")).addText((context.formatPhrase(RenderingContext.CAPABILITY_DOCUMENT_CAPS)));
411      addDocumentTable(status, res, x, conf, nextLevel);
412    }
413
414    
415    if (multExpectationsPresent) {
416      addWarningPanel(x,"?? - " + context.formatPhrase(RenderingContext.CAPABILITY_MULT_EXT));
417    }
418
419  }
420
421  private String getVersionPathComponent(String definition) {
422    if (Utilities.noString(definition)) return "";
423    if (!definition.startsWith(VERS_DEF_PREFIX)) return "";
424    String restOfDef[] = definition.substring(VERS_DEF_PREFIX.length()).split(" ");
425    if (restOfDef[1].startsWith("(")) return "R"+restOfDef[0];
426    return "";
427  }
428
429  public void describe(XhtmlNode x, CapabilityStatement cs) {
430    x.tx(display(cs));
431  }
432
433  public String display(CapabilityStatement cs) {
434    return cs.present();
435  }
436
437  private boolean hasOp(CapabilityStatementRestResourceComponent r, TypeRestfulInteraction on) {
438    for (ResourceInteractionComponent op : r.getInteraction()) {
439      if (op.getCode() == on)
440        return true;
441    }
442    return false;
443  }
444
445  private String showOp(CapabilityStatementRestResourceComponent r, TypeRestfulInteraction on) {
446    for (ResourceInteractionComponent op : r.getInteraction()) {
447      if (op.getCode() == on)
448        return "y";
449    }
450    return "";
451  }
452
453  private String showOp(CapabilityStatementRestComponent r, SystemRestfulInteraction on) {
454    for (SystemInteractionComponent op : r.getInteraction()) {
455      if (op.getCode() == on)
456        return "y";
457    }
458    return "";
459  }
460
461  private XhtmlNode addTableRow(XhtmlNode t, String name) {
462    XhtmlNode tr = t.tr();
463    tr.td().addText(name);
464    return tr.td();
465  }
466  
467  private void addTableRow(XhtmlNode t, String name, String value) {
468    XhtmlNode tr = t.tr();
469    tr.td().addText(name);
470    tr.td().addText(value);
471  }
472
473  @Nullable
474  private String getExtValueCode(Extension ext) {
475    if (ext != null) {
476      return ext.getValueCodeType().getCode();
477    }
478    return null;
479  }
480
481  private void addSupportedCSs(RenderingStatus status, XhtmlNode x, CapabilityStatement cap, ResourceWrapper res) throws UnsupportedEncodingException, IOException {
482    if (cap.hasInstantiates()) {
483      XhtmlNode p = x.para();
484      p.tx(cap.getInstantiates().size() > 1 ? "This CapabilityStatement instantiates these CapabilityStatements " : "This CapabilityStatement instantiates the CapabilityStatement ");
485      boolean first = true;
486      for (CanonicalType ct : cap.getInstantiates()) {
487        if (first) {first = false;} else {p.tx(", ");};
488        renderCanonical(status, res, p, CapabilityStatement.class, ct);
489      }
490    }
491    if (cap.hasImports()) {
492      XhtmlNode p = x.para();
493      p.tx(cap.getImports().size() > 1 ? "This CapabilityStatement imports these CapabilityStatements " : "This CapabilityStatement imports the CapabilityStatement ");
494      boolean first = true;
495      for (CanonicalType ct : cap.getImports()) {
496        if (first) {first = false;} else {p.tx(", ");};
497        renderCanonical(status, res, p, CapabilityStatement.class, ct);
498      }      
499    }
500  }
501  
502  private void addSupportedIGs(XhtmlNode x, CapabilityStatement cap) {
503    String capExpectation=null;
504    if (cap.hasImplementationGuide()) {
505      ArrayList<String> igShoulds = new ArrayList<String>();
506      ArrayList<String> igShalls = new ArrayList<String>();
507      ArrayList<String> igMays = new ArrayList<String>();
508      int i=0;
509      for (CanonicalType c : cap.getImplementationGuide())
510      {
511        capExpectation=getExtValueCode(c.getExtensionByUrl(EXPECTATION));
512        if (!Utilities.noString(capExpectation)) {
513          if (capExpectation.equals("SHALL")) {
514            igShalls.add(c.asStringValue());
515          }
516          else if (capExpectation.equals("SHOULD")) {
517            igShoulds.add(c.asStringValue());
518          }
519          else if (capExpectation.equals("MAY")) {
520            igMays.add(c.asStringValue());
521          }
522        }
523        else {
524          igShalls.add(c.asStringValue());   //default to SHALL
525        }
526      }
527      XhtmlNode ul = null;
528      if (igShalls.size() > 0) {
529        x.h(3,context.prefixAnchor("shallIGs")).addText(context.formatPhrase(RenderingContext.CAPABILTY_SHALL_SUPP));
530        ul = x.ul();
531        for (String url : igShalls) {
532          addResourceLink(ul.li(), url, url);
533          //ul.li().ah(url).addText(url);
534        }
535      }
536      if (igShoulds.size() > 0) {
537        x.h(3,context.prefixAnchor("shouldIGs")).addText(context.formatPhrase(RenderingContext.CAPABILITY_SHOULD_SUPP));
538        ul = x.ul();
539        for (String url : igShoulds) {
540          addResourceLink(ul.li(), url, url);
541          //ul.li().ah(url).addText(url);
542        }
543      }
544      if (igMays.size() > 0) {
545        x.h(3,context.prefixAnchor("mayIGs")).addText(context.formatPhrase(RenderingContext.CAPABILITY_MAY_SUPP));
546        ul = x.ul();
547        for (String url : igMays) {
548          addResourceLink(ul.li(), url, url);
549          //ul.li().ah(url).addText(url);
550        }
551      }
552
553    }
554  }
555
556  private void addSupportedFormats(XhtmlNode uList, CapabilityStatement conf) {
557    XhtmlNode lItem = uList.li();
558    lItem.addText(context.formatPhrase(RenderingContext.CAPABILITY_SUPP_FORM) + " ");
559    Boolean first = true;
560    String capExpectation = null;
561    for (CodeType c : conf.getFormat()) {
562      if (!first) {
563        lItem.addText(", ");
564      }
565      capExpectation = getExtValueCode(c.getExtensionByUrl(EXPECTATION));
566      if (!Utilities.noString(capExpectation)) {
567        lItem.addTag("strong").addText(capExpectation);
568        lItem.addText(" "+ (context.formatPhrase(RenderingContext.CAPABILITY_SUPP) + " "));
569      }
570      lItem.code().addText(c.getCode());
571      first = false;
572    }
573    lItem = uList.li();
574    lItem.addText(context.formatPhrase(RenderingContext.CAPABILITY_SUPP_PATCH_FORM) + " ");
575    first=true;
576    for (CodeType c : conf.getPatchFormat()) {
577      if (!first) {
578        lItem.addText(", ");
579      }
580      capExpectation = getExtValueCode(c.getExtensionByUrl(EXPECTATION));
581      if (!Utilities.noString(capExpectation)) {
582        lItem.addTag("strong").addText(capExpectation);
583        lItem.addText(" " + context.formatPhrase(RenderingContext.CAPABILITY_SUPP) + " ");
584      }
585      lItem.code().addText(c.getCode());
586      first = false;
587    }
588  }
589
590  private void addRestConfigPanel(XhtmlNode x, CapabilityStatementRestComponent rest, int nextLevel, int count) throws FHIRFormatError, DefinitionException, IOException {
591    XhtmlNode panel= null;
592    XhtmlNode body = null;
593    XhtmlNode row = null;
594    XhtmlNode cell = null;
595    XhtmlNode heading = null;
596    panel = x.div().attribute("class", "panel panel-default");
597    heading = panel.div().attribute("class", "panel-heading").h(nextLevel,context.prefixAnchor("mode" + Integer.toString(count))).attribute("class", "panel-title");
598    heading.addText("Mode: ");
599    heading.code().addText(rest.getMode().toCode());
600    body = panel.div().attribute("class", "panel-body");
601    addMarkdown(body, rest.getDocumentation());
602    //Security info
603    if (rest.hasSecurity()) {
604      body.div().attribute("class","lead").addTag("em").addText("Security");
605      String mdText = rest.getSecurity().getDescription();
606      boolean cors = rest.getSecurity().getCors();
607      List<CodeableConcept> services = rest.getSecurity().getService();
608      if (cors || services.size() >0) {
609        row = body.div().attribute("class","row");
610        row.div().attribute("class","col-lg-6").addText(getCorsText(cors));
611        cell = row.div().attribute("class","col-lg-6");
612        cell.addText("Security services supported: ");
613        addSeparatedListOfCodes(cell, getSecServices(services), ",");
614      } 
615    
616      if (!Utilities.noString(mdText)) {
617        addMarkdown(body.blockquote(),mdText);
618      }
619    }  
620    body.div().attribute("class","lead").addTag("em").addText(context.formatPhrase(RenderingContext.CAPABILITY_SUMM_SYS_INT));
621    addSystemInteractions(body, rest.getInteraction());
622        
623  }
624
625  private void addMessagingPanel(RenderingStatus status, ResourceWrapper res, XhtmlNode x, CapabilityStatementMessagingComponent msg, int nextLevel, int index, int total) throws FHIRFormatError, DefinitionException, IOException {
626    XhtmlNode panel= null;
627    XhtmlNode body = null;
628    XhtmlNode row = null;
629    XhtmlNode heading = null;
630
631    XhtmlNode table;
632    XhtmlNode tbody;
633    XhtmlNode tr;
634
635    panel = x.div().attribute("class", "panel panel-default");
636    heading = panel.div().attribute("class", "panel-heading").h(nextLevel,context.prefixAnchor("messaging_" + Integer.toString(index))).attribute("class", "panel-title");
637    if(total == 1)
638    {
639      heading.addText(context.formatPhrase(RenderingContext.CAPABILITY_MESSAGING_CAP));
640    }
641    else
642    {
643      heading.addText(context.formatPhrase(RenderingContext.CAPABILITY_MESSAGING_CAP) + " " + String.valueOf(index));
644    }
645
646    body = panel.div().attribute("class", "panel-body");
647
648    if(msg.hasReliableCache())
649    {
650      addLead(body, "Reliable Cache Length");
651      body.br();
652      body.addText(String.valueOf(msg.getReliableCache()) + " Minute(s)");
653      body.br();
654    }
655
656    if(msg.hasEndpoint())
657    {
658      body.h(nextLevel+1,context.prefixAnchor("msg_end_"+Integer.toString(index))).addText(context.formatPhrase(RenderingContext.CAPABILITY_ENDPOINTS));
659      table = body.table("table table-condensed table-hover", false);
660      tr = table.addTag("thead").tr();
661      tr.th().addText("Protocol");
662      tr.th().addText("Address");
663
664      tbody = table.addTag("tbody");
665      for (CapabilityStatementMessagingEndpointComponent end : msg.getEndpoint())
666      {
667        tr = tbody.tr();
668        renderDataType(status, tr.td(), wrapNC(end.getProtocol()));
669        renderUri(status,  tr.td(), wrapNC(end.getAddressElement()));
670      }
671      body.br();
672    }
673
674    if(msg.hasSupportedMessage())
675    {
676      body.h(nextLevel+1,context.prefixAnchor("msg_sm_"+Integer.toString(index))).addText(context.formatPhrase(RenderingContext.CAPABILITY_SUPP_MSGS));
677      table = body.table("table table-condensed table-hover", false);
678      tr = table.addTag("thead").tr();
679      tr.th().addText("Mode");
680      tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_DEFINITION));
681
682      tbody = table.addTag("tbody");
683      for (CapabilityStatementMessagingSupportedMessageComponent sup : msg.getSupportedMessage())
684      {
685        tr = tbody.tr();
686        tr.td().addText(sup.getMode().toCode());
687        renderCanonical(status, res, tr.td(), StructureDefinition.class, sup.getDefinitionElement());
688      }
689      if(msg.hasDocumentation())
690      {
691        addLead(body, context.formatPhrase(RenderingContext.GENERAL_DOCUMENTATION));
692        addMarkdown(body.blockquote(), msg.getDocumentation());
693      }
694      body.br();
695    }
696  }
697
698
699  private void addDocumentTable(RenderingStatus status, ResourceWrapper res, XhtmlNode x, CapabilityStatement conf, int nextLevel) throws FHIRFormatError, DefinitionException, IOException {
700    XhtmlNode table;
701    XhtmlNode tbody;
702    XhtmlNode tr;
703
704    table = x.table("table table-condensed table-hover", false);
705    tr = table.addTag("thead").tr();
706    tr.th().addText("Mode");
707    tr.th().addText(context.formatPhrase(RenderingContext.CAPABILITY_PROF_RES_DOC));
708    tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_DOCUMENTATION));
709
710    tbody = table.addTag("tbody");
711    for (CapabilityStatementDocumentComponent document : conf.getDocument()) {
712      tr = tbody.tr();
713      tr.td().addText(document.getMode().toCode());
714      renderCanonical(status, res, tr.td(), StructureDefinition.class, document.getProfileElement());
715      if(document.hasDocumentation())
716      {
717        addMarkdown(tr.td(), document.getDocumentation());
718      }
719      else
720      {
721        tr.td().nbsp();
722      }
723    }
724  }
725
726  private String getCorsText(boolean on) {
727    if (on) {
728      return context.formatPhrase(RenderingContext.CAPABILITY_CORS_YES);
729    }
730    return context.formatPhrase(RenderingContext.CAPABILITY_CORS_NO);
731  }
732
733  private List<String> getSecServices(List<CodeableConcept> services)
734  {
735    List<String> serviceStrings = new ArrayList<String>();
736    for (CodeableConcept c: services) {
737      if (c.hasCoding()){
738        serviceStrings.add(c.getCodingFirstRep().getCode());
739      }
740      else {
741        serviceStrings.add(c.getText());
742      }
743    }
744    return serviceStrings;
745  }
746
747
748  private void addSystemInteractions(XhtmlNode body, List<SystemInteractionComponent> interactions) {
749    if (interactions.size()==0) return;
750    XhtmlNode uList = body.ul();
751    String capExpectation = null;
752    String expName = null;
753    String documentation = null;
754    Map<String,String> expression = null;
755    ArrayList<Map<String,String>> shalls = new ArrayList<Map<String,String>>();
756    ArrayList<Map<String,String>> shoulds = new ArrayList<Map<String,String>>();
757    ArrayList<Map<String,String>> mays = new ArrayList<Map<String,String>>();
758    ArrayList<Map<String,String>> shouldnots = new ArrayList<Map<String,String>>();
759    ArrayList<Map<String,String>> supports = new ArrayList<Map<String,String>>();
760    for (SystemInteractionComponent comp : interactions) {
761      capExpectation=getExtValueCode(comp.getExtensionByUrl(EXPECTATION));
762      expName = comp.getCode().toCode();
763      documentation = comp.getDocumentation();
764      if (Utilities.noString(documentation)) {
765        documentation="";
766      }
767      expression = new HashMap<String,String>();
768      expression.put(expName,documentation);
769      if (!Utilities.noString(capExpectation)) {
770        if (capExpectation.equals("SHALL")) {
771          shalls.add(expression);
772        }
773        else if (capExpectation.equals("SHOULD")) {
774          shoulds.add(expression);
775        }
776        else if (capExpectation.equals("MAY")) {
777          mays.add(expression);
778        }
779        else if (capExpectation.equals("SHOULD-NOT")) {
780          shouldnots.add(expression);
781        }
782      }
783      else {
784        supports.add(expression);
785      }
786    }
787    addInteractionListItems(uList, "SHALL", shalls);
788    addInteractionListItems(uList, "SHOULD", shoulds);
789    addInteractionListItems(uList, "MAY", mays);
790    addInteractionListItems(uList, "SHOULD NOT", shouldnots);
791    addInteractionListItems(uList, null, supports);
792  }
793
794  private void addInteractionListItems(XhtmlNode uList, String verb, List<Map<String,String>> interactions) {
795    XhtmlNode item = null;
796    String interaction = null;
797    String documentation = null;
798    for (Map<String,String> interactionMap : interactions) {
799      item = uList.li();
800      if (Utilities.noString(verb)) {
801        item.addText(context.formatPhrase(RenderingContext.CAPABILITY_SUPPS_THE) + " ");
802      }
803      else {
804        item.addTag("strong").addText(verb + " ");
805        item.addText(context.formatPhrase(RenderingContext.CAPABILITY_SUPP_THE) + " ");
806      }
807      interaction = interactionMap.keySet().toArray()[0].toString();
808      item.code(interaction);
809      documentation = interactionMap.get(interaction);
810      if (Utilities.noString(documentation)) {
811        item.addText(context.formatPhrase(RenderingContext.CAPABILITY_INT));
812      }
813      else {
814        item.addText(context.formatPhrase(RenderingContext.CAPABILITY_INT_DESC));
815        try {
816          addMarkdown(item, documentation);
817        } catch (FHIRFormatError e) {
818          // TODO Auto-generated catch block
819          e.printStackTrace();
820        } catch (DefinitionException e) {
821          // TODO Auto-generated catch block
822          e.printStackTrace();
823        } catch (IOException e) {
824          // TODO Auto-generated catch block
825          e.printStackTrace();
826        }
827      }
828    }
829  }
830
831  private void addInteractionSummaryList(XhtmlNode uList, String verb, List<ResourceInteraction> interactions) {
832    if (interactions.size() == 0) return;
833    XhtmlNode item = uList.li();
834    if (Utilities.noString(verb)) {
835      item.addText(context.formatPhrase(RenderingContext.CAPABILITY_SUPPS) + " ");
836    }
837    else {
838      item.addTag("strong").addText(verb);
839      item.addText(" " + context.formatPhrase(RenderingContext.CAPABILITY_SUPP) + " ");
840    }
841
842    applyInteractionsList(item, interactions);  
843  }
844
845  private void addSummaryIntro(XhtmlNode x) {
846    XhtmlNode uList = null;
847    XhtmlNode lItem = null;
848    x.para().addText(context.formatPhrase(RenderingContext.CAPABILITY_SUMM_RES));
849    uList=x.ul();
850    uList.li().addText(context.formatPhrase(RenderingContext.CAPABILITY_REV_PROF));
851    lItem = uList.li();
852    lItem.addText(context.formatPhrase(RenderingContext.CAPABILITY_INTER_SUPP));
853    lItem.b().addTag("span").attribute("class","bg-info").addText("R");
854    lItem.addText("ead, ");
855    lItem.b().addTag("span").attribute("class","bg-info").addText("S");
856    lItem.addText("earch, ");
857    lItem.b().addTag("span").attribute("class","bg-info").addText("U");
858    lItem.addText("pdate, and ");
859    lItem.b().addTag("span").attribute("class","bg-info").addText("C");
860    lItem.addText("reate, are always shown, while ");
861    lItem.b().addTag("span").attribute("class","bg-info").addText("VR");
862    lItem.addText("ead, ");
863    lItem.b().addTag("span").attribute("class","bg-info").addText("P");
864    lItem.addText("atch, ");
865    lItem.b().addTag("span").attribute("class","bg-info").addText("D");
866    lItem.addText("elete, ");
867    lItem.b().addTag("span").attribute("class","bg-info").addText("H");
868    lItem.addText("istory on ");
869    lItem.b().addTag("span").attribute("class","bg-info").addText("I");
870    lItem.addText("nstance, or ");
871    lItem.b().addTag("span").attribute("class","bg-info").addText("H");
872    lItem.addText("istory on ");
873    lItem.b().addTag("span").attribute("class","bg-info").addText("T");
874    lItem.addText(context.formatPhrase(RenderingContext.CAPABILITY_TYP_PRES));
875    uList.li().addTag("span").addText(context.formatPhrase(RenderingContext.CAPABILITY_SEARCH_PAR) + " ");
876    lItem = uList.li();
877    lItem.addText(context.formatPhrase(RenderingContext.CAPABILITY_RES_ENB) + " ");
878    lItem.code().addText("_include");
879    lItem = uList.li();
880    lItem.addText(context.formatPhrase(RenderingContext.CAPABILITY_OTH_RES_ENB) + " ");
881    lItem.code().addText("_revinclude");
882    uList.li().addText(context.formatPhrase(RenderingContext.CAPABILITY_RES_OPER));
883  }
884
885  private void addSummaryTable(RenderingStatus status, ResourceWrapper res, XhtmlNode x, CapabilityStatement.CapabilityStatementRestComponent rest, boolean hasVRead, boolean hasPatch, boolean hasDelete, boolean hasHistory, boolean hasUpdates, int count) throws IOException {
886    XhtmlNode t = x.div().attribute("class","table-responsive").table("table table-condensed table-hover", false);
887    XhtmlNode tr = t.addTag("thead").tr();
888    tr.th().b().tx(context.formatPhrase(RenderingContext.CAPABILITY_RES_TYP));
889    tr.th().b().tx(context.formatPhrase(RenderingContext.GENERAL_PROF));
890    tr.th().attribute("class", "text-center").b().attribute("title", context.formatPhrase(RenderingContext.CAPABILITY_READ_INT)).tx("R");
891    if (hasVRead)
892      tr.th().attribute("class", "text-center").b().attribute("title", context.formatPhrase(RenderingContext.CAPABILITY_VREAD_INT)).tx("V-R");
893    tr.th().attribute("class", "text-center").b().attribute("title", context.formatPhrase(RenderingContext.CAPABILITY_SEARCH_INT)).tx("S");
894    tr.th().attribute("class", "text-center").b().attribute("title", context.formatPhrase(RenderingContext.CAPABILITY_UPDATE_INT)).tx("U");
895    if (hasPatch)
896      tr.th().attribute("class", "text-center").b().attribute("title", context.formatPhrase(RenderingContext.CAPABILITY_PATCH_INT)).tx("P");
897    tr.th().attribute("class", "text-center").b().attribute("title", context.formatPhrase(RenderingContext.CAPABILITY_CREATE_INT)).tx("C");
898    if (hasDelete)
899      tr.th().attribute("class", "text-center").b().attribute("title", context.formatPhrase(RenderingContext.CAPABILITY_DELETE_INT)).tx("D");
900    if (hasUpdates)
901      tr.th().attribute("class", "text-center").b().attribute("title", context.formatPhrase(RenderingContext.CAPABILITY_HISTORY_INT)).tx("H-I");
902    if (hasHistory)
903      tr.th().attribute("class", "text-center").b().attribute("title", context.formatPhrase(RenderingContext.CAPABILITY_HISTORY_TYPE)).tx("H-T");
904    tr.th().b().attribute("title", context.formatPhrase(RenderingContext.CAPABILITY_REQ_RECOM)).tx(context.formatPhrase(RenderingContext.CAPABILITY_SEARCHES));
905    tr.th().code().b().tx("_include");
906    tr.th().code().b().tx("_revinclude");
907    tr.th().b().tx(context.formatPhrase(RenderingContext.CAPABILITY_OP));
908
909    XhtmlNode tbody = t.addTag("tbody");
910    XhtmlNode profCell = null;
911    boolean hasProf = false;
912    boolean hasSupProf = false;
913    int resCount = 1;
914    String countString = "";
915    for (CapabilityStatementRestResourceComponent r : rest.getResource()) {
916      tr = tbody.tr();
917      countString = Integer.toString(count) + "-" + Integer.toString(resCount);
918      tr.td().ah("#" + context.prefixAnchor(r.getType() + countString)).addText(r.getType());
919      resCount++;
920      //Show profiles
921      profCell = tr.td();
922      hasProf = r.hasProfile();
923      hasSupProf = r.hasSupportedProfile();
924      if ((!hasProf) && (!hasSupProf)) {
925        profCell.nbsp();
926      }
927      else if (hasProf) {
928        addResourceLink(profCell, r.getProfile(), r.getProfile());
929        //profCell.ah(r.getProfile()).addText(r.getProfile());
930        if (hasSupProf) {
931          profCell.br();
932          profCell.addTag("em").addText(context.formatPhrase(RenderingContext.CAPABILITY_ADD_SUPP_PROF));
933          renderSupportedProfiles(status, res, profCell, r);
934        }
935      }
936      else {    //Case of only supported profiles
937        profCell.addText(context.formatPhrase(RenderingContext.CAPABILITY_SUPP_PROFS));
938        renderSupportedProfiles(status, res, profCell, r);
939      }
940      //Show capabilities
941      tr.td().attribute("class", "text-center").addText(showOp(r, TypeRestfulInteraction.READ));
942      if (hasVRead)
943        tr.td().attribute("class", "text-center").addText(showOp(r, TypeRestfulInteraction.VREAD));
944      tr.td().attribute("class", "text-center").addText(showOp(r, TypeRestfulInteraction.SEARCHTYPE));
945      tr.td().attribute("class", "text-center").addText(showOp(r, TypeRestfulInteraction.UPDATE));
946      if (hasPatch)
947        tr.td().attribute("class", "text-center").addText(showOp(r, TypeRestfulInteraction.PATCH));
948      tr.td().attribute("class", "text-center").addText(showOp(r, TypeRestfulInteraction.CREATE));
949      if (hasDelete)
950        tr.td().attribute("class", "text-center").addText(showOp(r, TypeRestfulInteraction.DELETE));
951      if (hasUpdates)
952        tr.td().attribute("class", "text-center").addText(showOp(r, TypeRestfulInteraction.HISTORYINSTANCE));
953      if (hasHistory)
954        tr.td().attribute("class", "text-center").addText(showOp(r, TypeRestfulInteraction.HISTORYTYPE));
955      //Show search parameters
956      List<String> stringList = new ArrayList<String>();
957      getParams(stringList,r.getSearchParam());
958      getCombinedParams(stringList,r.getExtensionsByUrl(COMBINED));
959      tr.td().addText(getSeparatedList(stringList,","));
960      //Show supported includes
961      stringList = getStringListFromStringTypeList(r.getSearchInclude());
962      addSeparatedListOfCodes(tr.td(), stringList, ",");
963      //Show supported revIncludes
964      stringList = getStringListFromStringTypeList(r.getSearchRevInclude());
965      addSeparatedListOfCodes(tr.td(), stringList, ",");
966      //Show supported operations
967      stringList = getStringListFromOperations(r.getOperation());
968      addSeparatedListOfCodes(tr.td(), stringList, ",");
969    }
970  }
971
972  private List<String> getCombinedParams(List<String> paramNames, List<Extension> paramExtensions) {
973    for (Extension e : paramExtensions) {
974      String capExpectation = expectationForDisplay(e,EXPECTATION);
975      if (!Utilities.noString(capExpectation)) {
976        if (capExpectation.equals("SHALL") || capExpectation.equals("SHOULD") || capExpectation.equals("MAY")) {
977          paramNames.add(printCombinedParams(e));
978        }
979      }
980    }
981    return paramNames;
982  }
983
984  private void renderSupportedProfiles(RenderingStatus status, ResourceWrapper res, XhtmlNode profCell, CapabilityStatementRestResourceComponent r) throws IOException {
985    for (CanonicalType sp: r.getSupportedProfile()) { 
986      profCell.br();
987      profCell.nbsp().nbsp();
988      renderCanonical(status, res, profCell, StructureDefinition.class, sp);
989    }
990    if (r.hasExtension(ExtensionDefinitions.EXT_PROFILE_MAPPING_NEW, ExtensionDefinitions.EXT_PROFILE_MAPPING_OLD)) {
991      profCell.br();
992      profCell.b().tx(context.formatPhrase(RenderingContext.CAPABILITY_PROF_MAP));
993      XhtmlNode tbl = profCell.table("grid", false);
994      boolean doco = false;
995      for (Extension ext : r.getExtensionsByUrl(ExtensionDefinitions.EXT_PROFILE_MAPPING_NEW, ExtensionDefinitions.EXT_PROFILE_MAPPING_OLD)) {
996        doco = doco || ext.hasExtension("documentation");
997      }
998      XhtmlNode tr = tbl.tr();
999      tr.th().tx(context.formatPhrase(RenderingContext.GENERAL_CRIT));
1000      tr.th().tx(context.formatPhrase(RenderingContext.GENERAL_PROF));
1001      if (doco) {
1002        tr.th().tx(context.formatPhrase(RenderingContext.GENERAL_CRIT));
1003      }
1004      for (Extension ext : r.getExtensionsByUrl(ExtensionDefinitions.EXT_PROFILE_MAPPING_NEW, ExtensionDefinitions.EXT_PROFILE_MAPPING_OLD)) {
1005        tr = tbl.tr();
1006        tr.td().code().tx(ExtensionUtilities.readStringExtension(ext, "search"));
1007        String url = ExtensionUtilities.readStringExtension(ext, "profile");
1008        StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, url);
1009        if (sd != null) {
1010          tr.td().code().ah(sd.getWebPath()).tx(sd.present());
1011        } else {
1012          tr.td().code().tx(url);
1013        }
1014        if (doco) {
1015          tr.td().code().markdown(ExtensionUtilities.readStringExtension(ext, "documentation"), "documentation"); 
1016        }
1017      }      
1018    }
1019  }
1020
1021  private String printCombinedParams(Extension e) {
1022    StringBuilder params = new StringBuilder();
1023    List<Extension> requiredParams = e.getExtensionsByUrl("required");
1024    List<Extension> optionalParams = e.getExtensionsByUrl("optional");
1025    boolean first = true;
1026    for (Extension param : requiredParams) {
1027      if (!first) {
1028        params.append("+");
1029      }
1030      first = false;
1031      params.append(param.getValueStringType());
1032    }
1033    for (Extension param : optionalParams) {
1034      params.append("+");
1035      params.append(param.getValueStringType());
1036    }
1037    return params.toString();
1038  }
1039
1040  private List<String> getParams(List<String> paramNames, List<CapabilityStatementRestResourceSearchParamComponent> params) {
1041    for (CapabilityStatementRestResourceSearchParamComponent p : params) {
1042      String capExpectation = expectationForDisplay(p,EXPECTATION);
1043      if (!Utilities.noString(capExpectation)) {
1044        if (capExpectation.equals("SHALL") || capExpectation.equals("SHOULD") || capExpectation.equals("MAY")) {
1045          paramNames.add(p.getName());
1046        }
1047      }
1048      else {  //When there is not extension, assume parameters are required
1049        paramNames.add(p.getName()); 
1050      }
1051    }
1052    return paramNames;
1053  }
1054
1055  private String getSeparatedList(List<String> list, String separator) {
1056    StringBuilder result = new StringBuilder();
1057    boolean first = true;
1058    for (String s : list) {
1059      if (!first) {
1060        result.append(separator + " ");
1061      }
1062      first = false;
1063      result.append(s);
1064    }
1065    return result.toString();
1066  }
1067
1068  private List<String> getStringListFromStringTypeList(List<StringType> list) {
1069    List<String> stringList = new ArrayList<String>();
1070    for (StringType st : list) {
1071      stringList.add(st.asStringValue());
1072    }
1073    return stringList;
1074  }
1075
1076  private void addSeparatedListOfCodes(XhtmlNode parent, List<String> list, String separator) {
1077    boolean first = true;
1078    for (String s : list) {
1079      if (!first) {
1080        parent.addText(separator + " ");
1081      }
1082      first = false;
1083      parent.code().addText(s);
1084    }
1085  }
1086
1087  private List<String> getStringListFromOperations(List<CapabilityStatementRestResourceOperationComponent> list) {
1088    List<String> result = new ArrayList<String>();
1089    for (CapabilityStatementRestResourceOperationComponent op : list) {
1090      String capExpectation = expectationForDisplay(op,EXPECTATION);
1091      if (!Utilities.noString(capExpectation)) {
1092        if (capExpectation.equals("SHALL") || capExpectation.equals("SHOULD") || capExpectation.equals("MAY")) {
1093          result.add("$"+op.getName());
1094        }
1095      }
1096      else {
1097        result.add("$"+op.getName());
1098      }
1099    }
1100    return result;
1101  }
1102
1103  private void applyInteractionsList(XhtmlNode item, List<ResourceInteraction> list) {
1104    List<String> noDocList = new ArrayList<String>();
1105    List<ResourceInteraction> docList = new ArrayList<ResourceInteraction>();
1106    for (ResourceInteraction inter : list) {
1107      if (Utilities.noString(inter.getDocumentation())) {
1108        noDocList.add(inter.getInteraction());
1109      }
1110      else {
1111        docList.add(inter);
1112      }
1113    }
1114    if (noDocList.size() > 0) {
1115      addSeparatedListOfCodes(item,noDocList, ",");
1116    }
1117    if (docList.size() > 0) {
1118      item.br();
1119      for (ResourceInteraction inter : docList) {
1120        item.code().addText(inter.getInteraction());
1121        try {
1122          addMarkdown(item, inter.getDocumentation());
1123        }
1124        catch(IOException e) {
1125          e.printStackTrace();
1126        }
1127      }
1128    }
1129    else {
1130      item.addText(".");
1131    }
1132  }
1133
1134  private void addResourceConfigPanel(RenderingStatus status, ResourceWrapper res, XhtmlNode x, CapabilityStatementRestResourceComponent r, int nextLevel, int count, int resCount, boolean igRenderingMode) throws FHIRFormatError, DefinitionException, IOException {
1135    XhtmlNode panel= null;
1136    XhtmlNode body = null;
1137    XhtmlNode panelHead = null;
1138    XhtmlNode panelRef = null;
1139    
1140    String countString = Integer.toString(count) + "-" + Integer.toString(resCount);
1141    panel = x.div().attribute("class", "panel panel-default");
1142    if (igRenderingMode) {
1143      panelHead = panel.div().attribute("class", "panel-heading").attribute("role", "tab").attribute("id","heading" + countString).h(nextLevel,context.prefixAnchor(r.getType() + countString)).attribute("class", "panel-title");
1144      panelRef = panelHead.ah("#collapse" + countString).attribute("role","button").attribute("data-toggle", "collapse").attribute("aria-expanded","true").attribute("aria-controls","collapse" + countString);
1145      //panelRef = panelHead.addTag("button").attribute("href","#collapse" + countString).attribute("role","button").attribute("data-toggle", "collapse").attribute("aria-expanded","false").attribute("aria-controls","collapse" + countString);
1146      //panelRef.span("float: right;","").attribute("class", "lead").addText("Resource Conformance: " + getResourceExpectation(r));
1147      panelRef.span("float: right;","").addText("Resource Conformance: " + getResourceExpectation(r));
1148      panelRef.addText(r.getType());
1149      body = panel.div().attribute("class", collapseClass).attribute("id","collapse" + countString).attribute("role","tabpanel").attribute("aria-labelledby","heading" + countString).div().attribute("class", "panel-body").div().attribute("class", "container");
1150    }
1151    else {
1152      panelHead = panel.div().attribute("class", "panel-heading").h(nextLevel,context.prefixAnchor(r.getType() + countString)).attribute("class", "panel-title");
1153      panelHead.span("float: right;","").addText(context.formatPhrase(RenderingContext.CAPABILITY_RES_CONF, getResourceExpectation(r)) + " ");
1154      panelHead.addText(r.getType());
1155      body = panel.div().attribute("class", "panel-body").div().attribute("class", "container");
1156    }
1157    
1158    
1159    //Top part of panel
1160    XhtmlNode cell = null;
1161    XhtmlNode row = body.div().attribute("class", "row");   
1162    String text = r.getProfile();
1163    boolean pullInteraction = false;
1164    String refPolicyWidth = "col-lg-3";
1165    if (!Utilities.noString(text)) {
1166      cell = row.div().attribute("class", "col-lg-6");
1167      addLead(cell,context.formatPhrase(RenderingContext.CAPABILITY_BASE_SYS));
1168      cell.br();
1169      renderCanonical(status, res, cell, StructureDefinition.class, r.getProfileElement());
1170      cell=row.div().attribute("class", "col-lg-3");
1171      addLead(cell, context.formatPhrase(RenderingContext.CAPABILITY_PROF_CONF));
1172      cell.br();
1173      cell.b().addText(getProfileExpectation(r.getProfileElement()));
1174    }
1175    else {   //No profile, use FHIR Core Resource
1176      cell = row.div().attribute("class", "col-lg-4");
1177      addLead(cell, context.formatPhrase(RenderingContext.CAPABILITY_FHIR));
1178      cell.br();
1179      cell.ah(currentFhirBase + r.getType().toLowerCase() + ".html").addText(r.getType());
1180      pullInteraction = true;
1181      refPolicyWidth = "col-lg-4";
1182    }
1183    
1184    cell = row.div().attribute("class", refPolicyWidth);
1185    addLead(cell,context.formatPhrase(RenderingContext.CAPABILITY_REF_PROF));
1186    cell.br();
1187    addSeparatedListOfCodes(cell, getReferencePolicyStrings(r.getReferencePolicy()) , ",");
1188    if (pullInteraction) {
1189      addInteractions(row, r, 4);
1190    }
1191    body.para();
1192    List<CanonicalType> supportedProfiles = r.getSupportedProfile();
1193    if (supportedProfiles.size() > 0) {
1194      row = body.div().attribute("class", "row");
1195      cell = row.div().attribute("class", "col-6");
1196      addLead(cell, context.formatPhrase(RenderingContext.CAPABILITY_SUPP_PROFS));
1197      XhtmlNode para = cell.para();
1198      boolean first = true;
1199      for (CanonicalType c : supportedProfiles) {
1200        if (!first) {
1201          para.br();
1202        }
1203        first=false;
1204        renderCanonical(status, res, para, StructureDefinition.class, c);
1205        //para.ah(c.asStringValue()).addText(c.asStringValue());
1206      }  
1207    }
1208    if (!pullInteraction) {
1209      if (supportedProfiles.size() < 1) {
1210        row = body.div().attribute("class", "row");
1211      }
1212      addInteractions(row, r, 6);
1213    }
1214
1215    //Resource Documentation
1216    body.para();
1217    String mdText = r.getDocumentation();
1218    if (!Utilities.noString(mdText)) {
1219      row = body.div().attribute("class", "row");
1220      cell = row.div().attribute("class", "col-12");
1221      addLead(cell, context.formatPhrase(RenderingContext.GENERAL_DOCUMENTATION));
1222      addMarkdown(cell.blockquote(), mdText);
1223    }
1224
1225    //Resource search parameters
1226    ResourceSearchParams sParams = collectParams(r);
1227    addSearchParams(body, sParams);
1228    //addSearchParamsDocumentation(body, sParams);
1229    //Resource operations
1230    ResourceOperations ops = collectOperations(r);
1231    addExtendedOperations(body, ops);
1232  }
1233
1234  private void addExtendedOperations(XhtmlNode body, ResourceOperations ops) {
1235    if (ops == null) return;
1236    Map<String, List<SingleOperation>> map = ops.getOperations();
1237    if (!hasOperations(map)) return;
1238    XhtmlNode row;
1239    XhtmlNode cell;
1240    XhtmlNode table;
1241    XhtmlNode tbody;
1242    XhtmlNode tr;
1243    row = body.div().attribute("class", "row");
1244    cell = row.div().attribute("class", "col-12");
1245    addLead(cell, context.formatPhrase(RenderingContext.CAPABILITY_EXT_OP));
1246    table = cell.table("table table-condensed table-hover", false);
1247    tr = table.addTag("thead").tr();
1248    tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_CONFORMANCE));
1249    tr.th().addText(context.formatPhrase(RenderingContext.CAPABILITY_OPER));
1250    tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_DOCUMENTATION));
1251    tbody = table.addTag("tbody");
1252    addOps(tbody, map, "supported");
1253    addOps(tbody, map, "SHALL");
1254    addOps(tbody, map, "SHOULD");
1255    addOps(tbody, map, "MAY");
1256    addOps(tbody, map, "SHOULD-NOT");
1257    return;
1258  }
1259
1260  private ResourceOperations collectOperations(CapabilityStatementRestResourceComponent r) {
1261    List <CapabilityStatementRestResourceOperationComponent> opList = r.getOperation();
1262    if (opList.size()==0) return null;
1263    String capExpectation;
1264    SingleOperation operation;
1265    ResourceOperations ops = new ResourceOperations();
1266    
1267    for ( CapabilityStatementRestResourceOperationComponent op : opList) {
1268      capExpectation = expectationForDisplay(op,EXPECTATION);
1269      if (Utilities.noString(capExpectation)) {
1270        capExpectation = "supported";
1271      }
1272      operation = new SingleOperation(op.getName(),op.getDefinition(),op.getDocumentation(),capExpectation);
1273      ops.addOperation(capExpectation, operation);
1274    }
1275    return ops;
1276  }
1277
1278  private void addInteractions(XhtmlNode row, CapabilityStatementRestResourceComponent r, int width) {
1279    String capExpectation;
1280    String widthString = "col-lg-" + Integer.toString(width);
1281    //Need to build a different structure
1282    List<ResourceInteraction> shalls = new ArrayList<ResourceInteraction>();
1283    List<ResourceInteraction> shoulds = new ArrayList<ResourceInteraction>();
1284    List<ResourceInteraction> mays = new ArrayList<ResourceInteraction>();
1285    List<ResourceInteraction> shouldnots = new ArrayList<ResourceInteraction>();
1286    List<ResourceInteraction> supporteds = new ArrayList<ResourceInteraction>();
1287
1288    ResourceInteraction tempInteraction = null;
1289
1290    for (ResourceInteractionComponent op : r.getInteraction()) {
1291      capExpectation = expectationForDisplay(op,EXPECTATION);
1292      tempInteraction = new ResourceInteraction(op.getCode().toCode(), op.getDocumentation());
1293      if (!Utilities.noString(capExpectation)) {
1294        switch(capExpectation) {
1295          case "SHALL"      : shalls.add(tempInteraction);
1296                              break;
1297          case "SHOULD"     : shoulds.add(tempInteraction);
1298                              break;
1299          case "MAY"        : mays.add(tempInteraction);
1300                              break;
1301          case "SHOULD-NOT" : shouldnots.add(tempInteraction);
1302                              break;
1303        }
1304      }
1305      else {
1306        supporteds.add(tempInteraction);
1307      }
1308    }
1309    XhtmlNode cell = row.div().attribute("class", widthString);
1310    addLead(cell, context.formatPhrase(RenderingContext.CAPABILITY_INT_SUMM));
1311    cell.br();
1312    XhtmlNode ul = cell.ul();
1313    addInteractionSummaryList(ul, "SHALL", shalls);
1314    addInteractionSummaryList(ul, "SHOULD", shoulds);
1315    addInteractionSummaryList(ul, "MAY", mays);
1316    addInteractionSummaryList(ul, "SHOULD-NOT", shouldnots);
1317    addInteractionSummaryList(ul, "", supporteds);
1318  }
1319
1320  private ResourceSearchParams collectParams(CapabilityStatementRestResourceComponent r) {
1321    ResourceSearchParams sParams = new ResourceSearchParams();
1322    String capExpectation;
1323    SingleParam param;
1324    for ( CapabilityStatementRestResourceSearchParamComponent sp : r.getSearchParam()) {
1325      capExpectation = expectationForDisplay(sp,EXPECTATION);
1326      if (Utilities.noString(capExpectation)) {
1327        capExpectation = "supported";
1328      }
1329      param = new SingleParam(sp.getName(),sp.getDefinition(),sp.getType().toCode(),sp.getDocumentation(),capExpectation, r.getType().toLowerCase());
1330      sParams.addIndividualbyName(param.getName(), param);
1331      sParams.addIndividualbyExp(capExpectation,param);
1332    }
1333    //CombinedSearchParam component;
1334    CombinedSearchParamSet combinedParams;
1335    String paramName;
1336    for (Extension e : r.getExtensionsByUrl(COMBINED)) {
1337      capExpectation = expectationForDisplay(e,EXPECTATION);
1338      if (Utilities.noString(capExpectation)) {
1339        capExpectation = "supported";
1340      }
1341      combinedParams = new CombinedSearchParamSet(capExpectation);
1342      for (Extension cmpnt : e.getExtensionsByUrl("required")) {
1343        paramName = cmpnt.getValueStringType().asStringValue();
1344        param = sParams.getIndbyName().get(paramName);
1345        if (param == null) {
1346          param = new SingleParam(paramName,"","<unknown>");
1347        }
1348        //component = new CombinedSearchParam(param, true);
1349        combinedParams.addParam(true, param);
1350      }
1351      for (Extension cmpnt : e.getExtensionsByUrl("optional")) {
1352        paramName = cmpnt.getValueStringType().asStringValue();
1353        param = sParams.getIndbyName().get(paramName);
1354        if (param == null) {
1355          param = new SingleParam(paramName);
1356        }
1357        //component = new CombinedSearchParam(param);
1358        combinedParams.addParam(false, param);
1359      }
1360      sParams.addCombinedParamSet(capExpectation, combinedParams);
1361    }
1362    return sParams;
1363  }
1364
1365  private void addSearchParams(XhtmlNode body, ResourceSearchParams sParams) {
1366    Map<String, List<CombinedSearchParamSet>> comboMap = sParams.getCombined();
1367    if (isCombinedEmpty(comboMap) && sParams.getIndbyName().size()==0) return;
1368    XhtmlNode row;
1369    XhtmlNode cell;
1370    XhtmlNode table;
1371    XhtmlNode tbody;
1372    XhtmlNode tr;
1373    row = body.div().attribute("class", "row");
1374    cell = row.div().attribute("class", "col-lg-7");
1375    addLead(cell, context.formatPhrase(RenderingContext.CAPABILITY_SEARCH_PARS));
1376    table = cell.table("table table-condensed table-hover", false);
1377    tr = table.addTag("thead").tr();
1378    tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_CONFORMANCE));
1379    tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_PAR));
1380    tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_TYPE));
1381    tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_DOCUMENTATION));
1382    tbody = table.addTag("tbody");
1383    Map<String,List<SingleParam>> map = sParams.getIndbyExp();
1384    addIndRows(tbody, map, "supported");
1385    addIndRows(tbody, map, "SHALL");
1386    addIndRows(tbody, map, "SHOULD");
1387    addIndRows(tbody, map, "MAY");
1388    addIndRows(tbody, map, "SHOULD-NOT");
1389    cell = row.div().attribute("class", "col-lg-5");
1390    if (!isCombinedEmpty(comboMap)) {
1391      addLead(cell, context.formatPhrase(RenderingContext.CAPABILITY_COMB_SEARCH_PAR));
1392      table = cell.table("table table-condensed table-hover", false);
1393      tr = table.addTag("thead").tr();
1394      tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_CONFORMANCE));
1395      tr.th().addText(context.formatPhrase(RenderingContext.GENERAL_PARS));
1396      tr.th().addText(context.formatPhrase(RenderingContext.CAPABILITY_TYPS));
1397      tbody = table.addTag("tbody");
1398      addComboRows(tbody, comboMap, "supported");
1399      addComboRows(tbody, comboMap, "SHALL");
1400      addComboRows(tbody, comboMap, "SHOULD");
1401      addComboRows(tbody, comboMap, "MAY");
1402      addComboRows(tbody, comboMap, "SHOULD-NOT");
1403    }
1404    else {
1405      cell.nbsp();
1406    }
1407  }
1408
1409  private void addIndRows(XhtmlNode tbody, Map<String, List<SingleParam>> map, String exp) {
1410    XhtmlNode tr;
1411    for (SingleParam param: map.get(exp)) {
1412      tr=tbody.tr();
1413      if (!exp.equals("supported")) {
1414        tr.td().b().addText(exp);
1415      }
1416      else {
1417        tr.td().b().addText("SHALL");
1418      }
1419      addResourceLink(tr.td(), param.getName(), param.getDefinition(),true, param.getHostResource());
1420      /*if (Utilities.noString(param.getDefinition())) {
1421        tr.td().addText(param.getName());
1422      }
1423      else {
1424        tr.td().ah(param.getDefinition()).addText(param.getName());
1425      }*/
1426      tr.td().code().addText(param.getType());
1427      try {
1428        addMarkdown(tr.td(), param.getDocumentation());
1429      } catch (FHIRFormatError e) {
1430        // TODO Auto-generated catch block
1431        e.printStackTrace();
1432      } catch (DefinitionException e) {
1433        // TODO Auto-generated catch block
1434        e.printStackTrace();
1435      } catch (IOException e) {
1436        // TODO Auto-generated catch block
1437        e.printStackTrace();
1438      }
1439    }
1440  }
1441
1442  private void addOps(XhtmlNode tbody, Map<String, List<SingleOperation>> map, String exp) {
1443    XhtmlNode tr;
1444    String name;
1445    String canonicalUri;
1446    for (SingleOperation operation: map.get(exp)) {
1447      tr = tbody.tr();
1448      name = "$" + operation.getName();
1449      if (!exp.equals("supported")) {
1450        tr.td().b().addText(exp);
1451      }
1452      else {
1453        tr.td().b().addText("SHALL");
1454      }
1455      canonicalUri = operation.getDefinition();
1456      addResourceLink(tr.td(), name, canonicalUri);
1457      /*
1458      if (Utilities.noString(operation.getDefinition())) {
1459        tr.td().addText(name);
1460      }
1461      else {
1462        tr.td().ah(operation.getDefinition()).addText(name);
1463        Resource cr = context.getContext().fetchResource(Resource.class, operation.getDefinition());
1464      } */
1465      try {
1466        addMarkdown(tr.td(), operation.getDocumentation());
1467      } catch (FHIRFormatError e) {
1468        // TODO Auto-generated catch block
1469        e.printStackTrace();
1470      } catch (DefinitionException e) {
1471        // TODO Auto-generated catch block
1472        e.printStackTrace();
1473      } catch (IOException e) {
1474        // TODO Auto-generated catch block
1475        e.printStackTrace();
1476      }
1477    }
1478  }
1479
1480  private void addComboRows(XhtmlNode tbody, Map<String, List<CombinedSearchParamSet>> map, String exp) {
1481    XhtmlNode tr,td;
1482    boolean first;
1483    for (CombinedSearchParamSet paramSet: map.get(exp)) {
1484      tr=tbody.tr();
1485      if (!exp.equals("supported")) {
1486        tr.td().b().addText(exp);
1487      }
1488      else {
1489        tr.td().nbsp();
1490      }
1491      //Parameter combination
1492      td = tr.td();
1493      first = true;
1494      for (SingleParam p : paramSet.getParams().get(true)) {
1495        if (!first) {
1496          td.addText("+");
1497        }
1498        first=false;
1499        if (Utilities.noString(p.getDefinition())) {
1500          td.addText(p.getName());
1501        }
1502        else {
1503          addResourceLink(td, p.getName(), p.getDefinition(), true, p.getHostResource());
1504          //td.ah(p.param.getDefinition()).addText(p.param.getName());
1505        }
1506      }
1507      if (paramSet.getParams().get(false).size() > 0) {
1508        td.addText("(");
1509        for (SingleParam p : paramSet.getParams().get(false)) {
1510          td.addText("+");
1511          if (Utilities.noString(p.getDefinition())) {
1512            td.addText(p.getName());
1513          }
1514          else {
1515            addResourceLink(td, p.getName(), p.getDefinition(), true, p.getHostResource());
1516            //td.ah(p.param.getDefinition()).addText(p.param.getName());
1517          }
1518        }
1519        td.addText(")");
1520      }
1521      //types combination
1522      td = tr.td();
1523      first = true;
1524      for (SingleParam p : paramSet.getParams().get(true)) {
1525        if (!first) {
1526          td.addText("+");
1527        }
1528        first=false;
1529        td.code().addText(p.getType());
1530      }
1531      if (paramSet.getParams().get(false).size() > 0) {
1532        td.addText("(");
1533        for (SingleParam p : paramSet.getParams().get(false)) {
1534          td.addText("+");
1535          if (!p.getType().equals("<unknown>")) {
1536            td.code().addText(p.getType());
1537          }
1538          else {
1539            td.addText(p.getType());
1540          }
1541        }
1542        td.addText(")");
1543      }
1544    }
1545  }
1546
1547  boolean isCombinedEmpty(Map<String, List<CombinedSearchParamSet>> combinedMap) {
1548    boolean result = true;
1549    for (String s : combinedMap.keySet()) {
1550      if (combinedMap.get(s).size() > 0) {
1551        result=false;
1552        break;
1553      }
1554    }
1555    return result;
1556  }
1557
1558  boolean hasOperations(Map<String, List<SingleOperation>> operationsMap) {
1559    boolean result = false;
1560    for (String s : operationsMap.keySet()) {
1561      if (operationsMap.get(s).size() > 0) {
1562        result=true;
1563        break;
1564      }
1565    }
1566    return result;
1567  }
1568
1569  private List<String> getReferencePolicyStrings(List<Enumeration<ReferenceHandlingPolicy>> list) {
1570    List<String> stringList = new ArrayList<String>();
1571    for (Enumeration<ReferenceHandlingPolicy> p : list) {
1572      stringList.add(p.getCode());
1573    }
1574    return stringList;
1575  }
1576  private String getResourceExpectation(CapabilityStatementRestResourceComponent r) {
1577    String capExpectation = expectationForDisplay(r,EXPECTATION);
1578    if (!Utilities.noString(capExpectation)) return capExpectation;
1579    boolean shalls = false;
1580    boolean shoulds = false;
1581    boolean mays = false;
1582    boolean shouldnots = false;
1583    for (ResourceInteractionComponent ric : r.getInteraction()) {
1584      capExpectation = expectationForDisplay(ric,EXPECTATION);
1585      if (!Utilities.noString(capExpectation)) {
1586        switch(capExpectation) {
1587          case "SHALL" :  shalls = true;
1588                          break;
1589          case "SHOULD" : shoulds = true;
1590                          break;
1591          case "MAY" :    mays = true;
1592                          break;
1593          case "SHOULD-NOT" : shouldnots = true;
1594                              break;
1595        }
1596      }
1597    }
1598    if (shalls) return "SHALL";
1599    //Check search parameters requirements
1600    for ( CapabilityStatementRestResourceSearchParamComponent sp : r.getSearchParam()) {
1601      capExpectation = expectationForDisplay(sp,EXPECTATION);
1602      if (!Utilities.noString(capExpectation)) {
1603        switch(capExpectation) {
1604          case "SHALL" :  shalls = true;
1605                          break;
1606          case "SHOULD" : shoulds = true;
1607                          break;
1608          case "MAY" :    mays = true;
1609                          break;
1610          case "SHOULD-NOT" : shouldnots = true;
1611                              break;
1612        }
1613      }
1614    }
1615    if (shalls) return "SHALL";
1616    if (shoulds) return "SHOULD";
1617    if (mays) return "MAY";
1618    if (shouldnots) return "SHOULD NOT";
1619    return "supported";
1620  }
1621
1622  private String getProfileExpectation(CanonicalType r) {
1623    String capExpectation = expectationForDisplay(r,EXPECTATION);
1624    if (!Utilities.noString(capExpectation)) return capExpectation;
1625    return "SHALL";
1626  }
1627
1628  private void addLead(XhtmlNode node, String text) {
1629    node.addTag("span").attribute("class", "lead").addText(text);
1630  }
1631
1632  private void addResourceLink(XhtmlNode node, String name, String canonicalUri) {
1633    addResourceLink(node, name, canonicalUri, false, "");
1634  }
1635
1636  private void addResourceLink(XhtmlNode node, String name, String canonicalUri, boolean isParam, String hostResource) {
1637    if (Utilities.noString(canonicalUri)) {
1638      node.addText(name);
1639      return;
1640    }
1641
1642    Resource cr = context.getContext().fetchResource(Resource.class, canonicalUri);
1643    if (cr == null) {
1644      node.addText(name);
1645    }
1646    else {
1647      //String path = cr.getUserString("path");
1648      String path = cr.getWebPath();
1649      if (Utilities.noString(path)) {
1650        if (isParam && (canonicalUri.toLowerCase().startsWith(SP_BASE)) && (!Utilities.noString(currentFhirBase)) && (!Utilities.noString(hostResource))) {
1651          String resourceName = "";
1652          if (canonicalUri.substring(SP_BASE.length()).split("-")[0].toLowerCase().equals("resource")) {
1653            resourceName = "resource";
1654          }
1655          else if (canonicalUri.substring(SP_BASE.length()).split("-")[0].toLowerCase().equals("domainresource")) {
1656            resourceName = "domainresource";
1657          }
1658          else {
1659            resourceName = hostResource;
1660          }
1661
1662          node.ah(currentFhirBase + resourceName + ".html#search").addText(name);
1663        }
1664        else {
1665          node.addText(name);
1666        }
1667      }
1668      else {
1669        node.ah(path).addText(name);
1670      }
1671    }
1672  }
1673
1674  private String expectationForDisplay(Element e, String url) {
1675    String result;
1676    try {
1677      result = e.getExtensionString(url);
1678      return result;
1679    }
1680    catch (FHIRException fex) {
1681      List<Extension> ext = e.getExtensionsByUrl(url); 
1682      if (ext.isEmpty()) 
1683        return null; 
1684      if (!ext.get(0).hasValue())
1685        return null;
1686      multExpectationsPresent = true;
1687      return ext.get(0).getValue().primitiveValue() + "-??";
1688    }
1689
1690  }
1691
1692  private void addWarningPanel(XhtmlNode node, String text) {
1693    XhtmlNode panel = node.addTag("div").attribute("class","panel panel-danger").addTag("div").attribute("class","panel-body");
1694    panel.addTag("span").attribute("class","label label-danger").addText(context.formatPhrase(RenderingContext.CAPABILITY_ERR_DET));
1695    panel.addText(" " + text);
1696  }
1697}