001package org.hl7.fhir.r4.utils;
002
003import java.util.ArrayList;
004import java.util.List;
005
006import org.hl7.fhir.r4.context.IWorkerContext;
007import org.hl7.fhir.r4.model.Base;
008import org.hl7.fhir.r4.model.Bundle;
009import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
010import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent;
011import org.hl7.fhir.r4.model.CodeableConcept;
012import org.hl7.fhir.r4.model.Coding;
013import org.hl7.fhir.r4.model.ContactDetail;
014import org.hl7.fhir.r4.model.ContactPoint;
015import org.hl7.fhir.r4.model.ContactPoint.ContactPointSystem;
016import org.hl7.fhir.r4.model.ElementDefinition;
017import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent;
018import org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent;
019import org.hl7.fhir.r4.model.Meta;
020import org.hl7.fhir.r4.model.OperationOutcome;
021import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity;
022import org.hl7.fhir.r4.model.OperationOutcome.OperationOutcomeIssueComponent;
023import org.hl7.fhir.r4.model.Reference;
024import org.hl7.fhir.r4.model.Resource;
025import org.hl7.fhir.r4.model.ResourceType;
026import org.hl7.fhir.r4.model.Type;
027import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
028import org.hl7.fhir.utilities.Utilities;
029import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
030
031/**
032 * Decoration utilities for various resource types
033 * @author Grahame
034 *
035 */
036public class ResourceUtilities {
037
038  public final static String FHIR_LANGUAGE = "urn:ietf:bcp:47";
039
040        public static boolean isAnError(OperationOutcome error) {
041                for (OperationOutcomeIssueComponent t : error.getIssue())
042                        if (t.getSeverity() == IssueSeverity.ERROR)
043                                return true;
044                        else if (t.getSeverity() == IssueSeverity.FATAL)
045                                return true;
046                return false;
047        }
048        
049        public static String getErrorDescription(OperationOutcome error) {  
050                if (error.hasText() && error.getText().hasDiv())
051                        return new XhtmlComposer(XhtmlComposer.XML).composePlainText(error.getText().getDiv());
052                
053                StringBuilder b = new StringBuilder();
054                for (OperationOutcomeIssueComponent t : error.getIssue())
055                        if (t.getSeverity() == IssueSeverity.ERROR)
056                                b.append("Error:" +t.getDetails()+"\r\n");
057                        else if (t.getSeverity() == IssueSeverity.FATAL)
058                                b.append("Fatal:" +t.getDetails()+"\r\n");
059                        else if (t.getSeverity() == IssueSeverity.WARNING)
060                                b.append("Warning:" +t.getDetails()+"\r\n");
061                        else if (t.getSeverity() == IssueSeverity.INFORMATION)
062                                b.append("Information:" +t.getDetails()+"\r\n");
063                return b.toString();
064  }
065
066  public static Resource getById(Bundle feed, ResourceType type, String reference) {
067    for (BundleEntryComponent item : feed.getEntry()) {
068      if (item.getResource().getId().equals(reference) && item.getResource().getResourceType() == type)
069        return item.getResource();
070    }
071    return null;
072  }
073
074  public static BundleEntryComponent getEntryById(Bundle feed, ResourceType type, String reference) {
075    for (BundleEntryComponent item : feed.getEntry()) {
076      if (item.getResource().getId().equals(reference) && item.getResource().getResourceType() == type)
077        return item;
078    }
079    return null;
080  }
081
082        public static String getLink(Bundle feed, String rel) {
083                for (BundleLinkComponent link : feed.getLink()) {
084                        if (link.getRelation().equals(rel))
085                                return link.getUrl();
086                }
087          return null;
088  }
089
090  public static Meta meta(Resource resource) {
091    if (!resource.hasMeta())
092      resource.setMeta(new Meta());
093    return resource.getMeta();
094  }
095
096//  public static String representDataElementCollection(IWorkerContext context, Bundle bundle, boolean profileLink, String linkBase) {
097//    StringBuilder b = new StringBuilder();
098//    DataElement common = showDECHeader(b, bundle);
099//    b.append("<table class=\"grid\">\r\n"); 
100//    List<String> cols = chooseColumns(bundle, common, b, profileLink);
101//    for (BundleEntryComponent e : bundle.getEntry()) {
102//      DataElement de = (DataElement) e.getResource();
103//      renderDE(de, cols, b, profileLink, linkBase);
104//    }
105//    b.append("</table>\r\n");
106//    return b.toString();
107//  }
108//
109//  
110//  private static void renderDE(DataElement de, List<String> cols, StringBuilder b, boolean profileLink, String linkBase) {
111//    b.append("<tr>");
112//    for (String col : cols) {
113//      String v;
114//      ElementDefinition dee = de.getElement().get(0);
115//      if (col.equals("DataElement.name")) {
116//        v = de.hasName() ? Utilities.escapeXml(de.getName()) : "";
117//      } else if (col.equals("DataElement.status")) {
118//        v = de.hasStatusElement() ? de.getStatusElement().asStringValue() : "";
119//      } else if (col.equals("DataElement.code")) {
120//        v = renderCoding(dee.getCode());
121//      } else if (col.equals("DataElement.type")) {
122//        v = dee.hasType() ? Utilities.escapeXml(dee.getType().get(0).getCode()) : "";
123//      } else if (col.equals("DataElement.units")) {
124//        v = renderDEUnits(ToolingExtensions.getAllowedUnits(dee));
125//      } else if (col.equals("DataElement.binding")) {
126//        v = renderBinding(dee.getBinding());
127//      } else if (col.equals("DataElement.minValue")) {
128//        v = ToolingExtensions.hasExtension(de, "http://hl7.org/fhir/StructureDefinition/minValue") ? Utilities.escapeXml(ToolingExtensions.readPrimitiveExtension(de, "http://hl7.org/fhir/StructureDefinition/minValue").asStringValue()) : "";
129//      } else if (col.equals("DataElement.maxValue")) {
130//        v = ToolingExtensions.hasExtension(de, "http://hl7.org/fhir/StructureDefinition/maxValue") ? Utilities.escapeXml(ToolingExtensions.readPrimitiveExtension(de, "http://hl7.org/fhir/StructureDefinition/maxValue").asStringValue()) : "";
131//      } else if (col.equals("DataElement.maxLength")) {
132//        v = ToolingExtensions.hasExtension(de, "http://hl7.org/fhir/StructureDefinition/maxLength") ? Utilities.escapeXml(ToolingExtensions.readPrimitiveExtension(de, "http://hl7.org/fhir/StructureDefinition/maxLength").asStringValue()) : "";
133//      } else if (col.equals("DataElement.mask")) {
134//        v = ToolingExtensions.hasExtension(de, "http://hl7.org/fhir/StructureDefinition/mask") ? Utilities.escapeXml(ToolingExtensions.readPrimitiveExtension(de, "http://hl7.org/fhir/StructureDefinition/mask").asStringValue()) : "";
135//      } else 
136//        throw new Error("Unknown column name: "+col);
137//
138//      b.append("<td>"+v+"</td>");
139//    }
140//    if (profileLink) {
141//      b.append("<td><a href=\""+linkBase+"-"+de.getId()+".html\">Profile</a>, <a href=\"http://www.opencem.org/#/20140917/Intermountain/"+de.getId()+"\">CEM</a>");
142//      if (ToolingExtensions.hasExtension(de, ToolingExtensions.EXT_CIMI_REFERENCE)) 
143//        b.append(", <a href=\""+ToolingExtensions.readStringExtension(de, ToolingExtensions.EXT_CIMI_REFERENCE)+"\">CIMI</a>");
144//      b.append("</td>");
145//    }
146//    b.append("</tr>\r\n");
147//  }
148
149  
150
151  private static String renderBinding(ElementDefinitionBindingComponent binding) {
152    // TODO Auto-generated method stub
153    return null;
154  }
155
156  private static String renderDEUnits(Type units) {
157    if (units == null || units.isEmpty())
158      return "";
159    if (units instanceof CodeableConcept)
160      return renderCodeable((CodeableConcept) units);
161    else
162      return "<a href=\""+Utilities.escapeXml(((Reference) units).getReference())+"\">"+Utilities.escapeXml(((Reference) units).getReference())+"</a>";
163      
164  }
165
166  private static String renderCodeable(CodeableConcept units) {
167    if (units == null || units.isEmpty())
168      return "";
169    String v = renderCoding(units.getCoding());
170    if (units.hasText())
171      v = v + " " +Utilities.escapeXml(units.getText());
172    return v;
173  }
174
175  private static String renderCoding(List<Coding> codes) {
176    CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
177    for (Coding c : codes)
178      b.append(renderCoding(c));
179    return b.toString();
180  }
181
182  private static String renderCoding(Coding code) {
183    if (code == null || code.isEmpty())
184      return "";
185    else
186      return "<span title=\""+Utilities.escapeXml(code.getSystem())+"\">"+Utilities.escapeXml(code.getCode())+"</span>";
187  }
188
189//  private static List<String> chooseColumns(Bundle bundle, DataElement common, StringBuilder b, boolean profileLink) {
190//    b.append("<tr>");
191//    List<String> results = new ArrayList<String>();
192//    results.add("DataElement.name");
193//    b.append("<td width=\"250\"><b>Name</b></td>");
194//    if (!common.hasStatus()) {
195//      results.add("DataElement.status");
196//      b.append("<td><b>Status</b></td>");
197//    }
198//    if (hasCode(bundle)) {
199//      results.add("DataElement.code");
200//      b.append("<td><b>Code</b></td>");
201//    }
202//    if (!common.getElement().get(0).hasType() && hasType(bundle)) {
203//      results.add("DataElement.type");
204//      b.append("<td><b>Type</b></td>");
205//    }
206//    if (hasUnits(bundle)) {
207//      results.add("DataElement.units");
208//      b.append("<td><b>Units</b></td>");
209//    }
210//    if (hasBinding(bundle)) {
211//      results.add("DataElement.binding");
212//      b.append("<td><b>Binding</b></td>");
213//    }
214//    if (hasExtension(bundle, "http://hl7.org/fhir/StructureDefinition/minValue")) {
215//      results.add("DataElement.minValue");
216//      b.append("<td><b>Min Value</b></td>");
217//    }
218//    if (hasExtension(bundle, "http://hl7.org/fhir/StructureDefinition/maxValue")) {
219//      results.add("DataElement.maxValue");
220//      b.append("<td><b>Max Value</b></td>");
221//    }
222//    if (hasExtension(bundle, "http://hl7.org/fhir/StructureDefinition/maxLength")) {
223//      results.add("DataElement.maxLength");
224//      b.append("<td><b>Max Length</b></td>");
225//    }
226//    if (hasExtension(bundle, "http://hl7.org/fhir/StructureDefinition/mask")) {
227//      results.add("DataElement.mask");
228//      b.append("<td><b>Mask</b></td>");
229//    }
230//    if (profileLink)
231//      b.append("<td><b>Links</b></td>");
232//    b.append("</tr>\r\n");
233//    return results;
234//  }
235//
236//  private static boolean hasExtension(Bundle bundle, String url) {
237//    for (BundleEntryComponent e : bundle.getEntry()) {
238//      DataElement de = (DataElement) e.getResource();
239//      if (ToolingExtensions.hasExtension(de, url))
240//        return true;
241//    }
242//    return false;
243//  }
244//
245//  private static boolean hasBinding(Bundle bundle) {
246//    for (BundleEntryComponent e : bundle.getEntry()) {
247//      DataElement de = (DataElement) e.getResource();
248//      if (de.getElement().get(0).hasBinding())
249//        return true;
250//    }
251//    return false;
252//  }
253//
254//  private static boolean hasCode(Bundle bundle) {
255//    for (BundleEntryComponent e : bundle.getEntry()) {
256//      DataElement de = (DataElement) e.getResource();
257//      if (de.getElement().get(0).hasCode())
258//        return true;
259//    }
260//    return false;
261//  }
262//
263//  private static boolean hasType(Bundle bundle) {
264//    for (BundleEntryComponent e : bundle.getEntry()) {
265//      DataElement de = (DataElement) e.getResource();
266//      if (de.getElement().get(0).hasType())
267//        return true;
268//    }
269//    return false;
270//  }
271//
272//  private static boolean hasUnits(Bundle bundle) {
273//    for (BundleEntryComponent e : bundle.getEntry()) {
274//      DataElement de = (DataElement) e.getResource();
275//      if (ToolingExtensions.getAllowedUnits(de.getElement().get(0)) != null)
276//        return true;
277//    }
278//    return false;
279//  }
280//
281//  private static DataElement showDECHeader(StringBuilder b, Bundle bundle) {
282//    DataElement meta = new DataElement();
283//    DataElement prototype = (DataElement) bundle.getEntry().get(0).getResource();
284//    meta.setPublisher(prototype.getPublisher());
285//    meta.getContact().addAll(prototype.getContact());
286//    meta.setStatus(prototype.getStatus());
287//    meta.setDate(prototype.getDate());
288//    meta.addElement().getType().addAll(prototype.getElement().get(0).getType());
289//
290//    for (BundleEntryComponent e : bundle.getEntry()) {
291//      DataElement de = (DataElement) e.getResource();
292//      if (!Base.compareDeep(de.getPublisherElement(), meta.getPublisherElement(), false))
293//        meta.setPublisherElement(null);
294//      if (!Base.compareDeep(de.getContact(), meta.getContact(), false))
295//        meta.getContact().clear();
296//      if (!Base.compareDeep(de.getStatusElement(), meta.getStatusElement(), false))
297//        meta.setStatusElement(null);
298//      if (!Base.compareDeep(de.getDateElement(), meta.getDateElement(), false))
299//        meta.setDateElement(null);
300//      if (!Base.compareDeep(de.getElement().get(0).getType(), meta.getElement().get(0).getType(), false))
301//        meta.getElement().get(0).getType().clear();
302//    }
303//    if (meta.hasPublisher() || meta.hasContact() || meta.hasStatus() || meta.hasDate() /* || meta.hasType() */) {
304//      b.append("<table class=\"grid\">\r\n"); 
305//      if (meta.hasPublisher())
306//        b.append("<tr><td>Publisher:</td><td>"+meta.getPublisher()+"</td></tr>\r\n");
307//      if (meta.hasContact()) {
308//        b.append("<tr><td>Contacts:</td><td>");
309//        boolean firsti = true;
310//        for (ContactDetail c : meta.getContact()) {
311//          if (firsti)
312//            firsti = false;
313//          else
314//            b.append("<br/>");
315//          if (c.hasName())
316//            b.append(Utilities.escapeXml(c.getName())+": ");
317//          boolean first = true;
318//          for (ContactPoint cp : c.getTelecom()) {
319//            if (first)
320//              first = false;
321//            else
322//              b.append(", ");
323//            renderContactPoint(b, cp);
324//          }
325//        }
326//        b.append("</td></tr>\r\n");
327//      }
328//      if (meta.hasStatus())
329//        b.append("<tr><td>Status:</td><td>"+meta.getStatus().toString()+"</td></tr>\r\n");
330//      if (meta.hasDate())
331//        b.append("<tr><td>Date:</td><td>"+meta.getDateElement().asStringValue()+"</td></tr>\r\n");
332//      if (meta.getElement().get(0).hasType())
333//        b.append("<tr><td>Type:</td><td>"+renderType(meta.getElement().get(0).getType())+"</td></tr>\r\n");
334//      b.append("</table>\r\n"); 
335//    }  
336//    return meta;
337//  }
338
339  private static String renderType(List<TypeRefComponent> type) {
340    if (type == null || type.isEmpty())
341      return "";
342    CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
343    for (TypeRefComponent c : type)
344      b.append(renderType(c));
345    return b.toString();
346  }
347
348  private static String renderType(TypeRefComponent type) {
349    if (type == null || type.isEmpty())
350      return "";
351    return type.getCode();
352  }
353
354  public static void renderContactPoint(StringBuilder b, ContactPoint cp) {
355    if (cp != null && !cp.isEmpty()) {
356      if (cp.getSystem() == ContactPointSystem.EMAIL)
357        b.append("<a href=\"mailto:"+cp.getValue()+"\">"+cp.getValue()+"</a>");
358      else if (cp.getSystem() == ContactPointSystem.FAX) 
359        b.append("Fax: "+cp.getValue());
360      else if (cp.getSystem() == ContactPointSystem.OTHER) 
361        b.append("<a href=\""+cp.getValue()+"\">"+cp.getValue()+"</a>");
362      else
363        b.append(cp.getValue());
364    }
365  }
366}