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