001/*
002 * #%L
003 * HAPI FHIR - Core Library
004 * %%
005 * Copyright (C) 2014 - 2025 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 * http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.context;
021
022import ca.uhn.fhir.context.api.AddProfileTagEnum;
023import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
024import ca.uhn.fhir.context.support.IValidationSupport;
025import ca.uhn.fhir.fhirpath.IFhirPath;
026import ca.uhn.fhir.i18n.HapiLocalizer;
027import ca.uhn.fhir.i18n.Msg;
028import ca.uhn.fhir.model.api.IElement;
029import ca.uhn.fhir.model.api.IFhirVersion;
030import ca.uhn.fhir.model.api.IResource;
031import ca.uhn.fhir.model.api.annotation.ResourceDef;
032import ca.uhn.fhir.model.view.ViewGenerator;
033import ca.uhn.fhir.narrative.INarrativeGenerator;
034import ca.uhn.fhir.parser.DataFormatException;
035import ca.uhn.fhir.parser.IParser;
036import ca.uhn.fhir.parser.IParserErrorHandler;
037import ca.uhn.fhir.parser.JsonParser;
038import ca.uhn.fhir.parser.LenientErrorHandler;
039import ca.uhn.fhir.parser.NDJsonParser;
040import ca.uhn.fhir.parser.RDFParser;
041import ca.uhn.fhir.parser.XmlParser;
042import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
043import ca.uhn.fhir.rest.client.api.IBasicClient;
044import ca.uhn.fhir.rest.client.api.IGenericClient;
045import ca.uhn.fhir.rest.client.api.IRestfulClient;
046import ca.uhn.fhir.rest.client.api.IRestfulClientFactory;
047import ca.uhn.fhir.system.HapiSystemProperties;
048import ca.uhn.fhir.util.FhirTerser;
049import ca.uhn.fhir.util.ReflectionUtil;
050import ca.uhn.fhir.util.VersionUtil;
051import ca.uhn.fhir.validation.FhirValidator;
052import jakarta.annotation.Nonnull;
053import jakarta.annotation.Nullable;
054import org.apache.commons.lang3.Validate;
055import org.apache.commons.lang3.exception.ExceptionUtils;
056import org.apache.jena.riot.Lang;
057import org.hl7.fhir.instance.model.api.IBase;
058import org.hl7.fhir.instance.model.api.IBaseBundle;
059import org.hl7.fhir.instance.model.api.IBaseResource;
060import org.hl7.fhir.instance.model.api.IPrimitiveType;
061
062import java.io.IOException;
063import java.io.InputStream;
064import java.lang.reflect.Method;
065import java.lang.reflect.Modifier;
066import java.util.ArrayList;
067import java.util.Arrays;
068import java.util.Collection;
069import java.util.Collections;
070import java.util.EnumMap;
071import java.util.Enumeration;
072import java.util.HashMap;
073import java.util.HashSet;
074import java.util.List;
075import java.util.Map;
076import java.util.Map.Entry;
077import java.util.Objects;
078import java.util.Optional;
079import java.util.Properties;
080import java.util.Set;
081import java.util.stream.Collectors;
082
083/**
084 * The FHIR context is the central starting point for the use of the HAPI FHIR API. It should be created once, and then
085 * used as a factory for various other types of objects (parsers, clients, etc.).
086 *
087 * <p>
088 * Important usage notes:
089 * </p>
090 * <ul>
091 * <li>
092 * Thread safety: <b>This class is thread safe</b> and may be shared between multiple processing
093 * threads, except for the {@link #registerCustomType} and {@link #registerCustomTypes} methods.
094 * </li>
095 * <li>
096 * Performance: <b>This class is expensive</b> to create, as it scans every resource class it needs to parse or encode
097 * to build up an internal model of those classes. For that reason, you should try to create one FhirContext instance
098 * which remains for the life of your application and reuse that instance. Note that it will not cause problems to
099 * create multiple instances (ie. resources originating from one FhirContext may be passed to parsers originating from
100 * another) but you will incur a performance penalty if a new FhirContext is created for every message you parse/encode.
101 * </li>
102 * </ul>
103 */
104public class FhirContext {
105
106        private static final List<Class<? extends IBaseResource>> EMPTY_LIST = Collections.emptyList();
107        private static final Map<FhirVersionEnum, FhirContext> ourStaticContexts =
108                        Collections.synchronizedMap(new EnumMap<>(FhirVersionEnum.class));
109        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirContext.class);
110        private final IFhirVersion myVersion;
111        private final Map<String, Class<? extends IBaseResource>> myDefaultTypeForProfile = new HashMap<>();
112        private final Set<PerformanceOptionsEnum> myPerformanceOptions = new HashSet<>();
113        private final Collection<Class<? extends IBaseResource>> myResourceTypesToScan;
114        private AddProfileTagEnum myAddProfileTagWhenEncoding = AddProfileTagEnum.ONLY_FOR_CUSTOM;
115        private volatile Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinition =
116                        Collections.emptyMap();
117        private ArrayList<Class<? extends IBase>> myCustomTypes;
118        private final Set<String> myCustomResourceNames = new HashSet<>();
119        private volatile Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = Collections.emptyMap();
120        private volatile boolean myInitialized;
121        private volatile boolean myInitializing = false;
122        private HapiLocalizer myLocalizer = new HapiLocalizer();
123        private volatile Map<String, BaseRuntimeElementDefinition<?>> myNameToElementDefinition = Collections.emptyMap();
124        private volatile Map<String, RuntimeResourceDefinition> myNameToResourceDefinition = Collections.emptyMap();
125        private volatile Map<String, Class<? extends IBaseResource>> myNameToResourceType;
126        private volatile INarrativeGenerator myNarrativeGenerator;
127        private volatile IParserErrorHandler myParserErrorHandler = new LenientErrorHandler();
128        private ParserOptions myParserOptions = new ParserOptions();
129        private volatile IRestfulClientFactory myRestfulClientFactory;
130        private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
131        private IValidationSupport myValidationSupport;
132        private Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> myVersionToNameToResourceType =
133                        Collections.emptyMap();
134        private volatile Set<String> myResourceNames;
135        private volatile Boolean myFormatXmlSupported;
136        private volatile Boolean myFormatJsonSupported;
137        private volatile Boolean myFormatNDJsonSupported;
138        private volatile Boolean myFormatRdfSupported;
139        private IFhirValidatorFactory myFhirValidatorFactory = FhirValidator::new;
140        /**
141         * If true, parsed resources will have the json string
142         * used to create them stored
143         * in the UserData.
144         *
145         * This is to help with validation, because the parser itself is far
146         * more lenient than validation might be.
147         */
148        private boolean myStoreResourceJsonFlag = false;
149
150        /**
151         * @deprecated It is recommended that you use one of the static initializer methods instead
152         * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()}
153         */
154        @Deprecated
155        public FhirContext() {
156                this(EMPTY_LIST);
157        }
158
159        /**
160         * @deprecated It is recommended that you use one of the static initializer methods instead
161         * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()}
162         */
163        @Deprecated
164        public FhirContext(final Class<? extends IBaseResource> theResourceType) {
165                this(toCollection(theResourceType));
166        }
167
168        /**
169         * @deprecated It is recommended that you use one of the static initializer methods instead
170         * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()}
171         */
172        @Deprecated
173        public FhirContext(final Class<?>... theResourceTypes) {
174                this(toCollection(theResourceTypes));
175        }
176
177        /**
178         * @deprecated It is recommended that you use one of the static initializer methods instead
179         * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()}
180         */
181        @Deprecated
182        public FhirContext(final Collection<Class<? extends IBaseResource>> theResourceTypes) {
183                this(null, theResourceTypes);
184        }
185
186        /**
187         * In most cases it is recommended that you use one of the static initializer methods instead
188         * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()}, but
189         * this method can also be used if you wish to supply the version programmatically.
190         */
191        public FhirContext(final FhirVersionEnum theVersion) {
192                this(theVersion, null);
193        }
194
195        private FhirContext(
196                        final FhirVersionEnum theVersion, final Collection<Class<? extends IBaseResource>> theResourceTypes) {
197                VersionUtil.getVersion();
198
199                if (theVersion != null) {
200                        if (!theVersion.isPresentOnClasspath()) {
201                                throw new IllegalStateException(Msg.code(1680)
202                                                + getLocalizer()
203                                                                .getMessage(FhirContext.class, "noStructuresForSpecifiedVersion", theVersion.name()));
204                        }
205                        myVersion = theVersion.getVersionImplementation();
206                } else if (FhirVersionEnum.DSTU2.isPresentOnClasspath()) {
207                        myVersion = FhirVersionEnum.DSTU2.getVersionImplementation();
208                } else if (FhirVersionEnum.DSTU2_HL7ORG.isPresentOnClasspath()) {
209                        myVersion = FhirVersionEnum.DSTU2_HL7ORG.getVersionImplementation();
210                } else if (FhirVersionEnum.DSTU2_1.isPresentOnClasspath()) {
211                        myVersion = FhirVersionEnum.DSTU2_1.getVersionImplementation();
212                } else if (FhirVersionEnum.DSTU3.isPresentOnClasspath()) {
213                        myVersion = FhirVersionEnum.DSTU3.getVersionImplementation();
214                } else if (FhirVersionEnum.R4.isPresentOnClasspath()) {
215                        myVersion = FhirVersionEnum.R4.getVersionImplementation();
216                } else if (FhirVersionEnum.R4B.isPresentOnClasspath()) {
217                        myVersion = FhirVersionEnum.R4B.getVersionImplementation();
218                } else {
219                        throw new IllegalStateException(
220                                        Msg.code(1681) + getLocalizer().getMessage(FhirContext.class, "noStructures"));
221                }
222
223                if (theVersion == null) {
224                        ourLog.info(
225                                        "Creating new FhirContext with auto-detected version [{}]. It is recommended to explicitly select a version for future compatibility by invoking FhirContext.forDstuX()",
226                                        myVersion.getVersion().name());
227                } else {
228                        if (HapiSystemProperties.isUnitTestModeEnabled()) {
229                                String calledAt = ExceptionUtils.getStackFrames(new Throwable())[4];
230                                ourLog.info(
231                                                "Creating new FHIR context for FHIR version [{}]{}",
232                                                myVersion.getVersion().name(),
233                                                calledAt);
234                        } else {
235                                ourLog.info(
236                                                "Creating new FHIR context for FHIR version [{}]",
237                                                myVersion.getVersion().name());
238                        }
239                }
240
241                myResourceTypesToScan = theResourceTypes;
242
243                /*
244                 * Check if we're running in Android mode and configure the context appropriately if so
245                 */
246                try {
247                        Class<?> clazz = Class.forName("ca.uhn.fhir.android.AndroidMarker");
248                        ourLog.info("Android mode detected, configuring FhirContext for Android operation");
249                        try {
250                                Method method = clazz.getMethod("configureContext", FhirContext.class);
251                                method.invoke(null, this);
252                        } catch (Throwable e) {
253                                ourLog.warn("Failed to configure context for Android operation", e);
254                        }
255                } catch (ClassNotFoundException e) {
256                        ourLog.trace("Android mode not detected");
257                }
258        }
259
260        /**
261         * @since 5.6.0
262         */
263        public static FhirContext forDstu2Cached() {
264                return forCached(FhirVersionEnum.DSTU2);
265        }
266
267        /**
268         * @since 6.2.0
269         */
270        public static FhirContext forDstu2Hl7OrgCached() {
271                return forCached(FhirVersionEnum.DSTU2_HL7ORG);
272        }
273
274        /**
275         * @since 5.5.0
276         */
277        public static FhirContext forDstu3Cached() {
278                return forCached(FhirVersionEnum.DSTU3);
279        }
280
281        /**
282         * @since 5.5.0
283         */
284        public static FhirContext forR4Cached() {
285                return forCached(FhirVersionEnum.R4);
286        }
287
288        /**
289         * @since 6.1.0
290         */
291        public static FhirContext forR4BCached() {
292                return forCached(FhirVersionEnum.R4B);
293        }
294
295        /**
296         * @since 5.5.0
297         */
298        public static FhirContext forR5Cached() {
299                return forCached(FhirVersionEnum.R5);
300        }
301
302        private String createUnknownResourceNameError(final String theResourceName, final FhirVersionEnum theVersion) {
303                return getLocalizer().getMessage(FhirContext.class, "unknownResourceName", theResourceName, theVersion);
304        }
305
306        private void ensureCustomTypeList() {
307                myClassToElementDefinition.clear();
308                if (myCustomTypes == null) {
309                        myCustomTypes = new ArrayList<>();
310                }
311        }
312
313        /**
314         * When encoding resources, this setting configures the parser to include
315         * an entry in the resource's metadata section which indicates which profile(s) the
316         * resource claims to conform to. The default is {@link AddProfileTagEnum#ONLY_FOR_CUSTOM}.
317         *
318         * @see #setAddProfileTagWhenEncoding(AddProfileTagEnum) for more information
319         */
320        public AddProfileTagEnum getAddProfileTagWhenEncoding() {
321                return myAddProfileTagWhenEncoding;
322        }
323
324        /**
325         * When encoding resources, this setting configures the parser to include
326         * an entry in the resource's metadata section which indicates which profile(s) the
327         * resource claims to conform to. The default is {@link AddProfileTagEnum#ONLY_FOR_CUSTOM}.
328         * <p>
329         * This feature is intended for situations where custom resource types are being used,
330         * avoiding the need to manually add profile declarations for these custom types.
331         * </p>
332         * <p>
333         * See <a href="http://jamesagnew.gihhub.io/hapi-fhir/doc_extensions.html">Profiling and Extensions</a>
334         * for more information on using custom types.
335         * </p>
336         * <p>
337         * Note that this feature automatically adds the profile, but leaves any profile tags
338         * which have been manually added in place as well.
339         * </p>
340         *
341         * @param theAddProfileTagWhenEncoding The add profile mode (must not be <code>null</code>)
342         */
343        public void setAddProfileTagWhenEncoding(final AddProfileTagEnum theAddProfileTagWhenEncoding) {
344                Validate.notNull(theAddProfileTagWhenEncoding, "theAddProfileTagWhenEncoding must not be null");
345                myAddProfileTagWhenEncoding = theAddProfileTagWhenEncoding;
346        }
347
348        Collection<RuntimeResourceDefinition> getAllResourceDefinitions() {
349                validateInitialized();
350                return myNameToResourceDefinition.values();
351        }
352
353        /**
354         * Returns the default resource type for the given profile
355         *
356         * @see #setDefaultTypeForProfile(String, Class)
357         */
358        public Class<? extends IBaseResource> getDefaultTypeForProfile(final String theProfile) {
359                validateInitialized();
360                return myDefaultTypeForProfile.get(theProfile);
361        }
362
363        /**
364         * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
365         * for extending the core library.
366         */
367        @SuppressWarnings("unchecked")
368        public BaseRuntimeElementDefinition<?> getElementDefinition(final Class<? extends IBase> theElementType) {
369                validateInitialized();
370                BaseRuntimeElementDefinition<?> retVal = myClassToElementDefinition.get(theElementType);
371                if (retVal == null) {
372                        retVal = scanDatatype((Class<? extends IElement>) theElementType);
373                }
374                return retVal;
375        }
376
377        /**
378         * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
379         * for extending the core library.
380         * <p>
381         * Note that this method is case insensitive!
382         * </p>
383         */
384        @Nullable
385        public BaseRuntimeElementDefinition<?> getElementDefinition(final String theElementName) {
386                validateInitialized();
387                return myNameToElementDefinition.get(theElementName.toLowerCase());
388        }
389
390        /**
391         * Returns all element definitions (resources, datatypes, etc.)
392         */
393        public Collection<BaseRuntimeElementDefinition<?>> getElementDefinitions() {
394                validateInitialized();
395                return Collections.unmodifiableCollection(myClassToElementDefinition.values());
396        }
397
398        /**
399         * This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with
400         * caution
401         */
402        public HapiLocalizer getLocalizer() {
403                if (myLocalizer == null) {
404                        myLocalizer = new HapiLocalizer();
405                }
406                return myLocalizer;
407        }
408
409        /**
410         * This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with
411         * caution
412         */
413        public void setLocalizer(final HapiLocalizer theMessages) {
414                myLocalizer = theMessages;
415        }
416
417        public INarrativeGenerator getNarrativeGenerator() {
418                return myNarrativeGenerator;
419        }
420
421        public FhirContext setNarrativeGenerator(final INarrativeGenerator theNarrativeGenerator) {
422                myNarrativeGenerator = theNarrativeGenerator;
423                return this;
424        }
425
426        /**
427         * Returns the parser options object which will be used to supply default
428         * options to newly created parsers
429         *
430         * @return The parser options - Will not return <code>null</code>
431         */
432        public ParserOptions getParserOptions() {
433                return myParserOptions;
434        }
435
436        /**
437         * Sets the parser options object which will be used to supply default
438         * options to newly created parsers
439         *
440         * @param theParserOptions The parser options object - Must not be <code>null</code>
441         */
442        public void setParserOptions(final ParserOptions theParserOptions) {
443                Validate.notNull(theParserOptions, "theParserOptions must not be null");
444                myParserOptions = theParserOptions;
445        }
446
447        /**
448         * Get the configured performance options
449         */
450        public Set<PerformanceOptionsEnum> getPerformanceOptions() {
451                return myPerformanceOptions;
452        }
453
454        // /**
455        // * Return an unmodifiable collection containing all known resource definitions
456        // */
457        // public Collection<RuntimeResourceDefinition> getResourceDefinitions() {
458        //
459        // Set<Class<? extends IBase>> datatypes = Collections.emptySet();
460        // Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> existing = Collections.emptyMap();
461        // HashMap<String, Class<? extends IBaseResource>> types = new HashMap<String, Class<? extends IBaseResource>>();
462        // ModelScanner.scanVersionPropertyFile(datatypes, types, myVersion.getVersion(), existing);
463        // for (int next : types.)
464        //
465        // return Collections.unmodifiableCollection(myIdToResourceDefinition.values());
466        // }
467
468        /**
469         * Sets the configured performance options
470         *
471         * @see PerformanceOptionsEnum for a list of available options
472         */
473        public void setPerformanceOptions(final Collection<PerformanceOptionsEnum> theOptions) {
474                myPerformanceOptions.clear();
475                if (theOptions != null) {
476                        myPerformanceOptions.addAll(theOptions);
477                }
478        }
479
480        /**
481         * Sets the configured performance options
482         *
483         * @see PerformanceOptionsEnum for a list of available options
484         */
485        public void setPerformanceOptions(final PerformanceOptionsEnum... thePerformanceOptions) {
486                Collection<PerformanceOptionsEnum> asList = null;
487                if (thePerformanceOptions != null) {
488                        asList = Arrays.asList(thePerformanceOptions);
489                }
490                setPerformanceOptions(asList);
491        }
492
493        /**
494         * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
495         * for extending the core library.
496         */
497        public RuntimeResourceDefinition getResourceDefinition(final Class<? extends IBaseResource> theResourceType) {
498                validateInitialized();
499                Validate.notNull(theResourceType, "theResourceType can not be null");
500
501                if (Modifier.isAbstract(theResourceType.getModifiers())) {
502                        throw new IllegalArgumentException(Msg.code(1682)
503                                        + "Can not scan abstract or interface class (resource definitions must be concrete classes): "
504                                        + theResourceType.getName());
505                }
506
507                RuntimeResourceDefinition retVal = (RuntimeResourceDefinition) myClassToElementDefinition.get(theResourceType);
508                if (retVal == null) {
509                        retVal = scanResourceType(theResourceType);
510                }
511
512                return retVal;
513        }
514
515        public RuntimeResourceDefinition getResourceDefinition(
516                        final FhirVersionEnum theVersion, final String theResourceName) {
517                Validate.notNull(theVersion, "theVersion can not be null");
518                validateInitialized();
519
520                if (theVersion.equals(myVersion.getVersion())) {
521                        return getResourceDefinition(theResourceName);
522                }
523
524                Map<String, Class<? extends IBaseResource>> nameToType = myVersionToNameToResourceType.get(theVersion);
525                if (nameToType == null) {
526                        nameToType = new HashMap<>();
527                        Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> existing = new HashMap<>();
528                        ModelScanner.scanVersionPropertyFile(null, nameToType, theVersion, existing);
529
530                        Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> newVersionToNameToResourceType =
531                                        new HashMap<>();
532                        newVersionToNameToResourceType.putAll(myVersionToNameToResourceType);
533                        newVersionToNameToResourceType.put(theVersion, nameToType);
534                        myVersionToNameToResourceType = newVersionToNameToResourceType;
535                }
536
537                Class<? extends IBaseResource> resourceType = nameToType.get(theResourceName.toLowerCase());
538                if (resourceType == null) {
539                        throw new DataFormatException(Msg.code(1683) + createUnknownResourceNameError(theResourceName, theVersion));
540                }
541
542                return getResourceDefinition(resourceType);
543        }
544
545        /**
546         * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
547         * for extending the core library.
548         */
549        public RuntimeResourceDefinition getResourceDefinition(final IBaseResource theResource) {
550                validateInitialized();
551                Validate.notNull(theResource, "theResource must not be null");
552                return getResourceDefinition(theResource.getClass());
553        }
554
555        /**
556         * Returns the name of a given resource class.
557         */
558        public String getResourceType(final Class<? extends IBaseResource> theResourceType) {
559                return getResourceDefinition(theResourceType).getName();
560        }
561
562        /**
563         * Returns the name of the scanned runtime model for the given type. This is an advanced feature which is generally only needed
564         * for extending the core library.
565         */
566        public String getResourceType(final IBaseResource theResource) {
567                return getResourceDefinition(theResource).getName();
568        }
569
570        /*
571         * Returns the type of the scanned runtime model for the given type. This is an advanced feature which is generally only needed
572         * for extending the core library.
573         * <p>
574         * Note that this method is case insensitive!
575         * </p>
576         *
577         * @throws DataFormatException If the resource name is not known
578         */
579        public String getResourceType(final String theResourceName) throws DataFormatException {
580                return getResourceDefinition(theResourceName).getName();
581        }
582
583        /*
584         * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
585         * for extending the core library.
586         * <p>
587         * Note that this method is case insensitive!
588         * </p>
589         *
590         * @throws DataFormatException If the resource name is not known
591         */
592        public RuntimeResourceDefinition getResourceDefinition(final String theResourceName) throws DataFormatException {
593                validateInitialized();
594                Validate.notBlank(theResourceName, "theResourceName must not be blank");
595
596                String resourceName = theResourceName.toLowerCase();
597                RuntimeResourceDefinition retVal = myNameToResourceDefinition.get(resourceName);
598
599                if (retVal == null) {
600                        Class<? extends IBaseResource> clazz = myNameToResourceType.get(resourceName.toLowerCase());
601                        if (clazz == null) {
602                                // ***********************************************************************
603                                // Multiple spots in HAPI FHIR and Smile CDR depend on DataFormatException
604                                // being thrown by this method, don't change that.
605                                // ***********************************************************************
606                                throw new DataFormatException(
607                                                Msg.code(1684) + createUnknownResourceNameError(theResourceName, myVersion.getVersion()));
608                        }
609                        if (IBaseResource.class.isAssignableFrom(clazz)) {
610                                retVal = scanResourceType(clazz);
611                        }
612                }
613                return retVal;
614        }
615
616        /**
617         * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
618         * for extending the core library.
619         */
620        public RuntimeResourceDefinition getResourceDefinitionById(final String theId) {
621                validateInitialized();
622                return myIdToResourceDefinition.get(theId);
623        }
624
625        /**
626         * Returns the scanned runtime models. This is an advanced feature which is generally only needed for extending the
627         * core library.
628         */
629        public Collection<RuntimeResourceDefinition> getResourceDefinitionsWithExplicitId() {
630                validateInitialized();
631                return myIdToResourceDefinition.values();
632        }
633
634        /**
635         * Returns an unmodifiable set containing all resource names known to this
636         * context
637         *
638         * @since 5.1.0
639         */
640        public Set<String> getResourceTypes() {
641                Set<String> resourceNames = myResourceNames;
642                if (resourceNames == null) {
643                        resourceNames = buildResourceNames();
644                        myResourceNames = resourceNames;
645                }
646                return resourceNames;
647        }
648
649        @Nonnull
650        private Set<String> buildResourceNames() {
651                Set<String> retVal = new HashSet<>();
652                Properties props = new Properties();
653                try (InputStream propFile = myVersion.getFhirVersionPropertiesFile()) {
654                        props.load(propFile);
655                } catch (IOException e) {
656                        throw new ConfigurationException(Msg.code(1685) + "Failed to load version properties file", e);
657                }
658                Enumeration<?> propNames = props.propertyNames();
659                while (propNames.hasMoreElements()) {
660                        String next = (String) propNames.nextElement();
661                        if (next.startsWith("resource.")) {
662                                retVal.add(next.substring("resource.".length()).trim());
663                        }
664                }
665                retVal.addAll(myCustomResourceNames);
666                return retVal;
667        }
668
669        /**
670         * Get the restful client factory. If no factory has been set, this will be initialized with
671         * a new ApacheRestfulClientFactory.
672         *
673         * @return the factory used to create the restful clients
674         */
675        public IRestfulClientFactory getRestfulClientFactory() {
676                if (myRestfulClientFactory == null) {
677                        try {
678                                myRestfulClientFactory = (IRestfulClientFactory) ReflectionUtil.newInstance(
679                                                Class.forName("ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory"),
680                                                FhirContext.class,
681                                                this);
682                        } catch (ClassNotFoundException e) {
683                                throw new ConfigurationException(
684                                                Msg.code(1686) + "hapi-fhir-client does not appear to be on the classpath");
685                        }
686                }
687                return myRestfulClientFactory;
688        }
689
690        /**
691         * Set the restful client factory
692         *
693         * @param theRestfulClientFactory The new client factory (must not be null)
694         */
695        public void setRestfulClientFactory(final IRestfulClientFactory theRestfulClientFactory) {
696                Validate.notNull(theRestfulClientFactory, "theRestfulClientFactory must not be null");
697                this.myRestfulClientFactory = theRestfulClientFactory;
698        }
699
700        public RuntimeChildUndeclaredExtensionDefinition getRuntimeChildUndeclaredExtensionDefinition() {
701                validateInitialized();
702                return myRuntimeChildUndeclaredExtensionDefinition;
703        }
704
705        /**
706         * Returns the validation support module configured for this context, creating a default
707         * implementation if no module has been passed in via the {@link #setValidationSupport(IValidationSupport)}
708         * method
709         *
710         * @see #setValidationSupport(IValidationSupport)
711         */
712        public IValidationSupport getValidationSupport() {
713                IValidationSupport retVal = myValidationSupport;
714                if (retVal == null) {
715                        retVal = new DefaultProfileValidationSupport(this);
716
717                        /*
718                         * If hapi-fhir-validation is on the classpath, we can create a much more robust
719                         * validation chain using the classes found in that package
720                         */
721                        String inMemoryTermSvcType =
722                                        "org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport";
723                        String commonCodeSystemsSupportType =
724                                        "org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService";
725                        String snapshotGeneratingType =
726                                        "org.hl7.fhir.common.hapi.validation.support.SnapshotGeneratingValidationSupport";
727                        if (ReflectionUtil.typeExists(inMemoryTermSvcType)) {
728                                IValidationSupport inMemoryTermSvc = ReflectionUtil.newInstanceOrReturnNull(
729                                                inMemoryTermSvcType,
730                                                IValidationSupport.class,
731                                                new Class<?>[] {FhirContext.class},
732                                                new Object[] {this});
733                                IValidationSupport commonCodeSystemsSupport = ReflectionUtil.newInstanceOrReturnNull(
734                                                commonCodeSystemsSupportType,
735                                                IValidationSupport.class,
736                                                new Class<?>[] {FhirContext.class},
737                                                new Object[] {this});
738                                IValidationSupport snapshotGeneratingSupport = null;
739                                if (getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
740                                        snapshotGeneratingSupport = ReflectionUtil.newInstanceOrReturnNull(
741                                                        snapshotGeneratingType,
742                                                        IValidationSupport.class,
743                                                        new Class<?>[] {FhirContext.class},
744                                                        new Object[] {this});
745                                }
746                                retVal = ReflectionUtil.newInstanceOrReturnNull(
747                                                "org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain",
748                                                IValidationSupport.class,
749                                                new Class<?>[] {IValidationSupport[].class},
750                                                new Object[] {
751                                                        new IValidationSupport[] {
752                                                                retVal, inMemoryTermSvc, commonCodeSystemsSupport, snapshotGeneratingSupport
753                                                        }
754                                                });
755                                assert retVal != null
756                                                : "Failed to instantiate "
757                                                                + "org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain";
758                        }
759
760                        myValidationSupport = retVal;
761                }
762                return retVal;
763        }
764
765        /**
766         * Sets the validation support module to use for this context. The validation support module
767         * is used to supply underlying infrastructure such as conformance resources (StructureDefinition, ValueSet, etc)
768         * as well as to provide terminology services to modules such as the validator and FluentPath executor
769         */
770        public void setValidationSupport(IValidationSupport theValidationSupport) {
771                myValidationSupport = theValidationSupport;
772        }
773
774        public void setStoreRawJson(boolean theStoreResourceJsonFlag) {
775                myStoreResourceJsonFlag = theStoreResourceJsonFlag;
776        }
777
778        public boolean isStoreResourceJson() {
779                return myStoreResourceJsonFlag;
780        }
781
782        public IFhirVersion getVersion() {
783                return myVersion;
784        }
785
786        /**
787         * Returns <code>true</code> if any default types for specific profiles have been defined
788         * within this context.
789         *
790         * @see #setDefaultTypeForProfile(String, Class)
791         * @see #getDefaultTypeForProfile(String)
792         */
793        public boolean hasDefaultTypeForProfile() {
794                validateInitialized();
795                return !myDefaultTypeForProfile.isEmpty();
796        }
797
798        /**
799         * @return Returns <code>true</code> if the XML serialization format is supported, based on the
800         * available libraries on the classpath.
801         *
802         * @since 5.4.0
803         */
804        public boolean isFormatXmlSupported() {
805                Boolean retVal = myFormatXmlSupported;
806                if (retVal == null) {
807                        retVal = tryToInitParser(() -> newXmlParser());
808                        myFormatXmlSupported = retVal;
809                }
810                return retVal;
811        }
812
813        /**
814         * @return Returns <code>true</code> if the JSON serialization format is supported, based on the
815         * available libraries on the classpath.
816         *
817         * @since 5.4.0
818         */
819        public boolean isFormatJsonSupported() {
820                Boolean retVal = myFormatJsonSupported;
821                if (retVal == null) {
822                        retVal = tryToInitParser(() -> newJsonParser());
823                        myFormatJsonSupported = retVal;
824                }
825                return retVal;
826        }
827
828        /**
829         * @return Returns <code>true</code> if the NDJSON serialization format is supported, based on the
830         * available libraries on the classpath.
831         *
832         * @since 5.6.0
833         */
834        public boolean isFormatNDJsonSupported() {
835                Boolean retVal = myFormatNDJsonSupported;
836                if (retVal == null) {
837                        retVal = tryToInitParser(() -> newNDJsonParser());
838                        myFormatNDJsonSupported = retVal;
839                }
840                return retVal;
841        }
842
843        /**
844         * @return Returns <code>true</code> if the RDF serialization format is supported, based on the
845         * available libraries on the classpath.
846         *
847         * @since 5.4.0
848         */
849        public boolean isFormatRdfSupported() {
850                Boolean retVal = myFormatRdfSupported;
851                if (retVal == null) {
852                        retVal = tryToInitParser(() -> newRDFParser());
853                        myFormatRdfSupported = retVal;
854                }
855                return retVal;
856        }
857
858        public IVersionSpecificBundleFactory newBundleFactory() {
859                return myVersion.newBundleFactory(this);
860        }
861
862        /**
863         * @since 2.2
864         * @deprecated Deprecated in HAPI FHIR 5.0.0. Use {@link #newFhirPath()} instead.
865         */
866        @Deprecated
867        public IFhirPath newFluentPath() {
868                return newFhirPath();
869        }
870
871        /**
872         * Creates a new FhirPath engine which can be used to evaluate
873         * path expressions over FHIR resources. Note that this engine will use the
874         * {@link IValidationSupport context validation support} module which is
875         * configured on the context at the time this method is called.
876         * <p>
877         * In other words, you may wish to call {@link #setValidationSupport(IValidationSupport)} before
878         * calling {@link #newFluentPath()}
879         * </p>
880         * <p>
881         * Note that this feature was added for FHIR DSTU3 and is not available
882         * for contexts configured to use an older version of FHIR. Calling this method
883         * on a context for a previous version of fhir will result in an
884         * {@link UnsupportedOperationException}
885         * </p>
886         *
887         * @since 5.0.0
888         */
889        public IFhirPath newFhirPath() {
890                return myVersion.createFhirPathExecutor(this);
891        }
892
893        /**
894         * Create and return a new JSON parser.
895         *
896         * <p>
897         * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread
898         * or every message being parsed/encoded.
899         * </p>
900         * <p>
901         * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed
902         * without incurring any performance penalty
903         * </p>
904         */
905        public IParser newJsonParser() {
906                return new JsonParser(this, myParserErrorHandler);
907        }
908
909        /**
910         * Create and return a new NDJSON parser.
911         *
912         * <p>
913         * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread
914         * or every message being parsed/encoded.
915         * </p>
916         * <p>
917         * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed
918         * without incurring any performance penalty
919         * </p>
920         * <p>
921         * The NDJsonParser provided here is expected to translate between legal NDJson and FHIR Bundles.
922         * In particular, it is able to encode the resources in a FHIR Bundle to NDJson, as well as decode
923         * NDJson into a FHIR "collection"-type Bundle populated with the resources described in the NDJson.
924         * It will throw an exception in the event where it is asked to encode to anything other than a FHIR Bundle
925         * or where it is asked to decode into anything other than a FHIR Bundle.
926         * </p>
927         */
928        public IParser newNDJsonParser() {
929                return new NDJsonParser(this, myParserErrorHandler);
930        }
931
932        /**
933         * Create and return a new RDF parser.
934         *
935         * <p>
936         * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread
937         * or every message being parsed/encoded.
938         * </p>
939         * <p>
940         * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed
941         * without incurring any performance penalty
942         * </p>
943         */
944        public IParser newRDFParser() {
945                return new RDFParser(this, myParserErrorHandler, Lang.TURTLE);
946        }
947
948        /**
949         * Instantiates a new client instance. This method requires an interface which is defined specifically for your use
950         * cases to contain methods for each of the RESTful operations you wish to implement (e.g. "read ImagingStudy",
951         * "search Patient by identifier", etc.). This interface must extend {@link IRestfulClient} (or commonly its
952         * sub-interface {@link IBasicClient}). See the <a
953         * href="https://hapifhir.io/hapi-fhir/docs/client/introduction.html">RESTful Client</a> documentation for more
954         * information on how to define this interface.
955         *
956         * <p>
957         * Performance Note: <b>This method is cheap</b> to call, and may be called once for every operation invocation
958         * without incurring any performance penalty
959         * </p>
960         *
961         * @param theClientType The client type, which is an interface type to be instantiated
962         * @param theServerBase The URL of the base for the restful FHIR server to connect to
963         * @return A newly created client
964         * @throws ConfigurationException If the interface type is not an interface
965         */
966        public <T extends IRestfulClient> T newRestfulClient(final Class<T> theClientType, final String theServerBase) {
967                return getRestfulClientFactory().newClient(theClientType, theServerBase);
968        }
969
970        /**
971         * Instantiates a new generic client. A generic client is able to perform any of the FHIR RESTful operations against
972         * a compliant server, but does not have methods defining the specific functionality required (as is the case with
973         * {@link #newRestfulClient(Class, String) non-generic clients}).
974         *
975         * <p>
976         * Performance Note: This method performs an additional GET request to /metadata before
977         * the desired request is performed.
978         * </p>
979         *
980         * @param theServerBase The URL of the base for the restful FHIR server to connect to
981         */
982        public IGenericClient newRestfulGenericClient(final String theServerBase) {
983                return getRestfulClientFactory().newGenericClient(theServerBase);
984        }
985
986        public FhirTerser newTerser() {
987                return new FhirTerser(this);
988        }
989
990        /**
991         * Create a new validator instance.
992         * <p>
993         * Note on thread safety: Validators are thread safe, you may use a single validator
994         * in multiple threads. (This is in contrast to parsers)
995         * </p>
996         */
997        public FhirValidator newValidator() {
998                return myFhirValidatorFactory.newFhirValidator(this);
999        }
1000
1001        public ViewGenerator newViewGenerator() {
1002                return new ViewGenerator(this);
1003        }
1004
1005        /**
1006         * Create and return a new XML parser.
1007         *
1008         * <p>
1009         * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread
1010         * or every message being parsed/encoded.
1011         * </p>
1012         * <p>
1013         * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed
1014         * without incurring any performance penalty
1015         * </p>
1016         */
1017        public IParser newXmlParser() {
1018                return new XmlParser(this, myParserErrorHandler);
1019        }
1020
1021        /**
1022         * This method may be used to register a custom resource or datatype. Note that by using
1023         * custom types, you are creating a system that will not interoperate with other systems that
1024         * do not know about your custom type. There are valid reasons however for wanting to create
1025         * custom types and this method can be used to enable them.
1026         * <p>
1027         * <b>THREAD SAFETY WARNING:</b> This method is not thread safe. It should be called before any
1028         * threads are able to call any methods on this context.
1029         * </p>
1030         *
1031         * @param theType The custom type to add (must not be <code>null</code>)
1032         */
1033        public void registerCustomType(final Class<? extends IBase> theType) {
1034                Validate.notNull(theType, "theType must not be null");
1035                ensureCustomTypeList();
1036                myCustomTypes.add(theType);
1037                myResourceNames = null;
1038        }
1039
1040        /**
1041         * This method may be used to register a custom resource or datatype. Note that by using
1042         * custom types, you are creating a system that will not interoperate with other systems that
1043         * do not know about your custom type. There are valid reasons however for wanting to create
1044         * custom types and this method can be used to enable them.
1045         * <p>
1046         * <b>THREAD SAFETY WARNING:</b> This method is not thread safe. It should be called before any
1047         * threads are able to call any methods on this context.
1048         * </p>
1049         *
1050         * @param theTypes The custom types to add (must not be <code>null</code> or contain null elements in the collection)
1051         */
1052        public void registerCustomTypes(final Collection<Class<? extends IBase>> theTypes) {
1053                Validate.notNull(theTypes, "theTypes must not be null");
1054                Validate.noNullElements(theTypes.toArray(), "theTypes must not contain any null elements");
1055
1056                ensureCustomTypeList();
1057
1058                myCustomTypes.addAll(theTypes);
1059                myResourceNames = null;
1060        }
1061
1062        private BaseRuntimeElementDefinition<?> scanDatatype(final Class<? extends IElement> theResourceType) {
1063                ArrayList<Class<? extends IElement>> resourceTypes = new ArrayList<>();
1064                resourceTypes.add(theResourceType);
1065                Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> defs = scanResourceTypes(resourceTypes);
1066                return defs.get(theResourceType);
1067        }
1068
1069        private RuntimeResourceDefinition scanResourceType(final Class<? extends IBaseResource> theResourceType) {
1070                ArrayList<Class<? extends IElement>> resourceTypes = new ArrayList<>();
1071                resourceTypes.add(theResourceType);
1072                Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> defs = scanResourceTypes(resourceTypes);
1073                return (RuntimeResourceDefinition) defs.get(theResourceType);
1074        }
1075
1076        private synchronized Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> scanResourceTypes(
1077                        final Collection<Class<? extends IElement>> theResourceTypes) {
1078                List<Class<? extends IBase>> typesToScan = new ArrayList<>();
1079                if (theResourceTypes != null) {
1080                        typesToScan.addAll(theResourceTypes);
1081                }
1082                if (myCustomTypes != null) {
1083                        typesToScan.addAll(myCustomTypes);
1084
1085                        myCustomResourceNames.addAll(myCustomTypes.stream()
1086                                        .map(customType -> Optional.ofNullable(customType.getAnnotation(ResourceDef.class))
1087                                                        .map(ResourceDef::name)
1088                                                        .orElse(null)) // This will be caught by the call to ModelScanner
1089                                        .filter(Objects::nonNull)
1090                                        .collect(Collectors.toSet()));
1091
1092                        myCustomTypes = null;
1093                }
1094
1095                ModelScanner scanner = new ModelScanner(this, myVersion.getVersion(), myClassToElementDefinition, typesToScan);
1096                if (myRuntimeChildUndeclaredExtensionDefinition == null) {
1097                        myRuntimeChildUndeclaredExtensionDefinition = scanner.getRuntimeChildUndeclaredExtensionDefinition();
1098                }
1099
1100                Map<String, BaseRuntimeElementDefinition<?>> nameToElementDefinition = new HashMap<>();
1101                nameToElementDefinition.putAll(myNameToElementDefinition);
1102                for (Entry<String, BaseRuntimeElementDefinition<?>> next :
1103                                scanner.getNameToElementDefinitions().entrySet()) {
1104                        if (!nameToElementDefinition.containsKey(next.getKey())) {
1105                                nameToElementDefinition.put(next.getKey().toLowerCase(), next.getValue());
1106                        }
1107                }
1108
1109                Map<String, RuntimeResourceDefinition> nameToResourceDefinition = new HashMap<>();
1110                nameToResourceDefinition.putAll(myNameToResourceDefinition);
1111                for (Entry<String, RuntimeResourceDefinition> next :
1112                                scanner.getNameToResourceDefinition().entrySet()) {
1113                        if (!nameToResourceDefinition.containsKey(next.getKey())) {
1114                                nameToResourceDefinition.put(next.getKey(), next.getValue());
1115                        }
1116                }
1117
1118                Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> classToElementDefinition = new HashMap<>();
1119                classToElementDefinition.putAll(myClassToElementDefinition);
1120                classToElementDefinition.putAll(scanner.getClassToElementDefinitions());
1121                for (BaseRuntimeElementDefinition<?> next : classToElementDefinition.values()) {
1122                        if (next instanceof RuntimeResourceDefinition) {
1123                                if ("Bundle".equals(next.getName())) {
1124                                        if (!IBaseBundle.class.isAssignableFrom(next.getImplementingClass())) {
1125                                                throw new ConfigurationException(Msg.code(1687)
1126                                                                + "Resource type declares resource name Bundle but does not implement IBaseBundle");
1127                                        }
1128                                }
1129                        }
1130                }
1131
1132                Map<String, RuntimeResourceDefinition> idToElementDefinition = new HashMap<>();
1133                idToElementDefinition.putAll(myIdToResourceDefinition);
1134                idToElementDefinition.putAll(scanner.getIdToResourceDefinition());
1135
1136                myNameToElementDefinition = nameToElementDefinition;
1137                myClassToElementDefinition = classToElementDefinition;
1138                myIdToResourceDefinition = idToElementDefinition;
1139                myNameToResourceDefinition = nameToResourceDefinition;
1140
1141                myNameToResourceType = scanner.getNameToResourceType();
1142
1143                myInitialized = true;
1144                return classToElementDefinition;
1145        }
1146
1147        /**
1148         * Sets the default type which will be used when parsing a resource that is found to be
1149         * of the given profile.
1150         * <p>
1151         * For example, this method is invoked with the profile string of
1152         * <code>"http://example.com/some_patient_profile"</code> and the type of <code>MyPatient.class</code>,
1153         * if the parser is parsing a resource and finds that it declares that it conforms to that profile,
1154         * the <code>MyPatient</code> type will be used unless otherwise specified.
1155         * </p>
1156         *
1157         * @param theProfile The profile string, e.g. <code>"http://example.com/some_patient_profile"</code>. Must not be
1158         *                   <code>null</code> or empty.
1159         * @param theClass   The resource type, or <code>null</code> to clear any existing type
1160         */
1161        public void setDefaultTypeForProfile(final String theProfile, final Class<? extends IBaseResource> theClass) {
1162                Validate.notBlank(theProfile, "theProfile must not be null or empty");
1163                if (theClass == null) {
1164                        myDefaultTypeForProfile.remove(theProfile);
1165                } else {
1166                        myDefaultTypeForProfile.put(theProfile, theClass);
1167                }
1168        }
1169
1170        /**
1171         * Sets a parser error handler to use by default on all parsers
1172         *
1173         * @param theParserErrorHandler The error handler
1174         */
1175        public FhirContext setParserErrorHandler(final IParserErrorHandler theParserErrorHandler) {
1176                Validate.notNull(theParserErrorHandler, "theParserErrorHandler must not be null");
1177                myParserErrorHandler = theParserErrorHandler;
1178                return this;
1179        }
1180
1181        /**
1182         * Set the factory method used to create FhirValidator instances
1183         *
1184         * @param theFhirValidatorFactory
1185         * @return this
1186         * @since 5.6.0
1187         */
1188        public FhirContext setFhirValidatorFactory(IFhirValidatorFactory theFhirValidatorFactory) {
1189                myFhirValidatorFactory = theFhirValidatorFactory;
1190                return this;
1191        }
1192
1193        @SuppressWarnings({"cast"})
1194        private List<Class<? extends IElement>> toElementList(
1195                        final Collection<Class<? extends IBaseResource>> theResourceTypes) {
1196                if (theResourceTypes == null) {
1197                        return null;
1198                }
1199                List<Class<? extends IElement>> resTypes = new ArrayList<>();
1200                for (Class<? extends IBaseResource> next : theResourceTypes) {
1201                        resTypes.add(next);
1202                }
1203                return resTypes;
1204        }
1205
1206        private void validateInitialized() {
1207                // See #610
1208                if (!myInitialized) {
1209                        synchronized (this) {
1210                                if (!myInitialized && !myInitializing) {
1211                                        myInitializing = true;
1212                                        try {
1213                                                scanResourceTypes(toElementList(myResourceTypesToScan));
1214                                        } catch (Exception e) {
1215                                                ourLog.error("Failed to initialize FhirContext", e);
1216                                                throw e;
1217                                        } finally {
1218                                                myInitializing = false;
1219                                        }
1220                                }
1221                        }
1222                }
1223        }
1224
1225        @Override
1226        public String toString() {
1227                return "FhirContext[" + myVersion.getVersion().name() + "]";
1228        }
1229
1230        // TODO KHS add the other primitive types
1231        @Deprecated(since = "6.6.0", forRemoval = true)
1232        public IPrimitiveType<Boolean> getPrimitiveBoolean(Boolean theValue) {
1233                return newPrimitiveBoolean(theValue);
1234        }
1235
1236        public IPrimitiveType<Boolean> newPrimitiveBoolean(Boolean theValue) {
1237                IPrimitiveType<Boolean> retval =
1238                                (IPrimitiveType<Boolean>) getElementDefinition("boolean").newInstance();
1239                retval.setValue(theValue);
1240                return retval;
1241        }
1242
1243        public IPrimitiveType<String> newPrimitiveString(String theValue) {
1244                IPrimitiveType<String> retval =
1245                                (IPrimitiveType<String>) getElementDefinition("string").newInstance();
1246                retval.setValue(theValue);
1247                return retval;
1248        }
1249
1250        private static boolean tryToInitParser(Runnable run) {
1251                boolean retVal;
1252                try {
1253                        run.run();
1254                        retVal = true;
1255                } catch (UnsupportedClassVersionError | Exception | NoClassDefFoundError e) {
1256                        retVal = false;
1257                }
1258                return retVal;
1259        }
1260
1261        /**
1262         * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU2}
1263         */
1264        public static FhirContext forDstu2() {
1265                return new FhirContext(FhirVersionEnum.DSTU2);
1266        }
1267
1268        /**
1269         * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2_HL7ORG DSTU2} (using the Reference
1270         * Implementation Structures)
1271         */
1272        public static FhirContext forDstu2Hl7Org() {
1273                return new FhirContext(FhirVersionEnum.DSTU2_HL7ORG);
1274        }
1275
1276        /**
1277         * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU2} (2016 May DSTU3 Snapshot)
1278         */
1279        public static FhirContext forDstu2_1() {
1280                return new FhirContext(FhirVersionEnum.DSTU2_1);
1281        }
1282
1283        /**
1284         * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU3 DSTU3}
1285         *
1286         * @since 1.4
1287         */
1288        public static FhirContext forDstu3() {
1289                return new FhirContext(FhirVersionEnum.DSTU3);
1290        }
1291
1292        /**
1293         * Creates and returns a new FhirContext with version {@link FhirVersionEnum#R4 R4}
1294         *
1295         * @since 3.0.0
1296         */
1297        public static FhirContext forR4() {
1298                return new FhirContext(FhirVersionEnum.R4);
1299        }
1300
1301        /**
1302         * Creates and returns a new FhirContext with version {@link FhirVersionEnum#R4B R4B}
1303         *
1304         * @since 6.2.0
1305         */
1306        public static FhirContext forR4B() {
1307                return new FhirContext(FhirVersionEnum.R4B);
1308        }
1309
1310        /**
1311         * Creates and returns a new FhirContext with version {@link FhirVersionEnum#R5 R5}
1312         *
1313         * @since 4.0.0
1314         */
1315        public static FhirContext forR5() {
1316                return new FhirContext(FhirVersionEnum.R5);
1317        }
1318
1319        /**
1320         * Returns a statically cached {@literal FhirContext} instance for the given version, creating one if none exists in the
1321         * cache. One FhirContext will be kept in the cache for each FHIR version that is requested (by calling
1322         * this method for that version), and the cache will never be expired.
1323         *
1324         * @since 5.1.0
1325         */
1326        public static FhirContext forCached(FhirVersionEnum theFhirVersionEnum) {
1327                return ourStaticContexts.computeIfAbsent(theFhirVersionEnum, FhirContext::forVersion);
1328        }
1329
1330        /**
1331         * An uncached version of forCached()
1332         * @return a new FhirContext for theFhirVersionEnum
1333         */
1334        public static FhirContext forVersion(FhirVersionEnum theFhirVersionEnum) {
1335                return new FhirContext(theFhirVersionEnum);
1336        }
1337
1338        private static Collection<Class<? extends IBaseResource>> toCollection(
1339                        Class<? extends IBaseResource> theResourceType) {
1340                ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<>(1);
1341                retVal.add(theResourceType);
1342                return retVal;
1343        }
1344
1345        @SuppressWarnings("unchecked")
1346        private static List<Class<? extends IBaseResource>> toCollection(final Class<?>[] theResourceTypes) {
1347                ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<Class<? extends IBaseResource>>(1);
1348                for (Class<?> clazz : theResourceTypes) {
1349                        if (!IResource.class.isAssignableFrom(clazz)) {
1350                                throw new IllegalArgumentException(Msg.code(1688) + clazz.getCanonicalName() + " is not an instance of "
1351                                                + IResource.class.getSimpleName());
1352                        }
1353                        retVal.add((Class<? extends IResource>) clazz);
1354                }
1355                return retVal;
1356        }
1357}