001package org.hl7.fhir.dstu3.hapi.ctx; 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 ca.uhn.fhir.sl.cache.Cache; 010import ca.uhn.fhir.sl.cache.CacheFactory; 011import ca.uhn.fhir.system.HapiSystemProperties; 012import ca.uhn.fhir.util.CoverageIgnore; 013import org.apache.commons.lang3.Validate; 014import org.hl7.fhir.dstu3.context.IWorkerContext; 015import org.hl7.fhir.dstu3.formats.IParser; 016import org.hl7.fhir.dstu3.formats.ParserType; 017import org.hl7.fhir.dstu3.model.CodeSystem; 018import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; 019import org.hl7.fhir.dstu3.model.CodeableConcept; 020import org.hl7.fhir.dstu3.model.Coding; 021import org.hl7.fhir.dstu3.model.ConceptMap; 022import org.hl7.fhir.dstu3.model.ExpansionProfile; 023import org.hl7.fhir.dstu3.model.MetadataResource; 024import org.hl7.fhir.dstu3.model.Resource; 025import org.hl7.fhir.dstu3.model.ResourceType; 026import org.hl7.fhir.dstu3.model.StructureDefinition; 027import org.hl7.fhir.dstu3.model.ValueSet; 028import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; 029import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent; 030import org.hl7.fhir.dstu3.terminologies.ValueSetExpander; 031import org.hl7.fhir.dstu3.utils.INarrativeGenerator; 032import org.hl7.fhir.dstu3.utils.validation.IResourceValidator; 033import org.hl7.fhir.exceptions.FHIRException; 034import org.hl7.fhir.utilities.FhirPublication; 035import org.hl7.fhir.utilities.i18n.I18nBase; 036import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 037import org.hl7.fhir.utilities.validation.ValidationOptions; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041import java.util.ArrayList; 042import java.util.Arrays; 043import java.util.Collections; 044import java.util.HashSet; 045import java.util.List; 046import java.util.Set; 047 048import static org.apache.commons.lang3.StringUtils.isNotBlank; 049 050public final class HapiWorkerContext extends I18nBase implements IWorkerContext { 051 private static final Logger ourLog = LoggerFactory.getLogger(HapiWorkerContext.class); 052 private final FhirContext myCtx; 053 private final Cache<String, Resource> myFetchedResourceCache; 054 private IValidationSupport myValidationSupport; 055 private ExpansionProfile myExpansionProfile; 056 057 public HapiWorkerContext(FhirContext theCtx, IValidationSupport theValidationSupport) { 058 Validate.notNull(theCtx, "theCtx must not be null"); 059 Validate.notNull(theValidationSupport, "theValidationSupport must not be null"); 060 myCtx = theCtx; 061 myValidationSupport = theValidationSupport; 062 063 long timeoutMillis = HapiSystemProperties.getTestValidationResourceCachesMs(); 064 myFetchedResourceCache = CacheFactory.build(timeoutMillis); 065 // Set a default locale 066 setValidationMessageLanguage(getLocale()); 067 } 068 069 @Override 070 @CoverageIgnore 071 public List<MetadataResource> allConformanceResources() { 072 throw new UnsupportedOperationException(Msg.code(610)); 073 } 074 075 @Override 076 public List<StructureDefinition> allStructures() { 077 return myValidationSupport.fetchAllStructureDefinitions(); 078 } 079 080 @Override 081 public ValueSetExpansionComponent expandVS(ConceptSetComponent theInc, boolean theHierarchical) { 082 ValueSet input = new ValueSet(); 083 input.getCompose().addInclude(theInc); 084 IValidationSupport.ValueSetExpansionOutcome output = 085 myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), null, input); 086 ValueSet outputValueSet = (ValueSet) output.getValueSet(); 087 if (outputValueSet != null) { 088 return outputValueSet.getExpansion(); 089 } else { 090 return null; 091 } 092 } 093 094 @Override 095 public StructureDefinition fetchTypeDefinition(String theCode) { 096 return fetchResource( 097 org.hl7.fhir.dstu3.model.StructureDefinition.class, 098 "http://hl7.org/fhir/StructureDefinition/" + theCode); 099 } 100 101 @Override 102 public CodeSystem fetchCodeSystem(String theSystem) { 103 if (myValidationSupport == null) { 104 return null; 105 } else { 106 return (CodeSystem) myValidationSupport.fetchCodeSystem(theSystem); 107 } 108 } 109 110 @Override 111 public <T extends Resource> T fetchResource(Class<T> theClass, String theUri) { 112 Validate.notBlank(theUri, "theUri must not be null or blank"); 113 if (myValidationSupport == null) { 114 return null; 115 } else { 116 try { 117 //noinspection unchecked 118 return (T) myFetchedResourceCache.get(theUri, t -> { 119 T resource = myValidationSupport.fetchResource(theClass, theUri); 120 if (resource == null) { 121 throw new IllegalArgumentException(Msg.code(611)); 122 } 123 return resource; 124 }); 125 } catch (IllegalArgumentException e) { 126 return null; 127 } 128 } 129 } 130 131 @Override 132 public <T extends Resource> T fetchResourceWithException(Class<T> theClass_, String theUri) throws FHIRException { 133 T retVal = fetchResource(theClass_, theUri); 134 if (retVal == null) { 135 throw new FHIRException(Msg.code(612) + "Unable to fetch " + theUri); 136 } 137 return retVal; 138 } 139 140 @Override 141 public List<ConceptMap> findMapsForSource(String theUrl) { 142 throw new UnsupportedOperationException(Msg.code(613)); 143 } 144 145 @Override 146 public ValueSetExpander.ValueSetExpansionOutcome expandVS(ValueSet source, boolean cacheOk, boolean heiarchical) { 147 throw new UnsupportedOperationException(Msg.code(614)); 148 } 149 150 @Override 151 public String getAbbreviation(String theName) { 152 throw new UnsupportedOperationException(Msg.code(615)); 153 } 154 155 @Override 156 public ExpansionProfile getExpansionProfile() { 157 return myExpansionProfile; 158 } 159 160 @Override 161 public void setExpansionProfile(ExpansionProfile theExpProfile) { 162 myExpansionProfile = theExpProfile; 163 } 164 165 @Override 166 public INarrativeGenerator getNarrativeGenerator(String thePrefix, String theBasePath) { 167 throw new UnsupportedOperationException(Msg.code(616)); 168 } 169 170 @Override 171 public IResourceValidator newValidator() throws FHIRException { 172 throw new UnsupportedOperationException(Msg.code(617)); 173 } 174 175 @Override 176 public IParser getParser(ParserType theType) { 177 throw new UnsupportedOperationException(Msg.code(618)); 178 } 179 180 @Override 181 public IParser getParser(String theType) { 182 throw new UnsupportedOperationException(Msg.code(619)); 183 } 184 185 @Override 186 public List<String> getResourceNames() { 187 List<String> result = new ArrayList<>(); 188 for (ResourceType next : ResourceType.values()) { 189 result.add(next.name()); 190 } 191 Collections.sort(result); 192 return result; 193 } 194 195 @Override 196 public Set<String> getResourceNamesAsSet() { 197 return new HashSet<>(getResourceNames()); 198 } 199 200 @Override 201 public List<String> getTypeNames() { 202 throw new UnsupportedOperationException(Msg.code(620)); 203 } 204 205 @Override 206 public String getVersion() { 207 return myCtx.getVersion().getVersion().getFhirVersionString(); 208 } 209 210 @Override 211 @CoverageIgnore 212 public boolean hasCache() { 213 throw new UnsupportedOperationException(Msg.code(621)); 214 } 215 216 @Override 217 public <T extends Resource> boolean hasResource(Class<T> theClass_, String theUri) { 218 throw new UnsupportedOperationException(Msg.code(622)); 219 } 220 221 @Override 222 public boolean isNoTerminologyServer() { 223 return false; 224 } 225 226 @Override 227 public IParser newJsonParser() { 228 throw new UnsupportedOperationException(Msg.code(623)); 229 } 230 231 @Override 232 public IParser newXmlParser() { 233 throw new UnsupportedOperationException(Msg.code(624)); 234 } 235 236 @Override 237 public String oid2Uri(String theCode) { 238 throw new UnsupportedOperationException(Msg.code(625)); 239 } 240 241 @Override 242 public void setLogger(ILoggingService theLogger) { 243 throw new UnsupportedOperationException(Msg.code(626)); 244 } 245 246 @Override 247 public boolean supportsSystem(String theSystem) { 248 if (myValidationSupport == null) { 249 return false; 250 } else { 251 return myValidationSupport.isCodeSystemSupported( 252 new ValidationSupportContext(myValidationSupport), theSystem); 253 } 254 } 255 256 @Override 257 public Set<String> typeTails() { 258 return new HashSet<>(Arrays.asList( 259 "Integer", 260 "UnsignedInt", 261 "PositiveInt", 262 "Decimal", 263 "DateTime", 264 "Date", 265 "Time", 266 "Instant", 267 "String", 268 "Uri", 269 "Oid", 270 "Uuid", 271 "Id", 272 "Boolean", 273 "Code", 274 "Markdown", 275 "Base64Binary", 276 "Coding", 277 "CodeableConcept", 278 "Attachment", 279 "Identifier", 280 "Quantity", 281 "SampledData", 282 "Range", 283 "Period", 284 "Ratio", 285 "HumanName", 286 "Address", 287 "ContactPoint", 288 "Timing", 289 "Reference", 290 "Annotation", 291 "Signature", 292 "Meta")); 293 } 294 295 @Override 296 public ValidationResult validateCode(CodeableConcept theCode, ValueSet theVs) { 297 for (Coding next : theCode.getCoding()) { 298 ValidationResult retVal = validateCode(next, theVs); 299 if (retVal.isOk()) { 300 return retVal; 301 } 302 } 303 304 return new ValidationResult(IssueSeverity.ERROR, null); 305 } 306 307 @Override 308 public ValidationResult validateCode(Coding theCode, ValueSet theVs) { 309 String system = theCode.getSystem(); 310 String code = theCode.getCode(); 311 String display = theCode.getDisplay(); 312 return validateCode(system, code, display, theVs); 313 } 314 315 @Override 316 public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) { 317 ValidationOptions options = new ValidationOptions(FhirPublication.fromCode( 318 myValidationSupport.getFhirContext().getVersion().toString())); 319 IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode( 320 new ValidationSupportContext(myValidationSupport), 321 convertConceptValidationOptions(options), 322 theSystem, 323 theCode, 324 theDisplay, 325 null); 326 if (result == null) { 327 return null; 328 } 329 330 IssueSeverity severity = null; 331 if (result.getSeverity() != null) { 332 severity = IssueSeverity.fromCode(result.getSeverityCode()); 333 } 334 ConceptDefinitionComponent definition = new ConceptDefinitionComponent().setCode(result.getCode()); 335 return new ValidationResult(severity, result.getMessage(), definition); 336 } 337 338 public static ConceptValidationOptions convertConceptValidationOptions(ValidationOptions theOptions) { 339 ConceptValidationOptions retVal = new ConceptValidationOptions(); 340 if (theOptions.isGuessSystem()) { 341 retVal = retVal.setInferSystem(true); 342 } 343 return retVal; 344 } 345 346 @Override 347 public ValidationResult validateCode( 348 String theSystem, String theCode, String theDisplay, ConceptSetComponent theVsi) { 349 throw new UnsupportedOperationException(Msg.code(627)); 350 } 351 352 @Override 353 public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ValueSet theVs) { 354 355 IValidationSupport.CodeValidationResult outcome; 356 ValidationOptions options = new ValidationOptions(FhirPublication.fromCode( 357 myValidationSupport.getFhirContext().getVersion().toString())); 358 if (isNotBlank(theVs.getUrl())) { 359 outcome = myValidationSupport.validateCode( 360 new ValidationSupportContext(myValidationSupport), 361 convertConceptValidationOptions(options), 362 theSystem, 363 theCode, 364 theDisplay, 365 theVs.getUrl()); 366 } else { 367 outcome = myValidationSupport.validateCodeInValueSet( 368 new ValidationSupportContext(myValidationSupport), 369 convertConceptValidationOptions(options), 370 theSystem, 371 theCode, 372 theDisplay, 373 theVs); 374 } 375 376 if (outcome != null && outcome.isOk()) { 377 ConceptDefinitionComponent definition = new ConceptDefinitionComponent(); 378 definition.setCode(theCode); 379 definition.setDisplay(outcome.getDisplay()); 380 return new ValidationResult(definition); 381 } 382 383 return new ValidationResult( 384 IssueSeverity.ERROR, 385 "Unknown code[" + theCode + "] in system[" + Constants.codeSystemWithDefaultDescription(theSystem) 386 + "]"); 387 } 388}