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}