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