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