001/*-
002 * #%L
003 * HAPI FHIR - Core Library
004 * %%
005 * Copyright (C) 2014 - 2023 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.context.support;
021
022import ca.uhn.fhir.context.FhirContext;
023import ca.uhn.fhir.context.FhirVersionEnum;
024import ca.uhn.fhir.util.ILockable;
025import ca.uhn.fhir.util.ReflectionUtil;
026import org.hl7.fhir.instance.model.api.IBase;
027import org.hl7.fhir.instance.model.api.IBaseResource;
028import org.hl7.fhir.instance.model.api.IPrimitiveType;
029
030import javax.annotation.Nullable;
031import java.util.Collections;
032import java.util.HashMap;
033import java.util.List;
034import java.util.Map;
035import java.util.Optional;
036
037/**
038 * This class returns the vocabulary that is shipped with the base FHIR
039 * specification.
040 *
041 * Note that this class is version aware. For example, a request for
042 * <code>http://foo-codesystem|123</code> will only return a value if
043 * the built in resource if the version matches. Unversioned URLs
044 * should generally be used, and will return whatever version is
045 * present.
046 */
047public class DefaultProfileValidationSupport implements IValidationSupport {
048
049        private static final Map<FhirVersionEnum, IValidationSupport> ourImplementations = Collections.synchronizedMap(new HashMap<>());
050        private final FhirContext myCtx;
051        /**
052         * This module just delegates all calls to a concrete implementation which will
053         * be in this field. Which implementation gets used depends on the FHIR version.
054         */
055        private final IValidationSupport myDelegate;
056        private final Runnable myFlush;
057
058        /**
059         * Constructor
060         *
061         * @param theFhirContext The context to use
062         */
063        public DefaultProfileValidationSupport(FhirContext theFhirContext) {
064                myCtx = theFhirContext;
065
066                IValidationSupport strategy;
067                synchronized (ourImplementations) {
068                        strategy = ourImplementations.get(theFhirContext.getVersion().getVersion());
069
070                        if (strategy == null) {
071                                if (theFhirContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R5)) {
072                                        /*
073                                         * I don't love that we use reflection here, but this class is in
074                                         * hapi-fhir-base, and the class we're creating is in
075                                         * hapi-fhir-validation. There are complicated dependency chains that
076                                         * make this hard to clean up. At some point it'd be nice to figure out
077                                         * a cleaner solution though.
078                                         */
079                                        strategy = ReflectionUtil.newInstance("org.hl7.fhir.common.hapi.validation.support.DefaultProfileValidationSupportNpmStrategy", IValidationSupport.class, new Class[]{FhirContext.class}, new Object[]{theFhirContext});
080                                        ((ILockable)strategy).lock();
081                                } else {
082                                        strategy = new DefaultProfileValidationSupportBundleStrategy(theFhirContext);
083                                }
084                                ourImplementations.put(theFhirContext.getVersion().getVersion(), strategy);
085                        }
086                }
087
088                myDelegate = strategy;
089                if (myDelegate instanceof DefaultProfileValidationSupportBundleStrategy) {
090                        myFlush = ()->((DefaultProfileValidationSupportBundleStrategy) myDelegate).flush();
091                } else {
092                        myFlush = ()->{};
093                }
094        }
095
096        @Override
097        public List<IBaseResource> fetchAllConformanceResources() {
098                return myDelegate.fetchAllConformanceResources();
099        }
100
101        @Override
102        public <T extends IBaseResource> List<T> fetchAllStructureDefinitions() {
103                return myDelegate.fetchAllStructureDefinitions();
104        }
105
106        @Nullable
107        @Override
108        public <T extends IBaseResource> List<T> fetchAllNonBaseStructureDefinitions() {
109                return myDelegate.fetchAllNonBaseStructureDefinitions();
110        }
111
112
113        @Override
114        public IBaseResource fetchCodeSystem(String theSystem) {
115                return myDelegate.fetchCodeSystem(theSystem);
116        }
117
118        @Override
119        public IBaseResource fetchStructureDefinition(String theUrl) {
120                return myDelegate.fetchStructureDefinition(theUrl);
121        }
122
123        @Override
124        public IBaseResource fetchValueSet(String theUrl) {
125                return myDelegate.fetchValueSet(theUrl);
126        }
127
128        public void flush() {
129                myFlush.run();
130        }
131
132        @Override
133        public FhirContext getFhirContext() {
134                return myCtx;
135        }
136
137
138        @Nullable
139        public static String getConformanceResourceUrl(FhirContext theFhirContext, IBaseResource theResource) {
140                String urlValueString = null;
141                Optional<IBase> urlValue = theFhirContext.getResourceDefinition(theResource).getChildByName("url").getAccessor().getFirstValueOrNull(theResource);
142                if (urlValue.isPresent()) {
143                        IPrimitiveType<?> urlValueType = (IPrimitiveType<?>) urlValue.get();
144                        urlValueString = urlValueType.getValueAsString();
145                }
146                return urlValueString;
147        }
148
149
150}