001package org.hl7.fhir.instance.terminologies;
002
003import java.util.List;
004
005import org.hl7.fhir.instance.model.UriType;
006import org.hl7.fhir.instance.model.ValueSet;
007import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent;
008import org.hl7.fhir.instance.model.ValueSet.ConceptReferenceComponent;
009import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
010import org.hl7.fhir.instance.model.ValueSet.ConceptSetFilterComponent;
011import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionContainsComponent;
012import org.hl7.fhir.instance.terminologies.ValueSetExpander.ETooCostly;
013import org.hl7.fhir.instance.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
014import org.hl7.fhir.instance.utils.EOperationOutcome;
015import org.hl7.fhir.instance.utils.IWorkerContext;
016import org.hl7.fhir.instance.utils.IWorkerContext.ValidationResult;
017
018public class ValueSetCheckerSimple implements ValueSetChecker {
019
020  private ValueSet valueset;
021  private ValueSetExpanderFactory factory;
022  private IWorkerContext context;
023
024  public ValueSetCheckerSimple(ValueSet source, ValueSetExpanderFactory factory, IWorkerContext context) {
025    this.valueset = source;
026    this.factory = factory;
027    this.context = context;
028  }
029
030  @Override
031  public boolean codeInValueSet(String system, String code) throws EOperationOutcome, Exception {
032    if (valueset.hasCodeSystem() && system.equals(valueset.getCodeSystem().getSystem()) && codeInDefine(valueset.getCodeSystem().getConcept(), code, valueset.getCodeSystem().getCaseSensitive()))
033     return true;
034
035    if (valueset.hasCompose()) {
036      boolean ok = false;
037      for (UriType uri : valueset.getCompose().getImport()) {
038        ok = ok || inImport(uri.getValue(), system, code);
039      }
040      for (ConceptSetComponent vsi : valueset.getCompose().getInclude()) {
041        ok = ok || inComponent(vsi, system, code);
042      }
043      for (ConceptSetComponent vsi : valueset.getCompose().getExclude()) {
044        ok = ok && !inComponent(vsi, system, code);
045      }
046    }
047    
048    return false;
049  }
050
051  private boolean inImport(String uri, String system, String code) throws EOperationOutcome, Exception {
052    ValueSet vs = context.fetchResource(ValueSet.class, uri);
053    if (vs == null) 
054      return false ; // we can't tell
055    return codeInExpansion(factory.getExpander().expand(vs), system, code);
056  }
057
058  private boolean codeInExpansion(ValueSetExpansionOutcome vso, String system, String code) throws EOperationOutcome, Exception {
059    if (vso.getService() != null) {
060      return vso.getService().codeInValueSet(system, code);
061    } else {
062      for (ValueSetExpansionContainsComponent c : vso.getValueset().getExpansion().getContains()) {
063        if (code.equals(c.getCode()) && (system == null || system.equals(c.getSystem())))
064          return true;
065        if (codeinExpansion(c, system, code)) 
066          return true;
067      }
068    }
069    return false;
070  }
071
072  private boolean codeinExpansion(ValueSetExpansionContainsComponent cnt, String system, String code) {
073    for (ValueSetExpansionContainsComponent c : cnt.getContains()) {
074      if (code.equals(c.getCode()) && system.equals(c.getSystem().toString()))
075        return true;
076      if (codeinExpansion(c, system, code)) 
077        return true;
078    }
079    return false;
080  }
081
082
083  private boolean inComponent(ConceptSetComponent vsi, String system, String code) throws Exception {
084    if (!vsi.getSystem().equals(system))
085      return false; 
086    // whether we know the system or not, we'll accept the stated codes at face value
087    for (ConceptReferenceComponent cc : vsi.getConcept())
088      if (cc.getCode().equals(code)) {
089        return true;
090      }
091      
092    ValueSet def = context.fetchCodeSystem(system);
093    if (def != null) {
094      if (!def.getCodeSystem().getCaseSensitive()) {
095        // well, ok, it's not case sensitive - we'll check that too now
096        for (ConceptReferenceComponent cc : vsi.getConcept())
097          if (cc.getCode().equalsIgnoreCase(code)) {
098            return false;
099          }
100      }
101      if (vsi.getConcept().isEmpty() && vsi.getFilter().isEmpty()) {
102        return codeInDefine(def.getCodeSystem().getConcept(), code, def.getCodeSystem().getCaseSensitive());
103      }
104      for (ConceptSetFilterComponent f: vsi.getFilter())
105        throw new Error("not done yet: "+f.getValue());
106
107      return false;
108    } else if (context.supportsSystem(system)) {
109      ValidationResult vv = context.validateCode(system, code, null, vsi);
110      return vv.isOk();
111    } else
112      // we don't know this system, and can't resolve it
113      return false;
114  }
115
116  private boolean codeInDefine(List<ConceptDefinitionComponent> concepts, String code, boolean caseSensitive) {
117    for (ConceptDefinitionComponent c : concepts) {
118      if (caseSensitive && code.equals(c.getCode()))
119        return true;
120      if (!caseSensitive && code.equalsIgnoreCase(c.getCode()))
121        return true;
122      if (codeInDefine(c.getConcept(), code, caseSensitive))
123        return true;
124    }
125    return false;
126  }
127
128}