001package org.hl7.fhir.dstu2016may.hapi.validation;
002
003import ca.uhn.fhir.i18n.Msg;
004import ca.uhn.fhir.context.FhirContext;
005import ca.uhn.fhir.context.support.ConceptValidationOptions;
006import ca.uhn.fhir.context.support.IValidationSupport;
007import ca.uhn.fhir.context.support.ValidationSupportContext;
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
100        @Override
101        public IParser getParser(ParserType theType) {
102                throw new UnsupportedOperationException(Msg.code(472));
103        }
104
105        @Override
106        public IParser getParser(String theType) {
107                throw new UnsupportedOperationException(Msg.code(473));
108        }
109
110        @Override
111        public List<String> getResourceNames() {
112                List<String> result = new ArrayList<>();
113                for (ResourceType next : ResourceType.values()) {
114                        result.add(next.name());
115                }
116                Collections.sort(result);
117                return result;
118        }
119
120        @Override
121        public <T extends Resource> boolean hasResource(Class<T> theClass_, String theUri) {
122                throw new UnsupportedOperationException(Msg.code(474));
123        }
124
125        @Override
126        public IParser newJsonParser() {
127                throw new UnsupportedOperationException(Msg.code(475));
128        }
129
130        @Override
131        public IParser newXmlParser() {
132                throw new UnsupportedOperationException(Msg.code(476));
133        }
134
135        @Override
136        public INarrativeGenerator getNarrativeGenerator(String theS, String theS1) {
137                throw new UnsupportedOperationException(Msg.code(477));
138        }
139
140        @Override
141        public String oid2Uri(String theCode) {
142                throw new UnsupportedOperationException(Msg.code(478));
143        }
144
145        @Override
146        public StructureDefinition fetchTypeDefinition(String typeName) {
147                return fetchResource(org.hl7.fhir.dstu2016may.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName);
148        }
149
150        @Override
151        public boolean supportsSystem(String theSystem) {
152                if (myValidationSupport == null) {
153                        return false;
154                } else {
155                        return myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), theSystem);
156                }
157        }
158
159        @Override
160        public Set<String> typeTails() {
161                return new HashSet<>(Arrays.asList("Integer", "UnsignedInt", "PositiveInt", "Decimal", "DateTime", "Date", "Time", "Instant", "String", "Uri", "Oid", "Uuid", "Id", "Boolean", "Code",
162                        "Markdown", "Base64Binary", "Coding", "CodeableConcept", "Attachment", "Identifier", "Quantity", "SampledData", "Range", "Period", "Ratio", "HumanName", "Address", "ContactPoint",
163                        "Timing", "Reference", "Annotation", "Signature", "Meta"));
164        }
165
166        @Override
167        public ValidationResult validateCode(CodeableConcept theCode, ValueSet theVs) {
168                for (Coding next : theCode.getCoding()) {
169                        ValidationResult retVal = validateCode(next, theVs);
170                        if (retVal.isOk()) {
171                                return retVal;
172                        }
173                }
174
175                return new ValidationResult(null, null);
176        }
177
178        @Override
179        public ValidationResult validateCode(Coding theCode, ValueSet theVs) {
180                String system = theCode.getSystem();
181                String code = theCode.getCode();
182                String display = theCode.getDisplay();
183                return validateCode(system, code, display, theVs);
184        }
185
186        @Override
187        public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) {
188                IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport), new ConceptValidationOptions(), theSystem, theCode, theDisplay, null);
189                if (result == null) {
190                        return null;
191                }
192                OperationOutcome.IssueSeverity severity = null;
193                if (result.getSeverity() != null) {
194                        severity = OperationOutcome.IssueSeverity.fromCode(result.getSeverityCode());
195                }
196                ConceptDefinitionComponent definition = result.getCode() != null ? new ConceptDefinitionComponent().setCode(result.getCode()) : null;
197                return new ValidationResult(severity, result.getMessage(), definition);
198        }
199
200        @Override
201        public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ConceptSetComponent theVsi) {
202                throw new UnsupportedOperationException(Msg.code(479));
203        }
204
205        @Override
206        public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ValueSet theVs) {
207
208                if (theVs != null && isNotBlank(theCode)) {
209                        for (ConceptSetComponent next : theVs.getCompose().getInclude()) {
210                                if (isBlank(theSystem) || theSystem.equals(next.getSystem())) {
211                                        for (ConceptReferenceComponent nextCode : next.getConcept()) {
212                                                if (theCode.equals(nextCode.getCode())) {
213                                                        CodeType code = new CodeType(theCode);
214                                                        return new ValidationResult(new ConceptDefinitionComponent(code));
215                                                }
216                                        }
217                                }
218                        }
219                }
220
221
222                boolean caseSensitive = true;
223                if (isNotBlank(theSystem)) {
224                        CodeSystem system = fetchCodeSystem(theSystem);
225                        if (system == null) {
226                                return new ValidationResult(OperationOutcome.IssueSeverity.INFORMATION, "Code " + Constants.codeSystemWithDefaultDescription(theSystem) + "/" + theCode + " was not validated because the code system is not present");
227                        }
228
229                        if (system.hasCaseSensitive()) {
230                                caseSensitive = system.getCaseSensitive();
231                        }
232                }
233
234                String wantCode = theCode;
235                if (!caseSensitive) {
236                        wantCode = wantCode.toUpperCase();
237                }
238
239                ValueSetExpansionOutcome expandedValueSet = null;
240
241                /*
242                 * The following valueset is a special case, since the BCP codesystem is very difficult to expand
243                 */
244                if (theVs != null && "http://hl7.org/fhir/ValueSet/languages".equals(theVs.getUrl())) {
245                        ValueSet expansion = new ValueSet();
246                        for (ConceptSetComponent nextInclude : theVs.getCompose().getInclude()) {
247                                for (ConceptReferenceComponent nextConcept : nextInclude.getConcept()) {
248                                        expansion.getExpansion().addContains().setCode(nextConcept.getCode()).setDisplay(nextConcept.getDisplay());
249                                }
250                        }
251                        expandedValueSet = new ValueSetExpansionOutcome(expansion);
252                }
253
254                if (expandedValueSet == null) {
255                        expandedValueSet = expandVS(theVs, true);
256                }
257
258                for (ValueSetExpansionContainsComponent next : expandedValueSet.getValueset().getExpansion().getContains()) {
259                        String nextCode = next.getCode();
260                        if (!caseSensitive) {
261                                nextCode = nextCode.toUpperCase();
262                        }
263
264                        if (nextCode.equals(wantCode)) {
265                                if (theSystem == null || next.getSystem().equals(theSystem)) {
266                                        ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
267                                        definition.setCode(next.getCode());
268                                        definition.setDisplay(next.getDisplay());
269                                        return new ValidationResult(definition);
270                                }
271                        }
272                }
273
274                return new ValidationResult(OperationOutcome.IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + Constants.codeSystemWithDefaultDescription(theSystem) + "]");
275        }
276
277        @Override
278        public ValueSetExpansionOutcome expandVS(ValueSet theSource, boolean theCacheOk) {
279                throw new UnsupportedOperationException(Msg.code(480));
280        }
281
282        @Override
283        public ValueSetExpansionComponent expandVS(ConceptSetComponent theInc) {
284                throw new UnsupportedOperationException(Msg.code(481));
285        }
286
287
288}