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