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.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 jakarta.annotation.Nullable; 027import org.hl7.fhir.instance.model.api.IBase; 028import org.hl7.fhir.instance.model.api.IBaseResource; 029import org.hl7.fhir.instance.model.api.IPrimitiveType; 030 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 = 050 Collections.synchronizedMap(new HashMap<>()); 051 private final FhirContext myCtx; 052 /** 053 * This module just delegates all calls to a concrete implementation which will 054 * be in this field. Which implementation gets used depends on the FHIR version. 055 */ 056 private final IValidationSupport myDelegate; 057 058 private final Runnable myFlush; 059 060 /** 061 * Constructor 062 * 063 * @param theFhirContext The context to use 064 */ 065 public DefaultProfileValidationSupport(FhirContext theFhirContext) { 066 myCtx = theFhirContext; 067 068 IValidationSupport strategy; 069 synchronized (ourImplementations) { 070 strategy = ourImplementations.get(theFhirContext.getVersion().getVersion()); 071 072 if (strategy == null) { 073 if (theFhirContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R5)) { 074 /* 075 * I don't love that we use reflection here, but this class is in 076 * hapi-fhir-base, and the class we're creating is in 077 * hapi-fhir-validation. There are complicated dependency chains that 078 * make this hard to clean up. At some point it'd be nice to figure out 079 * a cleaner solution though. 080 */ 081 strategy = ReflectionUtil.newInstance( 082 "org.hl7.fhir.common.hapi.validation.support.DefaultProfileValidationSupportNpmStrategy", 083 IValidationSupport.class, 084 new Class[] {FhirContext.class}, 085 new Object[] {theFhirContext}); 086 ((ILockable) strategy).lock(); 087 } else { 088 strategy = new DefaultProfileValidationSupportBundleStrategy(theFhirContext); 089 } 090 ourImplementations.put(theFhirContext.getVersion().getVersion(), strategy); 091 } 092 } 093 094 myDelegate = strategy; 095 if (myDelegate instanceof DefaultProfileValidationSupportBundleStrategy) { 096 myFlush = () -> ((DefaultProfileValidationSupportBundleStrategy) myDelegate).flush(); 097 } else { 098 myFlush = () -> {}; 099 } 100 } 101 102 @Override 103 public String getName() { 104 return myCtx.getVersion().getVersion() + " FHIR Standard Profile Validation Support"; 105 } 106 107 @Override 108 public List<IBaseResource> fetchAllConformanceResources() { 109 return myDelegate.fetchAllConformanceResources(); 110 } 111 112 @Override 113 public <T extends IBaseResource> List<T> fetchAllStructureDefinitions() { 114 return myDelegate.fetchAllStructureDefinitions(); 115 } 116 117 @Nullable 118 @Override 119 public <T extends IBaseResource> List<T> fetchAllNonBaseStructureDefinitions() { 120 return myDelegate.fetchAllNonBaseStructureDefinitions(); 121 } 122 123 @Override 124 public IBaseResource fetchCodeSystem(String theSystem) { 125 return myDelegate.fetchCodeSystem(theSystem); 126 } 127 128 @Override 129 public IBaseResource fetchStructureDefinition(String theUrl) { 130 return myDelegate.fetchStructureDefinition(theUrl); 131 } 132 133 @Override 134 public IBaseResource fetchValueSet(String theUrl) { 135 return myDelegate.fetchValueSet(theUrl); 136 } 137 138 public void flush() { 139 myFlush.run(); 140 } 141 142 @Override 143 public FhirContext getFhirContext() { 144 return myCtx; 145 } 146 147 @Nullable 148 public static String getConformanceResourceUrl(FhirContext theFhirContext, IBaseResource theResource) { 149 String urlValueString = null; 150 Optional<IBase> urlValue = theFhirContext 151 .getResourceDefinition(theResource) 152 .getChildByName("url") 153 .getAccessor() 154 .getFirstValueOrNull(theResource); 155 if (urlValue.isPresent()) { 156 IPrimitiveType<?> urlValueType = (IPrimitiveType<?>) urlValue.get(); 157 urlValueString = urlValueType.getValueAsString(); 158 } 159 return urlValueString; 160 } 161}