001package org.hl7.fhir.dstu2016may.utils; 002 003import java.net.URISyntaxException; 004 005/* 006Copyright (c) 2011+, HL7, Inc 007All rights reserved. 008 009Redistribution and use in source and binary forms, with or without modification, 010are permitted provided that the following conditions are met: 011 012 * Redistributions of source code must retain the above copyright notice, this 013 list of conditions and the following disclaimer. 014 * Redistributions in binary form must reproduce the above copyright notice, 015 this list of conditions and the following disclaimer in the documentation 016 and/or other materials provided with the distribution. 017 * Neither the name of HL7 nor the names of its contributors may be used to 018 endorse or promote products derived from this software without specific 019 prior written permission. 020 021THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 022ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 023WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 024IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 025INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 026NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 027PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 028WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 029ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 030POSSIBILITY OF SUCH DAMAGE. 031 032 */ 033 034import java.util.ArrayList; 035import java.util.Iterator; 036import java.util.List; 037 038import org.apache.commons.lang3.StringUtils; 039import org.hl7.fhir.dstu2016may.model.BooleanType; 040import org.hl7.fhir.dstu2016may.model.CodeSystem; 041import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionComponent; 042import org.hl7.fhir.dstu2016may.model.CodeType; 043import org.hl7.fhir.dstu2016may.model.CodeableConcept; 044import org.hl7.fhir.dstu2016may.model.Coding; 045import org.hl7.fhir.dstu2016may.model.DataElement; 046import org.hl7.fhir.dstu2016may.model.DomainResource; 047import org.hl7.fhir.dstu2016may.model.Element; 048import org.hl7.fhir.dstu2016may.model.ElementDefinition; 049import org.hl7.fhir.dstu2016may.model.Extension; 050import org.hl7.fhir.dstu2016may.model.ExtensionHelper; 051import org.hl7.fhir.dstu2016may.model.Factory; 052import org.hl7.fhir.dstu2016may.model.Identifier; 053import org.hl7.fhir.dstu2016may.model.IntegerType; 054import org.hl7.fhir.dstu2016may.model.MarkdownType; 055import org.hl7.fhir.dstu2016may.model.PrimitiveType; 056import org.hl7.fhir.dstu2016may.model.Questionnaire.QuestionnaireItemComponent; 057import org.hl7.fhir.dstu2016may.model.Questionnaire.QuestionnaireItemType; 058import org.hl7.fhir.dstu2016may.model.StringType; 059import org.hl7.fhir.dstu2016may.model.Type; 060import org.hl7.fhir.dstu2016may.model.UriType; 061import org.hl7.fhir.dstu2016may.model.ValueSet; 062import org.hl7.fhir.utilities.validation.ValidationMessage.Source; 063import org.hl7.fhir.exceptions.FHIRFormatError; 064 065 066public class ToolingExtensions { 067 068 // validated 069 public static final String EXT_SUBSUMES = "http://hl7.org/fhir/StructureDefinition/codesystem-subsumes"; 070 private static final String EXT_OID = "http://hl7.org/fhir/StructureDefinition/valueset-oid"; 071// public static final String EXT_DEPRECATED = "http://hl7.org/fhir/StructureDefinition/codesystem-deprecated"; 072 public static final String EXT_DEFINITION = "http://hl7.org/fhir/StructureDefinition/valueset-definition"; 073 public static final String EXT_COMMENT = "http://hl7.org/fhir/StructureDefinition/valueset-comments"; 074 private static final String EXT_IDENTIFIER = "http://hl7.org/fhir/StructureDefinition/identifier"; 075 private static final String EXT_TRANSLATION = "http://hl7.org/fhir/StructureDefinition/translation"; 076 public static final String EXT_ISSUE_SOURCE = "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-source"; 077 public static final String EXT_DISPLAY_HINT = "http://hl7.org/fhir/StructureDefinition/structuredefinition-display-hint"; 078 public static final String EXT_REPLACED_BY = "http://hl7.org/fhir/StructureDefinition/valueset-replacedby"; 079 public static final String EXT_JSON_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type"; 080 public static final String EXT_XML_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-type"; 081 public static final String EXT_REGEX = "http://hl7.org/fhir/StructureDefinition/structuredefinition-regex"; 082 public static final String EXT_CONTROL = "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl"; 083 public static final String EXT_MINOCCURS = "http://hl7.org/fhir/StructureDefinition/questionnaire-minOccurs"; 084 public static final String EXT_MAXOCCURS = "http://hl7.org/fhir/StructureDefinition/questionnaire-maxOccurs"; 085 public static final String EXT_ALLOWEDRESOURCE = "http://hl7.org/fhir/StructureDefinition/questionnaire-allowedResource"; 086 public static final String EXT_REFERENCEFILTER = "http://hl7.org/fhir/StructureDefinition/questionnaire-referenceFilter"; 087 088 // unregistered? 089 090// public static final String EXT_FLYOVER = "http://hl7.org/fhir/Profile/questionnaire-extensions#flyover"; 091// private static final String EXT_QTYPE = "http://www.healthintersections.com.au/fhir/Profile/metadata#type"; 092// private static final String EXT_QREF = "http://www.healthintersections.com.au/fhir/Profile/metadata#reference"; 093// private static final String EXTENSION_FILTER_ONLY = "http://www.healthintersections.com.au/fhir/Profile/metadata#expandNeedsFilter"; 094// private static final String EXT_TYPE = "http://www.healthintersections.com.au/fhir/Profile/metadata#type"; 095// private static final String EXT_REFERENCE = "http://www.healthintersections.com.au/fhir/Profile/metadata#reference"; 096 private static final String EXT_FHIRTYPE = "http://hl7.org/fhir/StructureDefinition/questionnaire-fhirType"; 097 private static final String EXT_ALLOWABLE_UNITS = "http://hl7.org/fhir/StructureDefinition/elementdefinition-allowedUnits"; 098 public static final String EXT_CIMI_REFERENCE = "http://hl7.org/fhir/StructureDefinition/cimi-reference"; 099 public static final String EXT_UNCLOSED = "http://hl7.org/fhir/StructureDefinition/valueset-unclosed"; 100 public static final String EXT_FMM_LEVEL = "http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm"; 101 102 103 // specific extension helpers 104 105 public static Extension makeIssueSource(Source source) { 106 Extension ex = new Extension(); 107 // todo: write this up and get it published with the pack (and handle the redirect?) 108 ex.setUrl(ToolingExtensions.EXT_ISSUE_SOURCE); 109 CodeType c = new CodeType(); 110 c.setValue(source.toString()); 111 ex.setValue(c); 112 return ex; 113 } 114 115 public static boolean hasExtension(DomainResource de, String url) { 116 return getExtension(de, url) != null; 117 } 118 119 public static boolean hasExtension(Element e, String url) { 120 return getExtension(e, url) != null; 121 } 122 123 public static void addStringExtension(DomainResource dr, String url, String content) { 124 if (!StringUtils.isBlank(content)) { 125 Extension ex = getExtension(dr, url); 126 if (ex != null) 127 ex.setValue(new StringType(content)); 128 else 129 dr.getExtension().add(Factory.newExtension(url, new StringType(content), true)); 130 } 131 } 132 133 public static void addMarkdownExtension(DomainResource dr, String url, String content) { 134 if (!StringUtils.isBlank(content)) { 135 Extension ex = getExtension(dr, url); 136 if (ex != null) 137 ex.setValue(new StringType(content)); 138 else 139 dr.getExtension().add(Factory.newExtension(url, new MarkdownType(content), true)); 140 } 141 } 142 143 public static void addStringExtension(Element e, String url, String content) { 144 if (!StringUtils.isBlank(content)) { 145 Extension ex = getExtension(e, url); 146 if (ex != null) 147 ex.setValue(new StringType(content)); 148 else 149 e.getExtension().add(Factory.newExtension(url, new StringType(content), true)); 150 } 151 } 152 153 public static void addIntegerExtension(DomainResource dr, String url, int value) { 154 Extension ex = getExtension(dr, url); 155 if (ex != null) 156 ex.setValue(new IntegerType(value)); 157 else 158 dr.getExtension().add(Factory.newExtension(url, new IntegerType(value), true)); 159 } 160 161 public static void addComment(Element nc, String comment) { 162 if (!StringUtils.isBlank(comment)) 163 nc.getExtension().add(Factory.newExtension(EXT_COMMENT, Factory.newString_(comment), true)); 164 } 165 166// public static void markDeprecated(Element nc) { 167// setDeprecated(nc); 168// } 169// 170 public static void addSubsumes(ConceptDefinitionComponent nc, String code) { 171 nc.getModifierExtension().add(Factory.newExtension(EXT_SUBSUMES, Factory.newCode(code), true)); 172 } 173 174 public static void addDefinition(Element nc, String definition) { 175 if (!StringUtils.isBlank(definition)) 176 nc.getExtension().add(Factory.newExtension(EXT_DEFINITION, Factory.newString_(definition), true)); 177 } 178 179 public static void addDisplayHint(Element def, String hint) { 180 if (!StringUtils.isBlank(hint)) 181 def.getExtension().add(Factory.newExtension(EXT_DISPLAY_HINT, Factory.newString_(hint), true)); 182 } 183 184 public static String getDisplayHint(Element def) { 185 return readStringExtension(def, EXT_DISPLAY_HINT); 186 } 187 188 public static String readStringExtension(Element c, String uri) { 189 Extension ex = ExtensionHelper.getExtension(c, uri); 190 if (ex == null) 191 return null; 192 if (ex.getValue() instanceof UriType) 193 return ((UriType) ex.getValue()).getValue(); 194 if (!(ex.getValue() instanceof StringType)) 195 return null; 196 return ((StringType) ex.getValue()).getValue(); 197 } 198 199 public static String readStringExtension(DomainResource c, String uri) { 200 Extension ex = getExtension(c, uri); 201 if (ex == null) 202 return null; 203 if ((ex.getValue() instanceof StringType)) 204 return ((StringType) ex.getValue()).getValue(); 205 if ((ex.getValue() instanceof UriType)) 206 return ((UriType) ex.getValue()).getValue(); 207 if ((ex.getValue() instanceof MarkdownType)) 208 return ((MarkdownType) ex.getValue()).getValue(); 209 return null; 210 } 211 212 @SuppressWarnings("unchecked") 213 public static PrimitiveType<Type> readPrimitiveExtension(DomainResource c, String uri) { 214 Extension ex = getExtension(c, uri); 215 if (ex == null) 216 return null; 217 return (PrimitiveType<Type>) ex.getValue(); 218 } 219 220 public static boolean findStringExtension(Element c, String uri) { 221 Extension ex = ExtensionHelper.getExtension(c, uri); 222 if (ex == null) 223 return false; 224 if (!(ex.getValue() instanceof StringType)) 225 return false; 226 return !StringUtils.isBlank(((StringType) ex.getValue()).getValue()); 227 } 228 229 public static Boolean readBooleanExtension(Element c, String uri) { 230 Extension ex = ExtensionHelper.getExtension(c, uri); 231 if (ex == null) 232 return null; 233 if (!(ex.getValue() instanceof BooleanType)) 234 return null; 235 return ((BooleanType) ex.getValue()).getValue(); 236 } 237 238 public static boolean findBooleanExtension(Element c, String uri) { 239 Extension ex = ExtensionHelper.getExtension(c, uri); 240 if (ex == null) 241 return false; 242 if (!(ex.getValue() instanceof BooleanType)) 243 return false; 244 return true; 245 } 246 247 public static String getComment(ConceptDefinitionComponent c) { 248 return readStringExtension(c, EXT_COMMENT); 249 } 250// 251// public static Boolean getDeprecated(Element c) { 252// return readBooleanExtension(c, EXT_DEPRECATED); 253// } 254 255 public static boolean hasComment(ConceptDefinitionComponent c) { 256 return findStringExtension(c, EXT_COMMENT); 257 } 258 259// public static boolean hasDeprecated(Element c) { 260// return findBooleanExtension(c, EXT_DEPRECATED); 261// } 262 263 public static List<CodeType> getSubsumes(ConceptDefinitionComponent c) { 264 List<CodeType> res = new ArrayList<CodeType>(); 265 266 for (Extension e : c.getExtension()) { 267 if (EXT_SUBSUMES.equals(e.getUrl())) 268 res.add((CodeType) e.getValue()); 269 } 270 return res; 271 } 272 273 public static void addFlyOver(QuestionnaireItemComponent item, String text){ 274 if (!StringUtils.isBlank(text)) { 275 QuestionnaireItemComponent display = item.addItem(); 276 display.setType(QuestionnaireItemType.DISPLAY); 277 display.setText(text); 278 display.getExtension().add(Factory.newExtension(EXT_CONTROL, Factory.newCodeableConcept("flyover", "http://hl7.org/fhir/questionnaire-item-control", "Fly-over"), true)); 279 } 280 } 281 282 public static void addMin(QuestionnaireItemComponent item, int min) { 283 item.getExtension().add(Factory.newExtension(EXT_MINOCCURS, Factory.newInteger(min), true)); 284 } 285 286 public static void addMax(QuestionnaireItemComponent item, int max) { 287 item.getExtension().add(Factory.newExtension(EXT_MAXOCCURS, Factory.newInteger(max), true)); 288 } 289 290 public static void addFhirType(QuestionnaireItemComponent group, String value) { 291 group.getExtension().add(Factory.newExtension(EXT_FHIRTYPE, Factory.newString_(value), true)); 292 } 293 294 public static void addControl(QuestionnaireItemComponent group, String value) { 295 group.getExtension().add(Factory.newExtension(EXT_CONTROL, Factory.newCodeableConcept(value, "http://hl7.org/fhir/questionnaire-item-control", value), true)); 296 } 297 298 public static void addAllowedResource(QuestionnaireItemComponent group, String value) { 299 group.getExtension().add(Factory.newExtension(EXT_ALLOWEDRESOURCE, Factory.newCode(value), true)); 300 } 301 302 public static void addReferenceFilter(QuestionnaireItemComponent group, String value) { 303 group.getExtension().add(Factory.newExtension(EXT_REFERENCEFILTER, Factory.newString_(value), true)); 304 } 305 306 public static void addIdentifier(Element element, Identifier value) { 307 element.getExtension().add(Factory.newExtension(EXT_IDENTIFIER, value, true)); 308 } 309 310 /** 311 * @param name the identity of the extension of interest 312 * @return The extension, if on this element, else null 313 */ 314 public static Extension getExtension(DomainResource resource, String name) { 315 if (name == null) 316 return null; 317 if (!resource.hasExtension()) 318 return null; 319 for (Extension e : resource.getExtension()) { 320 if (name.equals(e.getUrl())) 321 return e; 322 } 323 return null; 324 } 325 326 public static Extension getExtension(Element el, String name) { 327 if (name == null) 328 return null; 329 if (!el.hasExtension()) 330 return null; 331 for (Extension e : el.getExtension()) { 332 if (name.equals(e.getUrl())) 333 return e; 334 } 335 return null; 336 } 337 338 public static void setStringExtension(DomainResource resource, String uri, String value) { 339 Extension ext = getExtension(resource, uri); 340 if (ext != null) 341 ext.setValue(new StringType(value)); 342 else 343 resource.getExtension().add(new Extension(new UriType(uri)).setValue(new StringType(value))); 344 } 345 346 public static String getOID(CodeSystem define) { 347 return readStringExtension(define, EXT_OID); 348 } 349 350 public static String getOID(ValueSet vs) { 351 return readStringExtension(vs, EXT_OID); 352 } 353 354 public static void setOID(CodeSystem define, String oid) throws FHIRFormatError, URISyntaxException { 355 if (!oid.startsWith("urn:oid:")) 356 throw new FHIRFormatError("Error in OID format"); 357 if (oid.startsWith("urn:oid:urn:oid:")) 358 throw new FHIRFormatError("Error in OID format"); 359 if (!hasExtension(define, EXT_OID)) 360 define.getExtension().add(Factory.newExtension(EXT_OID, Factory.newUri(oid), false)); 361 else if (!oid.equals(readStringExtension(define, EXT_OID))) 362 throw new Error("Attempt to assign multiple OIDs to a code system"); 363 } 364 public static void setOID(ValueSet vs, String oid) throws FHIRFormatError, URISyntaxException { 365 if (!oid.startsWith("urn:oid:")) 366 throw new FHIRFormatError("Error in OID format"); 367 if (oid.startsWith("urn:oid:urn:oid:")) 368 throw new FHIRFormatError("Error in OID format"); 369 if (!hasExtension(vs, EXT_OID)) 370 vs.getExtension().add(Factory.newExtension(EXT_OID, Factory.newUri(oid), false)); 371 else if (!oid.equals(readStringExtension(vs, EXT_OID))) 372 throw new Error("Attempt to assign multiple OIDs to value set "+vs.getName()+" ("+vs.getUrl()+"). Has "+readStringExtension(vs, EXT_OID)+", trying to add "+oid); 373 } 374 375 public static boolean hasLanguageTranslation(Element element, String lang) { 376 for (Extension e : element.getExtension()) { 377 if (e.getUrl().equals(EXT_TRANSLATION)) { 378 Extension e1 = ExtensionHelper.getExtension(e, "lang"); 379 380 if (e1 != null && e1.getValue() instanceof CodeType && ((CodeType) e.getValue()).getValue().equals(lang)) 381 return true; 382 } 383 } 384 return false; 385 } 386 387 public static String getLanguageTranslation(Element element, String lang) { 388 for (Extension e : element.getExtension()) { 389 if (e.getUrl().equals(EXT_TRANSLATION)) { 390 Extension e1 = ExtensionHelper.getExtension(e, "lang"); 391 392 if (e1 != null && e1.getValue() instanceof CodeType && ((CodeType) e.getValue()).getValue().equals(lang)) { 393 e1 = ExtensionHelper.getExtension(e, "content"); 394 return ((StringType) e.getValue()).getValue(); 395 } 396 } 397 } 398 return null; 399 } 400 401 public static void addLanguageTranslation(Element element, String lang, String value) { 402 Extension extension = new Extension().setUrl(EXT_TRANSLATION); 403 extension.addExtension().setUrl("lang").setValue(new StringType(lang)); 404 extension.addExtension().setUrl("content").setValue(new StringType(value)); 405 element.getExtension().add(extension); 406 } 407 408 public static Type getAllowedUnits(ElementDefinition eld) { 409 for (Extension e : eld.getExtension()) 410 if (e.getUrl().equals(EXT_ALLOWABLE_UNITS)) 411 return e.getValue(); 412 return null; 413 } 414 415 public static void setAllowableUnits(ElementDefinition eld, CodeableConcept cc) { 416 for (Extension e : eld.getExtension()) 417 if (e.getUrl().equals(EXT_ALLOWABLE_UNITS)) { 418 e.setValue(cc); 419 return; 420 } 421 eld.getExtension().add(new Extension().setUrl(EXT_ALLOWABLE_UNITS).setValue(cc)); 422 } 423 424 public static List<Extension> getExtensions(Element element, String url) { 425 List<Extension> results = new ArrayList<Extension>(); 426 for (Extension ex : element.getExtension()) 427 if (ex.getUrl().equals(url)) 428 results.add(ex); 429 return results; 430 } 431 432 public static List<Extension> getExtensions(DomainResource resource, String url) { 433 List<Extension> results = new ArrayList<Extension>(); 434 for (Extension ex : resource.getExtension()) 435 if (ex.getUrl().equals(url)) 436 results.add(ex); 437 return results; 438 } 439 440 public static void addDEReference(DataElement de, String value) { 441 for (Extension e : de.getExtension()) 442 if (e.getUrl().equals(EXT_CIMI_REFERENCE)) { 443 e.setValue(new UriType(value)); 444 return; 445 } 446 de.getExtension().add(new Extension().setUrl(EXT_CIMI_REFERENCE).setValue(new UriType(value))); 447 } 448 449// public static void setDeprecated(Element nc) { 450// for (Extension e : nc.getExtension()) 451// if (e.getUrl().equals(EXT_DEPRECATED)) { 452// e.setValue(new BooleanType(true)); 453// return; 454// } 455// nc.getExtension().add(new Extension().setUrl(EXT_DEPRECATED).setValue(new BooleanType(true))); 456// } 457 458 public static void setExtension(Element focus, String url, Coding c) { 459 for (Extension e : focus.getExtension()) 460 if (e.getUrl().equals(url)) { 461 e.setValue(c); 462 return; 463 } 464 focus.getExtension().add(new Extension().setUrl(url).setValue(c)); 465 } 466 467 public static void removeExtension(DomainResource focus, String url) { 468 Iterator<Extension> i = focus.getExtension().iterator(); 469 while (i.hasNext()) { 470 Extension e = i.next(); // must be called before you can call i.remove() 471 if (e.getUrl().equals(url)) { 472 i.remove(); 473 } 474 } 475 } 476 477 public static void removeExtension(Element focus, String url) { 478 Iterator<Extension> i = focus.getExtension().iterator(); 479 while (i.hasNext()) { 480 Extension e = i.next(); // must be called before you can call i.remove() 481 if (e.getUrl().equals(url)) { 482 i.remove(); 483 } 484 } 485 } 486 487 public static boolean hasOID(ValueSet vs) { 488 return hasExtension(vs, EXT_OID); 489 } 490 491 492 public static void setStringExtension(Element element, String uri, String value) { 493 Extension ext = getExtension(element, uri); 494 if (ext != null) 495 ext.setValue(new StringType(value)); 496 else 497 element.getExtension().add(new Extension(new UriType(uri)).setValue(new StringType(value))); 498 } 499 500}