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