001/* 002 * #%L 003 * HAPI FHIR - Core Library 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.context; 021 022import ca.uhn.fhir.context.api.AddProfileTagEnum; 023import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; 024import ca.uhn.fhir.context.support.IValidationSupport; 025import ca.uhn.fhir.fhirpath.IFhirPath; 026import ca.uhn.fhir.i18n.HapiLocalizer; 027import ca.uhn.fhir.i18n.Msg; 028import ca.uhn.fhir.model.api.IElement; 029import ca.uhn.fhir.model.api.IFhirVersion; 030import ca.uhn.fhir.model.api.IResource; 031import ca.uhn.fhir.model.api.annotation.ResourceDef; 032import ca.uhn.fhir.model.view.ViewGenerator; 033import ca.uhn.fhir.narrative.INarrativeGenerator; 034import ca.uhn.fhir.parser.DataFormatException; 035import ca.uhn.fhir.parser.IParser; 036import ca.uhn.fhir.parser.IParserErrorHandler; 037import ca.uhn.fhir.parser.JsonParser; 038import ca.uhn.fhir.parser.LenientErrorHandler; 039import ca.uhn.fhir.parser.NDJsonParser; 040import ca.uhn.fhir.parser.RDFParser; 041import ca.uhn.fhir.parser.XmlParser; 042import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; 043import ca.uhn.fhir.rest.client.api.IBasicClient; 044import ca.uhn.fhir.rest.client.api.IGenericClient; 045import ca.uhn.fhir.rest.client.api.IRestfulClient; 046import ca.uhn.fhir.rest.client.api.IRestfulClientFactory; 047import ca.uhn.fhir.system.HapiSystemProperties; 048import ca.uhn.fhir.util.FhirTerser; 049import ca.uhn.fhir.util.ReflectionUtil; 050import ca.uhn.fhir.util.VersionUtil; 051import ca.uhn.fhir.validation.FhirValidator; 052import jakarta.annotation.Nonnull; 053import jakarta.annotation.Nullable; 054import org.apache.commons.lang3.Validate; 055import org.apache.commons.lang3.exception.ExceptionUtils; 056import org.apache.jena.riot.Lang; 057import org.hl7.fhir.instance.model.api.IBase; 058import org.hl7.fhir.instance.model.api.IBaseBundle; 059import org.hl7.fhir.instance.model.api.IBaseResource; 060import org.hl7.fhir.instance.model.api.IPrimitiveType; 061 062import java.io.IOException; 063import java.io.InputStream; 064import java.lang.reflect.Method; 065import java.lang.reflect.Modifier; 066import java.util.ArrayList; 067import java.util.Arrays; 068import java.util.Collection; 069import java.util.Collections; 070import java.util.EnumMap; 071import java.util.Enumeration; 072import java.util.HashMap; 073import java.util.HashSet; 074import java.util.List; 075import java.util.Map; 076import java.util.Map.Entry; 077import java.util.Objects; 078import java.util.Optional; 079import java.util.Properties; 080import java.util.Set; 081import java.util.stream.Collectors; 082 083/** 084 * The FHIR context is the central starting point for the use of the HAPI FHIR API. It should be created once, and then 085 * used as a factory for various other types of objects (parsers, clients, etc.). 086 * 087 * <p> 088 * Important usage notes: 089 * </p> 090 * <ul> 091 * <li> 092 * Thread safety: <b>This class is thread safe</b> and may be shared between multiple processing 093 * threads, except for the {@link #registerCustomType} and {@link #registerCustomTypes} methods. 094 * </li> 095 * <li> 096 * Performance: <b>This class is expensive</b> to create, as it scans every resource class it needs to parse or encode 097 * to build up an internal model of those classes. For that reason, you should try to create one FhirContext instance 098 * which remains for the life of your application and reuse that instance. Note that it will not cause problems to 099 * create multiple instances (ie. resources originating from one FhirContext may be passed to parsers originating from 100 * another) but you will incur a performance penalty if a new FhirContext is created for every message you parse/encode. 101 * </li> 102 * </ul> 103 */ 104public class FhirContext { 105 106 private static final List<Class<? extends IBaseResource>> EMPTY_LIST = Collections.emptyList(); 107 private static final Map<FhirVersionEnum, FhirContext> ourStaticContexts = 108 Collections.synchronizedMap(new EnumMap<>(FhirVersionEnum.class)); 109 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirContext.class); 110 private final IFhirVersion myVersion; 111 private final Map<String, Class<? extends IBaseResource>> myDefaultTypeForProfile = new HashMap<>(); 112 private final Set<PerformanceOptionsEnum> myPerformanceOptions = new HashSet<>(); 113 private final Collection<Class<? extends IBaseResource>> myResourceTypesToScan; 114 private AddProfileTagEnum myAddProfileTagWhenEncoding = AddProfileTagEnum.ONLY_FOR_CUSTOM; 115 private volatile Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinition = 116 Collections.emptyMap(); 117 private ArrayList<Class<? extends IBase>> myCustomTypes; 118 private final Set<String> myCustomResourceNames = new HashSet<>(); 119 private volatile Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = Collections.emptyMap(); 120 private volatile boolean myInitialized; 121 private volatile boolean myInitializing = false; 122 private HapiLocalizer myLocalizer = new HapiLocalizer(); 123 private volatile Map<String, BaseRuntimeElementDefinition<?>> myNameToElementDefinition = Collections.emptyMap(); 124 private volatile Map<String, RuntimeResourceDefinition> myNameToResourceDefinition = Collections.emptyMap(); 125 private volatile Map<String, Class<? extends IBaseResource>> myNameToResourceType; 126 private volatile INarrativeGenerator myNarrativeGenerator; 127 private volatile IParserErrorHandler myParserErrorHandler = new LenientErrorHandler(); 128 private ParserOptions myParserOptions = new ParserOptions(); 129 private volatile IRestfulClientFactory myRestfulClientFactory; 130 private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition; 131 private IValidationSupport myValidationSupport; 132 private Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> myVersionToNameToResourceType = 133 Collections.emptyMap(); 134 private volatile Set<String> myResourceNames; 135 private volatile Boolean myFormatXmlSupported; 136 private volatile Boolean myFormatJsonSupported; 137 private volatile Boolean myFormatNDJsonSupported; 138 private volatile Boolean myFormatRdfSupported; 139 private IFhirValidatorFactory myFhirValidatorFactory = FhirValidator::new; 140 141 /** 142 * @deprecated It is recommended that you use one of the static initializer methods instead 143 * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} 144 */ 145 @Deprecated 146 public FhirContext() { 147 this(EMPTY_LIST); 148 } 149 150 /** 151 * @deprecated It is recommended that you use one of the static initializer methods instead 152 * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} 153 */ 154 @Deprecated 155 public FhirContext(final Class<? extends IBaseResource> theResourceType) { 156 this(toCollection(theResourceType)); 157 } 158 159 /** 160 * @deprecated It is recommended that you use one of the static initializer methods instead 161 * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} 162 */ 163 @Deprecated 164 public FhirContext(final Class<?>... theResourceTypes) { 165 this(toCollection(theResourceTypes)); 166 } 167 168 /** 169 * @deprecated It is recommended that you use one of the static initializer methods instead 170 * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} 171 */ 172 @Deprecated 173 public FhirContext(final Collection<Class<? extends IBaseResource>> theResourceTypes) { 174 this(null, theResourceTypes); 175 } 176 177 /** 178 * In most cases it is recommended that you use one of the static initializer methods instead 179 * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()}, but 180 * this method can also be used if you wish to supply the version programmatically. 181 */ 182 public FhirContext(final FhirVersionEnum theVersion) { 183 this(theVersion, null); 184 } 185 186 private FhirContext( 187 final FhirVersionEnum theVersion, final Collection<Class<? extends IBaseResource>> theResourceTypes) { 188 VersionUtil.getVersion(); 189 190 if (theVersion != null) { 191 if (!theVersion.isPresentOnClasspath()) { 192 throw new IllegalStateException(Msg.code(1680) 193 + getLocalizer() 194 .getMessage(FhirContext.class, "noStructuresForSpecifiedVersion", theVersion.name())); 195 } 196 myVersion = theVersion.getVersionImplementation(); 197 } else if (FhirVersionEnum.DSTU2.isPresentOnClasspath()) { 198 myVersion = FhirVersionEnum.DSTU2.getVersionImplementation(); 199 } else if (FhirVersionEnum.DSTU2_HL7ORG.isPresentOnClasspath()) { 200 myVersion = FhirVersionEnum.DSTU2_HL7ORG.getVersionImplementation(); 201 } else if (FhirVersionEnum.DSTU2_1.isPresentOnClasspath()) { 202 myVersion = FhirVersionEnum.DSTU2_1.getVersionImplementation(); 203 } else if (FhirVersionEnum.DSTU3.isPresentOnClasspath()) { 204 myVersion = FhirVersionEnum.DSTU3.getVersionImplementation(); 205 } else if (FhirVersionEnum.R4.isPresentOnClasspath()) { 206 myVersion = FhirVersionEnum.R4.getVersionImplementation(); 207 } else if (FhirVersionEnum.R4B.isPresentOnClasspath()) { 208 myVersion = FhirVersionEnum.R4B.getVersionImplementation(); 209 } else { 210 throw new IllegalStateException( 211 Msg.code(1681) + getLocalizer().getMessage(FhirContext.class, "noStructures")); 212 } 213 214 if (theVersion == null) { 215 ourLog.info( 216 "Creating new FhirContext with auto-detected version [{}]. It is recommended to explicitly select a version for future compatibility by invoking FhirContext.forDstuX()", 217 myVersion.getVersion().name()); 218 } else { 219 if (HapiSystemProperties.isUnitTestModeEnabled()) { 220 String calledAt = ExceptionUtils.getStackFrames(new Throwable())[4]; 221 ourLog.info( 222 "Creating new FHIR context for FHIR version [{}]{}", 223 myVersion.getVersion().name(), 224 calledAt); 225 } else { 226 ourLog.info( 227 "Creating new FHIR context for FHIR version [{}]", 228 myVersion.getVersion().name()); 229 } 230 } 231 232 myResourceTypesToScan = theResourceTypes; 233 234 /* 235 * Check if we're running in Android mode and configure the context appropriately if so 236 */ 237 try { 238 Class<?> clazz = Class.forName("ca.uhn.fhir.android.AndroidMarker"); 239 ourLog.info("Android mode detected, configuring FhirContext for Android operation"); 240 try { 241 Method method = clazz.getMethod("configureContext", FhirContext.class); 242 method.invoke(null, this); 243 } catch (Throwable e) { 244 ourLog.warn("Failed to configure context for Android operation", e); 245 } 246 } catch (ClassNotFoundException e) { 247 ourLog.trace("Android mode not detected"); 248 } 249 } 250 251 /** 252 * @since 5.6.0 253 */ 254 public static FhirContext forDstu2Cached() { 255 return forCached(FhirVersionEnum.DSTU2); 256 } 257 258 /** 259 * @since 6.2.0 260 */ 261 public static FhirContext forDstu2Hl7OrgCached() { 262 return forCached(FhirVersionEnum.DSTU2_HL7ORG); 263 } 264 265 /** 266 * @since 5.5.0 267 */ 268 public static FhirContext forDstu3Cached() { 269 return forCached(FhirVersionEnum.DSTU3); 270 } 271 272 /** 273 * @since 5.5.0 274 */ 275 public static FhirContext forR4Cached() { 276 return forCached(FhirVersionEnum.R4); 277 } 278 279 /** 280 * @since 6.1.0 281 */ 282 public static FhirContext forR4BCached() { 283 return forCached(FhirVersionEnum.R4B); 284 } 285 286 /** 287 * @since 5.5.0 288 */ 289 public static FhirContext forR5Cached() { 290 return forCached(FhirVersionEnum.R5); 291 } 292 293 private String createUnknownResourceNameError(final String theResourceName, final FhirVersionEnum theVersion) { 294 return getLocalizer().getMessage(FhirContext.class, "unknownResourceName", theResourceName, theVersion); 295 } 296 297 private void ensureCustomTypeList() { 298 myClassToElementDefinition.clear(); 299 if (myCustomTypes == null) { 300 myCustomTypes = new ArrayList<>(); 301 } 302 } 303 304 /** 305 * When encoding resources, this setting configures the parser to include 306 * an entry in the resource's metadata section which indicates which profile(s) the 307 * resource claims to conform to. The default is {@link AddProfileTagEnum#ONLY_FOR_CUSTOM}. 308 * 309 * @see #setAddProfileTagWhenEncoding(AddProfileTagEnum) for more information 310 */ 311 public AddProfileTagEnum getAddProfileTagWhenEncoding() { 312 return myAddProfileTagWhenEncoding; 313 } 314 315 /** 316 * When encoding resources, this setting configures the parser to include 317 * an entry in the resource's metadata section which indicates which profile(s) the 318 * resource claims to conform to. The default is {@link AddProfileTagEnum#ONLY_FOR_CUSTOM}. 319 * <p> 320 * This feature is intended for situations where custom resource types are being used, 321 * avoiding the need to manually add profile declarations for these custom types. 322 * </p> 323 * <p> 324 * See <a href="http://jamesagnew.gihhub.io/hapi-fhir/doc_extensions.html">Profiling and Extensions</a> 325 * for more information on using custom types. 326 * </p> 327 * <p> 328 * Note that this feature automatically adds the profile, but leaves any profile tags 329 * which have been manually added in place as well. 330 * </p> 331 * 332 * @param theAddProfileTagWhenEncoding The add profile mode (must not be <code>null</code>) 333 */ 334 public void setAddProfileTagWhenEncoding(final AddProfileTagEnum theAddProfileTagWhenEncoding) { 335 Validate.notNull(theAddProfileTagWhenEncoding, "theAddProfileTagWhenEncoding must not be null"); 336 myAddProfileTagWhenEncoding = theAddProfileTagWhenEncoding; 337 } 338 339 Collection<RuntimeResourceDefinition> getAllResourceDefinitions() { 340 validateInitialized(); 341 return myNameToResourceDefinition.values(); 342 } 343 344 /** 345 * Returns the default resource type for the given profile 346 * 347 * @see #setDefaultTypeForProfile(String, Class) 348 */ 349 public Class<? extends IBaseResource> getDefaultTypeForProfile(final String theProfile) { 350 validateInitialized(); 351 return myDefaultTypeForProfile.get(theProfile); 352 } 353 354 /** 355 * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed 356 * for extending the core library. 357 */ 358 @SuppressWarnings("unchecked") 359 public BaseRuntimeElementDefinition<?> getElementDefinition(final Class<? extends IBase> theElementType) { 360 validateInitialized(); 361 BaseRuntimeElementDefinition<?> retVal = myClassToElementDefinition.get(theElementType); 362 if (retVal == null) { 363 retVal = scanDatatype((Class<? extends IElement>) theElementType); 364 } 365 return retVal; 366 } 367 368 /** 369 * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed 370 * for extending the core library. 371 * <p> 372 * Note that this method is case insensitive! 373 * </p> 374 */ 375 @Nullable 376 public BaseRuntimeElementDefinition<?> getElementDefinition(final String theElementName) { 377 validateInitialized(); 378 return myNameToElementDefinition.get(theElementName.toLowerCase()); 379 } 380 381 /** 382 * Returns all element definitions (resources, datatypes, etc.) 383 */ 384 public Collection<BaseRuntimeElementDefinition<?>> getElementDefinitions() { 385 validateInitialized(); 386 return Collections.unmodifiableCollection(myClassToElementDefinition.values()); 387 } 388 389 /** 390 * This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with 391 * caution 392 */ 393 public HapiLocalizer getLocalizer() { 394 if (myLocalizer == null) { 395 myLocalizer = new HapiLocalizer(); 396 } 397 return myLocalizer; 398 } 399 400 /** 401 * This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with 402 * caution 403 */ 404 public void setLocalizer(final HapiLocalizer theMessages) { 405 myLocalizer = theMessages; 406 } 407 408 public INarrativeGenerator getNarrativeGenerator() { 409 return myNarrativeGenerator; 410 } 411 412 public FhirContext setNarrativeGenerator(final INarrativeGenerator theNarrativeGenerator) { 413 myNarrativeGenerator = theNarrativeGenerator; 414 return this; 415 } 416 417 /** 418 * Returns the parser options object which will be used to supply default 419 * options to newly created parsers 420 * 421 * @return The parser options - Will not return <code>null</code> 422 */ 423 public ParserOptions getParserOptions() { 424 return myParserOptions; 425 } 426 427 /** 428 * Sets the parser options object which will be used to supply default 429 * options to newly created parsers 430 * 431 * @param theParserOptions The parser options object - Must not be <code>null</code> 432 */ 433 public void setParserOptions(final ParserOptions theParserOptions) { 434 Validate.notNull(theParserOptions, "theParserOptions must not be null"); 435 myParserOptions = theParserOptions; 436 } 437 438 /** 439 * Get the configured performance options 440 */ 441 public Set<PerformanceOptionsEnum> getPerformanceOptions() { 442 return myPerformanceOptions; 443 } 444 445 // /** 446 // * Return an unmodifiable collection containing all known resource definitions 447 // */ 448 // public Collection<RuntimeResourceDefinition> getResourceDefinitions() { 449 // 450 // Set<Class<? extends IBase>> datatypes = Collections.emptySet(); 451 // Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> existing = Collections.emptyMap(); 452 // HashMap<String, Class<? extends IBaseResource>> types = new HashMap<String, Class<? extends IBaseResource>>(); 453 // ModelScanner.scanVersionPropertyFile(datatypes, types, myVersion.getVersion(), existing); 454 // for (int next : types.) 455 // 456 // return Collections.unmodifiableCollection(myIdToResourceDefinition.values()); 457 // } 458 459 /** 460 * Sets the configured performance options 461 * 462 * @see PerformanceOptionsEnum for a list of available options 463 */ 464 public void setPerformanceOptions(final Collection<PerformanceOptionsEnum> theOptions) { 465 myPerformanceOptions.clear(); 466 if (theOptions != null) { 467 myPerformanceOptions.addAll(theOptions); 468 } 469 } 470 471 /** 472 * Sets the configured performance options 473 * 474 * @see PerformanceOptionsEnum for a list of available options 475 */ 476 public void setPerformanceOptions(final PerformanceOptionsEnum... thePerformanceOptions) { 477 Collection<PerformanceOptionsEnum> asList = null; 478 if (thePerformanceOptions != null) { 479 asList = Arrays.asList(thePerformanceOptions); 480 } 481 setPerformanceOptions(asList); 482 } 483 484 /** 485 * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed 486 * for extending the core library. 487 */ 488 public RuntimeResourceDefinition getResourceDefinition(final Class<? extends IBaseResource> theResourceType) { 489 validateInitialized(); 490 Validate.notNull(theResourceType, "theResourceType can not be null"); 491 492 if (Modifier.isAbstract(theResourceType.getModifiers())) { 493 throw new IllegalArgumentException(Msg.code(1682) 494 + "Can not scan abstract or interface class (resource definitions must be concrete classes): " 495 + theResourceType.getName()); 496 } 497 498 RuntimeResourceDefinition retVal = (RuntimeResourceDefinition) myClassToElementDefinition.get(theResourceType); 499 if (retVal == null) { 500 retVal = scanResourceType(theResourceType); 501 } 502 503 return retVal; 504 } 505 506 public RuntimeResourceDefinition getResourceDefinition( 507 final FhirVersionEnum theVersion, final String theResourceName) { 508 Validate.notNull(theVersion, "theVersion can not be null"); 509 validateInitialized(); 510 511 if (theVersion.equals(myVersion.getVersion())) { 512 return getResourceDefinition(theResourceName); 513 } 514 515 Map<String, Class<? extends IBaseResource>> nameToType = myVersionToNameToResourceType.get(theVersion); 516 if (nameToType == null) { 517 nameToType = new HashMap<>(); 518 Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> existing = new HashMap<>(); 519 ModelScanner.scanVersionPropertyFile(null, nameToType, theVersion, existing); 520 521 Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> newVersionToNameToResourceType = 522 new HashMap<>(); 523 newVersionToNameToResourceType.putAll(myVersionToNameToResourceType); 524 newVersionToNameToResourceType.put(theVersion, nameToType); 525 myVersionToNameToResourceType = newVersionToNameToResourceType; 526 } 527 528 Class<? extends IBaseResource> resourceType = nameToType.get(theResourceName.toLowerCase()); 529 if (resourceType == null) { 530 throw new DataFormatException(Msg.code(1683) + createUnknownResourceNameError(theResourceName, theVersion)); 531 } 532 533 return getResourceDefinition(resourceType); 534 } 535 536 /** 537 * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed 538 * for extending the core library. 539 */ 540 public RuntimeResourceDefinition getResourceDefinition(final IBaseResource theResource) { 541 validateInitialized(); 542 Validate.notNull(theResource, "theResource must not be null"); 543 return getResourceDefinition(theResource.getClass()); 544 } 545 546 /** 547 * Returns the name of a given resource class. 548 */ 549 public String getResourceType(final Class<? extends IBaseResource> theResourceType) { 550 return getResourceDefinition(theResourceType).getName(); 551 } 552 553 /** 554 * Returns the name of the scanned runtime model for the given type. This is an advanced feature which is generally only needed 555 * for extending the core library. 556 */ 557 public String getResourceType(final IBaseResource theResource) { 558 return getResourceDefinition(theResource).getName(); 559 } 560 561 /* 562 * Returns the type of the scanned runtime model for the given type. This is an advanced feature which is generally only needed 563 * for extending the core library. 564 * <p> 565 * Note that this method is case insensitive! 566 * </p> 567 * 568 * @throws DataFormatException If the resource name is not known 569 */ 570 public String getResourceType(final String theResourceName) throws DataFormatException { 571 return getResourceDefinition(theResourceName).getName(); 572 } 573 574 /* 575 * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed 576 * for extending the core library. 577 * <p> 578 * Note that this method is case insensitive! 579 * </p> 580 * 581 * @throws DataFormatException If the resource name is not known 582 */ 583 public RuntimeResourceDefinition getResourceDefinition(final String theResourceName) throws DataFormatException { 584 validateInitialized(); 585 Validate.notBlank(theResourceName, "theResourceName must not be blank"); 586 587 String resourceName = theResourceName.toLowerCase(); 588 RuntimeResourceDefinition retVal = myNameToResourceDefinition.get(resourceName); 589 590 if (retVal == null) { 591 Class<? extends IBaseResource> clazz = myNameToResourceType.get(resourceName.toLowerCase()); 592 if (clazz == null) { 593 // *********************************************************************** 594 // Multiple spots in HAPI FHIR and Smile CDR depend on DataFormatException 595 // being thrown by this method, don't change that. 596 // *********************************************************************** 597 throw new DataFormatException( 598 Msg.code(1684) + createUnknownResourceNameError(theResourceName, myVersion.getVersion())); 599 } 600 if (IBaseResource.class.isAssignableFrom(clazz)) { 601 retVal = scanResourceType(clazz); 602 } 603 } 604 return retVal; 605 } 606 607 /** 608 * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed 609 * for extending the core library. 610 */ 611 public RuntimeResourceDefinition getResourceDefinitionById(final String theId) { 612 validateInitialized(); 613 return myIdToResourceDefinition.get(theId); 614 } 615 616 /** 617 * Returns the scanned runtime models. This is an advanced feature which is generally only needed for extending the 618 * core library. 619 */ 620 public Collection<RuntimeResourceDefinition> getResourceDefinitionsWithExplicitId() { 621 validateInitialized(); 622 return myIdToResourceDefinition.values(); 623 } 624 625 /** 626 * Returns an unmodifiable set containing all resource names known to this 627 * context 628 * 629 * @since 5.1.0 630 */ 631 public Set<String> getResourceTypes() { 632 Set<String> resourceNames = myResourceNames; 633 if (resourceNames == null) { 634 resourceNames = buildResourceNames(); 635 myResourceNames = resourceNames; 636 } 637 return resourceNames; 638 } 639 640 @Nonnull 641 private Set<String> buildResourceNames() { 642 Set<String> retVal = new HashSet<>(); 643 Properties props = new Properties(); 644 try (InputStream propFile = myVersion.getFhirVersionPropertiesFile()) { 645 props.load(propFile); 646 } catch (IOException e) { 647 throw new ConfigurationException(Msg.code(1685) + "Failed to load version properties file", e); 648 } 649 Enumeration<?> propNames = props.propertyNames(); 650 while (propNames.hasMoreElements()) { 651 String next = (String) propNames.nextElement(); 652 if (next.startsWith("resource.")) { 653 retVal.add(next.substring("resource.".length()).trim()); 654 } 655 } 656 retVal.addAll(myCustomResourceNames); 657 return retVal; 658 } 659 660 /** 661 * Get the restful client factory. If no factory has been set, this will be initialized with 662 * a new ApacheRestfulClientFactory. 663 * 664 * @return the factory used to create the restful clients 665 */ 666 public IRestfulClientFactory getRestfulClientFactory() { 667 if (myRestfulClientFactory == null) { 668 try { 669 myRestfulClientFactory = (IRestfulClientFactory) ReflectionUtil.newInstance( 670 Class.forName("ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory"), 671 FhirContext.class, 672 this); 673 } catch (ClassNotFoundException e) { 674 throw new ConfigurationException( 675 Msg.code(1686) + "hapi-fhir-client does not appear to be on the classpath"); 676 } 677 } 678 return myRestfulClientFactory; 679 } 680 681 /** 682 * Set the restful client factory 683 * 684 * @param theRestfulClientFactory The new client factory (must not be null) 685 */ 686 public void setRestfulClientFactory(final IRestfulClientFactory theRestfulClientFactory) { 687 Validate.notNull(theRestfulClientFactory, "theRestfulClientFactory must not be null"); 688 this.myRestfulClientFactory = theRestfulClientFactory; 689 } 690 691 public RuntimeChildUndeclaredExtensionDefinition getRuntimeChildUndeclaredExtensionDefinition() { 692 validateInitialized(); 693 return myRuntimeChildUndeclaredExtensionDefinition; 694 } 695 696 /** 697 * Returns the validation support module configured for this context, creating a default 698 * implementation if no module has been passed in via the {@link #setValidationSupport(IValidationSupport)} 699 * method 700 * 701 * @see #setValidationSupport(IValidationSupport) 702 */ 703 public IValidationSupport getValidationSupport() { 704 IValidationSupport retVal = myValidationSupport; 705 if (retVal == null) { 706 retVal = new DefaultProfileValidationSupport(this); 707 708 /* 709 * If hapi-fhir-validation is on the classpath, we can create a much more robust 710 * validation chain using the classes found in that package 711 */ 712 String inMemoryTermSvcType = 713 "org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport"; 714 String commonCodeSystemsSupportType = 715 "org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService"; 716 if (ReflectionUtil.typeExists(inMemoryTermSvcType)) { 717 IValidationSupport inMemoryTermSvc = ReflectionUtil.newInstanceOrReturnNull( 718 inMemoryTermSvcType, 719 IValidationSupport.class, 720 new Class<?>[] {FhirContext.class}, 721 new Object[] {this}); 722 IValidationSupport commonCodeSystemsSupport = ReflectionUtil.newInstanceOrReturnNull( 723 commonCodeSystemsSupportType, 724 IValidationSupport.class, 725 new Class<?>[] {FhirContext.class}, 726 new Object[] {this}); 727 retVal = ReflectionUtil.newInstanceOrReturnNull( 728 "org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain", 729 IValidationSupport.class, 730 new Class<?>[] {IValidationSupport[].class}, 731 new Object[] {new IValidationSupport[] {retVal, inMemoryTermSvc, commonCodeSystemsSupport}}); 732 assert retVal != null 733 : "Failed to instantiate " 734 + "org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain"; 735 } 736 737 myValidationSupport = retVal; 738 } 739 return retVal; 740 } 741 742 /** 743 * Sets the validation support module to use for this context. The validation support module 744 * is used to supply underlying infrastructure such as conformance resources (StructureDefinition, ValueSet, etc) 745 * as well as to provide terminology services to modules such as the validator and FluentPath executor 746 */ 747 public void setValidationSupport(IValidationSupport theValidationSupport) { 748 myValidationSupport = theValidationSupport; 749 } 750 751 public IFhirVersion getVersion() { 752 return myVersion; 753 } 754 755 /** 756 * Returns <code>true</code> if any default types for specific profiles have been defined 757 * within this context. 758 * 759 * @see #setDefaultTypeForProfile(String, Class) 760 * @see #getDefaultTypeForProfile(String) 761 */ 762 public boolean hasDefaultTypeForProfile() { 763 validateInitialized(); 764 return !myDefaultTypeForProfile.isEmpty(); 765 } 766 767 /** 768 * @return Returns <code>true</code> if the XML serialization format is supported, based on the 769 * available libraries on the classpath. 770 * 771 * @since 5.4.0 772 */ 773 public boolean isFormatXmlSupported() { 774 Boolean retVal = myFormatXmlSupported; 775 if (retVal == null) { 776 retVal = tryToInitParser(() -> newXmlParser()); 777 myFormatXmlSupported = retVal; 778 } 779 return retVal; 780 } 781 782 /** 783 * @return Returns <code>true</code> if the JSON serialization format is supported, based on the 784 * available libraries on the classpath. 785 * 786 * @since 5.4.0 787 */ 788 public boolean isFormatJsonSupported() { 789 Boolean retVal = myFormatJsonSupported; 790 if (retVal == null) { 791 retVal = tryToInitParser(() -> newJsonParser()); 792 myFormatJsonSupported = retVal; 793 } 794 return retVal; 795 } 796 797 /** 798 * @return Returns <code>true</code> if the NDJSON serialization format is supported, based on the 799 * available libraries on the classpath. 800 * 801 * @since 5.6.0 802 */ 803 public boolean isFormatNDJsonSupported() { 804 Boolean retVal = myFormatNDJsonSupported; 805 if (retVal == null) { 806 retVal = tryToInitParser(() -> newNDJsonParser()); 807 myFormatNDJsonSupported = retVal; 808 } 809 return retVal; 810 } 811 812 /** 813 * @return Returns <code>true</code> if the RDF serialization format is supported, based on the 814 * available libraries on the classpath. 815 * 816 * @since 5.4.0 817 */ 818 public boolean isFormatRdfSupported() { 819 Boolean retVal = myFormatRdfSupported; 820 if (retVal == null) { 821 retVal = tryToInitParser(() -> newRDFParser()); 822 myFormatRdfSupported = retVal; 823 } 824 return retVal; 825 } 826 827 public IVersionSpecificBundleFactory newBundleFactory() { 828 return myVersion.newBundleFactory(this); 829 } 830 831 /** 832 * @since 2.2 833 * @deprecated Deprecated in HAPI FHIR 5.0.0. Use {@link #newFhirPath()} instead. 834 */ 835 @Deprecated 836 public IFhirPath newFluentPath() { 837 return newFhirPath(); 838 } 839 840 /** 841 * Creates a new FhirPath engine which can be used to evaluate 842 * path expressions over FHIR resources. Note that this engine will use the 843 * {@link IValidationSupport context validation support} module which is 844 * configured on the context at the time this method is called. 845 * <p> 846 * In other words, you may wish to call {@link #setValidationSupport(IValidationSupport)} before 847 * calling {@link #newFluentPath()} 848 * </p> 849 * <p> 850 * Note that this feature was added for FHIR DSTU3 and is not available 851 * for contexts configured to use an older version of FHIR. Calling this method 852 * on a context for a previous version of fhir will result in an 853 * {@link UnsupportedOperationException} 854 * </p> 855 * 856 * @since 5.0.0 857 */ 858 public IFhirPath newFhirPath() { 859 return myVersion.createFhirPathExecutor(this); 860 } 861 862 /** 863 * Create and return a new JSON parser. 864 * 865 * <p> 866 * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread 867 * or every message being parsed/encoded. 868 * </p> 869 * <p> 870 * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed 871 * without incurring any performance penalty 872 * </p> 873 */ 874 public IParser newJsonParser() { 875 return new JsonParser(this, myParserErrorHandler); 876 } 877 878 /** 879 * Create and return a new NDJSON parser. 880 * 881 * <p> 882 * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread 883 * or every message being parsed/encoded. 884 * </p> 885 * <p> 886 * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed 887 * without incurring any performance penalty 888 * </p> 889 * <p> 890 * The NDJsonParser provided here is expected to translate between legal NDJson and FHIR Bundles. 891 * In particular, it is able to encode the resources in a FHIR Bundle to NDJson, as well as decode 892 * NDJson into a FHIR "collection"-type Bundle populated with the resources described in the NDJson. 893 * It will throw an exception in the event where it is asked to encode to anything other than a FHIR Bundle 894 * or where it is asked to decode into anything other than a FHIR Bundle. 895 * </p> 896 */ 897 public IParser newNDJsonParser() { 898 return new NDJsonParser(this, myParserErrorHandler); 899 } 900 901 /** 902 * Create and return a new RDF parser. 903 * 904 * <p> 905 * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread 906 * or every message being parsed/encoded. 907 * </p> 908 * <p> 909 * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed 910 * without incurring any performance penalty 911 * </p> 912 */ 913 public IParser newRDFParser() { 914 return new RDFParser(this, myParserErrorHandler, Lang.TURTLE); 915 } 916 917 /** 918 * Instantiates a new client instance. This method requires an interface which is defined specifically for your use 919 * cases to contain methods for each of the RESTful operations you wish to implement (e.g. "read ImagingStudy", 920 * "search Patient by identifier", etc.). This interface must extend {@link IRestfulClient} (or commonly its 921 * sub-interface {@link IBasicClient}). See the <a 922 * href="https://hapifhir.io/hapi-fhir/docs/client/introduction.html">RESTful Client</a> documentation for more 923 * information on how to define this interface. 924 * 925 * <p> 926 * Performance Note: <b>This method is cheap</b> to call, and may be called once for every operation invocation 927 * without incurring any performance penalty 928 * </p> 929 * 930 * @param theClientType The client type, which is an interface type to be instantiated 931 * @param theServerBase The URL of the base for the restful FHIR server to connect to 932 * @return A newly created client 933 * @throws ConfigurationException If the interface type is not an interface 934 */ 935 public <T extends IRestfulClient> T newRestfulClient(final Class<T> theClientType, final String theServerBase) { 936 return getRestfulClientFactory().newClient(theClientType, theServerBase); 937 } 938 939 /** 940 * Instantiates a new generic client. A generic client is able to perform any of the FHIR RESTful operations against 941 * a compliant server, but does not have methods defining the specific functionality required (as is the case with 942 * {@link #newRestfulClient(Class, String) non-generic clients}). 943 * 944 * <p> 945 * Performance Note: This method performs an additional GET request to /metadata before 946 * the desired request is performed. 947 * </p> 948 * 949 * @param theServerBase The URL of the base for the restful FHIR server to connect to 950 */ 951 public IGenericClient newRestfulGenericClient(final String theServerBase) { 952 return getRestfulClientFactory().newGenericClient(theServerBase); 953 } 954 955 public FhirTerser newTerser() { 956 return new FhirTerser(this); 957 } 958 959 /** 960 * Create a new validator instance. 961 * <p> 962 * Note on thread safety: Validators are thread safe, you may use a single validator 963 * in multiple threads. (This is in contrast to parsers) 964 * </p> 965 */ 966 public FhirValidator newValidator() { 967 return myFhirValidatorFactory.newFhirValidator(this); 968 } 969 970 public ViewGenerator newViewGenerator() { 971 return new ViewGenerator(this); 972 } 973 974 /** 975 * Create and return a new XML parser. 976 * 977 * <p> 978 * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread 979 * or every message being parsed/encoded. 980 * </p> 981 * <p> 982 * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed 983 * without incurring any performance penalty 984 * </p> 985 */ 986 public IParser newXmlParser() { 987 return new XmlParser(this, myParserErrorHandler); 988 } 989 990 /** 991 * This method may be used to register a custom resource or datatype. Note that by using 992 * custom types, you are creating a system that will not interoperate with other systems that 993 * do not know about your custom type. There are valid reasons however for wanting to create 994 * custom types and this method can be used to enable them. 995 * <p> 996 * <b>THREAD SAFETY WARNING:</b> This method is not thread safe. It should be called before any 997 * threads are able to call any methods on this context. 998 * </p> 999 * 1000 * @param theType The custom type to add (must not be <code>null</code>) 1001 */ 1002 public void registerCustomType(final Class<? extends IBase> theType) { 1003 Validate.notNull(theType, "theType must not be null"); 1004 ensureCustomTypeList(); 1005 myCustomTypes.add(theType); 1006 myResourceNames = null; 1007 } 1008 1009 /** 1010 * This method may be used to register a custom resource or datatype. Note that by using 1011 * custom types, you are creating a system that will not interoperate with other systems that 1012 * do not know about your custom type. There are valid reasons however for wanting to create 1013 * custom types and this method can be used to enable them. 1014 * <p> 1015 * <b>THREAD SAFETY WARNING:</b> This method is not thread safe. It should be called before any 1016 * threads are able to call any methods on this context. 1017 * </p> 1018 * 1019 * @param theTypes The custom types to add (must not be <code>null</code> or contain null elements in the collection) 1020 */ 1021 public void registerCustomTypes(final Collection<Class<? extends IBase>> theTypes) { 1022 Validate.notNull(theTypes, "theTypes must not be null"); 1023 Validate.noNullElements(theTypes.toArray(), "theTypes must not contain any null elements"); 1024 1025 ensureCustomTypeList(); 1026 1027 myCustomTypes.addAll(theTypes); 1028 myResourceNames = null; 1029 } 1030 1031 private BaseRuntimeElementDefinition<?> scanDatatype(final Class<? extends IElement> theResourceType) { 1032 ArrayList<Class<? extends IElement>> resourceTypes = new ArrayList<>(); 1033 resourceTypes.add(theResourceType); 1034 Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> defs = scanResourceTypes(resourceTypes); 1035 return defs.get(theResourceType); 1036 } 1037 1038 private RuntimeResourceDefinition scanResourceType(final Class<? extends IBaseResource> theResourceType) { 1039 ArrayList<Class<? extends IElement>> resourceTypes = new ArrayList<>(); 1040 resourceTypes.add(theResourceType); 1041 Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> defs = scanResourceTypes(resourceTypes); 1042 return (RuntimeResourceDefinition) defs.get(theResourceType); 1043 } 1044 1045 private synchronized Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> scanResourceTypes( 1046 final Collection<Class<? extends IElement>> theResourceTypes) { 1047 List<Class<? extends IBase>> typesToScan = new ArrayList<>(); 1048 if (theResourceTypes != null) { 1049 typesToScan.addAll(theResourceTypes); 1050 } 1051 if (myCustomTypes != null) { 1052 typesToScan.addAll(myCustomTypes); 1053 1054 myCustomResourceNames.addAll(myCustomTypes.stream() 1055 .map(customType -> Optional.ofNullable(customType.getAnnotation(ResourceDef.class)) 1056 .map(ResourceDef::name) 1057 .orElse(null)) // This will be caught by the call to ModelScanner 1058 .filter(Objects::nonNull) 1059 .collect(Collectors.toSet())); 1060 1061 myCustomTypes = null; 1062 } 1063 1064 ModelScanner scanner = new ModelScanner(this, myVersion.getVersion(), myClassToElementDefinition, typesToScan); 1065 if (myRuntimeChildUndeclaredExtensionDefinition == null) { 1066 myRuntimeChildUndeclaredExtensionDefinition = scanner.getRuntimeChildUndeclaredExtensionDefinition(); 1067 } 1068 1069 Map<String, BaseRuntimeElementDefinition<?>> nameToElementDefinition = new HashMap<>(); 1070 nameToElementDefinition.putAll(myNameToElementDefinition); 1071 for (Entry<String, BaseRuntimeElementDefinition<?>> next : 1072 scanner.getNameToElementDefinitions().entrySet()) { 1073 if (!nameToElementDefinition.containsKey(next.getKey())) { 1074 nameToElementDefinition.put(next.getKey().toLowerCase(), next.getValue()); 1075 } 1076 } 1077 1078 Map<String, RuntimeResourceDefinition> nameToResourceDefinition = new HashMap<>(); 1079 nameToResourceDefinition.putAll(myNameToResourceDefinition); 1080 for (Entry<String, RuntimeResourceDefinition> next : 1081 scanner.getNameToResourceDefinition().entrySet()) { 1082 if (!nameToResourceDefinition.containsKey(next.getKey())) { 1083 nameToResourceDefinition.put(next.getKey(), next.getValue()); 1084 } 1085 } 1086 1087 Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> classToElementDefinition = new HashMap<>(); 1088 classToElementDefinition.putAll(myClassToElementDefinition); 1089 classToElementDefinition.putAll(scanner.getClassToElementDefinitions()); 1090 for (BaseRuntimeElementDefinition<?> next : classToElementDefinition.values()) { 1091 if (next instanceof RuntimeResourceDefinition) { 1092 if ("Bundle".equals(next.getName())) { 1093 if (!IBaseBundle.class.isAssignableFrom(next.getImplementingClass())) { 1094 throw new ConfigurationException(Msg.code(1687) 1095 + "Resource type declares resource name Bundle but does not implement IBaseBundle"); 1096 } 1097 } 1098 } 1099 } 1100 1101 Map<String, RuntimeResourceDefinition> idToElementDefinition = new HashMap<>(); 1102 idToElementDefinition.putAll(myIdToResourceDefinition); 1103 idToElementDefinition.putAll(scanner.getIdToResourceDefinition()); 1104 1105 myNameToElementDefinition = nameToElementDefinition; 1106 myClassToElementDefinition = classToElementDefinition; 1107 myIdToResourceDefinition = idToElementDefinition; 1108 myNameToResourceDefinition = nameToResourceDefinition; 1109 1110 myNameToResourceType = scanner.getNameToResourceType(); 1111 1112 myInitialized = true; 1113 return classToElementDefinition; 1114 } 1115 1116 /** 1117 * Sets the default type which will be used when parsing a resource that is found to be 1118 * of the given profile. 1119 * <p> 1120 * For example, this method is invoked with the profile string of 1121 * <code>"http://example.com/some_patient_profile"</code> and the type of <code>MyPatient.class</code>, 1122 * if the parser is parsing a resource and finds that it declares that it conforms to that profile, 1123 * the <code>MyPatient</code> type will be used unless otherwise specified. 1124 * </p> 1125 * 1126 * @param theProfile The profile string, e.g. <code>"http://example.com/some_patient_profile"</code>. Must not be 1127 * <code>null</code> or empty. 1128 * @param theClass The resource type, or <code>null</code> to clear any existing type 1129 */ 1130 public void setDefaultTypeForProfile(final String theProfile, final Class<? extends IBaseResource> theClass) { 1131 Validate.notBlank(theProfile, "theProfile must not be null or empty"); 1132 if (theClass == null) { 1133 myDefaultTypeForProfile.remove(theProfile); 1134 } else { 1135 myDefaultTypeForProfile.put(theProfile, theClass); 1136 } 1137 } 1138 1139 /** 1140 * Sets a parser error handler to use by default on all parsers 1141 * 1142 * @param theParserErrorHandler The error handler 1143 */ 1144 public FhirContext setParserErrorHandler(final IParserErrorHandler theParserErrorHandler) { 1145 Validate.notNull(theParserErrorHandler, "theParserErrorHandler must not be null"); 1146 myParserErrorHandler = theParserErrorHandler; 1147 return this; 1148 } 1149 1150 /** 1151 * Set the factory method used to create FhirValidator instances 1152 * 1153 * @param theFhirValidatorFactory 1154 * @return this 1155 * @since 5.6.0 1156 */ 1157 public FhirContext setFhirValidatorFactory(IFhirValidatorFactory theFhirValidatorFactory) { 1158 myFhirValidatorFactory = theFhirValidatorFactory; 1159 return this; 1160 } 1161 1162 @SuppressWarnings({"cast"}) 1163 private List<Class<? extends IElement>> toElementList( 1164 final Collection<Class<? extends IBaseResource>> theResourceTypes) { 1165 if (theResourceTypes == null) { 1166 return null; 1167 } 1168 List<Class<? extends IElement>> resTypes = new ArrayList<>(); 1169 for (Class<? extends IBaseResource> next : theResourceTypes) { 1170 resTypes.add(next); 1171 } 1172 return resTypes; 1173 } 1174 1175 private void validateInitialized() { 1176 // See #610 1177 if (!myInitialized) { 1178 synchronized (this) { 1179 if (!myInitialized && !myInitializing) { 1180 myInitializing = true; 1181 try { 1182 scanResourceTypes(toElementList(myResourceTypesToScan)); 1183 } catch (Exception e) { 1184 ourLog.error("Failed to initialize FhirContext", e); 1185 throw e; 1186 } finally { 1187 myInitializing = false; 1188 } 1189 } 1190 } 1191 } 1192 } 1193 1194 @Override 1195 public String toString() { 1196 return "FhirContext[" + myVersion.getVersion().name() + "]"; 1197 } 1198 1199 // TODO KHS add the other primitive types 1200 @Deprecated(since = "6.6.0", forRemoval = true) 1201 public IPrimitiveType<Boolean> getPrimitiveBoolean(Boolean theValue) { 1202 return newPrimitiveBoolean(theValue); 1203 } 1204 1205 public IPrimitiveType<Boolean> newPrimitiveBoolean(Boolean theValue) { 1206 IPrimitiveType<Boolean> retval = 1207 (IPrimitiveType<Boolean>) getElementDefinition("boolean").newInstance(); 1208 retval.setValue(theValue); 1209 return retval; 1210 } 1211 1212 public IPrimitiveType<String> newPrimitiveString(String theValue) { 1213 IPrimitiveType<String> retval = 1214 (IPrimitiveType<String>) getElementDefinition("string").newInstance(); 1215 retval.setValue(theValue); 1216 return retval; 1217 } 1218 1219 private static boolean tryToInitParser(Runnable run) { 1220 boolean retVal; 1221 try { 1222 run.run(); 1223 retVal = true; 1224 } catch (UnsupportedClassVersionError | Exception | NoClassDefFoundError e) { 1225 retVal = false; 1226 } 1227 return retVal; 1228 } 1229 1230 /** 1231 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU2} 1232 */ 1233 public static FhirContext forDstu2() { 1234 return new FhirContext(FhirVersionEnum.DSTU2); 1235 } 1236 1237 /** 1238 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2_HL7ORG DSTU2} (using the Reference 1239 * Implementation Structures) 1240 */ 1241 public static FhirContext forDstu2Hl7Org() { 1242 return new FhirContext(FhirVersionEnum.DSTU2_HL7ORG); 1243 } 1244 1245 /** 1246 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU2} (2016 May DSTU3 Snapshot) 1247 */ 1248 public static FhirContext forDstu2_1() { 1249 return new FhirContext(FhirVersionEnum.DSTU2_1); 1250 } 1251 1252 /** 1253 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU3 DSTU3} 1254 * 1255 * @since 1.4 1256 */ 1257 public static FhirContext forDstu3() { 1258 return new FhirContext(FhirVersionEnum.DSTU3); 1259 } 1260 1261 /** 1262 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#R4 R4} 1263 * 1264 * @since 3.0.0 1265 */ 1266 public static FhirContext forR4() { 1267 return new FhirContext(FhirVersionEnum.R4); 1268 } 1269 1270 /** 1271 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#R4B R4B} 1272 * 1273 * @since 6.2.0 1274 */ 1275 public static FhirContext forR4B() { 1276 return new FhirContext(FhirVersionEnum.R4B); 1277 } 1278 1279 /** 1280 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#R5 R5} 1281 * 1282 * @since 4.0.0 1283 */ 1284 public static FhirContext forR5() { 1285 return new FhirContext(FhirVersionEnum.R5); 1286 } 1287 1288 /** 1289 * Returns a statically cached {@literal FhirContext} instance for the given version, creating one if none exists in the 1290 * cache. One FhirContext will be kept in the cache for each FHIR version that is requested (by calling 1291 * this method for that version), and the cache will never be expired. 1292 * 1293 * @since 5.1.0 1294 */ 1295 public static FhirContext forCached(FhirVersionEnum theFhirVersionEnum) { 1296 return ourStaticContexts.computeIfAbsent(theFhirVersionEnum, v -> new FhirContext(v)); 1297 } 1298 1299 private static Collection<Class<? extends IBaseResource>> toCollection( 1300 Class<? extends IBaseResource> theResourceType) { 1301 ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<>(1); 1302 retVal.add(theResourceType); 1303 return retVal; 1304 } 1305 1306 @SuppressWarnings("unchecked") 1307 private static List<Class<? extends IBaseResource>> toCollection(final Class<?>[] theResourceTypes) { 1308 ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<Class<? extends IBaseResource>>(1); 1309 for (Class<?> clazz : theResourceTypes) { 1310 if (!IResource.class.isAssignableFrom(clazz)) { 1311 throw new IllegalArgumentException(Msg.code(1688) + clazz.getCanonicalName() + " is not an instance of " 1312 + IResource.class.getSimpleName()); 1313 } 1314 retVal.add((Class<? extends IResource>) clazz); 1315 } 1316 return retVal; 1317 } 1318}