001package org.hl7.fhir.dstu3.hapi.ctx; 002 003import ca.uhn.fhir.context.FhirContext; 004import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 005import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 006import ca.uhn.fhir.util.CoverageIgnore; 007import org.apache.commons.lang3.Validate; 008import org.hl7.fhir.dstu3.context.IWorkerContext; 009import org.hl7.fhir.dstu3.formats.IParser; 010import org.hl7.fhir.dstu3.formats.ParserType; 011import org.hl7.fhir.dstu3.model.*; 012import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; 013import org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceComponent; 014import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; 015import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent; 016import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent; 017import org.hl7.fhir.dstu3.terminologies.ValueSetExpander; 018import org.hl7.fhir.dstu3.terminologies.ValueSetExpanderFactory; 019import org.hl7.fhir.dstu3.terminologies.ValueSetExpanderSimple; 020import org.hl7.fhir.dstu3.utils.INarrativeGenerator; 021import org.hl7.fhir.exceptions.FHIRException; 022import org.hl7.fhir.exceptions.TerminologyServiceException; 023import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 024 025import java.util.*; 026 027import static org.apache.commons.lang3.StringUtils.isBlank; 028import static org.apache.commons.lang3.StringUtils.isNotBlank; 029 030public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander, ValueSetExpanderFactory { 031 private final FhirContext myCtx; 032 private Map<String, Resource> myFetchedResourceCache = new HashMap<String, Resource>(); 033 private IValidationSupport myValidationSupport; 034 private ExpansionProfile myExpansionProfile; 035 036 public HapiWorkerContext(FhirContext theCtx, IValidationSupport theValidationSupport) { 037 Validate.notNull(theCtx, "theCtx must not be null"); 038 Validate.notNull(theValidationSupport, "theValidationSupport must not be null"); 039 myCtx = theCtx; 040 myValidationSupport = theValidationSupport; 041 } 042 043 @Override 044 public List<StructureDefinition> allStructures() { 045 return myValidationSupport.fetchAllStructureDefinitions(myCtx); 046 } 047 048 @Override 049 public CodeSystem fetchCodeSystem(String theSystem) { 050 if (myValidationSupport == null) { 051 return null; 052 } else { 053 return myValidationSupport.fetchCodeSystem(myCtx, theSystem); 054 } 055 } 056 057 @Override 058 public <T extends Resource> T fetchResource(Class<T> theClass, String theUri) { 059 if (myValidationSupport == null) { 060 return null; 061 } else { 062 @SuppressWarnings("unchecked") 063 T retVal = (T) myFetchedResourceCache.get(theUri); 064 if (retVal == null) { 065 retVal = myValidationSupport.fetchResource(myCtx, theClass, theUri); 066 if (retVal != null) { 067 myFetchedResourceCache.put(theUri, retVal); 068 } 069 } 070 return retVal; 071 } 072 } 073 074 @Override 075 public List<ConceptMap> findMapsForSource(String theUrl) { 076 throw new UnsupportedOperationException(); 077 } 078 079 @Override 080 public String getAbbreviation(String theName) { 081 throw new UnsupportedOperationException(); 082 } 083 084 @Override 085 public ValueSetExpander getExpander() { 086 ValueSetExpanderSimple retVal = new ValueSetExpanderSimple(this, this); 087 retVal.setMaxExpansionSize(Integer.MAX_VALUE); 088 return retVal; 089 } 090 091 @Override 092 public INarrativeGenerator getNarrativeGenerator(String thePrefix, String theBasePath) { 093 throw new UnsupportedOperationException(); 094 } 095 096 @Override 097 public IParser getParser(ParserType theType) { 098 throw new UnsupportedOperationException(); 099 } 100 101 @Override 102 public IParser getParser(String theType) { 103 throw new UnsupportedOperationException(); 104 } 105 106 @Override 107 public List<String> getResourceNames() { 108 List<String> result = new ArrayList<String>(); 109 for (ResourceType next : ResourceType.values()) { 110 result.add(next.name()); 111 } 112 Collections.sort(result); 113 return result; 114 } 115 116 @Override 117 public <T extends Resource> boolean hasResource(Class<T> theClass_, String theUri) { 118 throw new UnsupportedOperationException(); 119 } 120 121 @Override 122 public IParser newJsonParser() { 123 throw new UnsupportedOperationException(); 124 } 125 126 127 @Override 128 public IParser newXmlParser() { 129 throw new UnsupportedOperationException(); 130 } 131 132 @Override 133 public String oid2Uri(String theCode) { 134 throw new UnsupportedOperationException(); 135 } 136 137 @Override 138 public boolean supportsSystem(String theSystem) { 139 if (myValidationSupport == null) { 140 return false; 141 } else { 142 return myValidationSupport.isCodeSystemSupported(myCtx, theSystem); 143 } 144 } 145 146 @Override 147 public Set<String> typeTails() { 148 return new HashSet<String>(Arrays.asList("Integer", "UnsignedInt", "PositiveInt", "Decimal", "DateTime", "Date", "Time", "Instant", "String", "Uri", "Oid", "Uuid", "Id", "Boolean", "Code", 149 "Markdown", "Base64Binary", "Coding", "CodeableConcept", "Attachment", "Identifier", "Quantity", "SampledData", "Range", "Period", "Ratio", "HumanName", "Address", "ContactPoint", 150 "Timing", "Reference", "Annotation", "Signature", "Meta")); 151 } 152 153 @Override 154 public ValidationResult validateCode(CodeableConcept theCode, ValueSet theVs) { 155 for (Coding next : theCode.getCoding()) { 156 ValidationResult retVal = validateCode(next, theVs); 157 if (retVal != null && retVal.isOk()) { 158 return retVal; 159 } 160 } 161 162 return new ValidationResult(null, null); 163 } 164 165 @Override 166 public ValidationResult validateCode(Coding theCode, ValueSet theVs) { 167 String system = theCode.getSystem(); 168 String code = theCode.getCode(); 169 String display = theCode.getDisplay(); 170 return validateCode(system, code, display, theVs); 171 } 172 173 @Override 174 public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) { 175 IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay); 176 if (result == null) { 177 return null; 178 } 179 return new ValidationResult(result.getSeverity(), result.getMessage(), result.asConceptDefinition()); 180 } 181 182 @Override 183 public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ConceptSetComponent theVsi) { 184 throw new UnsupportedOperationException(); 185 } 186 187 @Override 188 public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ValueSet theVs) { 189 190 if (theVs != null && isNotBlank(theCode)) { 191 for (ConceptSetComponent next : theVs.getCompose().getInclude()) { 192 if (isBlank(theSystem) || theSystem.equals(next.getSystem())) { 193 for (ConceptReferenceComponent nextCode : next.getConcept()) { 194 if (theCode.equals(nextCode.getCode())) { 195 CodeType code = new CodeType(theCode); 196 return new ValidationResult(new ConceptDefinitionComponent(code)); 197 } 198 } 199 } 200 } 201 } 202 203 204 boolean caseSensitive = true; 205 if (isNotBlank(theSystem)) { 206 CodeSystem system = fetchCodeSystem(theSystem); 207 if (system == null) { 208 return new ValidationResult(IssueSeverity.INFORMATION, "Code " + theSystem + "/" + theCode + " was not validated because the code system is not present"); 209 } 210 211 if (system.hasCaseSensitive()) { 212 caseSensitive = system.getCaseSensitive(); 213 } 214 } 215 216 String wantCode = theCode; 217 if (!caseSensitive) { 218 wantCode = wantCode.toUpperCase(); 219 } 220 221 ValueSetExpansionOutcome expandedValueSet = null; 222 223 /* 224 * The following valueset is a special case, since the BCP codesystem is very difficult to expand 225 */ 226 if (theVs != null && "http://hl7.org/fhir/ValueSet/languages".equals(theVs.getId())) { 227 ValueSet expansion = new ValueSet(); 228 for (ConceptSetComponent nextInclude : theVs.getCompose().getInclude()) { 229 for (ConceptReferenceComponent nextConcept : nextInclude.getConcept()) { 230 expansion.getExpansion().addContains().setCode(nextConcept.getCode()).setDisplay(nextConcept.getDisplay()); 231 } 232 } 233 expandedValueSet = new ValueSetExpansionOutcome(expansion); 234 } 235 236 if (expandedValueSet == null) { 237 expandedValueSet = expand(theVs, null); 238 } 239 240 for (ValueSetExpansionContainsComponent next : expandedValueSet.getValueset().getExpansion().getContains()) { 241 String nextCode = next.getCode(); 242 if (!caseSensitive) { 243 nextCode = nextCode.toUpperCase(); 244 } 245 246 if (nextCode.equals(wantCode)) { 247 if (theSystem == null || next.getSystem().equals(theSystem)) { 248 ConceptDefinitionComponent definition = new ConceptDefinitionComponent(); 249 definition.setCode(next.getCode()); 250 definition.setDisplay(next.getDisplay()); 251 ValidationResult retVal = new ValidationResult(definition); 252 return retVal; 253 } 254 } 255 } 256 257 return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + theSystem + "]"); 258 } 259 260 @Override 261 @CoverageIgnore 262 public List<MetadataResource> allConformanceResources() { 263 throw new UnsupportedOperationException(); 264 } 265 266 @Override 267 @CoverageIgnore 268 public boolean hasCache() { 269 throw new UnsupportedOperationException(); 270 } 271 272 @Override 273 public ValueSetExpansionOutcome expand(ValueSet theSource, ExpansionProfile theProfile) { 274 ValueSetExpansionOutcome vso; 275 try { 276 vso = getExpander().expand(theSource, theProfile); 277 } catch (InvalidRequestException e) { 278 throw e; 279 } catch (Exception e) { 280 throw new InternalErrorException(e); 281 } 282 if (vso.getError() != null) { 283 throw new InvalidRequestException(vso.getError()); 284 } else { 285 return vso; 286 } 287 } 288 289 @Override 290 public ExpansionProfile getExpansionProfile() { 291 return myExpansionProfile; 292 } 293 294 @Override 295 public void setExpansionProfile(ExpansionProfile theExpProfile) { 296 myExpansionProfile = theExpProfile; 297 } 298 299 @Override 300 public ValueSetExpansionOutcome expandVS(ValueSet theSource, boolean theCacheOk, boolean theHeiarchical) { 301 throw new UnsupportedOperationException(); 302 } 303 304 @Override 305 public ValueSetExpansionComponent expandVS(ConceptSetComponent theInc, boolean theHeiarchical) throws TerminologyServiceException { 306 return myValidationSupport.expandValueSet(myCtx, theInc); 307 } 308 309 @Override 310 public void setLogger(ILoggingService theLogger) { 311 throw new UnsupportedOperationException(); 312 } 313 314 @Override 315 public String getVersion() { 316 return myCtx.getVersion().getVersion().getFhirVersionString(); 317 } 318 319 @Override 320 public boolean isNoTerminologyServer() { 321 return false; 322 } 323 324 @Override 325 public <T extends Resource> T fetchResourceWithException(Class<T> theClass_, String theUri) throws FHIRException { 326 T retVal = fetchResource(theClass_, theUri); 327 if (retVal == null) { 328 throw new FHIRException("Unable to fetch " + theUri); 329 } 330 return retVal; 331 } 332 333 @Override 334 public List<String> getTypeNames() { 335 throw new UnsupportedOperationException(); 336 } 337 338}