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