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