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