package org.hl7.fhir.dstu2016may.hapi.validation; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.Validate; import org.hl7.fhir.dstu2016may.formats.IParser; import org.hl7.fhir.dstu2016may.formats.ParserType; import org.hl7.fhir.dstu2016may.hapi.validation.IValidationSupport.CodeValidationResult; import org.hl7.fhir.dstu2016may.model.CodeSystem; import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.dstu2016may.model.CodeType; import org.hl7.fhir.dstu2016may.model.CodeableConcept; import org.hl7.fhir.dstu2016may.model.Coding; import org.hl7.fhir.dstu2016may.model.ConceptMap; import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueSeverity; import org.hl7.fhir.dstu2016may.model.Resource; import org.hl7.fhir.dstu2016may.model.ResourceType; import org.hl7.fhir.dstu2016may.model.StructureDefinition; import org.hl7.fhir.dstu2016may.model.ValueSet; import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptReferenceComponent; import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpander; import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpanderFactory; import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpanderSimple; import org.hl7.fhir.dstu2016may.utils.IWorkerContext; import org.hl7.fhir.dstu2016may.validation.IResourceValidator; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; public final class HapiWorkerContext implements IWorkerContext, ValueSetExpanderFactory { private final FhirContext myCtx; private Map<String, Resource> myFetchedResourceCache = new HashMap<String, Resource>(); private IValidationSupport myValidationSupport; public HapiWorkerContext(FhirContext theCtx, IValidationSupport theValidationSupport) { Validate.notNull(theCtx, "theCtx must not be null"); Validate.notNull(theValidationSupport, "theValidationSupport must not be null"); myCtx = theCtx; myValidationSupport = theValidationSupport; } @Override public List<StructureDefinition> allStructures() { return myValidationSupport.fetchAllStructureDefinitions(myCtx); } @Override public CodeSystem fetchCodeSystem(String theSystem) { if (myValidationSupport == null) { return null; } else { return myValidationSupport.fetchCodeSystem(myCtx, theSystem); } } @Override public <T extends Resource> T fetchResource(Class<T> theClass, String theUri) { if (myValidationSupport == null) { return null; } else { @SuppressWarnings("unchecked") T retVal = (T) myFetchedResourceCache.get(theUri); if (retVal == null) { retVal = myValidationSupport.fetchResource(myCtx, theClass, theUri); if (retVal != null) { myFetchedResourceCache.put(theUri, retVal); } } return retVal; } } @Override public List<ConceptMap> findMapsForSource(String theUrl) { throw new UnsupportedOperationException(); } @Override public String getAbbreviation(String theName) { throw new UnsupportedOperationException(); } @Override public IParser getParser(ParserType theType) { throw new UnsupportedOperationException(); } @Override public IParser getParser(String theType) { throw new UnsupportedOperationException(); } @Override public List<String> getResourceNames() { List<String> result = new ArrayList<String>(); for (ResourceType next : ResourceType.values()) { result.add(next.name()); } Collections.sort(result); return result; } @Override public <T extends Resource> boolean hasResource(Class<T> theClass_, String theUri) { throw new UnsupportedOperationException(); } @Override public IParser newJsonParser() { throw new UnsupportedOperationException(); } @Override public IResourceValidator newValidator() { throw new UnsupportedOperationException(); } @Override public IParser newXmlParser() { throw new UnsupportedOperationException(); } @Override public String oid2Uri(String theCode) { throw new UnsupportedOperationException(); } @Override public boolean supportsSystem(String theSystem) { if (myValidationSupport == null) { return false; } else { return myValidationSupport.isCodeSystemSupported(myCtx, theSystem); } } @Override public Set<String> typeTails() { return new HashSet<String>(Arrays.asList("Integer", "UnsignedInt", "PositiveInt", "Decimal", "DateTime", "Date", "Time", "Instant", "String", "Uri", "Oid", "Uuid", "Id", "Boolean", "Code", "Markdown", "Base64Binary", "Coding", "CodeableConcept", "Attachment", "Identifier", "Quantity", "SampledData", "Range", "Period", "Ratio", "HumanName", "Address", "ContactPoint", "Timing", "Reference", "Annotation", "Signature", "Meta")); } @Override public ValidationResult validateCode(CodeableConcept theCode, ValueSet theVs) { for (Coding next : theCode.getCoding()) { ValidationResult retVal = validateCode(next, theVs); if (retVal != null && retVal.isOk()) { return retVal; } } return new ValidationResult(null, null); } @Override public ValidationResult validateCode(Coding theCode, ValueSet theVs) { String system = theCode.getSystem(); String code = theCode.getCode(); String display = theCode.getDisplay(); return validateCode(system, code, display, theVs); } @Override public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) { CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay); if (result == null) { return null; } return new ValidationResult(result.getSeverity(), result.getMessage(), result.asConceptDefinition()); } @Override public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ConceptSetComponent theVsi) { throw new UnsupportedOperationException(); } @Override public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ValueSet theVs) { if (theVs != null && isNotBlank(theCode)) { for (ConceptSetComponent next : theVs.getCompose().getInclude()) { if (isBlank(theSystem) || theSystem.equals(next.getSystem())) { for (ConceptReferenceComponent nextCode : next.getConcept()) { if (theCode.equals(nextCode.getCode())) { CodeType code = new CodeType(theCode); return new ValidationResult(new ConceptDefinitionComponent(code)); } } } } } boolean caseSensitive = true; if (isNotBlank(theSystem)) { CodeSystem system = fetchCodeSystem(theSystem); if (system == null) { return new ValidationResult(IssueSeverity.INFORMATION, "Code " + theSystem + "/" + theCode + " was not validated because the code system is not present"); } if (system.hasCaseSensitive()) { caseSensitive = system.getCaseSensitive(); } } String wantCode = theCode; if (!caseSensitive) { wantCode = wantCode.toUpperCase(); } ValueSetExpansionOutcome expandedValueSet = null; /* * The following valueset is a special case, since the BCP codesystem is very difficult to expand */ if (theVs != null && "http://hl7.org/fhir/ValueSet/languages".equals(theVs.getId())) { ValueSet expansion = new ValueSet(); for (ConceptSetComponent nextInclude : theVs.getCompose().getInclude()) { for (ConceptReferenceComponent nextConcept : nextInclude.getConcept()) { expansion.getExpansion().addContains().setCode(nextConcept.getCode()).setDisplay(nextConcept.getDisplay()); } } expandedValueSet = new ValueSetExpansionOutcome(expansion); } if (expandedValueSet == null) { expandedValueSet = expandVS(theVs, true); } for (ValueSetExpansionContainsComponent next : expandedValueSet.getValueset().getExpansion().getContains()) { String nextCode = next.getCode(); if (!caseSensitive) { nextCode = nextCode.toUpperCase(); } if (nextCode.equals(wantCode)) { if (theSystem == null || next.getSystem().equals(theSystem)) { ConceptDefinitionComponent definition = new ConceptDefinitionComponent(); definition.setCode(next.getCode()); definition.setDisplay(next.getDisplay()); ValidationResult retVal = new ValidationResult(definition); return retVal; } } } return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + theSystem + "]"); } @Override public ValueSetExpander getExpander() { ValueSetExpanderSimple retVal = new ValueSetExpanderSimple(this, this); return retVal; } @Override public ValueSetExpansionOutcome expandVS(ValueSet theSource, boolean theCacheOk) { ValueSetExpansionOutcome vso; try { vso = getExpander().expand(theSource); } catch (InvalidRequestException e) { throw e; } catch (Exception e) { throw new InternalErrorException(e); } if (vso.getError() != null) { throw new InvalidRequestException(vso.getError()); } else { return vso; } } @Override public ValueSetExpansionComponent expandVS(ConceptSetComponent theInc) { throw new UnsupportedOperationException(); } }