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 IBaseResource expandValueSet(IBaseResource theInput) {
139 		ValueSet valueSetToExpand = (ValueSet) theInput;
140 		return super.expandValueSet(valueSetToExpand);
141 	}
142 
143 
144 	@Override
145 	public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
146 		ValueSet valueSetToExpand = new ValueSet();
147 		valueSetToExpand.getCompose().addInclude(theInclude);
148 		return super.expandValueSet(valueSetToExpand).getExpansion();
149 	}
150 
151 	@Override
152 	public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
153 		return null;
154 	}
155 
156 	@Override
157 	public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
158 		return Collections.emptyList();
159 	}
160 
161 	@CoverageIgnore
162 	@Override
163 	public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
164 		return null;
165 	}
166 
167 	@Override
168 	public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
169 		return null;
170 	}
171 
172 	@CoverageIgnore
173 	@Override
174 	public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
175 		return null;
176 	}
177 
178 	private void findCodesAbove(CodeSystem theSystem, String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate) {
179 		List<ConceptDefinitionComponent> conceptList = theSystem.getConcept();
180 		for (ConceptDefinitionComponent next : conceptList) {
181 			addTreeIfItContainsCode(theSystemString, next, theCode, theListToPopulate);
182 		}
183 	}
184 
185 	@Override
186 	public List<VersionIndependentConcept> findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) {
187 		ArrayList<VersionIndependentConcept> retVal = new ArrayList<>();
188 		CodeSystem system = myValidationSupport.fetchCodeSystem(myContext, theSystem);
189 		if (system != null) {
190 			findCodesAbove(system, theSystem, theCode, retVal);
191 		}
192 		return retVal;
193 	}
194 
195 	private void findCodesBelow(CodeSystem theSystem, String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate) {
196 		List<ConceptDefinitionComponent> conceptList = theSystem.getConcept();
197 		findCodesBelow(theSystemString, theCode, theListToPopulate, conceptList);
198 	}
199 
200 	private void findCodesBelow(String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate, List<ConceptDefinitionComponent> conceptList) {
201 		for (ConceptDefinitionComponent next : conceptList) {
202 			if (theCode.equals(next.getCode())) {
203 				addAllChildren(theSystemString, next, theListToPopulate);
204 			} else {
205 				findCodesBelow(theSystemString, theCode, theListToPopulate, next.getConcept());
206 			}
207 		}
208 	}
209 
210 	@Override
211 	public List<VersionIndependentConcept> findCodesBelowUsingBuiltInSystems(String theSystem, String theCode) {
212 		ArrayList<VersionIndependentConcept> retVal = new ArrayList<>();
213 		CodeSystem system = myValidationSupport.fetchCodeSystem(myContext, theSystem);
214 		if (system != null) {
215 			findCodesBelow(system, theSystem, theCode, retVal);
216 		}
217 		return retVal;
218 	}
219 
220 	@Override
221 	protected CodeSystem getCodeSystemFromContext(String theSystem) {
222 		return myValidationSupport.fetchCodeSystem(myContext, theSystem);
223 	}
224 
225 	@Override
226 	public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
227 		return myTerminologySvc.supportsSystem(theSystem);
228 	}
229 
230 	@CoverageIgnore
231 	@Override
232 	public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
233 		TermConcept code = myTerminologySvc.findCode(theCodeSystem, theCode);
234 		if (code != null) {
235 			ConceptDefinitionComponent def = new ConceptDefinitionComponent();
236 			def.setCode(code.getCode());
237 			def.setDisplay(code.getDisplay());
238 			CodeValidationResult retVal = new CodeValidationResult(def);
239 			retVal.setProperties(code.toValidationProperties());
240 			return retVal;
241 		}
242 
243 		return new CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode);
244 	}
245 
246 }