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