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