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 List<VersionIndependentConcept> expandValueSet(String theValueSet) {
171 		ValueSet vs = myValidationSupport.fetchResource(myContext, ValueSet.class, theValueSet);
172 		if (vs == null) {
173 			return Collections.emptyList();
174 		}
175 
176 		org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
177 		try {
178 			valueSetToExpandR4 = VersionConvertor_30_40.convertValueSet(vs);
179 		} catch (FHIRException e) {
180 			throw new InternalErrorException(e);
181 		}
182 
183 
184 		return expandValueSetAndReturnVersionIndependentConcepts(valueSetToExpandR4);
185 	}
186 
187 	@Override
188 	public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
189 		return null;
190 	}
191 
192 	@Override
193 	public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
194 		return Collections.emptyList();
195 	}
196 
197 	@CoverageIgnore
198 	@Override
199 	public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
200 		return null;
201 	}
202 
203 	@Override
204 	public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
205 		return null;
206 	}
207 
208 	@CoverageIgnore
209 	@Override
210 	public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
211 		return null;
212 	}
213 
214 	private void findCodesAbove(CodeSystem theSystem, String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate) {
215 		List<ConceptDefinitionComponent> conceptList = theSystem.getConcept();
216 		for (ConceptDefinitionComponent next : conceptList) {
217 			addTreeIfItContainsCode(theSystemString, next, theCode, theListToPopulate);
218 		}
219 	}
220 
221 	@Override
222 	public List<VersionIndependentConcept> findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) {
223 		ArrayList<VersionIndependentConcept> retVal = new ArrayList<>();
224 		CodeSystem system = myValidationSupport.fetchCodeSystem(myContext, theSystem);
225 		if (system != null) {
226 			findCodesAbove(system, theSystem, theCode, retVal);
227 		}
228 		return retVal;
229 	}
230 
231 	private void findCodesBelow(CodeSystem theSystem, String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate) {
232 		List<ConceptDefinitionComponent> conceptList = theSystem.getConcept();
233 		findCodesBelow(theSystemString, theCode, theListToPopulate, conceptList);
234 	}
235 
236 	private void findCodesBelow(String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate, List<ConceptDefinitionComponent> conceptList) {
237 		for (ConceptDefinitionComponent next : conceptList) {
238 			if (theCode.equals(next.getCode())) {
239 				addAllChildren(theSystemString, next, theListToPopulate);
240 			} else {
241 				findCodesBelow(theSystemString, theCode, theListToPopulate, next.getConcept());
242 			}
243 		}
244 	}
245 
246 	@Override
247 	public List<VersionIndependentConcept> findCodesBelowUsingBuiltInSystems(String theSystem, String theCode) {
248 		ArrayList<VersionIndependentConcept> retVal = new ArrayList<>();
249 		CodeSystem system = myValidationSupport.fetchCodeSystem(myContext, theSystem);
250 		if (system != null) {
251 			findCodesBelow(system, theSystem, theCode, retVal);
252 		}
253 		return retVal;
254 	}
255 
256 	@Override
257 	protected org.hl7.fhir.r4.model.CodeSystem getCodeSystemFromContext(String theSystem) {
258 		CodeSystem codeSystem = myValidationSupport.fetchCodeSystem(myContext, theSystem);
259 		try {
260 			return VersionConvertor_30_40.convertCodeSystem(codeSystem);
261 		} catch (FHIRException e) {
262 			throw new InternalErrorException(e);
263 		}
264 	}
265 
266 	@Override
267 	public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
268 		return myTerminologySvc.supportsSystem(theSystem);
269 	}
270 
271 	@CoverageIgnore
272 	@Override
273 	public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
274 		TermConcept code = myTerminologySvc.findCode(theCodeSystem, theCode);
275 		if (code != null) {
276 			ConceptDefinitionComponent def = new ConceptDefinitionComponent();
277 			def.setCode(code.getCode());
278 			def.setDisplay(code.getDisplay());
279 			CodeValidationResult retVal = new CodeValidationResult(def);
280 			retVal.setProperties(code.toValidationProperties());
281 			retVal.setCodeSystemName(code.getCodeSystemVersion().getCodeSystem().getName());
282 			return retVal;
283 		}
284 
285 		return new CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode);
286 	}
287 
288 
289 }