001package org.hl7.fhir.dstu2.utils;
002
003/*
004  Copyright (c) 2011+, HL7, Inc.
005  All rights reserved.
006  
007  Redistribution and use in source and binary forms, with or without modification, 
008  are permitted provided that the following conditions are met:
009    
010   * Redistributions of source code must retain the above copyright notice, this 
011     list of conditions and the following disclaimer.
012   * Redistributions in binary form must reproduce the above copyright notice, 
013     this list of conditions and the following disclaimer in the documentation 
014     and/or other materials provided with the distribution.
015   * Neither the name of HL7 nor the names of its contributors may be used to 
016     endorse or promote products derived from this software without specific 
017     prior written permission.
018  
019  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
020  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
021  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
022  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
023  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
024  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
025  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
026  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
027  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
028  POSSIBILITY OF SUCH DAMAGE.
029  
030 */
031
032import java.util.ArrayList;
033import java.util.List;
034
035import org.hl7.fhir.dstu2.model.Base;
036import org.hl7.fhir.dstu2.model.Bundle;
037import org.hl7.fhir.dstu2.model.Bundle.BundleEntryComponent;
038import org.hl7.fhir.dstu2.model.Bundle.BundleLinkComponent;
039import org.hl7.fhir.dstu2.model.CodeableConcept;
040import org.hl7.fhir.dstu2.model.Coding;
041import org.hl7.fhir.dstu2.model.ContactPoint;
042import org.hl7.fhir.dstu2.model.ContactPoint.ContactPointSystem;
043import org.hl7.fhir.dstu2.model.DataElement;
044import org.hl7.fhir.dstu2.model.DataElement.DataElementContactComponent;
045import org.hl7.fhir.dstu2.model.ElementDefinition;
046import org.hl7.fhir.dstu2.model.ElementDefinition.ElementDefinitionBindingComponent;
047import org.hl7.fhir.dstu2.model.ElementDefinition.TypeRefComponent;
048import org.hl7.fhir.dstu2.model.Meta;
049import org.hl7.fhir.dstu2.model.OperationOutcome;
050import org.hl7.fhir.dstu2.model.OperationOutcome.IssueSeverity;
051import org.hl7.fhir.dstu2.model.OperationOutcome.OperationOutcomeIssueComponent;
052import org.hl7.fhir.dstu2.model.Reference;
053import org.hl7.fhir.dstu2.model.Resource;
054import org.hl7.fhir.dstu2.model.ResourceType;
055import org.hl7.fhir.dstu2.model.Type;
056import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
057import org.hl7.fhir.utilities.Utilities;
058import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
059
060/**
061 * Decoration utilities for various resource types
062 * 
063 * @author Grahame
064 *
065 */
066@Deprecated
067public class ResourceUtilities {
068
069  public final static String FHIR_LANGUAGE = "urn:ietf:bcp:47";
070
071  public static boolean isAnError(OperationOutcome error) {
072    for (OperationOutcomeIssueComponent t : error.getIssue())
073      if (t.getSeverity() == IssueSeverity.ERROR)
074        return true;
075      else if (t.getSeverity() == IssueSeverity.FATAL)
076        return true;
077    return false;
078  }
079
080  public static String getErrorDescription(OperationOutcome error) {
081    if (error.hasText() && error.getText().hasDiv())
082      return new XhtmlComposer(true, false).composePlainText(error.getText().getDiv());
083
084    StringBuilder b = new StringBuilder();
085    for (OperationOutcomeIssueComponent t : error.getIssue())
086      if (t.getSeverity() == IssueSeverity.ERROR)
087        b.append("Error:" + t.getDetails() + "\r\n");
088      else if (t.getSeverity() == IssueSeverity.FATAL)
089        b.append("Fatal:" + t.getDetails() + "\r\n");
090      else if (t.getSeverity() == IssueSeverity.WARNING)
091        b.append("Warning:" + t.getDetails() + "\r\n");
092      else if (t.getSeverity() == IssueSeverity.INFORMATION)
093        b.append("Information:" + t.getDetails() + "\r\n");
094    return b.toString();
095  }
096
097  public static Resource getById(Bundle feed, ResourceType type, String reference) {
098    for (BundleEntryComponent item : feed.getEntry()) {
099      if (item.getResource().getId().equals(reference) && item.getResource().getResourceType() == type)
100        return item.getResource();
101    }
102    return null;
103  }
104
105  public static BundleEntryComponent getEntryById(Bundle feed, ResourceType type, String reference) {
106    for (BundleEntryComponent item : feed.getEntry()) {
107      if (item.getResource().getId().equals(reference) && item.getResource().getResourceType() == type)
108        return item;
109    }
110    return null;
111  }
112
113  public static String getLink(Bundle feed, String rel) {
114    for (BundleLinkComponent link : feed.getLink()) {
115      if (link.getRelation().equals(rel))
116        return link.getUrl();
117    }
118    return null;
119  }
120
121  public static Meta meta(Resource resource) {
122    if (!resource.hasMeta())
123      resource.setMeta(new Meta());
124    return resource.getMeta();
125  }
126
127  public static String representDataElementCollection(IWorkerContext context, Bundle bundle, boolean profileLink,
128      String linkBase) {
129    StringBuilder b = new StringBuilder();
130    DataElement common = showDECHeader(b, bundle);
131    b.append("<table class=\"grid\">\r\n");
132    List<String> cols = chooseColumns(bundle, common, b, profileLink);
133    for (BundleEntryComponent e : bundle.getEntry()) {
134      DataElement de = (DataElement) e.getResource();
135      renderDE(de, cols, b, profileLink, linkBase);
136    }
137    b.append("</table>\r\n");
138    return b.toString();
139  }
140
141  private static void renderDE(DataElement de, List<String> cols, StringBuilder b, boolean profileLink,
142      String linkBase) {
143    b.append("<tr>");
144    for (String col : cols) {
145      String v;
146      ElementDefinition dee = de.getElement().get(0);
147      if (col.equals("DataElement.name")) {
148        v = de.hasName() ? Utilities.escapeXml(de.getName()) : "";
149      } else if (col.equals("DataElement.status")) {
150        v = de.hasStatusElement() ? de.getStatusElement().asStringValue() : "";
151      } else if (col.equals("DataElement.code")) {
152        v = renderCoding(dee.getCode());
153      } else if (col.equals("DataElement.type")) {
154        v = dee.hasType() ? Utilities.escapeXml(dee.getType().get(0).getCode()) : "";
155      } else if (col.equals("DataElement.units")) {
156        v = renderDEUnits(ToolingExtensions.getAllowedUnits(dee));
157      } else if (col.equals("DataElement.binding")) {
158        v = renderBinding(dee.getBinding());
159      } else if (col.equals("DataElement.minValue")) {
160        v = ToolingExtensions.hasExtension(de, "http://hl7.org/fhir/StructureDefinition/minValue")
161            ? Utilities.escapeXml(ToolingExtensions
162                .readPrimitiveExtension(de, "http://hl7.org/fhir/StructureDefinition/minValue").asStringValue())
163            : "";
164      } else if (col.equals("DataElement.maxValue")) {
165        v = ToolingExtensions.hasExtension(de, "http://hl7.org/fhir/StructureDefinition/maxValue")
166            ? Utilities.escapeXml(ToolingExtensions
167                .readPrimitiveExtension(de, "http://hl7.org/fhir/StructureDefinition/maxValue").asStringValue())
168            : "";
169      } else if (col.equals("DataElement.maxLength")) {
170        v = ToolingExtensions.hasExtension(de, "http://hl7.org/fhir/StructureDefinition/maxLength")
171            ? Utilities.escapeXml(ToolingExtensions
172                .readPrimitiveExtension(de, "http://hl7.org/fhir/StructureDefinition/maxLength").asStringValue())
173            : "";
174      } else if (col.equals("DataElement.mask")) {
175        v = ToolingExtensions.hasExtension(de, "http://hl7.org/fhir/StructureDefinition/mask")
176            ? Utilities.escapeXml(ToolingExtensions
177                .readPrimitiveExtension(de, "http://hl7.org/fhir/StructureDefinition/mask").asStringValue())
178            : "";
179      } else
180        throw new Error("Unknown column name: " + col);
181
182      b.append("<td>" + v + "</td>");
183    }
184    if (profileLink) {
185      b.append("<td><a href=\"" + linkBase + "-" + de.getId()
186          + ".html\">Profile</a>, <a href=\"http://www.opencem.org/#/20140917/Intermountain/" + de.getId()
187          + "\">CEM</a>");
188      if (ToolingExtensions.hasExtension(de, ToolingExtensions.EXT_CIMI_REFERENCE))
189        b.append(", <a href=\"" + ToolingExtensions.readStringExtension(de, ToolingExtensions.EXT_CIMI_REFERENCE)
190            + "\">CIMI</a>");
191      b.append("</td>");
192    }
193    b.append("</tr>\r\n");
194  }
195
196  private static String renderBinding(ElementDefinitionBindingComponent binding) {
197    // TODO Auto-generated method stub
198    return null;
199  }
200
201  private static String renderDEUnits(Type units) {
202    if (units == null || units.isEmpty())
203      return "";
204    if (units instanceof CodeableConcept)
205      return renderCodeable((CodeableConcept) units);
206    else
207      return "<a href=\"" + Utilities.escapeXml(((Reference) units).getReference()) + "\">"
208          + Utilities.escapeXml(((Reference) units).getReference()) + "</a>";
209
210  }
211
212  private static String renderCodeable(CodeableConcept units) {
213    if (units == null || units.isEmpty())
214      return "";
215    String v = renderCoding(units.getCoding());
216    if (units.hasText())
217      v = v + " " + Utilities.escapeXml(units.getText());
218    return v;
219  }
220
221  private static String renderCoding(List<Coding> codes) {
222    CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
223    for (Coding c : codes)
224      b.append(renderCoding(c));
225    return b.toString();
226  }
227
228  private static String renderCoding(Coding code) {
229    if (code == null || code.isEmpty())
230      return "";
231    else
232      return "<span title=\"" + Utilities.escapeXml(code.getSystem()) + "\">" + Utilities.escapeXml(code.getCode())
233          + "</span>";
234  }
235
236  private static List<String> chooseColumns(Bundle bundle, DataElement common, StringBuilder b, boolean profileLink) {
237    b.append("<tr>");
238    List<String> results = new ArrayList<String>();
239    results.add("DataElement.name");
240    b.append("<td width=\"250\"><b>Name</b></td>");
241    if (!common.hasStatus()) {
242      results.add("DataElement.status");
243      b.append("<td><b>Status</b></td>");
244    }
245    if (hasCode(bundle)) {
246      results.add("DataElement.code");
247      b.append("<td><b>Code</b></td>");
248    }
249    if (!common.getElement().get(0).hasType() && hasType(bundle)) {
250      results.add("DataElement.type");
251      b.append("<td><b>Type</b></td>");
252    }
253    if (hasUnits(bundle)) {
254      results.add("DataElement.units");
255      b.append("<td><b>Units</b></td>");
256    }
257    if (hasBinding(bundle)) {
258      results.add("DataElement.binding");
259      b.append("<td><b>Binding</b></td>");
260    }
261    if (hasExtension(bundle, "http://hl7.org/fhir/StructureDefinition/minValue")) {
262      results.add("DataElement.minValue");
263      b.append("<td><b>Min Value</b></td>");
264    }
265    if (hasExtension(bundle, "http://hl7.org/fhir/StructureDefinition/maxValue")) {
266      results.add("DataElement.maxValue");
267      b.append("<td><b>Max Value</b></td>");
268    }
269    if (hasExtension(bundle, "http://hl7.org/fhir/StructureDefinition/maxLength")) {
270      results.add("DataElement.maxLength");
271      b.append("<td><b>Max Length</b></td>");
272    }
273    if (hasExtension(bundle, "http://hl7.org/fhir/StructureDefinition/mask")) {
274      results.add("DataElement.mask");
275      b.append("<td><b>Mask</b></td>");
276    }
277    if (profileLink)
278      b.append("<td><b>Links</b></td>");
279    b.append("</tr>\r\n");
280    return results;
281  }
282
283  private static boolean hasExtension(Bundle bundle, String url) {
284    for (BundleEntryComponent e : bundle.getEntry()) {
285      DataElement de = (DataElement) e.getResource();
286      if (ToolingExtensions.hasExtension(de, url))
287        return true;
288    }
289    return false;
290  }
291
292  private static boolean hasBinding(Bundle bundle) {
293    for (BundleEntryComponent e : bundle.getEntry()) {
294      DataElement de = (DataElement) e.getResource();
295      if (de.getElement().get(0).hasBinding())
296        return true;
297    }
298    return false;
299  }
300
301  private static boolean hasCode(Bundle bundle) {
302    for (BundleEntryComponent e : bundle.getEntry()) {
303      DataElement de = (DataElement) e.getResource();
304      if (de.getElement().get(0).hasCode())
305        return true;
306    }
307    return false;
308  }
309
310  private static boolean hasType(Bundle bundle) {
311    for (BundleEntryComponent e : bundle.getEntry()) {
312      DataElement de = (DataElement) e.getResource();
313      if (de.getElement().get(0).hasType())
314        return true;
315    }
316    return false;
317  }
318
319  private static boolean hasUnits(Bundle bundle) {
320    for (BundleEntryComponent e : bundle.getEntry()) {
321      DataElement de = (DataElement) e.getResource();
322      if (ToolingExtensions.getAllowedUnits(de.getElement().get(0)) != null)
323        return true;
324    }
325    return false;
326  }
327
328  private static DataElement showDECHeader(StringBuilder b, Bundle bundle) {
329    DataElement meta = new DataElement();
330    DataElement prototype = (DataElement) bundle.getEntry().get(0).getResource();
331    meta.setPublisher(prototype.getPublisher());
332    meta.getContact().addAll(prototype.getContact());
333    meta.setStatus(prototype.getStatus());
334    meta.setDate(prototype.getDate());
335    meta.addElement().getType().addAll(prototype.getElement().get(0).getType());
336
337    for (BundleEntryComponent e : bundle.getEntry()) {
338      DataElement de = (DataElement) e.getResource();
339      if (!Base.compareDeep(de.getPublisherElement(), meta.getPublisherElement(), false))
340        meta.setPublisherElement(null);
341      if (!Base.compareDeep(de.getContact(), meta.getContact(), false))
342        meta.getContact().clear();
343      if (!Base.compareDeep(de.getStatusElement(), meta.getStatusElement(), false))
344        meta.setStatusElement(null);
345      if (!Base.compareDeep(de.getDateElement(), meta.getDateElement(), false))
346        meta.setDateElement(null);
347      if (!Base.compareDeep(de.getElement().get(0).getType(), meta.getElement().get(0).getType(), false))
348        meta.getElement().get(0).getType().clear();
349    }
350    if (meta.hasPublisher() || meta.hasContact() || meta.hasStatus() || meta.hasDate() /* || meta.hasType() */) {
351      b.append("<table class=\"grid\">\r\n");
352      if (meta.hasPublisher())
353        b.append("<tr><td>Publisher:</td><td>" + meta.getPublisher() + "</td></tr>\r\n");
354      if (meta.hasContact()) {
355        b.append("<tr><td>Contacts:</td><td>");
356        boolean firsti = true;
357        for (DataElementContactComponent c : meta.getContact()) {
358          if (firsti)
359            firsti = false;
360          else
361            b.append("<br/>");
362          if (c.hasName())
363            b.append(Utilities.escapeXml(c.getName()) + ": ");
364          boolean first = true;
365          for (ContactPoint cp : c.getTelecom()) {
366            if (first)
367              first = false;
368            else
369              b.append(", ");
370            renderContactPoint(b, cp);
371          }
372        }
373        b.append("</td></tr>\r\n");
374      }
375      if (meta.hasStatus())
376        b.append("<tr><td>Status:</td><td>" + meta.getStatus().toString() + "</td></tr>\r\n");
377      if (meta.hasDate())
378        b.append("<tr><td>Date:</td><td>" + meta.getDateElement().asStringValue() + "</td></tr>\r\n");
379      if (meta.getElement().get(0).hasType())
380        b.append("<tr><td>Type:</td><td>" + renderType(meta.getElement().get(0).getType()) + "</td></tr>\r\n");
381      b.append("</table>\r\n");
382    }
383    return meta;
384  }
385
386  private static String renderType(List<TypeRefComponent> type) {
387    if (type == null || type.isEmpty())
388      return "";
389    CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
390    for (TypeRefComponent c : type)
391      b.append(renderType(c));
392    return b.toString();
393  }
394
395  private static String renderType(TypeRefComponent type) {
396    if (type == null || type.isEmpty())
397      return "";
398    return type.getCode();
399  }
400
401  public static void renderContactPoint(StringBuilder b, ContactPoint cp) {
402    if (cp != null && !cp.isEmpty()) {
403      if (cp.getSystem() == ContactPointSystem.EMAIL)
404        b.append("<a href=\"mailto:" + cp.getValue() + "\">" + cp.getValue() + "</a>");
405      else if (cp.getSystem() == ContactPointSystem.FAX)
406        b.append("Fax: " + cp.getValue());
407      else if (cp.getSystem() == ContactPointSystem.OTHER)
408        b.append("<a href=\"" + cp.getValue() + "\">" + cp.getValue() + "</a>");
409      else
410        b.append(cp.getValue());
411    }
412  }
413}