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