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