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}