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