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}