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