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}