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}