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 032 033 034import java.util.ArrayList; 035import java.util.List; 036 037import org.hl7.fhir.dstu2.model.Base; 038import org.hl7.fhir.dstu2.model.Bundle; 039import org.hl7.fhir.dstu2.model.Bundle.BundleEntryComponent; 040import org.hl7.fhir.dstu2.model.Bundle.BundleLinkComponent; 041import org.hl7.fhir.dstu2.model.CodeableConcept; 042import org.hl7.fhir.dstu2.model.Coding; 043import org.hl7.fhir.dstu2.model.ContactPoint; 044import org.hl7.fhir.dstu2.model.ContactPoint.ContactPointSystem; 045import org.hl7.fhir.dstu2.model.DataElement; 046import org.hl7.fhir.dstu2.model.DataElement.DataElementContactComponent; 047import org.hl7.fhir.dstu2.model.ElementDefinition; 048import org.hl7.fhir.dstu2.model.ElementDefinition.ElementDefinitionBindingComponent; 049import org.hl7.fhir.dstu2.model.ElementDefinition.TypeRefComponent; 050import org.hl7.fhir.dstu2.model.Meta; 051import org.hl7.fhir.dstu2.model.OperationOutcome; 052import org.hl7.fhir.dstu2.model.OperationOutcome.IssueSeverity; 053import org.hl7.fhir.dstu2.model.OperationOutcome.OperationOutcomeIssueComponent; 054import org.hl7.fhir.dstu2.model.Reference; 055import org.hl7.fhir.dstu2.model.Resource; 056import org.hl7.fhir.dstu2.model.ResourceType; 057import org.hl7.fhir.dstu2.model.Type; 058import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 059import org.hl7.fhir.utilities.Utilities; 060import org.hl7.fhir.utilities.xhtml.XhtmlComposer; 061 062/** 063 * Decoration utilities for various resource types 064 * @author Grahame 065 * 066 */ 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, 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 141 private static void renderDE(DataElement de, List<String> cols, StringBuilder b, boolean profileLink, 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") ? Utilities.escapeXml(ToolingExtensions.readPrimitiveExtension(de, "http://hl7.org/fhir/StructureDefinition/minValue").asStringValue()) : ""; 160 } else if (col.equals("DataElement.maxValue")) { 161 v = ToolingExtensions.hasExtension(de, "http://hl7.org/fhir/StructureDefinition/maxValue") ? Utilities.escapeXml(ToolingExtensions.readPrimitiveExtension(de, "http://hl7.org/fhir/StructureDefinition/maxValue").asStringValue()) : ""; 162 } else if (col.equals("DataElement.maxLength")) { 163 v = ToolingExtensions.hasExtension(de, "http://hl7.org/fhir/StructureDefinition/maxLength") ? Utilities.escapeXml(ToolingExtensions.readPrimitiveExtension(de, "http://hl7.org/fhir/StructureDefinition/maxLength").asStringValue()) : ""; 164 } else if (col.equals("DataElement.mask")) { 165 v = ToolingExtensions.hasExtension(de, "http://hl7.org/fhir/StructureDefinition/mask") ? Utilities.escapeXml(ToolingExtensions.readPrimitiveExtension(de, "http://hl7.org/fhir/StructureDefinition/mask").asStringValue()) : ""; 166 } else 167 throw new Error("Unknown column name: "+col); 168 169 b.append("<td>"+v+"</td>"); 170 } 171 if (profileLink) { 172 b.append("<td><a href=\""+linkBase+"-"+de.getId()+".html\">Profile</a>, <a href=\"http://www.opencem.org/#/20140917/Intermountain/"+de.getId()+"\">CEM</a>"); 173 if (ToolingExtensions.hasExtension(de, ToolingExtensions.EXT_CIMI_REFERENCE)) 174 b.append(", <a href=\""+ToolingExtensions.readStringExtension(de, ToolingExtensions.EXT_CIMI_REFERENCE)+"\">CIMI</a>"); 175 b.append("</td>"); 176 } 177 b.append("</tr>\r\n"); 178 } 179 180 181 182 private static String renderBinding(ElementDefinitionBindingComponent binding) { 183 // TODO Auto-generated method stub 184 return null; 185 } 186 187 private static String renderDEUnits(Type units) { 188 if (units == null || units.isEmpty()) 189 return ""; 190 if (units instanceof CodeableConcept) 191 return renderCodeable((CodeableConcept) units); 192 else 193 return "<a href=\""+Utilities.escapeXml(((Reference) units).getReference())+"\">"+Utilities.escapeXml(((Reference) units).getReference())+"</a>"; 194 195 } 196 197 private static String renderCodeable(CodeableConcept units) { 198 if (units == null || units.isEmpty()) 199 return ""; 200 String v = renderCoding(units.getCoding()); 201 if (units.hasText()) 202 v = v + " " +Utilities.escapeXml(units.getText()); 203 return v; 204 } 205 206 private static String renderCoding(List<Coding> codes) { 207 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 208 for (Coding c : codes) 209 b.append(renderCoding(c)); 210 return b.toString(); 211 } 212 213 private static String renderCoding(Coding code) { 214 if (code == null || code.isEmpty()) 215 return ""; 216 else 217 return "<span title=\""+Utilities.escapeXml(code.getSystem())+"\">"+Utilities.escapeXml(code.getCode())+"</span>"; 218 } 219 220 private static List<String> chooseColumns(Bundle bundle, DataElement common, StringBuilder b, boolean profileLink) { 221 b.append("<tr>"); 222 List<String> results = new ArrayList<String>(); 223 results.add("DataElement.name"); 224 b.append("<td width=\"250\"><b>Name</b></td>"); 225 if (!common.hasStatus()) { 226 results.add("DataElement.status"); 227 b.append("<td><b>Status</b></td>"); 228 } 229 if (hasCode(bundle)) { 230 results.add("DataElement.code"); 231 b.append("<td><b>Code</b></td>"); 232 } 233 if (!common.getElement().get(0).hasType() && hasType(bundle)) { 234 results.add("DataElement.type"); 235 b.append("<td><b>Type</b></td>"); 236 } 237 if (hasUnits(bundle)) { 238 results.add("DataElement.units"); 239 b.append("<td><b>Units</b></td>"); 240 } 241 if (hasBinding(bundle)) { 242 results.add("DataElement.binding"); 243 b.append("<td><b>Binding</b></td>"); 244 } 245 if (hasExtension(bundle, "http://hl7.org/fhir/StructureDefinition/minValue")) { 246 results.add("DataElement.minValue"); 247 b.append("<td><b>Min Value</b></td>"); 248 } 249 if (hasExtension(bundle, "http://hl7.org/fhir/StructureDefinition/maxValue")) { 250 results.add("DataElement.maxValue"); 251 b.append("<td><b>Max Value</b></td>"); 252 } 253 if (hasExtension(bundle, "http://hl7.org/fhir/StructureDefinition/maxLength")) { 254 results.add("DataElement.maxLength"); 255 b.append("<td><b>Max Length</b></td>"); 256 } 257 if (hasExtension(bundle, "http://hl7.org/fhir/StructureDefinition/mask")) { 258 results.add("DataElement.mask"); 259 b.append("<td><b>Mask</b></td>"); 260 } 261 if (profileLink) 262 b.append("<td><b>Links</b></td>"); 263 b.append("</tr>\r\n"); 264 return results; 265 } 266 267 private static boolean hasExtension(Bundle bundle, String url) { 268 for (BundleEntryComponent e : bundle.getEntry()) { 269 DataElement de = (DataElement) e.getResource(); 270 if (ToolingExtensions.hasExtension(de, url)) 271 return true; 272 } 273 return false; 274 } 275 276 private static boolean hasBinding(Bundle bundle) { 277 for (BundleEntryComponent e : bundle.getEntry()) { 278 DataElement de = (DataElement) e.getResource(); 279 if (de.getElement().get(0).hasBinding()) 280 return true; 281 } 282 return false; 283 } 284 285 private static boolean hasCode(Bundle bundle) { 286 for (BundleEntryComponent e : bundle.getEntry()) { 287 DataElement de = (DataElement) e.getResource(); 288 if (de.getElement().get(0).hasCode()) 289 return true; 290 } 291 return false; 292 } 293 294 private static boolean hasType(Bundle bundle) { 295 for (BundleEntryComponent e : bundle.getEntry()) { 296 DataElement de = (DataElement) e.getResource(); 297 if (de.getElement().get(0).hasType()) 298 return true; 299 } 300 return false; 301 } 302 303 private static boolean hasUnits(Bundle bundle) { 304 for (BundleEntryComponent e : bundle.getEntry()) { 305 DataElement de = (DataElement) e.getResource(); 306 if (ToolingExtensions.getAllowedUnits(de.getElement().get(0)) != null) 307 return true; 308 } 309 return false; 310 } 311 312 private static DataElement showDECHeader(StringBuilder b, Bundle bundle) { 313 DataElement meta = new DataElement(); 314 DataElement prototype = (DataElement) bundle.getEntry().get(0).getResource(); 315 meta.setPublisher(prototype.getPublisher()); 316 meta.getContact().addAll(prototype.getContact()); 317 meta.setStatus(prototype.getStatus()); 318 meta.setDate(prototype.getDate()); 319 meta.addElement().getType().addAll(prototype.getElement().get(0).getType()); 320 321 for (BundleEntryComponent e : bundle.getEntry()) { 322 DataElement de = (DataElement) e.getResource(); 323 if (!Base.compareDeep(de.getPublisherElement(), meta.getPublisherElement(), false)) 324 meta.setPublisherElement(null); 325 if (!Base.compareDeep(de.getContact(), meta.getContact(), false)) 326 meta.getContact().clear(); 327 if (!Base.compareDeep(de.getStatusElement(), meta.getStatusElement(), false)) 328 meta.setStatusElement(null); 329 if (!Base.compareDeep(de.getDateElement(), meta.getDateElement(), false)) 330 meta.setDateElement(null); 331 if (!Base.compareDeep(de.getElement().get(0).getType(), meta.getElement().get(0).getType(), false)) 332 meta.getElement().get(0).getType().clear(); 333 } 334 if (meta.hasPublisher() || meta.hasContact() || meta.hasStatus() || meta.hasDate() /* || meta.hasType() */) { 335 b.append("<table class=\"grid\">\r\n"); 336 if (meta.hasPublisher()) 337 b.append("<tr><td>Publisher:</td><td>"+meta.getPublisher()+"</td></tr>\r\n"); 338 if (meta.hasContact()) { 339 b.append("<tr><td>Contacts:</td><td>"); 340 boolean firsti = true; 341 for (DataElementContactComponent c : meta.getContact()) { 342 if (firsti) 343 firsti = false; 344 else 345 b.append("<br/>"); 346 if (c.hasName()) 347 b.append(Utilities.escapeXml(c.getName())+": "); 348 boolean first = true; 349 for (ContactPoint cp : c.getTelecom()) { 350 if (first) 351 first = false; 352 else 353 b.append(", "); 354 renderContactPoint(b, cp); 355 } 356 } 357 b.append("</td></tr>\r\n"); 358 } 359 if (meta.hasStatus()) 360 b.append("<tr><td>Status:</td><td>"+meta.getStatus().toString()+"</td></tr>\r\n"); 361 if (meta.hasDate()) 362 b.append("<tr><td>Date:</td><td>"+meta.getDateElement().asStringValue()+"</td></tr>\r\n"); 363 if (meta.getElement().get(0).hasType()) 364 b.append("<tr><td>Type:</td><td>"+renderType(meta.getElement().get(0).getType())+"</td></tr>\r\n"); 365 b.append("</table>\r\n"); 366 } 367 return meta; 368 } 369 370 private static String renderType(List<TypeRefComponent> type) { 371 if (type == null || type.isEmpty()) 372 return ""; 373 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 374 for (TypeRefComponent c : type) 375 b.append(renderType(c)); 376 return b.toString(); 377 } 378 379 private static String renderType(TypeRefComponent type) { 380 if (type == null || type.isEmpty()) 381 return ""; 382 return type.getCode(); 383 } 384 385 public static void renderContactPoint(StringBuilder b, ContactPoint cp) { 386 if (cp != null && !cp.isEmpty()) { 387 if (cp.getSystem() == ContactPointSystem.EMAIL) 388 b.append("<a href=\"mailto:"+cp.getValue()+"\">"+cp.getValue()+"</a>"); 389 else if (cp.getSystem() == ContactPointSystem.FAX) 390 b.append("Fax: "+cp.getValue()); 391 else if (cp.getSystem() == ContactPointSystem.OTHER) 392 b.append("<a href=\""+cp.getValue()+"\">"+cp.getValue()+"</a>"); 393 else 394 b.append(cp.getValue()); 395 } 396 } 397}