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