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