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}