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