001package org.hl7.fhir.r5.renderers;
002
003import java.io.IOException;
004import java.io.UnsupportedEncodingException;
005
006import org.hl7.fhir.exceptions.DefinitionException;
007import org.hl7.fhir.exceptions.FHIRFormatError;
008import org.hl7.fhir.r5.model.CanonicalType;
009import org.hl7.fhir.r5.model.CapabilityStatement;
010import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestComponent;
011import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
012import org.hl7.fhir.r5.model.CapabilityStatement.ResourceInteractionComponent;
013import org.hl7.fhir.r5.model.CapabilityStatement.SystemInteractionComponent;
014import org.hl7.fhir.r5.model.CapabilityStatement.SystemRestfulInteraction;
015import org.hl7.fhir.r5.model.CapabilityStatement.TypeRestfulInteraction;
016import org.hl7.fhir.r5.model.Extension;
017import org.hl7.fhir.r5.model.Resource;
018import org.hl7.fhir.r5.model.StructureDefinition;
019import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
020import org.hl7.fhir.r5.renderers.utils.RenderingContext;
021import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
022import org.hl7.fhir.r5.utils.ToolingExtensions;
023import org.hl7.fhir.utilities.xhtml.XhtmlNode;
024
025public class CapabilityStatementRenderer extends ResourceRenderer {
026
027  public CapabilityStatementRenderer(RenderingContext context) {
028    super(context);
029  }
030
031  public CapabilityStatementRenderer(RenderingContext context, ResourceContext rcontext) {
032    super(context, rcontext);
033  }
034  
035  public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
036    return render(x, (CapabilityStatement) dr);
037  }
038
039  public boolean render(XhtmlNode x, CapabilityStatement conf) throws FHIRFormatError, DefinitionException, IOException {
040    x.h2().addText(conf.getName());
041    addMarkdown(x, conf.getDescription());
042    if (conf.getRest().size() > 0) {
043      CapabilityStatementRestComponent rest = conf.getRest().get(0);
044      XhtmlNode t = x.table(null);
045      if (rest.hasMode()) {
046        addTableRow(t, "Mode", rest.getMode().toString());
047      }
048      addMarkdown(addTableRow(t, "Description"), rest.getDocumentation());
049
050      addTableRow(t, "Transaction", showOp(rest, SystemRestfulInteraction.TRANSACTION));
051      addTableRow(t, "System History", showOp(rest, SystemRestfulInteraction.HISTORYSYSTEM));
052      addTableRow(t, "System Search", showOp(rest, SystemRestfulInteraction.SEARCHSYSTEM));
053
054      boolean hasVRead = false;
055      boolean hasPatch = false;
056      boolean hasDelete = false;
057      boolean hasHistory = false;
058      boolean hasUpdates = false;
059      for (CapabilityStatementRestResourceComponent r : rest.getResource()) {
060        hasVRead = hasVRead || hasOp(r, TypeRestfulInteraction.VREAD);
061        hasPatch = hasPatch || hasOp(r, TypeRestfulInteraction.PATCH);
062        hasDelete = hasDelete || hasOp(r, TypeRestfulInteraction.DELETE);
063        hasHistory = hasHistory || hasOp(r, TypeRestfulInteraction.HISTORYTYPE);
064        hasUpdates = hasUpdates || hasOp(r, TypeRestfulInteraction.HISTORYINSTANCE);
065      }
066
067      t = x.table(null);
068      XhtmlNode tr = t.tr();
069      tr.th().b().tx("Resource Type");
070      tr.th().b().tx("Profile");
071      tr.th().b().attribute("title", "GET a resource (read interaction)").tx("Read");
072      if (hasVRead)
073        tr.th().b().attribute("title", "GET past versions of resources (vread interaction)").tx("V-Read");
074      tr.th().b().attribute("title", "GET all set of resources of the type (search interaction)").tx("Search");
075      tr.th().b().attribute("title", "PUT a new resource version (update interaction)").tx("Update");
076      if (hasPatch)
077        tr.th().b().attribute("title", "PATCH a new resource version (patch interaction)").tx("Patch");
078      tr.th().b().attribute("title", "POST a new resource (create interaction)").tx("Create");
079      if (hasDelete)
080        tr.th().b().attribute("title", "DELETE a resource (delete interaction)").tx("Delete");
081      if (hasUpdates)
082        tr.th().b().attribute("title", "GET changes to a resource (history interaction on instance)").tx("Updates");
083      if (hasHistory)
084        tr.th().b().attribute("title", "GET changes for all resources of the type (history interaction on type)").tx("History");
085
086      XhtmlNode profCell = null;
087      boolean hasProf = false;
088      boolean hasSupProf = false;
089      for (CapabilityStatementRestResourceComponent r : rest.getResource()) {
090        tr = t.tr();
091        tr.td().addText(r.getType());
092
093        //Show profiles
094        profCell = tr.td();
095        hasProf = r.hasProfile();
096        hasSupProf = r.hasSupportedProfile();
097        if ((!hasProf) && (!hasSupProf)) {
098          profCell.nbsp();
099        }
100        else if (hasProf) {
101          profCell.ah(r.getProfile()).addText(r.getProfile());
102          if (hasSupProf) {
103            profCell.br();
104            profCell.addText("Additional supported profiles:");
105            renderSupportedProfiles(profCell, r);
106          }
107        }
108        else {    //Case of only supported profiles
109          profCell.addText("Supported profiles:");
110          renderSupportedProfiles(profCell, r);
111        }
112        //Show capabilities
113        tr.td().addText(showOp(r, TypeRestfulInteraction.READ));
114        if (hasVRead)
115          tr.td().addText(showOp(r, TypeRestfulInteraction.VREAD));
116        tr.td().addText(showOp(r, TypeRestfulInteraction.SEARCHTYPE));
117        tr.td().addText(showOp(r, TypeRestfulInteraction.UPDATE));
118        if (hasPatch)
119          tr.td().addText(showOp(r, TypeRestfulInteraction.PATCH));
120        tr.td().addText(showOp(r, TypeRestfulInteraction.CREATE));
121        if (hasDelete)
122          tr.td().addText(showOp(r, TypeRestfulInteraction.DELETE));
123        if (hasUpdates)
124          tr.td().addText(showOp(r, TypeRestfulInteraction.HISTORYINSTANCE));
125        if (hasHistory)
126          tr.td().addText(showOp(r, TypeRestfulInteraction.HISTORYTYPE));
127      }
128    }
129
130    return true;
131  }
132
133  private void renderSupportedProfiles(XhtmlNode profCell, CapabilityStatementRestResourceComponent r) throws IOException {
134    for (CanonicalType sp: r.getSupportedProfile()) { 
135      profCell.br();
136      profCell.nbsp().nbsp();
137      StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, sp.getValue());
138      if (sd != null) {
139        profCell.ah(sd.getWebPath()).addText(sd.present());
140      } else {
141        profCell.ah(sp.getValue()).addText(sp.getValue());        
142      }
143    }
144    if (r.hasExtension(ToolingExtensions.EXT_PROFILE_MAPPING)) {
145      profCell.br();
146      profCell.b().tx("Profile Mapping");
147      XhtmlNode tbl = profCell.table("grid");
148      boolean doco = false;
149      for (Extension ext : r.getExtensionsByUrl(ToolingExtensions.EXT_PROFILE_MAPPING)) {
150        doco = doco || ext.hasExtension("documentation");
151      }
152      XhtmlNode tr = tbl.tr();
153      tr.th().tx("Criteria");
154      tr.th().tx("Profile");
155      if (doco) {
156        tr.th().tx("Criteria");
157      }
158      for (Extension ext : r.getExtensionsByUrl(ToolingExtensions.EXT_PROFILE_MAPPING)) {
159        tr = tbl.tr();
160        tr.td().code().tx(ToolingExtensions.readStringExtension(ext, "search"));
161        String url = ToolingExtensions.readStringExtension(ext, "profile");
162        StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, url);
163        if (sd != null) {
164          tr.td().code().ah(sd.getWebPath()).tx(sd.present());
165        } else {
166          tr.td().code().tx(url);
167        }
168        if (doco) {
169          tr.td().code().markdown(ToolingExtensions.readStringExtension(ext, "documentation"), "documentation"); 
170        }
171      }      
172    }
173  }
174
175  public void describe(XhtmlNode x, CapabilityStatement cs) {
176    x.tx(display(cs));
177  }
178
179  public String display(CapabilityStatement cs) {
180    return cs.present();
181  }
182
183  @Override
184  public String display(Resource r) throws UnsupportedEncodingException, IOException {
185    return ((CapabilityStatement) r).present();
186  }
187
188
189  private boolean hasOp(CapabilityStatementRestResourceComponent r, TypeRestfulInteraction on) {
190    for (ResourceInteractionComponent op : r.getInteraction()) {
191      if (op.getCode() == on)
192        return true;
193    }
194    return false;
195  }
196
197  private String showOp(CapabilityStatementRestResourceComponent r, TypeRestfulInteraction on) {
198    for (ResourceInteractionComponent op : r.getInteraction()) {
199      if (op.getCode() == on)
200        return "y";
201    }
202    return "";
203  }
204
205  private String showOp(CapabilityStatementRestComponent r, SystemRestfulInteraction on) {
206    for (SystemInteractionComponent op : r.getInteraction()) {
207      if (op.getCode() == on)
208        return "y";
209    }
210    return "";
211  }
212
213  private XhtmlNode addTableRow(XhtmlNode t, String name) {
214    XhtmlNode tr = t.tr();
215    tr.td().addText(name);
216    return tr.td();
217  }
218  
219  private void addTableRow(XhtmlNode t, String name, String value) {
220    XhtmlNode tr = t.tr();
221    tr.td().addText(name);
222    tr.td().addText(value);
223  }
224
225  public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException {
226    if (r.has("title")) {
227      return r.children("title").get(0).getBase().primitiveValue();
228    }
229    if (r.has("name")) {
230      return r.children("name").get(0).getBase().primitiveValue();
231    }
232    return "??";
233  }
234
235}