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