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