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