001package org.hl7.fhir.dstu3.terminologies;
002
003/*-
004 * #%L
005 * org.hl7.fhir.dstu3
006 * %%
007 * Copyright (C) 2014 - 2019 Health Level 7
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023
024import java.util.List;
025
026import org.hl7.fhir.dstu3.context.IWorkerContext;
027import org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult;
028import org.hl7.fhir.dstu3.model.CodeSystem;
029import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
030import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
031import org.hl7.fhir.dstu3.model.UriType;
032import org.hl7.fhir.dstu3.model.ValueSet;
033import org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceComponent;
034import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
035import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetFilterComponent;
036import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent;
037import org.hl7.fhir.dstu3.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
038import org.hl7.fhir.dstu3.utils.EOperationOutcome;
039
040public class ValueSetCheckerSimple implements ValueSetChecker {
041
042  private ValueSet valueset;
043  private ValueSetExpanderFactory factory;
044  private IWorkerContext context;
045
046  public ValueSetCheckerSimple(ValueSet source, ValueSetExpanderFactory factory, IWorkerContext context) {
047    this.valueset = source;
048    this.factory = factory;
049    this.context = context;
050  }
051
052  @Override
053  public boolean codeInValueSet(String system, String code) throws EOperationOutcome, Exception {
054
055    if (valueset.hasCompose()) {
056      boolean ok = false;
057      for (ConceptSetComponent vsi : valueset.getCompose().getInclude()) {
058        ok = ok || inComponent(vsi, system, code);
059      }
060      for (ConceptSetComponent vsi : valueset.getCompose().getExclude()) {
061        ok = ok && !inComponent(vsi, system, code);
062      }
063    }
064    
065    return false;
066  }
067
068  private boolean inImport(String uri, String system, String code) throws EOperationOutcome, Exception {
069    ValueSet vs = context.fetchResource(ValueSet.class, uri);
070    if (vs == null) 
071      return false ; // we can't tell
072    return codeInExpansion(factory.getExpander().expand(vs, null), system, code);
073  }
074
075  private boolean codeInExpansion(ValueSetExpansionOutcome vso, String system, String code) throws EOperationOutcome, Exception {
076    if (vso.getService() != null) {
077      return vso.getService().codeInValueSet(system, code);
078    } else {
079      for (ValueSetExpansionContainsComponent c : vso.getValueset().getExpansion().getContains()) {
080        if (code.equals(c.getCode()) && (system == null || system.equals(c.getSystem())))
081          return true;
082        if (codeinExpansion(c, system, code)) 
083          return true;
084      }
085    }
086    return false;
087  }
088
089  private boolean codeinExpansion(ValueSetExpansionContainsComponent cnt, String system, String code) {
090    for (ValueSetExpansionContainsComponent c : cnt.getContains()) {
091      if (code.equals(c.getCode()) && system.equals(c.getSystem().toString()))
092        return true;
093      if (codeinExpansion(c, system, code)) 
094        return true;
095    }
096    return false;
097  }
098
099
100  private boolean inComponent(ConceptSetComponent vsi, String system, String code) throws Exception {
101    if (vsi.hasSystem() && !vsi.getSystem().equals(system))
102      return false; 
103    
104    for (UriType uri : vsi.getValueSet()) {
105      if (!inImport(uri.getValue(), system, code))
106        return false;
107    }
108
109    if (!vsi.hasSystem())
110      return false;
111    
112    // whether we know the system or not, we'll accept the stated codes at face value
113    for (ConceptReferenceComponent cc : vsi.getConcept())
114      if (cc.getCode().equals(code)) {
115        return true;
116      }
117      
118    CodeSystem def = context.fetchCodeSystem(system);
119    if (def != null && def.getContent() == CodeSystemContentMode.COMPLETE) {
120      if (!def.getCaseSensitive()) {
121        // well, ok, it's not case sensitive - we'll check that too now
122        for (ConceptReferenceComponent cc : vsi.getConcept())
123          if (cc.getCode().equalsIgnoreCase(code)) {
124            return false;
125          }
126      }
127      if (vsi.getConcept().isEmpty() && vsi.getFilter().isEmpty()) {
128        return codeInDefine(def.getConcept(), code, def.getCaseSensitive());
129      }
130      for (ConceptSetFilterComponent f: vsi.getFilter())
131        throw new Error("not done yet: "+f.getValue());
132
133      return false;
134    } else if (context.supportsSystem(system)) {
135      ValidationResult vv = context.validateCode(system, code, null, vsi);
136      return vv.isOk();
137    } else
138      // we don't know this system, and can't resolve it
139      return false;
140  }
141
142  private boolean codeInDefine(List<ConceptDefinitionComponent> concepts, String code, boolean caseSensitive) {
143    for (ConceptDefinitionComponent c : concepts) {
144      if (caseSensitive && code.equals(c.getCode()))
145        return true;
146      if (!caseSensitive && code.equalsIgnoreCase(c.getCode()))
147        return true;
148      if (codeInDefine(c.getConcept(), code, caseSensitive))
149        return true;
150    }
151    return false;
152  }
153
154}