View Javadoc
1   package ca.uhn.fhir.jpa.term;
2   
3   import ca.uhn.fhir.context.FhirContext;
4   import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
5   import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
6   import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
7   import ca.uhn.fhir.jpa.entity.TermConcept;
8   import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
9   import ca.uhn.fhir.util.CoverageIgnore;
10  import ca.uhn.fhir.util.UrlUtil;
11  import org.hl7.fhir.convertors.VersionConvertor_30_40;
12  import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
13  import org.hl7.fhir.dstu3.model.*;
14  import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
15  import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
16  import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
17  import org.hl7.fhir.exceptions.FHIRException;
18  import org.hl7.fhir.instance.model.api.IBaseResource;
19  import org.hl7.fhir.instance.model.api.IIdType;
20  import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
21  import org.springframework.beans.factory.annotation.Autowired;
22  import org.springframework.beans.factory.annotation.Qualifier;
23  
24  import javax.persistence.EntityManager;
25  import javax.persistence.PersistenceContext;
26  import javax.persistence.PersistenceContextType;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.List;
30  
31  import static org.apache.commons.lang3.StringUtils.isBlank;
32  import static org.apache.commons.lang3.StringUtils.isNotBlank;
33  
34  /*
35   * #%L
36   * HAPI FHIR JPA Server
37   * %%
38   * Copyright (C) 2014 - 2018 University Health Network
39   * %%
40   * Licensed under the Apache License, Version 2.0 (the "License");
41   * you may not use this file except in compliance with the License.
42   * You may obtain a copy of the License at
43   * 
44   * http://www.apache.org/licenses/LICENSE-2.0
45   * 
46   * Unless required by applicable law or agreed to in writing, software
47   * distributed under the License is distributed on an "AS IS" BASIS,
48   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
49   * See the License for the specific language governing permissions and
50   * limitations under the License.
51   * #L%
52   */
53  
54  public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implements IValidationSupport, IHapiTerminologySvcDstu3 {
55  
56  	@PersistenceContext(type = PersistenceContextType.TRANSACTION)
57  	protected EntityManager myEntityManager;
58  	@Autowired
59  	protected FhirContext myContext;
60  	@Autowired
61  	protected ITermCodeSystemDao myCodeSystemDao;
62  	@Autowired
63  	@Qualifier("myValueSetDaoDstu3")
64  	private IFhirResourceDao<ValueSet> myValueSetResourceDao;
65  	@Autowired
66  	@Qualifier("myConceptMapDaoDstu3")
67  	private IFhirResourceDao<ConceptMap> myConceptMapResourceDao;
68  	@Autowired
69  	private IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> myCodeSystemResourceDao;
70  	@Autowired
71  	private IValidationSupport myValidationSupport;
72  	@Autowired
73  	private IHapiTerminologySvc myTerminologySvc;
74  
75  	/**
76  	 * Constructor
77  	 */
78  	public HapiTerminologySvcDstu3() {
79  		super();
80  	}
81  
82  	private void addAllChildren(String theSystemString, ConceptDefinitionComponent theCode, List<VersionIndependentConcept> theListToPopulate) {
83  		if (isNotBlank(theCode.getCode())) {
84  			theListToPopulate.add(new VersionIndependentConcept(theSystemString, theCode.getCode()));
85  		}
86  		for (ConceptDefinitionComponent nextChild : theCode.getConcept()) {
87  			addAllChildren(theSystemString, nextChild, theListToPopulate);
88  		}
89  	}
90  
91  	private boolean addTreeIfItContainsCode(String theSystemString, ConceptDefinitionComponent theNext, String theCode, List<VersionIndependentConcept> theListToPopulate) {
92  		boolean foundCodeInChild = false;
93  		for (ConceptDefinitionComponent nextChild : theNext.getConcept()) {
94  			foundCodeInChild |= addTreeIfItContainsCode(theSystemString, nextChild, theCode, theListToPopulate);
95  		}
96  
97  		if (theCode.equals(theNext.getCode()) || foundCodeInChild) {
98  			theListToPopulate.add(new VersionIndependentConcept(theSystemString, theNext.getCode()));
99  			return true;
100 		}
101 
102 		return false;
103 	}
104 
105 	@Override
106 	protected IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource) {
107 		CodeSystem resourceToStore;
108 		try {
109 			resourceToStore = VersionConvertor_30_40.convertCodeSystem(theCodeSystemResource);
110 		} catch (FHIRException e) {
111 			throw new InternalErrorException(e);
112 		}
113 		if (isBlank(resourceToStore.getIdElement().getIdPart())) {
114 			String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl());
115 			return myCodeSystemResourceDao.update(resourceToStore, matchUrl).getId();
116 		} else {
117 			return myCodeSystemResourceDao.update(resourceToStore).getId();
118 		}
119 	}
120 
121 	@Override
122 	protected void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap) {
123 		ConceptMap resourceToStore;
124 		try {
125 			resourceToStore = VersionConvertor_30_40.convertConceptMap(theConceptMap);
126 		} catch (FHIRException e) {
127 			throw new InternalErrorException(e);
128 		}
129 		if (isBlank(resourceToStore.getIdElement().getIdPart())) {
130 			String matchUrl = "ConceptMap?url=" + UrlUtil.escapeUrlParam(theConceptMap.getUrl());
131 			myConceptMapResourceDao.update(resourceToStore, matchUrl);
132 		} else {
133 			myConceptMapResourceDao.update(resourceToStore);
134 		}
135 	}
136 
137 	@Override
138 	protected void createOrUpdateValueSet(org.hl7.fhir.r4.model.ValueSet theValueSet) {
139 		ValueSet valueSetDstu3;
140 		try {
141 			valueSetDstu3 = VersionConvertor_30_40.convertValueSet(theValueSet);
142 		} catch (FHIRException e) {
143 			throw new InternalErrorException(e);
144 		}
145 
146 		if (isBlank(valueSetDstu3.getIdElement().getIdPart())) {
147 			String matchUrl = "ValueSet?url=" + UrlUtil.escapeUrlParam(theValueSet.getUrl());
148 			myValueSetResourceDao.update(valueSetDstu3, matchUrl);
149 		} else {
150 			myValueSetResourceDao.update(valueSetDstu3);
151 		}
152 	}
153 
154 	@Override
155 	public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
156 		ValueSet valueSetToExpand = new ValueSet();
157 		valueSetToExpand.getCompose().addInclude(theInclude);
158 
159 		try {
160 			org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
161 			valueSetToExpandR4 = VersionConvertor_30_40.convertValueSet(valueSetToExpand);
162 			org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent expandedR4 = super.expandValueSet(valueSetToExpandR4).getExpansion();
163 			return VersionConvertor_30_40.convertValueSetExpansionComponent(expandedR4);
164 		} catch (FHIRException e) {
165 			throw new InternalErrorException(e);
166 		}
167 	}
168 
169 	@Override
170 	public IBaseResource expandValueSet(IBaseResource theInput) {
171 		ValueSet valueSetToExpand = (ValueSet) theInput;
172 
173 		try {
174 			org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
175 			valueSetToExpandR4 = VersionConvertor_30_40.convertValueSet(valueSetToExpand);
176 			org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSet(valueSetToExpandR4);
177 			return VersionConvertor_30_40.convertValueSet(expandedR4);
178 		} catch (FHIRException e) {
179 			throw new InternalErrorException(e);
180 		}
181 	}
182 
183 	@Override
184 	public List<VersionIndependentConcept> expandValueSet(String theValueSet) {
185 		ValueSet vs = myValidationSupport.fetchResource(myContext, ValueSet.class, theValueSet);
186 		if (vs == null) {
187 			return Collections.emptyList();
188 		}
189 
190 		org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
191 		try {
192 			valueSetToExpandR4 = VersionConvertor_30_40.convertValueSet(vs);
193 		} catch (FHIRException e) {
194 			throw new InternalErrorException(e);
195 		}
196 
197 
198 		return expandValueSetAndReturnVersionIndependentConcepts(valueSetToExpandR4);
199 	}
200 
201 	@Override
202 	public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
203 		return null;
204 	}
205 
206 	@Override
207 	public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
208 		return Collections.emptyList();
209 	}
210 
211 	@CoverageIgnore
212 	@Override
213 	public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
214 		return null;
215 	}
216 
217 	@Override
218 	public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
219 		return null;
220 	}
221 
222 	@CoverageIgnore
223 	@Override
224 	public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
225 		return null;
226 	}
227 
228 	private void findCodesAbove(CodeSystem theSystem, String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate) {
229 		List<ConceptDefinitionComponent> conceptList = theSystem.getConcept();
230 		for (ConceptDefinitionComponent next : conceptList) {
231 			addTreeIfItContainsCode(theSystemString, next, theCode, theListToPopulate);
232 		}
233 	}
234 
235 	@Override
236 	public List<VersionIndependentConcept> findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) {
237 		ArrayList<VersionIndependentConcept> retVal = new ArrayList<>();
238 		CodeSystem system = myValidationSupport.fetchCodeSystem(myContext, theSystem);
239 		if (system != null) {
240 			findCodesAbove(system, theSystem, theCode, retVal);
241 		}
242 		return retVal;
243 	}
244 
245 	private void findCodesBelow(CodeSystem theSystem, String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate) {
246 		List<ConceptDefinitionComponent> conceptList = theSystem.getConcept();
247 		findCodesBelow(theSystemString, theCode, theListToPopulate, conceptList);
248 	}
249 
250 	private void findCodesBelow(String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate, List<ConceptDefinitionComponent> conceptList) {
251 		for (ConceptDefinitionComponent next : conceptList) {
252 			if (theCode.equals(next.getCode())) {
253 				addAllChildren(theSystemString, next, theListToPopulate);
254 			} else {
255 				findCodesBelow(theSystemString, theCode, theListToPopulate, next.getConcept());
256 			}
257 		}
258 	}
259 
260 	@Override
261 	public List<VersionIndependentConcept> findCodesBelowUsingBuiltInSystems(String theSystem, String theCode) {
262 		ArrayList<VersionIndependentConcept> retVal = new ArrayList<>();
263 		CodeSystem system = myValidationSupport.fetchCodeSystem(myContext, theSystem);
264 		if (system != null) {
265 			findCodesBelow(system, theSystem, theCode, retVal);
266 		}
267 		return retVal;
268 	}
269 
270 	@Override
271 	protected org.hl7.fhir.r4.model.CodeSystem getCodeSystemFromContext(String theSystem) {
272 		CodeSystem codeSystem = myValidationSupport.fetchCodeSystem(myContext, theSystem);
273 		try {
274 			return VersionConvertor_30_40.convertCodeSystem(codeSystem);
275 		} catch (FHIRException e) {
276 			throw new InternalErrorException(e);
277 		}
278 	}
279 
280 	@Override
281 	public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
282 		return myTerminologySvc.supportsSystem(theSystem);
283 	}
284 
285 	@CoverageIgnore
286 	@Override
287 	public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
288 		TermConcept code = myTerminologySvc.findCode(theCodeSystem, theCode);
289 		if (code != null) {
290 			ConceptDefinitionComponent def = new ConceptDefinitionComponent();
291 			def.setCode(code.getCode());
292 			def.setDisplay(code.getDisplay());
293 			CodeValidationResult retVal = new CodeValidationResult(def);
294 			retVal.setProperties(code.toValidationProperties());
295 			retVal.setCodeSystemName(code.getCodeSystemVersion().getCodeSystem().getName());
296 			return retVal;
297 		}
298 
299 		return new CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode);
300 	}
301 
302 
303 }