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