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