001package org.hl7.fhir.dstu3.hapi.ctx;
002
003import ca.uhn.fhir.context.FhirContext;
004import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
005import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
006import ca.uhn.fhir.util.CoverageIgnore;
007import org.apache.commons.lang3.Validate;
008import org.hl7.fhir.dstu3.context.IWorkerContext;
009import org.hl7.fhir.dstu3.formats.IParser;
010import org.hl7.fhir.dstu3.formats.ParserType;
011import org.hl7.fhir.dstu3.model.*;
012import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
013import org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceComponent;
014import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
015import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
016import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent;
017import org.hl7.fhir.dstu3.terminologies.ValueSetExpander;
018import org.hl7.fhir.dstu3.terminologies.ValueSetExpanderFactory;
019import org.hl7.fhir.dstu3.terminologies.ValueSetExpanderSimple;
020import org.hl7.fhir.dstu3.utils.INarrativeGenerator;
021import org.hl7.fhir.exceptions.FHIRException;
022import org.hl7.fhir.exceptions.TerminologyServiceException;
023import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
024
025import java.util.*;
026
027import static org.apache.commons.lang3.StringUtils.isBlank;
028import static org.apache.commons.lang3.StringUtils.isNotBlank;
029
030public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander, ValueSetExpanderFactory {
031        private final FhirContext myCtx;
032        private Map<String, Resource> myFetchedResourceCache = new HashMap<String, Resource>();
033        private IValidationSupport myValidationSupport;
034        private ExpansionProfile myExpansionProfile;
035
036        public HapiWorkerContext(FhirContext theCtx, IValidationSupport theValidationSupport) {
037                Validate.notNull(theCtx, "theCtx must not be null");
038                Validate.notNull(theValidationSupport, "theValidationSupport must not be null");
039                myCtx = theCtx;
040                myValidationSupport = theValidationSupport;
041        }
042
043        @Override
044        public List<StructureDefinition> allStructures() {
045                return myValidationSupport.fetchAllStructureDefinitions(myCtx);
046        }
047
048        @Override
049        public CodeSystem fetchCodeSystem(String theSystem) {
050                if (myValidationSupport == null) {
051                        return null;
052                } else {
053                        return myValidationSupport.fetchCodeSystem(myCtx, theSystem);
054                }
055        }
056
057        @Override
058        public <T extends Resource> T fetchResource(Class<T> theClass, String theUri) {
059                if (myValidationSupport == null) {
060                        return null;
061                } else {
062                        @SuppressWarnings("unchecked")
063                        T retVal = (T) myFetchedResourceCache.get(theUri);
064                        if (retVal == null) {
065                                retVal = myValidationSupport.fetchResource(myCtx, theClass, theUri);
066                                if (retVal != null) {
067                                        myFetchedResourceCache.put(theUri, retVal);
068                                }
069                        }
070                        return retVal;
071                }
072        }
073
074        @Override
075        public List<ConceptMap> findMapsForSource(String theUrl) {
076                throw new UnsupportedOperationException();
077        }
078
079        @Override
080        public String getAbbreviation(String theName) {
081                throw new UnsupportedOperationException();
082        }
083
084        @Override
085        public ValueSetExpander getExpander() {
086                ValueSetExpanderSimple retVal = new ValueSetExpanderSimple(this, this);
087                retVal.setMaxExpansionSize(Integer.MAX_VALUE);
088                return retVal;
089        }
090
091        @Override
092        public INarrativeGenerator getNarrativeGenerator(String thePrefix, String theBasePath) {
093                throw new UnsupportedOperationException();
094        }
095
096        @Override
097        public IParser getParser(ParserType theType) {
098                throw new UnsupportedOperationException();
099        }
100
101        @Override
102        public IParser getParser(String theType) {
103                throw new UnsupportedOperationException();
104        }
105
106        @Override
107        public List<String> getResourceNames() {
108                List<String> result = new ArrayList<String>();
109                for (ResourceType next : ResourceType.values()) {
110                        result.add(next.name());
111                }
112                Collections.sort(result);
113                return result;
114        }
115
116        @Override
117        public <T extends Resource> boolean hasResource(Class<T> theClass_, String theUri) {
118                throw new UnsupportedOperationException();
119        }
120
121        @Override
122        public IParser newJsonParser() {
123                throw new UnsupportedOperationException();
124        }
125
126
127        @Override
128        public IParser newXmlParser() {
129                throw new UnsupportedOperationException();
130        }
131
132        @Override
133        public String oid2Uri(String theCode) {
134                throw new UnsupportedOperationException();
135        }
136
137        @Override
138        public boolean supportsSystem(String theSystem) {
139                if (myValidationSupport == null) {
140                        return false;
141                } else {
142                        return myValidationSupport.isCodeSystemSupported(myCtx, theSystem);
143                }
144        }
145
146        @Override
147        public Set<String> typeTails() {
148                return new HashSet<String>(Arrays.asList("Integer", "UnsignedInt", "PositiveInt", "Decimal", "DateTime", "Date", "Time", "Instant", "String", "Uri", "Oid", "Uuid", "Id", "Boolean", "Code",
149                                "Markdown", "Base64Binary", "Coding", "CodeableConcept", "Attachment", "Identifier", "Quantity", "SampledData", "Range", "Period", "Ratio", "HumanName", "Address", "ContactPoint",
150                                "Timing", "Reference", "Annotation", "Signature", "Meta"));
151        }
152
153        @Override
154        public ValidationResult validateCode(CodeableConcept theCode, ValueSet theVs) {
155                for (Coding next : theCode.getCoding()) {
156                        ValidationResult retVal = validateCode(next, theVs);
157                        if (retVal != null && retVal.isOk()) {
158                                return retVal;
159                        }
160                }
161
162                return new ValidationResult(null, null);
163        }
164
165        @Override
166        public ValidationResult validateCode(Coding theCode, ValueSet theVs) {
167                String system = theCode.getSystem();
168                String code = theCode.getCode();
169                String display = theCode.getDisplay();
170                return validateCode(system, code, display, theVs);
171        }
172
173        @Override
174        public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) {
175                IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay);
176                if (result == null) {
177                        return null;
178                }
179                return new ValidationResult(result.getSeverity(), result.getMessage(), result.asConceptDefinition());
180        }
181
182        @Override
183        public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ConceptSetComponent theVsi) {
184                throw new UnsupportedOperationException();
185        }
186
187        @Override
188        public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ValueSet theVs) {
189
190                if (theVs != null && isNotBlank(theCode)) {
191                        for (ConceptSetComponent next : theVs.getCompose().getInclude()) {
192                                if (isBlank(theSystem) || theSystem.equals(next.getSystem())) {
193                                        for (ConceptReferenceComponent nextCode : next.getConcept()) {
194                                                if (theCode.equals(nextCode.getCode())) {
195                                                        CodeType code = new CodeType(theCode);
196                                                        return new ValidationResult(new ConceptDefinitionComponent(code));
197                                                }
198                                        }
199                                }
200                        }
201                }
202
203
204                boolean caseSensitive = true;
205                if (isNotBlank(theSystem)) {
206                        CodeSystem system = fetchCodeSystem(theSystem);
207                        if (system == null) {
208                                return new ValidationResult(IssueSeverity.INFORMATION, "Code " + theSystem + "/" + theCode + " was not validated because the code system is not present");
209                        }
210
211                        if (system.hasCaseSensitive()) {
212                                caseSensitive = system.getCaseSensitive();
213                        }
214                }
215
216                String wantCode = theCode;
217                if (!caseSensitive) {
218                        wantCode = wantCode.toUpperCase();
219                }
220
221                ValueSetExpansionOutcome expandedValueSet = null;
222
223                /*
224                 * The following valueset is a special case, since the BCP codesystem is very difficult to expand
225                 */
226                if (theVs != null && "http://hl7.org/fhir/ValueSet/languages".equals(theVs.getId())) {
227                        ValueSet expansion = new ValueSet();
228                        for (ConceptSetComponent nextInclude : theVs.getCompose().getInclude()) {
229                                for (ConceptReferenceComponent nextConcept : nextInclude.getConcept()) {
230                                        expansion.getExpansion().addContains().setCode(nextConcept.getCode()).setDisplay(nextConcept.getDisplay());
231                                }
232                        }
233                        expandedValueSet = new ValueSetExpansionOutcome(expansion);
234                }
235
236                if (expandedValueSet == null) {
237                        expandedValueSet = expand(theVs, null);
238                }
239
240                for (ValueSetExpansionContainsComponent next : expandedValueSet.getValueset().getExpansion().getContains()) {
241                        String nextCode = next.getCode();
242                        if (!caseSensitive) {
243                                nextCode = nextCode.toUpperCase();
244                        }
245
246                        if (nextCode.equals(wantCode)) {
247                                if (theSystem == null || next.getSystem().equals(theSystem)) {
248                                        ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
249                                        definition.setCode(next.getCode());
250                                        definition.setDisplay(next.getDisplay());
251                                        ValidationResult retVal = new ValidationResult(definition);
252                                        return retVal;
253                                }
254                        }
255                }
256
257                return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + theSystem + "]");
258        }
259
260        @Override
261        @CoverageIgnore
262        public List<MetadataResource> allConformanceResources() {
263                throw new UnsupportedOperationException();
264        }
265
266        @Override
267        @CoverageIgnore
268        public boolean hasCache() {
269                throw new UnsupportedOperationException();
270        }
271
272        @Override
273        public ValueSetExpansionOutcome expand(ValueSet theSource, ExpansionProfile theProfile) {
274                ValueSetExpansionOutcome vso;
275                try {
276                        vso = getExpander().expand(theSource, theProfile);
277                } catch (InvalidRequestException e) {
278                        throw e;
279                } catch (Exception e) {
280                        throw new InternalErrorException(e);
281                }
282                if (vso.getError() != null) {
283                        throw new InvalidRequestException(vso.getError());
284                } else {
285                        return vso;
286                }
287        }
288
289        @Override
290        public ExpansionProfile getExpansionProfile() {
291                return myExpansionProfile;
292        }
293
294        @Override
295        public void setExpansionProfile(ExpansionProfile theExpProfile) {
296                myExpansionProfile = theExpProfile;
297        }
298
299        @Override
300        public ValueSetExpansionOutcome expandVS(ValueSet theSource, boolean theCacheOk, boolean theHeiarchical) {
301                throw new UnsupportedOperationException();
302        }
303
304        @Override
305        public ValueSetExpansionComponent expandVS(ConceptSetComponent theInc, boolean theHeiarchical) throws TerminologyServiceException {
306                return myValidationSupport.expandValueSet(myCtx, theInc);
307        }
308
309        @Override
310        public void setLogger(ILoggingService theLogger) {
311                throw new UnsupportedOperationException();
312        }
313
314        @Override
315        public String getVersion() {
316                return myCtx.getVersion().getVersion().getFhirVersionString();
317        }
318
319        @Override
320        public boolean isNoTerminologyServer() {
321                return false;
322        }
323
324        @Override
325        public <T extends Resource> T fetchResourceWithException(Class<T> theClass_, String theUri) throws FHIRException {
326                T retVal = fetchResource(theClass_, theUri);
327                if (retVal == null) {
328                        throw new FHIRException("Unable to fetch " + theUri);
329                }
330                return retVal;
331        }
332
333  @Override
334  public List<String> getTypeNames() {
335    throw new UnsupportedOperationException();
336  }
337
338}