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 String snapshotGeneratingType = 717 "org.hl7.fhir.common.hapi.validation.support.SnapshotGeneratingValidationSupport"; 718 if (ReflectionUtil.typeExists(inMemoryTermSvcType)) { 719 IValidationSupport inMemoryTermSvc = ReflectionUtil.newInstanceOrReturnNull( 720 inMemoryTermSvcType, 721 IValidationSupport.class, 722 new Class<?>[] {FhirContext.class}, 723 new Object[] {this}); 724 IValidationSupport commonCodeSystemsSupport = ReflectionUtil.newInstanceOrReturnNull( 725 commonCodeSystemsSupportType, 726 IValidationSupport.class, 727 new Class<?>[] {FhirContext.class}, 728 new Object[] {this}); 729 IValidationSupport snapshotGeneratingSupport = null; 730 if (getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) { 731 snapshotGeneratingSupport = ReflectionUtil.newInstanceOrReturnNull( 732 snapshotGeneratingType, 733 IValidationSupport.class, 734 new Class<?>[] {FhirContext.class}, 735 new Object[] {this}); 736 } 737 retVal = ReflectionUtil.newInstanceOrReturnNull( 738 "org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain", 739 IValidationSupport.class, 740 new Class<?>[] {IValidationSupport[].class}, 741 new Object[] { 742 new IValidationSupport[] { 743 retVal, inMemoryTermSvc, commonCodeSystemsSupport, snapshotGeneratingSupport 744 } 745 }); 746 assert retVal != null 747 : "Failed to instantiate " 748 + "org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain"; 749 } 750 751 myValidationSupport = retVal; 752 } 753 return retVal; 754 } 755 756 /** 757 * Sets the validation support module to use for this context. The validation support module 758 * is used to supply underlying infrastructure such as conformance resources (StructureDefinition, ValueSet, etc) 759 * as well as to provide terminology services to modules such as the validator and FluentPath executor 760 */ 761 public void setValidationSupport(IValidationSupport theValidationSupport) { 762 myValidationSupport = theValidationSupport; 763 } 764 765 public IFhirVersion getVersion() { 766 return myVersion; 767 } 768 769 /** 770 * Returns <code>true</code> if any default types for specific profiles have been defined 771 * within this context. 772 * 773 * @see #setDefaultTypeForProfile(String, Class) 774 * @see #getDefaultTypeForProfile(String) 775 */ 776 public boolean hasDefaultTypeForProfile() { 777 validateInitialized(); 778 return !myDefaultTypeForProfile.isEmpty(); 779 } 780 781 /** 782 * @return Returns <code>true</code> if the XML serialization format is supported, based on the 783 * available libraries on the classpath. 784 * 785 * @since 5.4.0 786 */ 787 public boolean isFormatXmlSupported() { 788 Boolean retVal = myFormatXmlSupported; 789 if (retVal == null) { 790 retVal = tryToInitParser(() -> newXmlParser()); 791 myFormatXmlSupported = retVal; 792 } 793 return retVal; 794 } 795 796 /** 797 * @return Returns <code>true</code> if the JSON serialization format is supported, based on the 798 * available libraries on the classpath. 799 * 800 * @since 5.4.0 801 */ 802 public boolean isFormatJsonSupported() { 803 Boolean retVal = myFormatJsonSupported; 804 if (retVal == null) { 805 retVal = tryToInitParser(() -> newJsonParser()); 806 myFormatJsonSupported = retVal; 807 } 808 return retVal; 809 } 810 811 /** 812 * @return Returns <code>true</code> if the NDJSON serialization format is supported, based on the 813 * available libraries on the classpath. 814 * 815 * @since 5.6.0 816 */ 817 public boolean isFormatNDJsonSupported() { 818 Boolean retVal = myFormatNDJsonSupported; 819 if (retVal == null) { 820 retVal = tryToInitParser(() -> newNDJsonParser()); 821 myFormatNDJsonSupported = retVal; 822 } 823 return retVal; 824 } 825 826 /** 827 * @return Returns <code>true</code> if the RDF serialization format is supported, based on the 828 * available libraries on the classpath. 829 * 830 * @since 5.4.0 831 */ 832 public boolean isFormatRdfSupported() { 833 Boolean retVal = myFormatRdfSupported; 834 if (retVal == null) { 835 retVal = tryToInitParser(() -> newRDFParser()); 836 myFormatRdfSupported = retVal; 837 } 838 return retVal; 839 } 840 841 public IVersionSpecificBundleFactory newBundleFactory() { 842 return myVersion.newBundleFactory(this); 843 } 844 845 /** 846 * @since 2.2 847 * @deprecated Deprecated in HAPI FHIR 5.0.0. Use {@link #newFhirPath()} instead. 848 */ 849 @Deprecated 850 public IFhirPath newFluentPath() { 851 return newFhirPath(); 852 } 853 854 /** 855 * Creates a new FhirPath engine which can be used to evaluate 856 * path expressions over FHIR resources. Note that this engine will use the 857 * {@link IValidationSupport context validation support} module which is 858 * configured on the context at the time this method is called. 859 * <p> 860 * In other words, you may wish to call {@link #setValidationSupport(IValidationSupport)} before 861 * calling {@link #newFluentPath()} 862 * </p> 863 * <p> 864 * Note that this feature was added for FHIR DSTU3 and is not available 865 * for contexts configured to use an older version of FHIR. Calling this method 866 * on a context for a previous version of fhir will result in an 867 * {@link UnsupportedOperationException} 868 * </p> 869 * 870 * @since 5.0.0 871 */ 872 public IFhirPath newFhirPath() { 873 return myVersion.createFhirPathExecutor(this); 874 } 875 876 /** 877 * Create and return a new JSON parser. 878 * 879 * <p> 880 * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread 881 * or every message being parsed/encoded. 882 * </p> 883 * <p> 884 * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed 885 * without incurring any performance penalty 886 * </p> 887 */ 888 public IParser newJsonParser() { 889 return new JsonParser(this, myParserErrorHandler); 890 } 891 892 /** 893 * Create and return a new NDJSON parser. 894 * 895 * <p> 896 * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread 897 * or every message being parsed/encoded. 898 * </p> 899 * <p> 900 * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed 901 * without incurring any performance penalty 902 * </p> 903 * <p> 904 * The NDJsonParser provided here is expected to translate between legal NDJson and FHIR Bundles. 905 * In particular, it is able to encode the resources in a FHIR Bundle to NDJson, as well as decode 906 * NDJson into a FHIR "collection"-type Bundle populated with the resources described in the NDJson. 907 * It will throw an exception in the event where it is asked to encode to anything other than a FHIR Bundle 908 * or where it is asked to decode into anything other than a FHIR Bundle. 909 * </p> 910 */ 911 public IParser newNDJsonParser() { 912 return new NDJsonParser(this, myParserErrorHandler); 913 } 914 915 /** 916 * Create and return a new RDF parser. 917 * 918 * <p> 919 * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread 920 * or every message being parsed/encoded. 921 * </p> 922 * <p> 923 * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed 924 * without incurring any performance penalty 925 * </p> 926 */ 927 public IParser newRDFParser() { 928 return new RDFParser(this, myParserErrorHandler, Lang.TURTLE); 929 } 930 931 /** 932 * Instantiates a new client instance. This method requires an interface which is defined specifically for your use 933 * cases to contain methods for each of the RESTful operations you wish to implement (e.g. "read ImagingStudy", 934 * "search Patient by identifier", etc.). This interface must extend {@link IRestfulClient} (or commonly its 935 * sub-interface {@link IBasicClient}). See the <a 936 * href="https://hapifhir.io/hapi-fhir/docs/client/introduction.html">RESTful Client</a> documentation for more 937 * information on how to define this interface. 938 * 939 * <p> 940 * Performance Note: <b>This method is cheap</b> to call, and may be called once for every operation invocation 941 * without incurring any performance penalty 942 * </p> 943 * 944 * @param theClientType The client type, which is an interface type to be instantiated 945 * @param theServerBase The URL of the base for the restful FHIR server to connect to 946 * @return A newly created client 947 * @throws ConfigurationException If the interface type is not an interface 948 */ 949 public <T extends IRestfulClient> T newRestfulClient(final Class<T> theClientType, final String theServerBase) { 950 return getRestfulClientFactory().newClient(theClientType, theServerBase); 951 } 952 953 /** 954 * Instantiates a new generic client. A generic client is able to perform any of the FHIR RESTful operations against 955 * a compliant server, but does not have methods defining the specific functionality required (as is the case with 956 * {@link #newRestfulClient(Class, String) non-generic clients}). 957 * 958 * <p> 959 * Performance Note: This method performs an additional GET request to /metadata before 960 * the desired request is performed. 961 * </p> 962 * 963 * @param theServerBase The URL of the base for the restful FHIR server to connect to 964 */ 965 public IGenericClient newRestfulGenericClient(final String theServerBase) { 966 return getRestfulClientFactory().newGenericClient(theServerBase); 967 } 968 969 public FhirTerser newTerser() { 970 return new FhirTerser(this); 971 } 972 973 /** 974 * Create a new validator instance. 975 * <p> 976 * Note on thread safety: Validators are thread safe, you may use a single validator 977 * in multiple threads. (This is in contrast to parsers) 978 * </p> 979 */ 980 public FhirValidator newValidator() { 981 return myFhirValidatorFactory.newFhirValidator(this); 982 } 983 984 public ViewGenerator newViewGenerator() { 985 return new ViewGenerator(this); 986 } 987 988 /** 989 * Create and return a new XML parser. 990 * 991 * <p> 992 * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread 993 * or every message being parsed/encoded. 994 * </p> 995 * <p> 996 * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed 997 * without incurring any performance penalty 998 * </p> 999 */ 1000 public IParser newXmlParser() { 1001 return new XmlParser(this, myParserErrorHandler); 1002 } 1003 1004 /** 1005 * This method may be used to register a custom resource or datatype. Note that by using 1006 * custom types, you are creating a system that will not interoperate with other systems that 1007 * do not know about your custom type. There are valid reasons however for wanting to create 1008 * custom types and this method can be used to enable them. 1009 * <p> 1010 * <b>THREAD SAFETY WARNING:</b> This method is not thread safe. It should be called before any 1011 * threads are able to call any methods on this context. 1012 * </p> 1013 * 1014 * @param theType The custom type to add (must not be <code>null</code>) 1015 */ 1016 public void registerCustomType(final Class<? extends IBase> theType) { 1017 Validate.notNull(theType, "theType must not be null"); 1018 ensureCustomTypeList(); 1019 myCustomTypes.add(theType); 1020 myResourceNames = null; 1021 } 1022 1023 /** 1024 * This method may be used to register a custom resource or datatype. Note that by using 1025 * custom types, you are creating a system that will not interoperate with other systems that 1026 * do not know about your custom type. There are valid reasons however for wanting to create 1027 * custom types and this method can be used to enable them. 1028 * <p> 1029 * <b>THREAD SAFETY WARNING:</b> This method is not thread safe. It should be called before any 1030 * threads are able to call any methods on this context. 1031 * </p> 1032 * 1033 * @param theTypes The custom types to add (must not be <code>null</code> or contain null elements in the collection) 1034 */ 1035 public void registerCustomTypes(final Collection<Class<? extends IBase>> theTypes) { 1036 Validate.notNull(theTypes, "theTypes must not be null"); 1037 Validate.noNullElements(theTypes.toArray(), "theTypes must not contain any null elements"); 1038 1039 ensureCustomTypeList(); 1040 1041 myCustomTypes.addAll(theTypes); 1042 myResourceNames = null; 1043 } 1044 1045 private BaseRuntimeElementDefinition<?> scanDatatype(final Class<? extends IElement> theResourceType) { 1046 ArrayList<Class<? extends IElement>> resourceTypes = new ArrayList<>(); 1047 resourceTypes.add(theResourceType); 1048 Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> defs = scanResourceTypes(resourceTypes); 1049 return defs.get(theResourceType); 1050 } 1051 1052 private RuntimeResourceDefinition scanResourceType(final Class<? extends IBaseResource> theResourceType) { 1053 ArrayList<Class<? extends IElement>> resourceTypes = new ArrayList<>(); 1054 resourceTypes.add(theResourceType); 1055 Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> defs = scanResourceTypes(resourceTypes); 1056 return (RuntimeResourceDefinition) defs.get(theResourceType); 1057 } 1058 1059 private synchronized Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> scanResourceTypes( 1060 final Collection<Class<? extends IElement>> theResourceTypes) { 1061 List<Class<? extends IBase>> typesToScan = new ArrayList<>(); 1062 if (theResourceTypes != null) { 1063 typesToScan.addAll(theResourceTypes); 1064 } 1065 if (myCustomTypes != null) { 1066 typesToScan.addAll(myCustomTypes); 1067 1068 myCustomResourceNames.addAll(myCustomTypes.stream() 1069 .map(customType -> Optional.ofNullable(customType.getAnnotation(ResourceDef.class)) 1070 .map(ResourceDef::name) 1071 .orElse(null)) // This will be caught by the call to ModelScanner 1072 .filter(Objects::nonNull) 1073 .collect(Collectors.toSet())); 1074 1075 myCustomTypes = null; 1076 } 1077 1078 ModelScanner scanner = new ModelScanner(this, myVersion.getVersion(), myClassToElementDefinition, typesToScan); 1079 if (myRuntimeChildUndeclaredExtensionDefinition == null) { 1080 myRuntimeChildUndeclaredExtensionDefinition = scanner.getRuntimeChildUndeclaredExtensionDefinition(); 1081 } 1082 1083 Map<String, BaseRuntimeElementDefinition<?>> nameToElementDefinition = new HashMap<>(); 1084 nameToElementDefinition.putAll(myNameToElementDefinition); 1085 for (Entry<String, BaseRuntimeElementDefinition<?>> next : 1086 scanner.getNameToElementDefinitions().entrySet()) { 1087 if (!nameToElementDefinition.containsKey(next.getKey())) { 1088 nameToElementDefinition.put(next.getKey().toLowerCase(), next.getValue()); 1089 } 1090 } 1091 1092 Map<String, RuntimeResourceDefinition> nameToResourceDefinition = new HashMap<>(); 1093 nameToResourceDefinition.putAll(myNameToResourceDefinition); 1094 for (Entry<String, RuntimeResourceDefinition> next : 1095 scanner.getNameToResourceDefinition().entrySet()) { 1096 if (!nameToResourceDefinition.containsKey(next.getKey())) { 1097 nameToResourceDefinition.put(next.getKey(), next.getValue()); 1098 } 1099 } 1100 1101 Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> classToElementDefinition = new HashMap<>(); 1102 classToElementDefinition.putAll(myClassToElementDefinition); 1103 classToElementDefinition.putAll(scanner.getClassToElementDefinitions()); 1104 for (BaseRuntimeElementDefinition<?> next : classToElementDefinition.values()) { 1105 if (next instanceof RuntimeResourceDefinition) { 1106 if ("Bundle".equals(next.getName())) { 1107 if (!IBaseBundle.class.isAssignableFrom(next.getImplementingClass())) { 1108 throw new ConfigurationException(Msg.code(1687) 1109 + "Resource type declares resource name Bundle but does not implement IBaseBundle"); 1110 } 1111 } 1112 } 1113 } 1114 1115 Map<String, RuntimeResourceDefinition> idToElementDefinition = new HashMap<>(); 1116 idToElementDefinition.putAll(myIdToResourceDefinition); 1117 idToElementDefinition.putAll(scanner.getIdToResourceDefinition()); 1118 1119 myNameToElementDefinition = nameToElementDefinition; 1120 myClassToElementDefinition = classToElementDefinition; 1121 myIdToResourceDefinition = idToElementDefinition; 1122 myNameToResourceDefinition = nameToResourceDefinition; 1123 1124 myNameToResourceType = scanner.getNameToResourceType(); 1125 1126 myInitialized = true; 1127 return classToElementDefinition; 1128 } 1129 1130 /** 1131 * Sets the default type which will be used when parsing a resource that is found to be 1132 * of the given profile. 1133 * <p> 1134 * For example, this method is invoked with the profile string of 1135 * <code>"http://example.com/some_patient_profile"</code> and the type of <code>MyPatient.class</code>, 1136 * if the parser is parsing a resource and finds that it declares that it conforms to that profile, 1137 * the <code>MyPatient</code> type will be used unless otherwise specified. 1138 * </p> 1139 * 1140 * @param theProfile The profile string, e.g. <code>"http://example.com/some_patient_profile"</code>. Must not be 1141 * <code>null</code> or empty. 1142 * @param theClass The resource type, or <code>null</code> to clear any existing type 1143 */ 1144 public void setDefaultTypeForProfile(final String theProfile, final Class<? extends IBaseResource> theClass) { 1145 Validate.notBlank(theProfile, "theProfile must not be null or empty"); 1146 if (theClass == null) { 1147 myDefaultTypeForProfile.remove(theProfile); 1148 } else { 1149 myDefaultTypeForProfile.put(theProfile, theClass); 1150 } 1151 } 1152 1153 /** 1154 * Sets a parser error handler to use by default on all parsers 1155 * 1156 * @param theParserErrorHandler The error handler 1157 */ 1158 public FhirContext setParserErrorHandler(final IParserErrorHandler theParserErrorHandler) { 1159 Validate.notNull(theParserErrorHandler, "theParserErrorHandler must not be null"); 1160 myParserErrorHandler = theParserErrorHandler; 1161 return this; 1162 } 1163 1164 /** 1165 * Set the factory method used to create FhirValidator instances 1166 * 1167 * @param theFhirValidatorFactory 1168 * @return this 1169 * @since 5.6.0 1170 */ 1171 public FhirContext setFhirValidatorFactory(IFhirValidatorFactory theFhirValidatorFactory) { 1172 myFhirValidatorFactory = theFhirValidatorFactory; 1173 return this; 1174 } 1175 1176 @SuppressWarnings({"cast"}) 1177 private List<Class<? extends IElement>> toElementList( 1178 final Collection<Class<? extends IBaseResource>> theResourceTypes) { 1179 if (theResourceTypes == null) { 1180 return null; 1181 } 1182 List<Class<? extends IElement>> resTypes = new ArrayList<>(); 1183 for (Class<? extends IBaseResource> next : theResourceTypes) { 1184 resTypes.add(next); 1185 } 1186 return resTypes; 1187 } 1188 1189 private void validateInitialized() { 1190 // See #610 1191 if (!myInitialized) { 1192 synchronized (this) { 1193 if (!myInitialized && !myInitializing) { 1194 myInitializing = true; 1195 try { 1196 scanResourceTypes(toElementList(myResourceTypesToScan)); 1197 } catch (Exception e) { 1198 ourLog.error("Failed to initialize FhirContext", e); 1199 throw e; 1200 } finally { 1201 myInitializing = false; 1202 } 1203 } 1204 } 1205 } 1206 } 1207 1208 @Override 1209 public String toString() { 1210 return "FhirContext[" + myVersion.getVersion().name() + "]"; 1211 } 1212 1213 // TODO KHS add the other primitive types 1214 @Deprecated(since = "6.6.0", forRemoval = true) 1215 public IPrimitiveType<Boolean> getPrimitiveBoolean(Boolean theValue) { 1216 return newPrimitiveBoolean(theValue); 1217 } 1218 1219 public IPrimitiveType<Boolean> newPrimitiveBoolean(Boolean theValue) { 1220 IPrimitiveType<Boolean> retval = 1221 (IPrimitiveType<Boolean>) getElementDefinition("boolean").newInstance(); 1222 retval.setValue(theValue); 1223 return retval; 1224 } 1225 1226 public IPrimitiveType<String> newPrimitiveString(String theValue) { 1227 IPrimitiveType<String> retval = 1228 (IPrimitiveType<String>) getElementDefinition("string").newInstance(); 1229 retval.setValue(theValue); 1230 return retval; 1231 } 1232 1233 private static boolean tryToInitParser(Runnable run) { 1234 boolean retVal; 1235 try { 1236 run.run(); 1237 retVal = true; 1238 } catch (UnsupportedClassVersionError | Exception | NoClassDefFoundError e) { 1239 retVal = false; 1240 } 1241 return retVal; 1242 } 1243 1244 /** 1245 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU2} 1246 */ 1247 public static FhirContext forDstu2() { 1248 return new FhirContext(FhirVersionEnum.DSTU2); 1249 } 1250 1251 /** 1252 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2_HL7ORG DSTU2} (using the Reference 1253 * Implementation Structures) 1254 */ 1255 public static FhirContext forDstu2Hl7Org() { 1256 return new FhirContext(FhirVersionEnum.DSTU2_HL7ORG); 1257 } 1258 1259 /** 1260 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU2} (2016 May DSTU3 Snapshot) 1261 */ 1262 public static FhirContext forDstu2_1() { 1263 return new FhirContext(FhirVersionEnum.DSTU2_1); 1264 } 1265 1266 /** 1267 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU3 DSTU3} 1268 * 1269 * @since 1.4 1270 */ 1271 public static FhirContext forDstu3() { 1272 return new FhirContext(FhirVersionEnum.DSTU3); 1273 } 1274 1275 /** 1276 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#R4 R4} 1277 * 1278 * @since 3.0.0 1279 */ 1280 public static FhirContext forR4() { 1281 return new FhirContext(FhirVersionEnum.R4); 1282 } 1283 1284 /** 1285 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#R4B R4B} 1286 * 1287 * @since 6.2.0 1288 */ 1289 public static FhirContext forR4B() { 1290 return new FhirContext(FhirVersionEnum.R4B); 1291 } 1292 1293 /** 1294 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#R5 R5} 1295 * 1296 * @since 4.0.0 1297 */ 1298 public static FhirContext forR5() { 1299 return new FhirContext(FhirVersionEnum.R5); 1300 } 1301 1302 /** 1303 * Returns a statically cached {@literal FhirContext} instance for the given version, creating one if none exists in the 1304 * cache. One FhirContext will be kept in the cache for each FHIR version that is requested (by calling 1305 * this method for that version), and the cache will never be expired. 1306 * 1307 * @since 5.1.0 1308 */ 1309 public static FhirContext forCached(FhirVersionEnum theFhirVersionEnum) { 1310 return ourStaticContexts.computeIfAbsent(theFhirVersionEnum, FhirContext::forVersion); 1311 } 1312 1313 /** 1314 * An uncached version of forCached() 1315 * @return a new FhirContext for theFhirVersionEnum 1316 */ 1317 public static FhirContext forVersion(FhirVersionEnum theFhirVersionEnum) { 1318 return new FhirContext(theFhirVersionEnum); 1319 } 1320 1321 private static Collection<Class<? extends IBaseResource>> toCollection( 1322 Class<? extends IBaseResource> theResourceType) { 1323 ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<>(1); 1324 retVal.add(theResourceType); 1325 return retVal; 1326 } 1327 1328 @SuppressWarnings("unchecked") 1329 private static List<Class<? extends IBaseResource>> toCollection(final Class<?>[] theResourceTypes) { 1330 ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<Class<? extends IBaseResource>>(1); 1331 for (Class<?> clazz : theResourceTypes) { 1332 if (!IResource.class.isAssignableFrom(clazz)) { 1333 throw new IllegalArgumentException(Msg.code(1688) + clazz.getCanonicalName() + " is not an instance of " 1334 + IResource.class.getSimpleName()); 1335 } 1336 retVal.add((Class<? extends IResource>) clazz); 1337 } 1338 return retVal; 1339 } 1340}