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