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