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