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