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