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