001package ca.uhn.fhir.context; 002 003import ca.uhn.fhir.context.api.AddProfileTagEnum; 004import ca.uhn.fhir.context.support.IContextValidationSupport; 005import ca.uhn.fhir.fluentpath.IFluentPath; 006import ca.uhn.fhir.i18n.HapiLocalizer; 007import ca.uhn.fhir.model.api.IElement; 008import ca.uhn.fhir.model.api.IFhirVersion; 009import ca.uhn.fhir.model.api.IResource; 010import ca.uhn.fhir.model.view.ViewGenerator; 011import ca.uhn.fhir.narrative.INarrativeGenerator; 012import ca.uhn.fhir.parser.*; 013import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; 014import ca.uhn.fhir.rest.client.api.IBasicClient; 015import ca.uhn.fhir.rest.client.api.IGenericClient; 016import ca.uhn.fhir.rest.client.api.IRestfulClient; 017import ca.uhn.fhir.rest.client.api.IRestfulClientFactory; 018import ca.uhn.fhir.util.FhirTerser; 019import ca.uhn.fhir.util.ReflectionUtil; 020import ca.uhn.fhir.util.VersionUtil; 021import ca.uhn.fhir.validation.FhirValidator; 022import org.apache.commons.lang3.Validate; 023import org.apache.commons.lang3.builder.ToStringBuilder; 024import org.apache.jena.riot.Lang; 025import org.hl7.fhir.instance.model.api.IBase; 026import org.hl7.fhir.instance.model.api.IBaseBundle; 027import org.hl7.fhir.instance.model.api.IBaseResource; 028 029import javax.annotation.Nullable; 030import java.io.IOException; 031import java.lang.reflect.Method; 032import java.lang.reflect.Modifier; 033import java.util.*; 034import java.util.Map.Entry; 035 036/* 037 * #%L 038 * HAPI FHIR - Core Library 039 * %% 040 * Copyright (C) 2014 - 2020 University Health Network 041 * %% 042 * Licensed under the Apache License, Version 2.0 (the "License"); 043 * you may not use this file except in compliance with the License. 044 * You may obtain a copy of the License at 045 * 046 * http://www.apache.org/licenses/LICENSE-2.0 047 * 048 * Unless required by applicable law or agreed to in writing, software 049 * distributed under the License is distributed on an "AS IS" BASIS, 050 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 051 * See the License for the specific language governing permissions and 052 * limitations under the License. 053 * #L% 054 */ 055 056/** 057 * The FHIR context is the central starting point for the use of the HAPI FHIR API. It should be created once, and then 058 * used as a factory for various other types of objects (parsers, clients, etc.). 059 * 060 * <p> 061 * Important usage notes: 062 * </p> 063 * <ul> 064 * <li> 065 * Thread safety: <b>This class is thread safe</b> and may be shared between multiple processing 066 * threads, except for the {@link #registerCustomType} and {@link #registerCustomTypes} methods. 067 * </li> 068 * <li> 069 * Performance: <b>This class is expensive</b> to create, as it scans every resource class it needs to parse or encode 070 * to build up an internal model of those classes. For that reason, you should try to create one FhirContext instance 071 * which remains for the life of your application and reuse that instance. Note that it will not cause problems to 072 * create multiple instances (ie. resources originating from one FhirContext may be passed to parsers originating from 073 * another) but you will incur a performance penalty if a new FhirContext is created for every message you parse/encode. 074 * </li> 075 * </ul> 076 */ 077public class FhirContext { 078 079 private static final List<Class<? extends IBaseResource>> EMPTY_LIST = Collections.emptyList(); 080 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirContext.class); 081 private final IFhirVersion myVersion; 082 private AddProfileTagEnum myAddProfileTagWhenEncoding = AddProfileTagEnum.ONLY_FOR_CUSTOM; 083 private volatile Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinition = Collections.emptyMap(); 084 private ArrayList<Class<? extends IBase>> myCustomTypes; 085 private Map<String, Class<? extends IBaseResource>> myDefaultTypeForProfile = new HashMap<>(); 086 private volatile Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = Collections.emptyMap(); 087 private volatile boolean myInitialized; 088 private volatile boolean myInitializing = false; 089 private HapiLocalizer myLocalizer = new HapiLocalizer(); 090 private volatile Map<String, BaseRuntimeElementDefinition<?>> myNameToElementDefinition = Collections.emptyMap(); 091 private volatile Map<String, RuntimeResourceDefinition> myNameToResourceDefinition = Collections.emptyMap(); 092 private volatile Map<String, Class<? extends IBaseResource>> myNameToResourceType; 093 private volatile INarrativeGenerator myNarrativeGenerator; 094 private volatile IParserErrorHandler myParserErrorHandler = new LenientErrorHandler(); 095 private ParserOptions myParserOptions = new ParserOptions(); 096 private Set<PerformanceOptionsEnum> myPerformanceOptions = new HashSet<>(); 097 private Collection<Class<? extends IBaseResource>> myResourceTypesToScan; 098 private volatile IRestfulClientFactory myRestfulClientFactory; 099 private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition; 100 private IContextValidationSupport<?, ?, ?, ?, ?, ?> myValidationSupport; 101 private Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> myVersionToNameToResourceType = Collections.emptyMap(); 102 103 /** 104 * @deprecated It is recommended that you use one of the static initializer methods instead 105 * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} 106 */ 107 @Deprecated 108 public FhirContext() { 109 this(EMPTY_LIST); 110 } 111 112 /** 113 * @deprecated It is recommended that you use one of the static initializer methods instead 114 * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} 115 */ 116 @Deprecated 117 public FhirContext(final Class<? extends IBaseResource> theResourceType) { 118 this(toCollection(theResourceType)); 119 } 120 121 /** 122 * @deprecated It is recommended that you use one of the static initializer methods instead 123 * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} 124 */ 125 @Deprecated 126 public FhirContext(final Class<?>... theResourceTypes) { 127 this(toCollection(theResourceTypes)); 128 } 129 130 /** 131 * @deprecated It is recommended that you use one of the static initializer methods instead 132 * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} 133 */ 134 @Deprecated 135 public FhirContext(final Collection<Class<? extends IBaseResource>> theResourceTypes) { 136 this(null, theResourceTypes); 137 } 138 139 /** 140 * In most cases it is recommended that you use one of the static initializer methods instead 141 * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()}, but 142 * this method can also be used if you wish to supply the version programmatically. 143 */ 144 public FhirContext(final FhirVersionEnum theVersion) { 145 this(theVersion, null); 146 } 147 148 private FhirContext(final FhirVersionEnum theVersion, final Collection<Class<? extends IBaseResource>> theResourceTypes) { 149 VersionUtil.getVersion(); 150 151 if (theVersion != null) { 152 if (!theVersion.isPresentOnClasspath()) { 153 throw new IllegalStateException(getLocalizer().getMessage(FhirContext.class, "noStructuresForSpecifiedVersion", theVersion.name())); 154 } 155 myVersion = theVersion.getVersionImplementation(); 156 } else if (FhirVersionEnum.DSTU2.isPresentOnClasspath()) { 157 myVersion = FhirVersionEnum.DSTU2.getVersionImplementation(); 158 } else if (FhirVersionEnum.DSTU2_HL7ORG.isPresentOnClasspath()) { 159 myVersion = FhirVersionEnum.DSTU2_HL7ORG.getVersionImplementation(); 160 } else if (FhirVersionEnum.DSTU2_1.isPresentOnClasspath()) { 161 myVersion = FhirVersionEnum.DSTU2_1.getVersionImplementation(); 162 } else if (FhirVersionEnum.DSTU3.isPresentOnClasspath()) { 163 myVersion = FhirVersionEnum.DSTU3.getVersionImplementation(); 164 } else if (FhirVersionEnum.R4.isPresentOnClasspath()) { 165 myVersion = FhirVersionEnum.R4.getVersionImplementation(); 166 } else { 167 throw new IllegalStateException(getLocalizer().getMessage(FhirContext.class, "noStructures")); 168 } 169 170 if (theVersion == null) { 171 ourLog.info("Creating new FhirContext with auto-detected version [{}]. It is recommended to explicitly select a version for future compatibility by invoking FhirContext.forDstuX()", 172 myVersion.getVersion().name()); 173 } else { 174 ourLog.info("Creating new FHIR context for FHIR version [{}]", myVersion.getVersion().name()); 175 } 176 177 myResourceTypesToScan = theResourceTypes; 178 179 /* 180 * Check if we're running in Android mode and configure the context appropriately if so 181 */ 182 try { 183 Class<?> clazz = Class.forName("ca.uhn.fhir.android.AndroidMarker"); 184 ourLog.info("Android mode detected, configuring FhirContext for Android operation"); 185 try { 186 Method method = clazz.getMethod("configureContext", FhirContext.class); 187 method.invoke(null, this); 188 } catch (Throwable e) { 189 ourLog.warn("Failed to configure context for Android operation", e); 190 } 191 } catch (ClassNotFoundException e) { 192 ourLog.trace("Android mode not detected"); 193 } 194 195 } 196 197 private String createUnknownResourceNameError(final String theResourceName, final FhirVersionEnum theVersion) { 198 return getLocalizer().getMessage(FhirContext.class, "unknownResourceName", theResourceName, theVersion); 199 } 200 201 private void ensureCustomTypeList() { 202 myClassToElementDefinition.clear(); 203 if (myCustomTypes == null) { 204 myCustomTypes = new ArrayList<>(); 205 } 206 } 207 208 /** 209 * When encoding resources, this setting configures the parser to include 210 * an entry in the resource's metadata section which indicates which profile(s) the 211 * resource claims to conform to. The default is {@link AddProfileTagEnum#ONLY_FOR_CUSTOM}. 212 * 213 * @see #setAddProfileTagWhenEncoding(AddProfileTagEnum) for more information 214 */ 215 public AddProfileTagEnum getAddProfileTagWhenEncoding() { 216 return myAddProfileTagWhenEncoding; 217 } 218 219 /** 220 * When encoding resources, this setting configures the parser to include 221 * an entry in the resource's metadata section which indicates which profile(s) the 222 * resource claims to conform to. The default is {@link AddProfileTagEnum#ONLY_FOR_CUSTOM}. 223 * <p> 224 * This feature is intended for situations where custom resource types are being used, 225 * avoiding the need to manually add profile declarations for these custom types. 226 * </p> 227 * <p> 228 * See <a href="http://jamesagnew.gihhub.io/hapi-fhir/doc_extensions.html">Profiling and Extensions</a> 229 * for more information on using custom types. 230 * </p> 231 * <p> 232 * Note that this feature automatically adds the profile, but leaves any profile tags 233 * which have been manually added in place as well. 234 * </p> 235 * 236 * @param theAddProfileTagWhenEncoding The add profile mode (must not be <code>null</code>) 237 */ 238 public void setAddProfileTagWhenEncoding(final AddProfileTagEnum theAddProfileTagWhenEncoding) { 239 Validate.notNull(theAddProfileTagWhenEncoding, "theAddProfileTagWhenEncoding must not be null"); 240 myAddProfileTagWhenEncoding = theAddProfileTagWhenEncoding; 241 } 242 243 Collection<RuntimeResourceDefinition> getAllResourceDefinitions() { 244 validateInitialized(); 245 return myNameToResourceDefinition.values(); 246 } 247 248 /** 249 * Returns the default resource type for the given profile 250 * 251 * @see #setDefaultTypeForProfile(String, Class) 252 */ 253 public Class<? extends IBaseResource> getDefaultTypeForProfile(final String theProfile) { 254 validateInitialized(); 255 return myDefaultTypeForProfile.get(theProfile); 256 } 257 258 /** 259 * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed 260 * for extending the core library. 261 */ 262 @SuppressWarnings("unchecked") 263 public BaseRuntimeElementDefinition<?> getElementDefinition(final Class<? extends IBase> theElementType) { 264 validateInitialized(); 265 BaseRuntimeElementDefinition<?> retVal = myClassToElementDefinition.get(theElementType); 266 if (retVal == null) { 267 retVal = scanDatatype((Class<? extends IElement>) theElementType); 268 } 269 return retVal; 270 } 271 272 /** 273 * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed 274 * for extending the core library. 275 * <p> 276 * Note that this method is case insensitive! 277 * </p> 278 */ 279 @Nullable 280 public BaseRuntimeElementDefinition<?> getElementDefinition(final String theElementName) { 281 validateInitialized(); 282 return myNameToElementDefinition.get(theElementName.toLowerCase()); 283 } 284 285 /** 286 * Returns all element definitions (resources, datatypes, etc.) 287 */ 288 public Collection<BaseRuntimeElementDefinition<?>> getElementDefinitions() { 289 validateInitialized(); 290 return Collections.unmodifiableCollection(myClassToElementDefinition.values()); 291 } 292 293 /** 294 * This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with 295 * caution 296 */ 297 public HapiLocalizer getLocalizer() { 298 if (myLocalizer == null) { 299 myLocalizer = new HapiLocalizer(); 300 } 301 return myLocalizer; 302 } 303 304 /** 305 * This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with 306 * caution 307 */ 308 public void setLocalizer(final HapiLocalizer theMessages) { 309 myLocalizer = theMessages; 310 } 311 312 public INarrativeGenerator getNarrativeGenerator() { 313 return myNarrativeGenerator; 314 } 315 316 public void setNarrativeGenerator(final INarrativeGenerator theNarrativeGenerator) { 317 myNarrativeGenerator = theNarrativeGenerator; 318 } 319 320 /** 321 * Returns the parser options object which will be used to supply default 322 * options to newly created parsers 323 * 324 * @return The parser options - Will not return <code>null</code> 325 */ 326 public ParserOptions getParserOptions() { 327 return myParserOptions; 328 } 329 330 /** 331 * Sets the parser options object which will be used to supply default 332 * options to newly created parsers 333 * 334 * @param theParserOptions The parser options object - Must not be <code>null</code> 335 */ 336 public void setParserOptions(final ParserOptions theParserOptions) { 337 Validate.notNull(theParserOptions, "theParserOptions must not be null"); 338 myParserOptions = theParserOptions; 339 } 340 341 /** 342 * Get the configured performance options 343 */ 344 public Set<PerformanceOptionsEnum> getPerformanceOptions() { 345 return myPerformanceOptions; 346 } 347 348 // /** 349 // * Return an unmodifiable collection containing all known resource definitions 350 // */ 351 // public Collection<RuntimeResourceDefinition> getResourceDefinitions() { 352 // 353 // Set<Class<? extends IBase>> datatypes = Collections.emptySet(); 354 // Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> existing = Collections.emptyMap(); 355 // HashMap<String, Class<? extends IBaseResource>> types = new HashMap<String, Class<? extends IBaseResource>>(); 356 // ModelScanner.scanVersionPropertyFile(datatypes, types, myVersion.getVersion(), existing); 357 // for (int next : types.) 358 // 359 // return Collections.unmodifiableCollection(myIdToResourceDefinition.values()); 360 // } 361 362 /** 363 * Sets the configured performance options 364 * 365 * @see PerformanceOptionsEnum for a list of available options 366 */ 367 public void setPerformanceOptions(final Collection<PerformanceOptionsEnum> theOptions) { 368 myPerformanceOptions.clear(); 369 if (theOptions != null) { 370 myPerformanceOptions.addAll(theOptions); 371 } 372 } 373 374 /** 375 * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed 376 * for extending the core library. 377 */ 378 public RuntimeResourceDefinition getResourceDefinition(final Class<? extends IBaseResource> theResourceType) { 379 validateInitialized(); 380 if (theResourceType == null) { 381 throw new NullPointerException("theResourceType can not be null"); 382 } 383 if (Modifier.isAbstract(theResourceType.getModifiers())) { 384 throw new IllegalArgumentException("Can not scan abstract or interface class (resource definitions must be concrete classes): " + theResourceType.getName()); 385 } 386 387 RuntimeResourceDefinition retVal = (RuntimeResourceDefinition) myClassToElementDefinition.get(theResourceType); 388 if (retVal == null) { 389 retVal = scanResourceType(theResourceType); 390 } 391 return retVal; 392 } 393 394 public RuntimeResourceDefinition getResourceDefinition(final FhirVersionEnum theVersion, final String theResourceName) { 395 Validate.notNull(theVersion, "theVersion can not be null"); 396 validateInitialized(); 397 398 if (theVersion.equals(myVersion.getVersion())) { 399 return getResourceDefinition(theResourceName); 400 } 401 402 Map<String, Class<? extends IBaseResource>> nameToType = myVersionToNameToResourceType.get(theVersion); 403 if (nameToType == null) { 404 nameToType = new HashMap<>(); 405 Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> existing = new HashMap<>(); 406 ModelScanner.scanVersionPropertyFile(null, nameToType, theVersion, existing); 407 408 Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> newVersionToNameToResourceType = new HashMap<>(); 409 newVersionToNameToResourceType.putAll(myVersionToNameToResourceType); 410 newVersionToNameToResourceType.put(theVersion, nameToType); 411 myVersionToNameToResourceType = newVersionToNameToResourceType; 412 } 413 414 Class<? extends IBaseResource> resourceType = nameToType.get(theResourceName.toLowerCase()); 415 if (resourceType == null) { 416 throw new DataFormatException(createUnknownResourceNameError(theResourceName, theVersion)); 417 } 418 419 return getResourceDefinition(resourceType); 420 } 421 422 /** 423 * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed 424 * for extending the core library. 425 */ 426 public RuntimeResourceDefinition getResourceDefinition(final IBaseResource theResource) { 427 validateInitialized(); 428 Validate.notNull(theResource, "theResource must not be null"); 429 return getResourceDefinition(theResource.getClass()); 430 } 431 432 /** 433 * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed 434 * for extending the core library. 435 * <p> 436 * Note that this method is case insensitive! 437 * </p> 438 * 439 * @throws DataFormatException If the resource name is not known 440 */ 441 public RuntimeResourceDefinition getResourceDefinition(final String theResourceName) throws DataFormatException { 442 validateInitialized(); 443 Validate.notBlank(theResourceName, "theResourceName must not be blank"); 444 445 String resourceName = theResourceName.toLowerCase(); 446 RuntimeResourceDefinition retVal = myNameToResourceDefinition.get(resourceName); 447 448 if (retVal == null) { 449 Class<? extends IBaseResource> clazz = myNameToResourceType.get(resourceName.toLowerCase()); 450 if (clazz == null) { 451 // *********************************************************************** 452 // Multiple spots in HAPI FHIR and Smile CDR depend on DataFormatException 453 // being thrown by this method, don't change that. 454 // *********************************************************************** 455 throw new DataFormatException(createUnknownResourceNameError(theResourceName, myVersion.getVersion())); 456 } 457 if (IBaseResource.class.isAssignableFrom(clazz)) { 458 retVal = scanResourceType(clazz); 459 } 460 } 461 462 return retVal; 463 } 464 465 /** 466 * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed 467 * for extending the core library. 468 */ 469 public RuntimeResourceDefinition getResourceDefinitionById(final String theId) { 470 validateInitialized(); 471 return myIdToResourceDefinition.get(theId); 472 } 473 474 /** 475 * Returns the scanned runtime models. This is an advanced feature which is generally only needed for extending the 476 * core library. 477 */ 478 public Collection<RuntimeResourceDefinition> getResourceDefinitionsWithExplicitId() { 479 validateInitialized(); 480 return myIdToResourceDefinition.values(); 481 } 482 483 /** 484 * Returns an unmodifiable set containing all resource names known to this 485 * context 486 */ 487 public Set<String> getResourceNames() { 488 Set<String> resourceNames = new HashSet<>(); 489 490 if (myNameToResourceDefinition.isEmpty()) { 491 Properties props = new Properties(); 492 try { 493 props.load(myVersion.getFhirVersionPropertiesFile()); 494 } catch (IOException theE) { 495 throw new ConfigurationException("Failed to load version properties file"); 496 } 497 Enumeration<?> propNames = props.propertyNames(); 498 while (propNames.hasMoreElements()) { 499 String next = (String) propNames.nextElement(); 500 if (next.startsWith("resource.")) { 501 resourceNames.add(next.substring("resource.".length()).trim()); 502 } 503 } 504 } 505 506 for (RuntimeResourceDefinition next : myNameToResourceDefinition.values()) { 507 resourceNames.add(next.getName()); 508 } 509 510 return Collections.unmodifiableSet(resourceNames); 511 } 512 513 /** 514 * Get the restful client factory. If no factory has been set, this will be initialized with 515 * a new ApacheRestfulClientFactory. 516 * 517 * @return the factory used to create the restful clients 518 */ 519 public IRestfulClientFactory getRestfulClientFactory() { 520 if (myRestfulClientFactory == null) { 521 try { 522 myRestfulClientFactory = (IRestfulClientFactory) ReflectionUtil.newInstance(Class.forName("ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory"), FhirContext.class, this); 523 } catch (ClassNotFoundException e) { 524 throw new ConfigurationException("hapi-fhir-client does not appear to be on the classpath"); 525 } 526 } 527 return myRestfulClientFactory; 528 } 529 530 /** 531 * Set the restful client factory 532 * 533 * @param theRestfulClientFactory 534 */ 535 public void setRestfulClientFactory(final IRestfulClientFactory theRestfulClientFactory) { 536 Validate.notNull(theRestfulClientFactory, "theRestfulClientFactory must not be null"); 537 this.myRestfulClientFactory = theRestfulClientFactory; 538 } 539 540 public RuntimeChildUndeclaredExtensionDefinition getRuntimeChildUndeclaredExtensionDefinition() { 541 validateInitialized(); 542 return myRuntimeChildUndeclaredExtensionDefinition; 543 } 544 545 /** 546 * Returns the validation support module configured for this context, creating a default 547 * implementation if no module has been passed in via the {@link #setValidationSupport(IContextValidationSupport)} 548 * method 549 * 550 * @see #setValidationSupport(IContextValidationSupport) 551 */ 552 public IContextValidationSupport<?, ?, ?, ?, ?, ?> getValidationSupport() { 553 if (myValidationSupport == null) { 554 myValidationSupport = myVersion.createValidationSupport(); 555 } 556 return myValidationSupport; 557 } 558 559 /** 560 * Sets the validation support module to use for this context. The validation support module 561 * is used to supply underlying infrastructure such as conformance resources (StructureDefinition, ValueSet, etc) 562 * as well as to provide terminology services to modules such as the validator and FluentPath executor 563 */ 564 public void setValidationSupport(IContextValidationSupport<?, ?, ?, ?, ?, ?> theValidationSupport) { 565 myValidationSupport = theValidationSupport; 566 } 567 568 public IFhirVersion getVersion() { 569 return myVersion; 570 } 571 572 /** 573 * Returns <code>true</code> if any default types for specific profiles have been defined 574 * within this context. 575 * 576 * @see #setDefaultTypeForProfile(String, Class) 577 * @see #getDefaultTypeForProfile(String) 578 */ 579 public boolean hasDefaultTypeForProfile() { 580 validateInitialized(); 581 return !myDefaultTypeForProfile.isEmpty(); 582 } 583 584 public IVersionSpecificBundleFactory newBundleFactory() { 585 return myVersion.newBundleFactory(this); 586 } 587 588 /** 589 * Creates a new FluentPath engine which can be used to exvaluate 590 * path expressions over FHIR resources. Note that this engine will use the 591 * {@link IContextValidationSupport context validation support} module which is 592 * configured on the context at the time this method is called. 593 * <p> 594 * In other words, call {@link #setValidationSupport(IContextValidationSupport)} before 595 * calling {@link #newFluentPath()} 596 * </p> 597 * <p> 598 * Note that this feature was added for FHIR DSTU3 and is not available 599 * for contexts configured to use an older version of FHIR. Calling this method 600 * on a context for a previous version of fhir will result in an 601 * {@link UnsupportedOperationException} 602 * </p> 603 * 604 * @since 2.2 605 */ 606 public IFluentPath newFluentPath() { 607 return myVersion.createFluentPathExecutor(this); 608 } 609 610 /** 611 * Create and return a new JSON parser. 612 * 613 * <p> 614 * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread 615 * or every message being parsed/encoded. 616 * </p> 617 * <p> 618 * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed 619 * without incurring any performance penalty 620 * </p> 621 */ 622 public IParser newJsonParser() { 623 return new JsonParser(this, myParserErrorHandler); 624 } 625 626 /** 627 * Create and return a new RDF parser. 628 * 629 * <p> 630 * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread 631 * or every message being parsed/encoded. 632 * </p> 633 * <p> 634 * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed 635 * without incurring any performance penalty 636 * </p> 637 * 638 * @deprecated THIS FEATURE IS NOT YET COMPLETE 639 */ 640 @Deprecated 641 public IParser newRDFParser() { 642 return new RDFParser(this, myParserErrorHandler, Lang.TURTLE); 643 } 644 645 646 /** 647 * Instantiates a new client instance. This method requires an interface which is defined specifically for your use 648 * cases to contain methods for each of the RESTful operations you wish to implement (e.g. "read ImagingStudy", 649 * "search Patient by identifier", etc.). This interface must extend {@link IRestfulClient} (or commonly its 650 * sub-interface {@link IBasicClient}). See the <a 651 * href="http://jamesagnew.github.io/hapi-fhir/doc_rest_client.html">RESTful Client</a> documentation for more 652 * information on how to define this interface. 653 * 654 * <p> 655 * Performance Note: <b>This method is cheap</b> to call, and may be called once for every operation invocation 656 * without incurring any performance penalty 657 * </p> 658 * 659 * @param theClientType The client type, which is an interface type to be instantiated 660 * @param theServerBase The URL of the base for the restful FHIR server to connect to 661 * @return A newly created client 662 * @throws ConfigurationException If the interface type is not an interface 663 */ 664 public <T extends IRestfulClient> T newRestfulClient(final Class<T> theClientType, final String theServerBase) { 665 return getRestfulClientFactory().newClient(theClientType, theServerBase); 666 } 667 668 /** 669 * Instantiates a new generic client. A generic client is able to perform any of the FHIR RESTful operations against 670 * a compliant server, but does not have methods defining the specific functionality required (as is the case with 671 * {@link #newRestfulClient(Class, String) non-generic clients}). 672 * 673 * <p> 674 * Performance Note: <b>This method is cheap</b> to call, and may be called once for every operation invocation 675 * without incurring any performance penalty 676 * </p> 677 * 678 * @param theServerBase The URL of the base for the restful FHIR server to connect to 679 */ 680 public IGenericClient newRestfulGenericClient(final String theServerBase) { 681 return getRestfulClientFactory().newGenericClient(theServerBase); 682 } 683 684 public FhirTerser newTerser() { 685 return new FhirTerser(this); 686 } 687 688 /** 689 * Create a new validator instance. 690 * <p> 691 * Note on thread safety: Validators are thread safe, you may use a single validator 692 * in multiple threads. (This is in contrast to parsers) 693 * </p> 694 */ 695 public FhirValidator newValidator() { 696 return new FhirValidator(this); 697 } 698 699 public ViewGenerator newViewGenerator() { 700 return new ViewGenerator(this); 701 } 702 703 /** 704 * Create and return a new XML parser. 705 * 706 * <p> 707 * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread 708 * or every message being parsed/encoded. 709 * </p> 710 * <p> 711 * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed 712 * without incurring any performance penalty 713 * </p> 714 */ 715 public IParser newXmlParser() { 716 return new XmlParser(this, myParserErrorHandler); 717 } 718 719 /** 720 * This method may be used to register a custom resource or datatype. Note that by using 721 * custom types, you are creating a system that will not interoperate with other systems that 722 * do not know about your custom type. There are valid reasons however for wanting to create 723 * custom types and this method can be used to enable them. 724 * <p> 725 * <b>THREAD SAFETY WARNING:</b> This method is not thread safe. It should be called before any 726 * threads are able to call any methods on this context. 727 * </p> 728 * 729 * @param theType The custom type to add (must not be <code>null</code>) 730 */ 731 public void registerCustomType(final Class<? extends IBase> theType) { 732 Validate.notNull(theType, "theType must not be null"); 733 734 ensureCustomTypeList(); 735 myCustomTypes.add(theType); 736 } 737 738 /** 739 * This method may be used to register a custom resource or datatype. Note that by using 740 * custom types, you are creating a system that will not interoperate with other systems that 741 * do not know about your custom type. There are valid reasons however for wanting to create 742 * custom types and this method can be used to enable them. 743 * <p> 744 * <b>THREAD SAFETY WARNING:</b> This method is not thread safe. It should be called before any 745 * threads are able to call any methods on this context. 746 * </p> 747 * 748 * @param theTypes The custom types to add (must not be <code>null</code> or contain null elements in the collection) 749 */ 750 public void registerCustomTypes(final Collection<Class<? extends IBase>> theTypes) { 751 Validate.notNull(theTypes, "theTypes must not be null"); 752 Validate.noNullElements(theTypes.toArray(), "theTypes must not contain any null elements"); 753 754 ensureCustomTypeList(); 755 756 myCustomTypes.addAll(theTypes); 757 } 758 759 private BaseRuntimeElementDefinition<?> scanDatatype(final Class<? extends IElement> theResourceType) { 760 ArrayList<Class<? extends IElement>> resourceTypes = new ArrayList<Class<? extends IElement>>(); 761 resourceTypes.add(theResourceType); 762 Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> defs = scanResourceTypes(resourceTypes); 763 return defs.get(theResourceType); 764 } 765 766 private RuntimeResourceDefinition scanResourceType(final Class<? extends IBaseResource> theResourceType) { 767 ArrayList<Class<? extends IElement>> resourceTypes = new ArrayList<Class<? extends IElement>>(); 768 resourceTypes.add(theResourceType); 769 Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> defs = scanResourceTypes(resourceTypes); 770 return (RuntimeResourceDefinition) defs.get(theResourceType); 771 } 772 773 private synchronized Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> scanResourceTypes(final Collection<Class<? extends IElement>> theResourceTypes) { 774 List<Class<? extends IBase>> typesToScan = new ArrayList<Class<? extends IBase>>(); 775 if (theResourceTypes != null) { 776 typesToScan.addAll(theResourceTypes); 777 } 778 if (myCustomTypes != null) { 779 typesToScan.addAll(myCustomTypes); 780 myCustomTypes = null; 781 } 782 783 ModelScanner scanner = new ModelScanner(this, myVersion.getVersion(), myClassToElementDefinition, typesToScan); 784 if (myRuntimeChildUndeclaredExtensionDefinition == null) { 785 myRuntimeChildUndeclaredExtensionDefinition = scanner.getRuntimeChildUndeclaredExtensionDefinition(); 786 } 787 788 Map<String, BaseRuntimeElementDefinition<?>> nameToElementDefinition = new HashMap<>(); 789 nameToElementDefinition.putAll(myNameToElementDefinition); 790 for (Entry<String, BaseRuntimeElementDefinition<?>> next : scanner.getNameToElementDefinitions().entrySet()) { 791 if (!nameToElementDefinition.containsKey(next.getKey())) { 792 nameToElementDefinition.put(next.getKey().toLowerCase(), next.getValue()); 793 } 794 } 795 796 Map<String, RuntimeResourceDefinition> nameToResourceDefinition = new HashMap<>(); 797 nameToResourceDefinition.putAll(myNameToResourceDefinition); 798 for (Entry<String, RuntimeResourceDefinition> next : scanner.getNameToResourceDefinition().entrySet()) { 799 if (!nameToResourceDefinition.containsKey(next.getKey())) { 800 nameToResourceDefinition.put(next.getKey(), next.getValue()); 801 } 802 } 803 804 Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> classToElementDefinition = new HashMap<>(); 805 classToElementDefinition.putAll(myClassToElementDefinition); 806 classToElementDefinition.putAll(scanner.getClassToElementDefinitions()); 807 for (BaseRuntimeElementDefinition<?> next : classToElementDefinition.values()) { 808 if (next instanceof RuntimeResourceDefinition) { 809 if ("Bundle".equals(next.getName())) { 810 if (!IBaseBundle.class.isAssignableFrom(next.getImplementingClass())) { 811 throw new ConfigurationException("Resource type declares resource name Bundle but does not implement IBaseBundle"); 812 } 813 } 814 } 815 } 816 817 Map<String, RuntimeResourceDefinition> idToElementDefinition = new HashMap<>(); 818 idToElementDefinition.putAll(myIdToResourceDefinition); 819 idToElementDefinition.putAll(scanner.getIdToResourceDefinition()); 820 821 myNameToElementDefinition = nameToElementDefinition; 822 myClassToElementDefinition = classToElementDefinition; 823 myIdToResourceDefinition = idToElementDefinition; 824 myNameToResourceDefinition = nameToResourceDefinition; 825 826 myNameToResourceType = scanner.getNameToResourceType(); 827 828 myInitialized = true; 829 return classToElementDefinition; 830 } 831 832 /** 833 * Sets the default type which will be used when parsing a resource that is found to be 834 * of the given profile. 835 * <p> 836 * For example, this method is invoked with the profile string of 837 * <code>"http://example.com/some_patient_profile"</code> and the type of <code>MyPatient.class</code>, 838 * if the parser is parsing a resource and finds that it declares that it conforms to that profile, 839 * the <code>MyPatient</code> type will be used unless otherwise specified. 840 * </p> 841 * 842 * @param theProfile The profile string, e.g. <code>"http://example.com/some_patient_profile"</code>. Must not be 843 * <code>null</code> or empty. 844 * @param theClass The resource type, or <code>null</code> to clear any existing type 845 */ 846 public void setDefaultTypeForProfile(final String theProfile, final Class<? extends IBaseResource> theClass) { 847 Validate.notBlank(theProfile, "theProfile must not be null or empty"); 848 if (theClass == null) { 849 myDefaultTypeForProfile.remove(theProfile); 850 } else { 851 myDefaultTypeForProfile.put(theProfile, theClass); 852 } 853 } 854 855 /** 856 * Sets a parser error handler to use by default on all parsers 857 * 858 * @param theParserErrorHandler The error handler 859 */ 860 public void setParserErrorHandler(final IParserErrorHandler theParserErrorHandler) { 861 Validate.notNull(theParserErrorHandler, "theParserErrorHandler must not be null"); 862 myParserErrorHandler = theParserErrorHandler; 863 } 864 865 /** 866 * Sets the configured performance options 867 * 868 * @see PerformanceOptionsEnum for a list of available options 869 */ 870 public void setPerformanceOptions(final PerformanceOptionsEnum... thePerformanceOptions) { 871 Collection<PerformanceOptionsEnum> asList = null; 872 if (thePerformanceOptions != null) { 873 asList = Arrays.asList(thePerformanceOptions); 874 } 875 setPerformanceOptions(asList); 876 } 877 878 @SuppressWarnings({"cast"}) 879 private List<Class<? extends IElement>> toElementList(final Collection<Class<? extends IBaseResource>> theResourceTypes) { 880 if (theResourceTypes == null) { 881 return null; 882 } 883 List<Class<? extends IElement>> resTypes = new ArrayList<>(); 884 for (Class<? extends IBaseResource> next : theResourceTypes) { 885 resTypes.add(next); 886 } 887 return resTypes; 888 } 889 890 private void validateInitialized() { 891 // See #610 892 if (!myInitialized) { 893 synchronized (this) { 894 if (!myInitialized && !myInitializing) { 895 myInitializing = true; 896 scanResourceTypes(toElementList(myResourceTypesToScan)); 897 } 898 } 899 } 900 } 901 902 @Override 903 public String toString() { 904 return "FhirContext[" + myVersion.getVersion().name() + "]"; 905 } 906 907 /** 908 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU2} 909 */ 910 public static FhirContext forDstu2() { 911 return new FhirContext(FhirVersionEnum.DSTU2); 912 } 913 914 /** 915 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2_HL7ORG DSTU2} (using the Reference 916 * Implementation Structures) 917 */ 918 public static FhirContext forDstu2Hl7Org() { 919 return new FhirContext(FhirVersionEnum.DSTU2_HL7ORG); 920 } 921 922 /** 923 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU2} (2016 May DSTU3 Snapshot) 924 */ 925 public static FhirContext forDstu2_1() { 926 return new FhirContext(FhirVersionEnum.DSTU2_1); 927 } 928 929 /** 930 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU3 DSTU3} 931 * 932 * @since 1.4 933 */ 934 public static FhirContext forDstu3() { 935 return new FhirContext(FhirVersionEnum.DSTU3); 936 } 937 938 /** 939 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#R4 R4} 940 * 941 * @since 3.0.0 942 */ 943 public static FhirContext forR4() { 944 return new FhirContext(FhirVersionEnum.R4); 945 } 946 947 /** 948 * Creates and returns a new FhirContext with version {@link FhirVersionEnum#R5 R5} 949 * 950 * @since 4.0.0 951 */ 952 public static FhirContext forR5() { 953 return new FhirContext(FhirVersionEnum.R5); 954 } 955 956 private static Collection<Class<? extends IBaseResource>> toCollection(Class<? extends IBaseResource> theResourceType) { 957 ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<>(1); 958 retVal.add(theResourceType); 959 return retVal; 960 } 961 962 @SuppressWarnings("unchecked") 963 private static List<Class<? extends IBaseResource>> toCollection(final Class<?>[] theResourceTypes) { 964 ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<Class<? extends IBaseResource>>(1); 965 for (Class<?> clazz : theResourceTypes) { 966 if (!IResource.class.isAssignableFrom(clazz)) { 967 throw new IllegalArgumentException(clazz.getCanonicalName() + " is not an instance of " + IResource.class.getSimpleName()); 968 } 969 retVal.add((Class<? extends IResource>) clazz); 970 } 971 return retVal; 972 } 973 974}