Fix bug handling filter, and refactor terminology code

This commit is contained in:
Grahame Grieve 2023-05-05 13:55:23 -05:00
parent 551fe6d7c0
commit b402fd1c45
77 changed files with 1045 additions and 1347 deletions

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r5.terminologies;
package org.hl7.fhir.convertors.misc;
/*
Copyright (c) 2011+, HL7, Inc.

View File

@ -2,7 +2,7 @@ package org.hl7.fhir.convertors.txClient;
import java.net.URISyntaxException;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.Utilities;
@ -10,7 +10,7 @@ import org.hl7.fhir.utilities.VersionUtilities;
public class TerminologyClientFactory {
public static TerminologyClient makeClient(String id, String url, String userAgent, FhirPublication v) throws URISyntaxException {
public static ITerminologyClient makeClient(String id, String url, String userAgent, FhirPublication v) throws URISyntaxException {
if (v == null)
return new TerminologyClientR5(id, checkEndsWith("/r4", url), userAgent);
switch (v) {
@ -33,7 +33,7 @@ public class TerminologyClientFactory {
}
}
public static TerminologyClient makeClient(String id, String url, String userAgent, String v) throws URISyntaxException {
public static ITerminologyClient makeClient(String id, String url, String userAgent, String v) throws URISyntaxException {
if (v == null)
return new TerminologyClientR5(id, checkEndsWith("/r4", url), userAgent);
v = VersionUtilities.getMajMin(v);

View File

@ -45,13 +45,13 @@ import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.utils.client.network.ClientHeaders;
import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
public class TerminologyClientR2 implements TerminologyClient {
public class TerminologyClientR2 implements ITerminologyClient {
private final FHIRToolingClient client; // todo: use the R2 client
private String id;
@ -125,19 +125,19 @@ public class TerminologyClientR2 implements TerminologyClient {
}
@Override
public TerminologyClient setTimeout(int i) {
public ITerminologyClient setTimeout(int i) {
client.setTimeout(i);
return this;
}
@Override
public TerminologyClient setLogger(ToolingClientLogger txLog) {
public ITerminologyClient setLogger(ToolingClientLogger txLog) {
client.setLogger(txLog);
return this;
}
@Override
public TerminologyClient setRetryCount(int retryCount) throws FHIRException {
public ITerminologyClient setRetryCount(int retryCount) throws FHIRException {
client.setRetryCount(retryCount);
return this;
}
@ -190,12 +190,12 @@ public class TerminologyClientR2 implements TerminologyClient {
}
@Override
public TerminologyClient setClientHeaders(ClientHeaders clientHeaders) {
public ITerminologyClient setClientHeaders(ClientHeaders clientHeaders) {
return null;
}
@Override
public TerminologyClient setUserAgent(String userAgent) {
public ITerminologyClient setUserAgent(String userAgent) {
client.setUserAgent(userAgent);
return this;
}

View File

@ -45,13 +45,13 @@ import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.utils.client.network.ClientHeaders;
import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
public class TerminologyClientR3 implements TerminologyClient {
public class TerminologyClientR3 implements ITerminologyClient {
private final FHIRToolingClient client; // todo: use the R2 client
private ClientHeaders clientHeaders;
@ -123,19 +123,19 @@ public class TerminologyClientR3 implements TerminologyClient {
}
@Override
public TerminologyClient setTimeout(int i) {
public ITerminologyClient setTimeout(int i) {
client.setTimeout(i);
return this;
}
@Override
public TerminologyClient setLogger(ToolingClientLogger txLog) {
public ITerminologyClient setLogger(ToolingClientLogger txLog) {
client.setLogger(txLog);
return this;
}
@Override
public TerminologyClient setRetryCount(int retryCount) throws FHIRException {
public ITerminologyClient setRetryCount(int retryCount) throws FHIRException {
client.setRetryCount(retryCount);
return this;
}
@ -188,14 +188,14 @@ public class TerminologyClientR3 implements TerminologyClient {
}
@Override
public TerminologyClient setClientHeaders(ClientHeaders clientHeaders) {
public ITerminologyClient setClientHeaders(ClientHeaders clientHeaders) {
this.clientHeaders = clientHeaders;
this.client.setClientHeaders(this.clientHeaders.headers());
return this;
}
@Override
public TerminologyClient setUserAgent(String userAgent) {
public ITerminologyClient setUserAgent(String userAgent) {
client.setUserAgent(userAgent);
return this;
}

View File

@ -48,13 +48,13 @@ import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.utils.client.network.ClientHeaders;
import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
public class TerminologyClientR4 implements TerminologyClient {
public class TerminologyClientR4 implements ITerminologyClient {
private final FHIRToolingClient client; // todo: use the R2 client
private ClientHeaders clientHeaders;
@ -139,19 +139,19 @@ public class TerminologyClientR4 implements TerminologyClient {
}
@Override
public TerminologyClient setTimeout(int i) {
public ITerminologyClient setTimeout(int i) {
client.setTimeout(i);
return this;
}
@Override
public TerminologyClient setLogger(ToolingClientLogger txLog) {
public ITerminologyClient setLogger(ToolingClientLogger txLog) {
client.setLogger(txLog);
return this;
}
@Override
public TerminologyClient setRetryCount(int retryCount) throws FHIRException {
public ITerminologyClient setRetryCount(int retryCount) throws FHIRException {
client.setRetryCount(retryCount);
return this;
}
@ -204,14 +204,14 @@ public class TerminologyClientR4 implements TerminologyClient {
}
@Override
public TerminologyClient setClientHeaders(ClientHeaders clientHeaders) {
public ITerminologyClient setClientHeaders(ClientHeaders clientHeaders) {
this.clientHeaders = clientHeaders;
this.client.setClientHeaders(this.clientHeaders.headers());
return this;
}
@Override
public TerminologyClient setUserAgent(String userAgent) {
public ITerminologyClient setUserAgent(String userAgent) {
client.setUserAgent(userAgent);
return this;
}

View File

@ -43,14 +43,14 @@ import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.utils.client.FHIRToolingClient;
import org.hl7.fhir.r5.utils.client.network.ClientHeaders;
import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
public class TerminologyClientR5 implements TerminologyClient {
public class TerminologyClientR5 implements ITerminologyClient {
private final FHIRToolingClient client;
private ClientHeaders clientHeaders;
@ -117,13 +117,13 @@ public class TerminologyClientR5 implements TerminologyClient {
}
@Override
public TerminologyClient setTimeout(int i) {
public ITerminologyClient setTimeout(int i) {
client.setTimeout(i);
return this;
}
@Override
public TerminologyClient setLogger(ToolingClientLogger txLog) {
public ITerminologyClient setLogger(ToolingClientLogger txLog) {
client.setLogger(txLog);
return this;
}
@ -139,7 +139,7 @@ public class TerminologyClientR5 implements TerminologyClient {
}
@Override
public TerminologyClient setRetryCount(int retryCount) throws FHIRException {
public ITerminologyClient setRetryCount(int retryCount) throws FHIRException {
client.setRetryCount(retryCount);
return this;
}
@ -178,14 +178,14 @@ public class TerminologyClientR5 implements TerminologyClient {
}
@Override
public TerminologyClient setClientHeaders(ClientHeaders clientHeaders) {
public ITerminologyClient setClientHeaders(ClientHeaders clientHeaders) {
this.clientHeaders = clientHeaders;
this.client.setClientHeaders(this.clientHeaders.headers());
return this;
}
@Override
public TerminologyClient setUserAgent(String userAgent) {
public ITerminologyClient setUserAgent(String userAgent) {
client.setUserAgent(userAgent);
return this;
}

View File

@ -1,6 +1,6 @@
package org.hl7.fhir.convertors.txClient;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.utilities.FhirPublication;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
@ -52,14 +52,14 @@ public class TerminologyClientFactoryTest {
@ParameterizedTest
@MethodSource("data")
public void testMakeClient(String url, FhirPublication fhirPublication, String expectedAddress) throws URISyntaxException {
TerminologyClient terminologyClient = TerminologyClientFactory.makeClient("id", url, "dummyUserAgent", fhirPublication);
ITerminologyClient terminologyClient = TerminologyClientFactory.makeClient("id", url, "dummyUserAgent", fhirPublication);
assertEquals(expectedAddress, terminologyClient.getAddress());
}
@Test
public void testMakeClientDstu1Fails() throws URISyntaxException {
assertThrows(Error.class, () -> {
TerminologyClient terminologyClient = TerminologyClientFactory.makeClient("id", "urldoesnotmatter", "dummyUserAgent", FhirPublication.DSTU1);
ITerminologyClient terminologyClient = TerminologyClientFactory.makeClient("id", "urldoesnotmatter", "dummyUserAgent", FhirPublication.DSTU1);
}
);
}
@ -67,7 +67,7 @@ public class TerminologyClientFactoryTest {
@Test
public void testMakeClientNullFails() throws URISyntaxException {
assertThrows(Error.class, () -> {
TerminologyClient terminologyClient = TerminologyClientFactory.makeClient("id", "urldoesnotmatter", "dummyUserAgent", FhirPublication.NULL);
ITerminologyClient terminologyClient = TerminologyClientFactory.makeClient("id", "urldoesnotmatter", "dummyUserAgent", FhirPublication.NULL);
}
);
}

View File

@ -17,7 +17,7 @@ import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;

View File

@ -41,7 +41,7 @@ import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.stringtemplate.v4.ST;
@ -1848,7 +1848,7 @@ public class ShExGenerator {
*/
private String genValueSet(ValueSet vs) {
ST vsd = tmplt(VALUE_SET_DEFINITION).add("vsuri", vsprefix(vs.getUrl())).add("comment", vs.getDescription());
ValueSetExpander.ValueSetExpansionOutcome vse = context.expandVS(vs, true, false);
ValueSetExpansionOutcome vse = context.expandVS(vs, true, false);
List<String> valid_codes = new ArrayList<String>();
if(vse != null &&
vse.getValueset() != null &&

View File

@ -89,7 +89,7 @@ import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.TranslatingUtilities;

View File

@ -110,12 +110,14 @@ import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy;
import org.hl7.fhir.r5.profilemodel.PEBuilder;
import org.hl7.fhir.r5.renderers.OperationOutcomeRenderer;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.terminologies.ValueSetCheckerSimple;
import org.hl7.fhir.r5.terminologies.ValueSetCheckerSimple.VSCheckerException;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.ValueSetExpanderSimple;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpander;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.validation.VSCheckerException;
import org.hl7.fhir.r5.terminologies.validation.ValueSetValidator;
import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientContext;
import org.hl7.fhir.r5.utils.PackageHackerR5;
import org.hl7.fhir.r5.utils.ResourceUtilities;
import org.hl7.fhir.r5.utils.ToolingExtensions;
@ -205,12 +207,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
private Object lock = new Object(); // used as a lock for the data that follows
protected String version; // although the internal resources are all R5, the version of FHIR they describe may not be
private String cacheId;
private boolean isTxCaching;
@Getter
private int serverQueryCount = 0;
private final Set<String> cached = new HashSet<>();
protected TerminologyClientContext tcc = new TerminologyClientContext();
private Map<String, Map<String, ResourceProxy>> allResourcesById = new HashMap<String, Map<String, ResourceProxy>>();
// all maps are to the full URI
private CanonicalResourceManager<CodeSystem> codeSystems = new CanonicalResourceManager<CodeSystem>(false);
@ -239,14 +238,11 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
protected Map<String, String> oidCache = new HashMap<>();
protected Map<String, Map<String, ValidationResult>> validationCache = new HashMap<String, Map<String,ValidationResult>>();
protected String tsServer;
protected String name;
private boolean allowLoadingDuplicates;
protected TerminologyClient txClient;
private final Set<String> codeSystemsUsed = new HashSet<>();
protected ToolingClientLogger txLog;
private TerminologyCapabilities txcaps;
private boolean canRunWithoutTerminology;
protected boolean noTerminologyServer;
private int expandCodesLimit = 1000;
@ -288,7 +284,6 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
allResourcesById.putAll(other.allResourcesById);
translator = other.translator;
codeSystems.copy(other.codeSystems);
txcaps = other.txcaps;
valueSets.copy(other.valueSets);
maps.copy(other.maps);
transforms.copy(other.transforms);
@ -305,11 +300,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
libraries.copy(libraries);
allowLoadingDuplicates = other.allowLoadingDuplicates;
tsServer = other.tsServer;
name = other.name;
txClient = other.txClient;
txLog = other.txLog;
txcaps = other.txcaps;
canRunWithoutTerminology = other.canRunWithoutTerminology;
noTerminologyServer = other.noTerminologyServer;
if (other.txCache != null)
@ -318,9 +310,6 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
logger = other.logger;
expParameters = other.expParameters;
version = other.version;
cacheId = other.cacheId;
isTxCaching = other.isTxCaching;
cached.addAll(other.cached);
supportedCodeSystems.addAll(other.supportedCodeSystems);
unsupportedCodeSystems.addAll(other.unsupportedCodeSystems);
codeSystemsUsed.addAll(other.codeSystemsUsed);
@ -331,6 +320,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
tlogging = other.tlogging;
locator = other.locator;
userAgent = other.userAgent;
tcc.copy(other.tcc);
}
}
@ -697,18 +687,18 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (noTerminologyServer) {
return false;
}
if (txcaps == null) {
if (tcc.getTxcaps() == null) {
try {
logger.logMessage("Terminology server: Check for supported code systems for "+system);
final TerminologyCapabilities capabilityStatement = txCache.hasTerminologyCapabilities() ? txCache.getTerminologyCapabilities() : txClient.getTerminologyCapabilities();
final TerminologyCapabilities capabilityStatement = txCache.hasTerminologyCapabilities() ? txCache.getTerminologyCapabilities() : tcc.getClient().getTerminologyCapabilities();
txCache.cacheTerminologyCapabilities(capabilityStatement);
setTxCaps(capabilityStatement);
} catch (Exception e) {
if (canRunWithoutTerminology) {
noTerminologyServer = true;
logger.logMessage("==============!! Running without terminology server !! ==============");
if (txClient!=null) {
logger.logMessage("txServer = "+txClient.getId());
if (tcc.getClient() != null) {
logger.logMessage("txServer = "+tcc.getClient().getId());
logger.logMessage("Error = "+e.getMessage()+"");
}
logger.logMessage("=====================================================================");
@ -787,7 +777,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
params.put("_incomplete", "true");
txLog("$expand on "+txCache.summary(vs));
try {
ValueSet result = txClient.expandValueset(vs, p, params);
ValueSet result = tcc.getClient().expandValueset(vs, p, params);
res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());
} catch (Exception e) {
res = new ValueSetExpansionOutcome(e.getMessage() == null ? e.getClass().getName() : e.getMessage(), TerminologyServiceErrorClass.UNKNOWN);
@ -855,7 +845,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
List<String> allErrors = new ArrayList<>();
// ok, first we try to expand locally
ValueSetExpanderSimple vse = constructValueSetExpanderSimple();
ValueSetExpander vse = constructValueSetExpanderSimple();
res = null;
try {
res = vse.expand(vs, p);
@ -878,7 +868,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
// if that failed, we try to expand on the server
if (addDependentResources(p, vs)) {
p.addParameter().setName("cache-id").setValue(new StringType(cacheId));
p.addParameter().setName("cache-id").setValue(new StringType(tcc.getCacheId()));
}
if (noTerminologyServer) {
@ -889,7 +879,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
params.put("_incomplete", "true");
txLog("$expand on "+txCache.summary(vs));
try {
ValueSet result = txClient.expandValueset(vs, p, params);
ValueSet result = tcc.getClient().expandValueset(vs, p, params);
if (!result.hasUrl()) {
result.setUrl(vs.getUrl());
}
@ -952,7 +942,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
for (CodingValidationRequest t : codes) {
if (!t.hasResult()) {
try {
ValueSetCheckerSimple vsc = constructValueSetCheckerSimple(options, vs);
ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs);
ValidationResult res = vsc.validateCode("Coding", t.getCoding());
if (txCache != null) {
txCache.cacheValidation(t.getCacheToken(), res, TerminologyCache.TRANSIENT);
@ -997,13 +987,13 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
if (batch.getEntry().size() > 0) {
txLog("$batch validate for "+batch.getEntry().size()+" codes on systems "+systems.toString());
if (txClient == null) {
if (tcc.getClient() == null) {
throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE));
}
if (txLog != null) {
txLog.clearLastId();
}
Bundle resp = txClient.validateBatch(batch);
Bundle resp = tcc.getClient().validateBatch(batch);
if (resp == null) {
throw new FHIRException(formatMessage(I18nConstants.TX_SERVER_NO_BATCH_RESPONSE));
}
@ -1074,8 +1064,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (options.isUseClient()) {
// ok, first we try to validate locally
try {
ValueSetCheckerSimple vsc = constructValueSetCheckerSimple(options, vs, ctxt);
if (!vsc.isServerSide(code.getSystem())) {
ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs, ctxt);
if (!ValueSetUtilities.isServerSide(code.getSystem())) {
res = vsc.validateCode(path, code);
if (txCache != null) {
txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
@ -1093,7 +1083,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
}
if (localError != null && txClient == null) {
if (localError != null && tcc.getClient() == null) {
return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.UNKNOWN, issues);
}
if (!options.isUseServer()) {
@ -1130,16 +1120,16 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
return res;
}
protected ValueSetExpanderSimple constructValueSetExpanderSimple() {
return new ValueSetExpanderSimple(this);
protected ValueSetExpander constructValueSetExpanderSimple() {
return new ValueSetExpander(this);
}
protected ValueSetCheckerSimple constructValueSetCheckerSimple( ValidationOptions options, ValueSet vs, ValidationContextCarrier ctxt) {
return new ValueSetCheckerSimple(options, vs, this, ctxt, expParameters, txcaps);
protected ValueSetValidator constructValueSetCheckerSimple( ValidationOptions options, ValueSet vs, ValidationContextCarrier ctxt) {
return new ValueSetValidator(options, vs, this, ctxt, expParameters, tcc.getTxcaps());
}
protected ValueSetCheckerSimple constructValueSetCheckerSimple( ValidationOptions options, ValueSet vs) {
return new ValueSetCheckerSimple(options, vs, this, expParameters, txcaps);
protected ValueSetValidator constructValueSetCheckerSimple( ValidationOptions options, ValueSet vs) {
return new ValueSetValidator(options, vs, this, expParameters, tcc.getTxcaps());
}
protected Parameters constructParameters(ValueSet vs, boolean hierarchical) {
@ -1149,7 +1139,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
boolean cached = addDependentResources(p, vs);
if (cached) {
p.addParameter().setName("cache-id").setValue(new StringType(cacheId));
p.addParameter().setName("cache-id").setValue(new StringType(tcc.getCacheId()));
}
return p;
}
@ -1218,7 +1208,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (options.isUseClient()) {
// ok, first we try to validate locally
try {
ValueSetCheckerSimple vsc = constructValueSetCheckerSimple(options, vs);
ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs);
res = vsc.validateCode("CodeableConcept", code);
txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
return res;
@ -1259,22 +1249,23 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
codeSystemsUsed.add(inc.getSystem());
}
}
if (vs != null) {
if (isTxCaching && cacheId != null && vs.getUrl() != null && cached.contains(vs.getUrl()+"|"+vs.getVersion())) {
if (tcc.isTxCaching() && tcc.getCacheId() != null && vs.getUrl() != null && tcc.getCached().contains(vs.getUrl()+"|"+vs.getVersion())) {
pin.addParameter().setName("url").setValue(new UriType(vs.getUrl()+(vs.hasVersion() ? "|"+vs.getVersion() : "")));
} else if (options.getVsAsUrl()){
pin.addParameter().setName("url").setValue(new UriType(vs.getUrl()));
} else {
pin.addParameter().setName("valueSet").setResource(vs);
if (vs.getUrl() != null) {
cached.add(vs.getUrl()+"|"+vs.getVersion());
tcc.getCached().add(vs.getUrl()+"|"+vs.getVersion());
}
}
cache = true;
addDependentResources(pin, vs);
}
if (cache) {
pin.addParameter().setName("cache-id").setValue(new StringType(cacheId));
pin.addParameter().setName("cache-id").setValue(new StringType(tcc.getCacheId()));
}
for (ParametersParameterComponent pp : pin.getParameter()) {
if (pp.getName().equals("profile")) {
@ -1288,14 +1279,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (txLog != null) {
txLog.clearLastId();
}
if (txClient == null) {
if (tcc.getClient() == null) {
throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE));
}
Parameters pOut;
if (vs == null) {
pOut = txClient.validateCS(pin);
pOut = tcc.getClient().validateCS(pin);
} else {
pOut = txClient.validateVS(pin);
pOut = tcc.getClient().validateVS(pin);
}
return processValidationResult(pOut);
}
@ -1317,8 +1308,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
ValueSet vs = fetchResource(ValueSet.class, c.getValue(), src);
if (vs != null) {
pin.addParameter().setName("tx-resource").setResource(vs);
if (isTxCaching && cacheId == null || !cached.contains(vs.getVUrl())) {
cached.add(vs.getVUrl());
if (tcc.isTxCaching() && tcc.getCacheId() == null || !tcc.getCached().contains(vs.getVUrl())) {
tcc.getCached().add(vs.getVUrl());
cache = true;
}
addDependentResources(pin, vs);
@ -1327,8 +1318,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
CodeSystem cs = fetchResource(CodeSystem.class, inc.getSystem(), src);
if (cs != null && (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == CodeSystemContentMode.FRAGMENT)) {
pin.addParameter().setName("tx-resource").setResource(cs);
if (isTxCaching && cacheId == null || !cached.contains(cs.getVUrl())) {
cached.add(cs.getVUrl());
if (tcc.isTxCaching() && tcc.getCacheId() == null || !tcc.getCached().contains(cs.getVUrl())) {
tcc.getCached().add(cs.getVUrl());
cache = true;
}
// todo: supplements
@ -1376,9 +1367,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
}
if (!ok) {
return new ValidationResult(IssueSeverity.ERROR, message+" (from "+txClient.getId()+")", err, null).setTxLink(txLog.getLastId());
return new ValidationResult(IssueSeverity.ERROR, message+" (from "+tcc.getClient().getId()+")", err, null).setTxLink(txLog.getLastId());
} else if (message != null && !message.equals("No Message returned")) {
return new ValidationResult(IssueSeverity.WARNING, message+" (from "+txClient.getId()+")", system, version, new ConceptDefinitionComponent().setDisplay(display).setCode(code), display, null).setTxLink(txLog.getLastId());
return new ValidationResult(IssueSeverity.WARNING, message+" (from "+tcc.getClient().getId()+")", system, version, new ConceptDefinitionComponent().setDisplay(display).setCode(code), display, null).setTxLink(txLog.getLastId());
} else if (display != null) {
return new ValidationResult(system, version, new ConceptDefinitionComponent().setDisplay(display).setCode(code), display).setTxLink(txLog.getLastId());
} else {
@ -1425,7 +1416,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
@Override
public boolean isNoTerminologyServer() {
return noTerminologyServer || txClient == null;
return noTerminologyServer || tcc.getClient() == null;
}
public void setNoTerminologyServer(boolean noTerminologyServer) {
@ -2376,41 +2367,41 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
public int getClientRetryCount() {
return txClient == null ? 0 : txClient.getRetryCount();
return tcc.getClient() == null ? 0 : tcc.getClient().getRetryCount();
}
public IWorkerContext setClientRetryCount(int value) {
if (txClient != null) {
txClient.setRetryCount(value);
if (tcc.getClient() != null) {
tcc.getClient().setRetryCount(value);
}
return this;
}
public TerminologyClient getTxClient() {
return txClient;
public ITerminologyClient getTxClient() {
return tcc.getClient();
}
public String getCacheId() {
return cacheId;
return tcc.getCacheId();
}
public void setCacheId(String cacheId) {
this.cacheId = cacheId;
tcc.setCacheId(cacheId);
}
public TerminologyCapabilities getTxCaps() {
return txcaps;
return tcc.getTxcaps();
}
public void setTxCaps(TerminologyCapabilities txCaps) {
this.txcaps = txCaps;
this.tcc.setTxcaps(txCaps);
if (txCaps != null) {
for (TerminologyCapabilitiesExpansionParameterComponent t : txcaps.getExpansion().getParameter()) {
for (TerminologyCapabilitiesExpansionParameterComponent t : tcc.getTxcaps().getExpansion().getParameter()) {
if ("cache-id".equals(t.getName())) {
isTxCaching = true;
tcc.setTxCaching(true);
}
}
for (TerminologyCapabilitiesCodeSystemComponent tccs : txcaps.getCodeSystem()) {
for (TerminologyCapabilitiesCodeSystemComponent tccs : tcc.getTxcaps().getCodeSystem()) {
supportedCodeSystems.add(tccs.getUri());
}
}
@ -2445,8 +2436,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
protected void setUserAgent(String userAgent) {
this.userAgent = userAgent;
if (txClient != null)
txClient.setUserAgent(userAgent);
if (tcc.getClient() != null)
tcc.getClient().setUserAgent(userAgent);
}

View File

@ -71,8 +71,8 @@ import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.profilemodel.PEDefinition;
import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy;
import org.hl7.fhir.r5.profilemodel.PEBuilder;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
import org.hl7.fhir.utilities.TimeTracker;

View File

@ -67,7 +67,7 @@ import org.hl7.fhir.r5.profilemodel.PEDefinition;
import org.hl7.fhir.r5.profilemodel.PEBuilder;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.terminologies.JurisdictionUtilities;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.R5Hacker;
import org.hl7.fhir.r5.utils.XVerExtensionManager;
@ -316,22 +316,22 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
loadBytes(name, stream);
}
public String connectToTSServer(TerminologyClient client, String log) {
public String connectToTSServer(ITerminologyClient client, String log) {
try {
txLog("Connect to "+client.getAddress());
txClient = client;
tcc.setClient(client);
if (log != null && (log.endsWith(".htm") || log.endsWith(".html"))) {
txLog = new HTMLClientLogger(log);
} else {
txLog = new TextClientLogger(log);
}
txClient.setLogger(txLog);
txClient.setUserAgent(userAgent);
tcc.getClient().setLogger(txLog);
tcc.getClient().setUserAgent(userAgent);
final CapabilityStatement capabilitiesStatementQuick = txCache.hasCapabilityStatement() ? txCache.getCapabilityStatement() : txClient.getCapabilitiesStatementQuick();
final CapabilityStatement capabilitiesStatementQuick = txCache.hasCapabilityStatement() ? txCache.getCapabilityStatement() : tcc.getClient().getCapabilitiesStatementQuick();
txCache.cacheCapabilityStatement(capabilitiesStatementQuick);
final TerminologyCapabilities capabilityStatement = txCache.hasTerminologyCapabilities() ? txCache.getTerminologyCapabilities() : txClient.getTerminologyCapabilities();
final TerminologyCapabilities capabilityStatement = txCache.hasTerminologyCapabilities() ? txCache.getTerminologyCapabilities() : tcc.getClient().getTerminologyCapabilities();
txCache.cacheTerminologyCapabilities(capabilityStatement);
setTxCaps(capabilityStatement);

View File

@ -51,8 +51,8 @@ import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;

View File

@ -57,7 +57,7 @@ import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.model.TypeConvertor;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.ElementDecoration;
import org.hl7.fhir.utilities.ElementDecoration.DecorationType;

View File

@ -26,7 +26,7 @@ import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator;

View File

@ -43,8 +43,8 @@ import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.LoincLinker;
import org.hl7.fhir.utilities.Utilities;

View File

@ -844,5 +844,20 @@ public class CodeSystemUtilities {
}
return false;
}
public static boolean hasPropertyDef(CodeSystem cs, String property) {
for (PropertyComponent pd : cs.getProperty()) {
if (pd.getCode().equals(property)) {
return true;
}
}
return false;
}
public static DataType getProperty(CodeSystem cs, String code, String property) {
ConceptDefinitionComponent def = getCode(cs, code);
ConceptPropertyComponent cp = getProperty(def, property);
return cp == null ? null : cp.getValue();
}
}

View File

@ -1,112 +0,0 @@
package org.hl7.fhir.r5.terminologies;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
/**
* The value set system has a collection of value sets
* that define code systems, and construct value sets from
* them
*
* Large external terminologies - LOINC, Snomed, etc - are too big, and
* trying to represent their definition as a native value set is too
* large. (e.g. LOINC + properties ~ 500MB). So we don't try. Instead.
* we assume that there's some external server that provides these
* services, using this interface
*
* The FHIR build tool uses http://fhir.healthintersections.com.au for
* these services
*
* @author Grahame
*
*/
public interface ITerminologyServices {
/**
* return true if the service handles code or value set resolution on the system
*/
public boolean supportsSystem(String system);
/**
* given a system|code, return a definition for it. Nil means not valid
*/
public ConceptDefinitionComponent getCodeDefinition(String system, String code);
public class ValidationResult {
private IssueSeverity severity;
private String message;
public ValidationResult(IssueSeverity severity, String message) {
super();
this.severity = severity;
this.message = message;
}
public IssueSeverity getSeverity() {
return severity;
}
public String getMessage() {
return message;
}
}
/**
* for this system|code and display, validate the triple against the rules of
* the underlying code system
*/
public ValidationResult validateCode(String system, String code, String display);
/**
* Expand the value set fragment (system | codes | filters). Note that this
* might fail if the expansion is very large. If the expansion fails, then the
* checkVS will be called instead
*/
public ValueSetExpansionComponent expandVS(ConceptSetComponent inc) throws Exception;
// public ValueSet expandVS(ValueSet vs) throws Exception;
public ValueSetExpansionOutcome expand(ValueSet vs);
/**
* Test the value set fragment (system | codes | filters).
*/
public boolean checkVS(ConceptSetComponent vsi, String system, String code);
public boolean verifiesSystem(String system);
}

View File

@ -0,0 +1,55 @@
package org.hl7.fhir.r5.terminologies;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hl7.fhir.r5.model.Enumerations.FHIRVersion;
public class TerminologyServerDetails {
public enum ServerAuthorizationMethod {
OPEN,
TOKEN,
SMART_ON_FHIR
}
private String name;
private ServerAuthorizationMethod auth;
private Map<FHIRVersion, String> endpoints = new HashMap<>();
private List<String> codeSystems = new ArrayList<>();
public boolean handlesSystem(String uri, String version) {
for (String s : codeSystems) {
if (s.contains("|")) {
String u = s.substring(0, s.lastIndexOf("|"));
String v = s.substring(s.lastIndexOf("|")+1);
if (v.equals(version) && (s.equals(uri) || uri.matches(s))) {
return true;
}
} else {
if (s.equals(uri) || uri.matches(s)) {
return true;
}
}
}
return false;
}
public String getName() {
return name;
}
public ServerAuthorizationMethod getAuth() {
return auth;
}
public Map<FHIRVersion, String> getEndpoints() {
return endpoints;
}
public List<String> getCodeSystems() {
return codeSystems;
}
}

View File

@ -1,37 +0,0 @@
package org.hl7.fhir.r5.terminologies;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
public class TerminologyServiceCache {
}

View File

@ -1,82 +0,0 @@
package org.hl7.fhir.r5.terminologies;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ETooCostly;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.utils.EOperationOutcome;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
public interface ValueSetChecker {
public static class ValidationProcessInfo {
private TerminologyServiceErrorClass err;
private List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
public TerminologyServiceErrorClass getErr() {
return err;
}
public void setErr(TerminologyServiceErrorClass err) {
this.err = err;
}
public List<OperationOutcomeIssueComponent> getIssues() {
return issues;
}
public void addIssue(List<OperationOutcomeIssueComponent> issues) {
issues.addAll(issues);
}
public boolean hasErrors() {
for (OperationOutcomeIssueComponent issue : issues) {
if (issue.getSeverity() == IssueSeverity.FATAL || issue.getSeverity() == IssueSeverity.ERROR) {
return true;
}
}
return false;
}
public String summary() {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder("; ");
for (OperationOutcomeIssueComponent issue : issues) {
b.append(issue.getDetails().getText());
}
return b.toString();
}
}
Boolean codeInValueSet(String system, String version, String code, ValidationProcessInfo info) throws ETooCostly, EOperationOutcome, Exception;
}

View File

@ -1,145 +0,0 @@
package org.hl7.fhir.r5.terminologies;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.ValueSet;
public interface ValueSetExpander {
public enum TerminologyServiceErrorClass {
UNKNOWN, NOSERVICE, SERVER_ERROR, VALUESET_UNSUPPORTED, CODESYSTEM_UNSUPPORTED, BLOCKED_BY_OPTIONS, INTERNAL_ERROR, BUSINESS_RULE, TOO_COSTLY;
public boolean isInfrastructure() {
return this == NOSERVICE || this == SERVER_ERROR || this == VALUESET_UNSUPPORTED;
}
}
public class ETooCostly extends Exception {
public ETooCostly(String msg) {
super(msg);
}
}
/**
* Some value sets are just too big to expand. Instead of an expanded value set,
* you get back an interface that can test membership - usually on a server somewhere
*
* @author Grahame
*/
public class ValueSetExpansionOutcome {
private ValueSet valueset;
private String error;
private TerminologyServiceErrorClass errorClass;
private String txLink;
private List<String> allErrors = new ArrayList<>();
public ValueSetExpansionOutcome(ValueSet valueset) {
super();
this.valueset = valueset;
this.error = null;
}
public ValueSetExpansionOutcome(ValueSet valueset, String error, TerminologyServiceErrorClass errorClass) {
super();
this.valueset = valueset;
this.error = error;
this.errorClass = errorClass;
allErrors.add(error);
}
public ValueSetExpansionOutcome(ValueSetChecker service, String error, TerminologyServiceErrorClass errorClass) {
super();
this.valueset = null;
this.error = error;
this.errorClass = errorClass;
allErrors.add(error);
}
public ValueSetExpansionOutcome(String error, TerminologyServiceErrorClass errorClass) {
this.valueset = null;
this.error = error;
this.errorClass = errorClass;
allErrors.add(error);
}
public ValueSetExpansionOutcome(String error, TerminologyServiceErrorClass errorClass, List<String> errList) {
this.valueset = null;
this.error = error;
this.errorClass = errorClass;
this.allErrors.addAll(errList);
if (!allErrors.contains(error)) {
allErrors.add(error);
}
if (!errList.contains(error)) {
errList.add(error);
}
}
public ValueSet getValueset() {
return valueset;
}
public String getError() {
return error;
}
public TerminologyServiceErrorClass getErrorClass() {
return errorClass;
}
public String getTxLink() {
return txLink;
}
public ValueSetExpansionOutcome setTxLink(String txLink) {
this.txLink = txLink;
return this;
}
public List<String> getAllErrors() {
return allErrors;
}
public boolean isOk() {
return (allErrors.isEmpty() || (allErrors.size() == 1 && allErrors.get(0) == null)) && error == null;
}
}
/**
*
* @param source the value set definition to expand
* @param profile a set of parameters affecting the outcome. If you don't supply parameters, the default internal parameters will be used.
*
* @return
* @throws ETooCostly
* @throws FileNotFoundException
* @throws IOException
*/
public ValueSetExpansionOutcome expand(ValueSet source, Parameters parameters) throws ETooCostly, FileNotFoundException, IOException;
}

View File

@ -1,65 +0,0 @@
package org.hl7.fhir.r5.terminologies;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
Copyright (c) 2011+, HL7, Inc
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
public interface ValueSetExpanderFactory {
public ValueSetExpander getExpander();
}

View File

@ -1,210 +0,0 @@
package org.hl7.fhir.r5.terminologies;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
Copyright (c) 2011+, HL7, Inc
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.formats.XmlParser;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.OperationOutcome;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
public class ValueSetExpansionCache implements ValueSetExpanderFactory {
public class CacheAwareExpander implements ValueSetExpander {
@Override
public ValueSetExpansionOutcome expand(ValueSet source, Parameters expParams) throws ETooCostly, IOException {
String cacheKey = makeCacheKey(source, expParams);
if (expansions.containsKey(cacheKey))
return expansions.get(cacheKey);
ValueSetExpander vse = new ValueSetExpanderSimple(context);
ValueSetExpansionOutcome vso = vse.expand(source, expParams);
if (vso.getError() != null) {
// well, we'll see if the designated server can expand it, and if it can, we'll cache it locally
vso = context.expandVS(source, false, expParams == null || !expParams.getParameterBool("excludeNested"));
if (cacheFolder != null) {
FileOutputStream s = new FileOutputStream(Utilities.path(cacheFolder, makeFileName(source.getUrl())));
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(s, vso.getValueset());
s.close();
}
}
if (vso.getValueset() != null)
expansions.put(cacheKey, vso);
return vso;
}
private String makeCacheKey(ValueSet source, Parameters expParams) throws IOException {
if (expParams == null)
return source.getUrl();
JsonParser p = new JsonParser();
String r = p.composeString(expParams);
return source.getUrl() + " " + r.hashCode();
}
}
private static final String VS_ID_EXT = "http://tools/cache";
private final Map<String, ValueSetExpansionOutcome> expansions = new HashMap<String, ValueSetExpansionOutcome>();
private final Map<String, CanonicalResource> canonicals = new HashMap<String, CanonicalResource>();
private final IWorkerContext context;
private final String cacheFolder;
private Object lock;
public ValueSetExpansionCache(IWorkerContext context, Object lock) {
super();
cacheFolder = null;
this.lock = lock;
this.context = context;
}
public ValueSetExpansionCache(IWorkerContext context, String cacheFolder, Object lock) throws FHIRFormatError, IOException {
super();
this.context = context;
this.cacheFolder = cacheFolder;
this.lock = lock;
if (this.cacheFolder != null)
loadCache();
}
private String makeFileName(String url) {
return url.replace("$", "").replace(":", "").replace("|", ".").replace("//", "/").replace("/", "_")+".xml";
}
private void loadCache() throws FHIRFormatError, IOException {
File[] files = new File(cacheFolder).listFiles();
for (File f : files) {
if (f.getName().endsWith(".xml")) {
final FileInputStream is = new FileInputStream(f);
try {
Resource r = new XmlParser().setOutputStyle(OutputStyle.PRETTY).parse(is);
if (r instanceof OperationOutcome) {
OperationOutcome oo = (OperationOutcome) r;
expansions.put(ToolingExtensions.getExtension(oo,VS_ID_EXT).getValue().toString(),
new ValueSetExpansionOutcome(new XhtmlComposer(XhtmlComposer.XML, false).composePlainText(oo.getText().getDiv()), TerminologyServiceErrorClass.UNKNOWN));
} else if (r instanceof ValueSet) {
ValueSet vs = (ValueSet) r;
if (vs.hasExpansion())
expansions.put(vs.getUrl(), new ValueSetExpansionOutcome(vs));
else {
canonicals.put(vs.getUrl(), vs);
if (vs.hasVersion())
canonicals.put(vs.getUrl()+"|"+vs.getVersion(), vs);
}
} else if (r instanceof CanonicalResource) {
CanonicalResource md = (CanonicalResource) r;
canonicals.put(md.getUrl(), md);
if (md.hasVersion())
canonicals.put(md.getUrl()+"|"+md.getVersion(), md);
}
} finally {
IOUtils.closeQuietly(is);
}
}
}
}
@Override
public ValueSetExpander getExpander() {
return new CacheAwareExpander();
// return new ValueSetExpander(valuesets, codesystems);
}
public CanonicalResource getStoredResource(String canonicalUri) {
synchronized (lock) {
return canonicals.get(canonicalUri);
}
}
public void storeResource(CanonicalResource md) throws IOException {
synchronized (lock) {
canonicals.put(md.getUrl(), md);
if (md.hasVersion())
canonicals.put(md.getUrl()+"|"+md.getVersion(), md);
}
if (cacheFolder != null) {
FileOutputStream s = new FileOutputStream(Utilities.path(cacheFolder, makeFileName(md.getUrl()+"|"+md.getVersion())));
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(s, md);
s.close();
}
}
}

View File

@ -68,6 +68,11 @@ import org.hl7.fhir.utilities.Utilities;
public class ValueSetUtilities {
public static boolean isServerSide(String url) {
return Utilities.existsInList(url, "http://hl7.org/fhir/sid/cvx");
}
public static ValueSet makeShareable(ValueSet vs) {
if (!vs.hasExperimental()) {
vs.setExperimental(false);

View File

@ -1,12 +0,0 @@
package org.hl7.fhir.r5.terminologies;
import org.hl7.fhir.utilities.Utilities;
public class ValueSetWorker {
public boolean isServerSide(String url) {
return Utilities.existsInList(url, "http://hl7.org/fhir/sid/cvx");
}
}

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r5.terminologies;
package org.hl7.fhir.r5.terminologies.client;
/*
Copyright (c) 2011+, HL7, Inc.
@ -40,7 +40,7 @@ import org.hl7.fhir.utilities.ToolingClientLogger;
import java.util.EnumSet;
import java.util.Map;
public interface TerminologyClient {
public interface ITerminologyClient {
EnumSet<FhirPublication> supportableVersions();
void setAllowedVersions(EnumSet<FhirPublication> versions);
@ -54,15 +54,15 @@ public interface TerminologyClient {
ValueSet expandValueset(ValueSet vs, Parameters p, Map<String, String> params) throws FHIRException;
Parameters validateCS(Parameters pin) throws FHIRException;
Parameters validateVS(Parameters pin) throws FHIRException;
TerminologyClient setTimeout(int i) throws FHIRException;
TerminologyClient setLogger(ToolingClientLogger txLog) throws FHIRException;
ITerminologyClient setTimeout(int i) throws FHIRException;
ITerminologyClient setLogger(ToolingClientLogger txLog) throws FHIRException;
int getRetryCount() throws FHIRException;
TerminologyClient setRetryCount(int retryCount) throws FHIRException;
ITerminologyClient setRetryCount(int retryCount) throws FHIRException;
CapabilityStatement getCapabilitiesStatementQuick() throws FHIRException;
Parameters lookupCode(Map<String, String> params) throws FHIRException;
Bundle validateBatch(Bundle batch);
CanonicalResource read(String type, String id);
ClientHeaders getClientHeaders();
TerminologyClient setClientHeaders(ClientHeaders clientHeaders);
TerminologyClient setUserAgent(String userAgent);
ITerminologyClient setClientHeaders(ClientHeaders clientHeaders);
ITerminologyClient setUserAgent(String userAgent);
}

View File

@ -0,0 +1,81 @@
package org.hl7.fhir.r5.terminologies.client;
import java.util.HashSet;
import java.util.Set;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
public class TerminologyClientContext {
private String cacheId;
private boolean isTxCaching;
private int serverQueryCount = 0;
private final Set<String> cached = new HashSet<>();
// protected String tsServer;
private ITerminologyClient client;
private TerminologyCapabilities txcaps;
public String getCacheId() {
return cacheId;
}
public void setCacheId(String cacheId) {
this.cacheId = cacheId;
}
public boolean isTxCaching() {
return isTxCaching;
}
public void setTxCaching(boolean isTxCaching) {
this.isTxCaching = isTxCaching;
}
public int getServerQueryCount() {
return serverQueryCount;
}
public void setServerQueryCount(int serverQueryCount) {
this.serverQueryCount = serverQueryCount;
}
public Set<String> getCached() {
return cached;
}
// public String getTsServer() {
// return tsServer;
// }
//
// public void setTsServer(String tsServer) {
// this.tsServer = tsServer;
// }
public ITerminologyClient getClient() {
return client;
}
public void setClient(ITerminologyClient client) {
this.client = client;
}
public TerminologyCapabilities getTxcaps() {
return txcaps;
}
public void setTxcaps(TerminologyCapabilities txcaps) {
this.txcaps = txcaps;
}
public void copy(TerminologyClientContext other) {
cacheId = other.cacheId;
isTxCaching = other.isTxCaching;
cached.addAll(other.cached);
// tsServer = other.tsServer;
client = other.client;
txcaps = other.txcaps;
}
}

View File

@ -0,0 +1,18 @@
package org.hl7.fhir.r5.terminologies.expansion;
import java.util.List;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
public class AllConceptsFilter extends ConceptFilter {
public AllConceptsFilter(List<String> allErrors) {
super(allErrors);
}
@Override
public boolean includeConcept(CodeSystem cs, ConceptDefinitionComponent def) {
return true;
}
}

View File

@ -0,0 +1,28 @@
package org.hl7.fhir.r5.terminologies.expansion;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
public abstract class ConceptFilter {
private List<String> allErrors;
protected FHIRException fail(String msg) {
allErrors.add(msg);
return new FHIRException(msg);
}
public ConceptFilter(List<String> allErrors) {
super();
this.allErrors = allErrors;
}
public abstract boolean includeConcept(CodeSystem cs, ConceptDefinitionComponent def);
}

View File

@ -0,0 +1,7 @@
package org.hl7.fhir.r5.terminologies.expansion;
import org.hl7.fhir.exceptions.FHIRException;
public class EFinished extends FHIRException {
}

View File

@ -0,0 +1,9 @@
package org.hl7.fhir.r5.terminologies.expansion;
public class ETooCostly extends Exception {
public ETooCostly(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,59 @@
package org.hl7.fhir.r5.terminologies.expansion;
import java.util.List;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent;
import org.hl7.fhir.r5.model.CodeSystem.PropertyType;
import org.hl7.fhir.r5.model.Enumerations.FilterOperator;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
public class PropertyFilter extends ConceptFilter {
private ConceptSetFilterComponent filter;
private PropertyComponent property;
public PropertyFilter(List<String> allErrors, ConceptSetFilterComponent fc, PropertyComponent propertyDefinition) {
super (allErrors);
this.filter = fc;
this.property = propertyDefinition;
}
@Override
public boolean includeConcept(CodeSystem cs, ConceptDefinitionComponent def) {
ConceptPropertyComponent pc = getPropertyForConcept(def);
if (pc != null) {
String v = pc.getValue().isPrimitive() ? pc.getValue().primitiveValue() : null;
switch (filter.getOp()) {
case DESCENDENTOF: throw fail("not supported yet: "+filter.getOp().toCode());
case EQUAL: return filter.getValue().equals(v);
case EXISTS: throw fail("not supported yet: "+filter.getOp().toCode());
case GENERALIZES: throw fail("not supported yet: "+filter.getOp().toCode());
case IN: throw fail("not supported yet: "+filter.getOp().toCode());
case ISA: throw fail("not supported yet: "+filter.getOp().toCode());
case ISNOTA: throw fail("not supported yet: "+filter.getOp().toCode());
case NOTIN: throw fail("not supported yet: "+filter.getOp().toCode());
case NULL: throw fail("not supported yet: "+filter.getOp().toCode());
case REGEX: throw fail("not supported yet: "+filter.getOp().toCode());
default:
throw fail("Shouldn't get here");
}
} else if (property.getType() == PropertyType.BOOLEAN && filter.getOp() == FilterOperator.EQUAL) {
return "false".equals(filter.getValue());
} else {
return false;
}
}
private ConceptPropertyComponent getPropertyForConcept(ConceptDefinitionComponent def) {
for (ConceptPropertyComponent pc : def.getProperty()) {
if (pc.getCode().equals(property.getCode())) {
return pc;
}
}
return null;
}
}

View File

@ -0,0 +1,21 @@
package org.hl7.fhir.r5.terminologies.expansion;
import java.util.List;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
public class RegexFilter extends ConceptFilter {
private String regex;
protected RegexFilter(List<String> allErrors, String regex) {
super(allErrors);
this.regex = regex;
}
@Override
public boolean includeConcept(CodeSystem cs, ConceptDefinitionComponent def) {
return def.getCode().matches(regex);
}
}

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r5.terminologies;
package org.hl7.fhir.r5.terminologies.expansion;
/*
Copyright (c) 2011+, HL7, Inc.
@ -69,11 +69,7 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.NotImplementedException;
import org.hl7.fhir.exceptions.FHIRException;
@ -92,7 +88,6 @@ import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionDesignationComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent;
import org.hl7.fhir.r5.model.CodeSystem.PropertyType;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.DateTimeType;
import org.hl7.fhir.r5.model.Enumerations.FilterOperator;
@ -115,121 +110,40 @@ import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionPropertyComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpanderSimple.EFinished;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
import org.hl7.fhir.r5.terminologies.providers.CodeSystemProvider;
import org.hl7.fhir.r5.terminologies.providers.CodeSystemProviderExtension;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import com.google.errorprone.annotations.NoAllocation;
public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetExpander {
import alleycats.std.all;
public class EFinished extends FHIRException {
public class ValueSetExpander {
}
public interface IConceptFilter {
boolean includeConcept(CodeSystem cs, ConceptDefinitionComponent def);
}
public class PropertyFilter implements IConceptFilter {
private ConceptSetFilterComponent filter;
private PropertyComponent property;
public PropertyFilter(ConceptSetFilterComponent fc, PropertyComponent propertyDefinition) {
this.filter = fc;
this.property = propertyDefinition;
}
@Override
public boolean includeConcept(CodeSystem cs, ConceptDefinitionComponent def) {
ConceptPropertyComponent pc = getPropertyForConcept(def);
if (pc != null) {
String v = pc.getValue().isPrimitive() ? pc.getValue().primitiveValue() : null;
switch (filter.getOp()) {
case DESCENDENTOF: throw fail("not supported yet: "+filter.getOp().toCode());
case EQUAL: return filter.getValue().equals(v);
case EXISTS: throw fail("not supported yet: "+filter.getOp().toCode());
case GENERALIZES: throw fail("not supported yet: "+filter.getOp().toCode());
case IN: throw fail("not supported yet: "+filter.getOp().toCode());
case ISA: throw fail("not supported yet: "+filter.getOp().toCode());
case ISNOTA: throw fail("not supported yet: "+filter.getOp().toCode());
case NOTIN: throw fail("not supported yet: "+filter.getOp().toCode());
case NULL: throw fail("not supported yet: "+filter.getOp().toCode());
case REGEX: throw fail("not supported yet: "+filter.getOp().toCode());
default:
throw fail("Shouldn't get here");
}
} else if (property.getType() == PropertyType.BOOLEAN && filter.getOp() == FilterOperator.EQUAL) {
return "false".equals(filter.getValue());
} else {
return false;
}
}
private ConceptPropertyComponent getPropertyForConcept(ConceptDefinitionComponent def) {
for (ConceptPropertyComponent pc : def.getProperty()) {
if (pc.getCode().equals(property.getCode())) {
return pc;
}
}
return null;
}
}
public class AllConceptsFilter implements IConceptFilter {
@Override
public boolean includeConcept(CodeSystem cs, ConceptDefinitionComponent def) {
return true;
}
}
public class RegexFilter implements IConceptFilter {
private String regex;
protected RegexFilter(String regex) {
super();
this.regex = regex;
}
@Override
public boolean includeConcept(CodeSystem cs, ConceptDefinitionComponent def) {
return def.getCode().matches(regex);
}
}
private static final boolean REPORT_VERSION_ANYWAY = false;
private List<ValueSetExpansionContainsComponent> codes = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private List<ValueSetExpansionContainsComponent> roots = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>();
private IWorkerContext context;
private boolean canBeHeirarchy = true;
private boolean includeAbstract = true;
private Set<String> excludeKeys = new HashSet<String>();
private Set<String> excludeSystems = new HashSet<String>();
private ValueSet focus;
private int maxExpansionSize = 1000;
private int offset;
private int count;
private List<String> allErrors = new ArrayList<>();
private List<String> requiredSupplements = new ArrayList<>();
private int total;
private int maxExpansionSize = 1000;
private WorkingContext dwc = new WorkingContext();
private boolean checkCodesWhenExpanding;
private boolean includeAbstract = true;
public ValueSetExpanderSimple(IWorkerContext context) {
public ValueSetExpander(IWorkerContext context) {
super();
this.context = context;
}
public ValueSetExpanderSimple(IWorkerContext context, List<String> allErrors) {
public ValueSetExpander(IWorkerContext context, List<String> allErrors) {
super();
this.context = context;
this.allErrors = allErrors;
@ -239,7 +153,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
maxExpansionSize = theMaxExpansionSize;
}
private ValueSetExpansionContainsComponent addCode(String system, String code, String display, String dispLang, ValueSetExpansionContainsComponent parent, List<ConceptDefinitionDesignationComponent> designations, Parameters expParams,
private ValueSetExpansionContainsComponent addCode(WorkingContext wc, String system, String code, String display, String dispLang, ValueSetExpansionContainsComponent parent, List<ConceptDefinitionDesignationComponent> designations, Parameters expParams,
boolean isAbstract, boolean inactive, String definition, List<ValueSet> filters, boolean noInactive, boolean deprecated, List<ValueSetExpansionPropertyComponent> vsProp,
List<ConceptPropertyComponent> csProps, List<org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent> expProps, List<Extension> csExtList, List<Extension> vsExtList) throws ETooCostly {
@ -352,27 +266,25 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
}
}
String s = key(n);
if (map.containsKey(s) || excludeKeys.contains(s)) {
canBeHeirarchy = false;
if (wc.getMap().containsKey(s) || wc.getExcludeKeys().contains(s)) {
wc.setCanBeHeirarchy(false);
} else {
codes.add(n);
map.put(s, n);
total++;
if (total > maxExpansionSize) {
if (offset+count > 0 && total > offset+count) {
total = -1;
wc.getCodes().add(n);
wc.getMap().put(s, n);
wc.incTotal();
if (wc == dwc && wc.getTotal() > maxExpansionSize) {
if (wc.getOffset()+wc.getCount() > 0 && wc.getTotal() > wc.getOffset()+wc.getCount()) {
wc.setTotal(-1);
throw new EFinished();
}
throw failCostly(context.formatMessage(I18nConstants.VALUESET_TOO_COSTLY, focus.getUrl(), ">" + Integer.toString(maxExpansionSize)));
}
}
if (canBeHeirarchy && parent != null) {
if (wc.isCanBeHeirarchy() && parent != null) {
parent.getContains().add(n);
} else {
roots.add(n);
wc.getRoots().add(n);
}
return n;
}
@ -404,12 +316,12 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
return null;
}
private void addCodeAndDescendents(ValueSetExpansionContainsComponent focus, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filters, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps, ValueSet vsSrc) throws FHIRException, ETooCostly {
private void addCodeAndDescendents(WorkingContext wc, ValueSetExpansionContainsComponent focus, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filters, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps, ValueSet vsSrc) throws FHIRException, ETooCostly {
focus.checkNoModifiers("Expansion.contains", "expanding");
ValueSetExpansionContainsComponent np = addCode(focus.getSystem(), focus.getCode(), focus.getDisplay(), vsSrc.getLanguage(), parent,
ValueSetExpansionContainsComponent np = addCode(wc, focus.getSystem(), focus.getCode(), focus.getDisplay(), vsSrc.getLanguage(), parent,
convert(focus.getDesignation()), expParams, focus.getAbstract(), focus.getInactive(), focus.getExtensionString(ToolingExtensions.EXT_DEFINITION), filters, noInactive, false, vsProps, null, focus.getProperty(), null, focus.getExtension());
for (ValueSetExpansionContainsComponent c : focus.getContains())
addCodeAndDescendents(c, np, expParams, filters, noInactive, vsProps, vsSrc);
addCodeAndDescendents(wc, c, np, expParams, filters, noInactive, vsProps, vsSrc);
}
private List<ConceptDefinitionDesignationComponent> convert(List<ConceptReferenceDesignationComponent> designations) {
@ -424,8 +336,8 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
return list;
}
private void addCodeAndDescendents(CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filters,
ConceptDefinitionComponent exclusion, IConceptFilter filterFunc, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps) throws FHIRException, ETooCostly {
private void addCodeAndDescendents(WorkingContext wc,CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filters,
ConceptDefinitionComponent exclusion, ConceptFilter filterFunc, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps, List<WorkingContext> otherFilters) throws FHIRException, ETooCostly {
def.checkNoModifiers("Code in Code System", "expanding");
if (exclusion != null) {
if (exclusion.getCode().equals(def.getCode()))
@ -435,20 +347,21 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
boolean abs = CodeSystemUtilities.isNotSelectable(cs, def);
boolean inc = CodeSystemUtilities.isInactive(cs, def);
boolean dep = CodeSystemUtilities.isDeprecated(cs, def, false);
if ((includeAbstract || !abs) && filterFunc.includeConcept(cs, def)) {
np = addCode(system, def.getCode(), def.getDisplay(), cs.getLanguage(), parent, def.getDesignation(), expParams, abs, inc, def.getDefinition(), filters, noInactive, dep, vsProps, def.getProperty(), null, def.getExtension(), null);
if ((includeAbstract || !abs) && filterFunc.includeConcept(cs, def) && passesOtherFilters(otherFilters, cs, def.getCode())) {
np = addCode(wc, system, def.getCode(), def.getDisplay(), cs.getLanguage(), parent, def.getDesignation(), expParams, abs, inc, def.getDefinition(), filters, noInactive, dep, vsProps, def.getProperty(), null, def.getExtension(), null);
}
for (ConceptDefinitionComponent c : def.getConcept()) {
addCodeAndDescendents(cs, system, c, np, expParams, filters, exclusion, filterFunc, noInactive, vsProps);
addCodeAndDescendents(wc, cs, system, c, np, expParams, filters, exclusion, filterFunc, noInactive, vsProps, otherFilters);
}
if (def.hasUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK)) {
List<ConceptDefinitionComponent> children = (List<ConceptDefinitionComponent>) def.getUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK);
for (ConceptDefinitionComponent c : children)
addCodeAndDescendents(cs, system, c, np, expParams, filters, exclusion, filterFunc, noInactive, vsProps);
addCodeAndDescendents(wc, cs, system, c, np, expParams, filters, exclusion, filterFunc, noInactive, vsProps, otherFilters);
}
}
private void addCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params, Parameters expParams, List<ValueSet> filters, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps, ValueSet vsSrc) throws ETooCostly, FHIRException {
if (expand != null) {
if (expand.getContains().size() > maxExpansionSize)
@ -462,18 +375,18 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
}
}
private void excludeCode(String theSystem, String theCode) {
private void excludeCode(WorkingContext wc, String theSystem, String theCode) {
ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
n.setSystem(theSystem);
n.setCode(theCode);
String s = key(n);
excludeKeys.add(s);
wc.getExcludeKeys().add(s);
}
private void excludeCodes(ConceptSetComponent exc, List<ValueSetExpansionParameterComponent> params, String ctxt) throws FHIRException {
private void excludeCodes(WorkingContext wc, ConceptSetComponent exc, List<ValueSetExpansionParameterComponent> params, String ctxt) throws FHIRException {
exc.checkNoModifiers("Compose.exclude", "expanding");
if (exc.hasSystem() && exc.getConcept().size() == 0 && exc.getFilter().size() == 0) {
excludeSystems.add(exc.getSystem());
wc.getExcludeSystems().add(exc.getSystem());
}
if (exc.hasValueSet())
@ -486,12 +399,12 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
ValueSet valueset = vse.getValueset();
if (valueset == null)
throw failTSE("Error Expanding ValueSet: "+vse.getError());
excludeCodes(valueset.getExpansion(), params);
excludeCodes(wc, valueset.getExpansion(), params);
return;
}
for (ConceptReferenceComponent c : exc.getConcept()) {
excludeCode(exc.getSystem(), c.getCode());
excludeCode(wc, exc.getSystem(), c.getCode());
}
if (exc.getFilter().size() > 0)
@ -500,9 +413,9 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
private void excludeCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params) {
private void excludeCodes(WorkingContext wc, ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params) {
for (ValueSetExpansionContainsComponent c : expand.getContains()) {
excludeCode(c.getSystem(), c.getCode());
excludeCode(wc, c.getSystem(), c.getCode());
}
}
@ -514,7 +427,6 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
return false;
}
@Override
public ValueSetExpansionOutcome expand(ValueSet source, Parameters expParams) {
allErrors.clear();
try {
@ -557,15 +469,15 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
focus.getExpansion().addParameter().setName(p.getName()).setValue(p.getValue());
}
if ("offset".equals(p.getName()) && p.hasValueIntegerType()) {
offset = p.getValueIntegerType().getValue();
if (offset < 0) {
offset = 0;
dwc.setOffset(p.getValueIntegerType().getValue());
if (dwc.getOffset() < 0) {
dwc.setOffset(0);
}
}
if ("count".equals(p.getName()) && p.hasValueIntegerType()) {
count = p.getValueIntegerType().getValue();
if (count < 0) {
count = 0;
dwc.setCount(p.getValueIntegerType().getValue());
if (dwc.getCount() < 0) {
dwc.setCount(0);
}
}
}
@ -586,20 +498,20 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
// nothing - we intended to trap this here
}
if (canBeHeirarchy) {
for (ValueSetExpansionContainsComponent c : roots) {
if (dwc.isCanBeHeirarchy()) {
for (ValueSetExpansionContainsComponent c : dwc.getRoots()) {
focus.getExpansion().getContains().add(c);
}
} else {
int i = 0;
int cc = 0;
for (ValueSetExpansionContainsComponent c : codes) {
if (map.containsKey(key(c)) && (includeAbstract || !c.getAbstract())) { // we may have added abstract codes earlier while we still thought it might be heirarchical, but later we gave up, so now ignore them
if (offset == 0 || i >= offset) {
for (ValueSetExpansionContainsComponent c : dwc.getCodes()) {
if (dwc.getMap().containsKey(key(c)) && (includeAbstract || !c.getAbstract())) { // we may have added abstract codes earlier while we still thought it might be heirarchical, but later we gave up, so now ignore them
if (dwc.getOffset() == 0 || i >= dwc.getOffset()) {
focus.getExpansion().getContains().add(c);
c.getContains().clear(); // make sure any heirarchy is wiped
cc++;
if (cc == count) {
if (cc == dwc.getCount()) {
break;
}
}
@ -608,8 +520,8 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
}
}
if (total >= 0) {
focus.getExpansion().setTotal(total);
if (dwc.getTotal() >= 0) {
focus.getExpansion().setTotal(dwc.getTotal());
}
if (!requiredSupplements.isEmpty()) {
return new ValueSetExpansionOutcome("Required supplements not found: "+requiredSupplements.toString(), TerminologyServiceErrorClass.BUSINESS_RULE, allErrors);
@ -618,6 +530,11 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
focus.setCompose(null);
focus.getExtension().clear();
focus.setPublisher(null);
focus.setDescription(null);
focus.setPurpose(null);
focus.getContact().clear();
focus.setCopyright(null);
focus.setText(null);
}
return new ValueSetExpansionOutcome(focus);
}
@ -645,16 +562,16 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
compose.checkNoModifiers("ValueSet.compose", "expanding");
// Exclude comes first because we build up a map of things to exclude
for (ConceptSetComponent inc : compose.getExclude())
excludeCodes(inc, exp.getParameter(), ctxt);
canBeHeirarchy = !expParams.getParameterBool("excludeNested") && excludeKeys.isEmpty() && excludeSystems.isEmpty() && offset+count == 0;
excludeCodes(dwc, inc, exp.getParameter(), ctxt);
dwc.setCanBeHeirarchy(!expParams.getParameterBool("excludeNested") && dwc.getExcludeKeys().isEmpty() && dwc.getExcludeSystems().isEmpty() && dwc.getOffset()+dwc.getCount() == 0);
includeAbstract = !expParams.getParameterBool("excludeNotForUI");
boolean first = true;
for (ConceptSetComponent inc : compose.getInclude()) {
if (first == true)
first = false;
else
canBeHeirarchy = false;
includeCodes(inc, exp, expParams, canBeHeirarchy, compose.hasInactive() ? !compose.getInactive() : checkNoInActiveFromParam(expParams), extensions, valueSet);
dwc.setCanBeHeirarchy(false);
includeCodes(inc, exp, expParams, dwc.isCanBeHeirarchy(), compose.hasInactive() ? !compose.getInactive() : checkNoInActiveFromParam(expParams), extensions, valueSet);
}
}
@ -672,7 +589,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
return false;
}
private ValueSet importValueSet(String value, ValueSetExpansionComponent exp, Parameters expParams, boolean noInactive, ValueSet valueSet) throws ETooCostly, TerminologyServiceException, FileNotFoundException, IOException, FHIRFormatError {
private ValueSet importValueSet(WorkingContext wc, String value, ValueSetExpansionComponent exp, Parameters expParams, boolean noInactive, ValueSet valueSet) throws ETooCostly, TerminologyServiceException, FileNotFoundException, IOException, FHIRFormatError {
if (value == null)
throw fail("unable to find value set with no identity");
ValueSet vs = context.fetchResource(ValueSet.class, value, valueSet);
@ -687,7 +604,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
expParams = expParams.copy();
expParams.addParameter("activeOnly", true);
}
ValueSetExpansionOutcome vso = new ValueSetExpanderSimple(context, allErrors).expand(vs, expParams);
ValueSetExpansionOutcome vso = new ValueSetExpander(context, allErrors).expand(vs, expParams);
if (vso.getError() != null) {
addErrors(vso.getAllErrors());
throw fail("Unable to expand imported value set "+vs.getUrl()+": " + vso.getError());
@ -711,9 +628,9 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
exp.getParameter().add(p);
}
if (isValueSetUnionImports(valueSet)) {
copyExpansion(vso.getValueset().getExpansion().getContains());
copyExpansion(wc, vso.getValueset().getExpansion().getContains());
}
canBeHeirarchy = false; // if we're importing a value set, we have to be combining, so we won't try for a heirarchy
wc.setCanBeHeirarchy(false); // if we're importing a value set, we have to be combining, so we won't try for a heirarchy
return vso.getValueset();
}
@ -727,7 +644,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
}
}
public void copyExpansion(List<ValueSetExpansionContainsComponent> list) {
public void copyExpansion(WorkingContext wc,List<ValueSetExpansionContainsComponent> list) {
for (ValueSetExpansionContainsComponent cc : list) {
ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
n.setSystem(cc.getSystem());
@ -738,12 +655,12 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
n.getDesignation().addAll(cc.getDesignation());
String s = key(n);
if (!map.containsKey(s) && !excludeKeys.contains(s)) {
codes.add(n);
map.put(s, n);
total++;
if (!wc.getMap().containsKey(s) && !wc.getExcludeKeys().contains(s)) {
wc.getCodes().add(n);
wc.getMap().put(s, n);
wc.incTotal();
}
copyExpansion(cc.getContains());
copyExpansion(wc, cc.getContains());
}
}
@ -758,7 +675,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
private void copyImportContains(List<ValueSetExpansionContainsComponent> list, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filter, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps, ValueSet vsSrc) throws FHIRException, ETooCostly {
for (ValueSetExpansionContainsComponent c : list) {
c.checkNoModifiers("Imported Expansion in Code System", "expanding");
ValueSetExpansionContainsComponent np = addCode(c.getSystem(), c.getCode(), c.getDisplay(), vsSrc.getLanguage(), parent, null, expParams, c.getAbstract(), c.getInactive(), c.getExtensionString(ToolingExtensions.EXT_DEFINITION),
ValueSetExpansionContainsComponent np = addCode(dwc, c.getSystem(), c.getCode(), c.getDisplay(), vsSrc.getLanguage(), parent, null, expParams, c.getAbstract(), c.getInactive(), c.getExtensionString(ToolingExtensions.EXT_DEFINITION),
filter, noInactive, false, vsProps, null, c.getProperty(), null, c.getExtension());
copyImportContains(c.getContains(), np, expParams, filter, noInactive, vsProps, vsSrc);
}
@ -768,7 +685,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
inc.checkNoModifiers("Compose.include", "expanding");
List<ValueSet> imports = new ArrayList<ValueSet>();
for (CanonicalType imp : inc.getValueSet()) {
imports.add(importValueSet(imp.getValue(), exp, expParams, noInactive, valueSet));
imports.add(importValueSet(dwc, imp.getValue(), exp, expParams, noInactive, valueSet));
}
if (!inc.hasSystem()) {
@ -780,7 +697,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
copyImportContains(base.getExpansion().getContains(), null, expParams, imports, noInactive, base.getExpansion().getProperty(), base);
} else {
CodeSystem cs = context.fetchSupplementedCodeSystem(inc.getSystem());
if (isServerSide(inc.getSystem()) || (cs == null || (cs.getContent() != CodeSystemContentMode.COMPLETE && cs.getContent() != CodeSystemContentMode.FRAGMENT))) {
if (ValueSetUtilities.isServerSide(inc.getSystem()) || (cs == null || (cs.getContent() != CodeSystemContentMode.COMPLETE && cs.getContent() != CodeSystemContentMode.FRAGMENT))) {
doServerIncludeCodes(inc, heirarchical, exp, imports, expParams, extensions, noInactive, valueSet.getExpansion().getProperty());
} else {
if (cs.hasUserData("supplements.installed")) {
@ -824,7 +741,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
}
}
for (ValueSetExpansionContainsComponent cc : vs.getExpansion().getContains()) {
addCodeAndDescendents(cc, null, expParams, imports, noInactive, vsProps, vs);
addCodeAndDescendents(dwc, cc, null, expParams, imports, noInactive, vsProps, vs);
}
}
@ -847,7 +764,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) {
// special case - add all the code system
for (ConceptDefinitionComponent def : cs.getConcept()) {
addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, null, new AllConceptsFilter(), noInactive, exp.getProperty());
addCodeAndDescendents(dwc, cs, inc.getSystem(), def, null, expParams, imports, null, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), null);
}
if (cs.getContent() == CodeSystemContentMode.FRAGMENT) {
addFragmentWarning(exp, cs);
@ -858,7 +775,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
}
if (!inc.getConcept().isEmpty()) {
canBeHeirarchy = false;
dwc.setCanBeHeirarchy(false);
for (ConceptReferenceComponent c : inc.getConcept()) {
c.checkNoModifiers("Code in Value Set", "expanding");
ConceptDefinitionComponent def = CodeSystemUtilities.findCode(cs.getConcept(), c.getCode());
@ -879,69 +796,81 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
inactive = CodeSystemUtilities.isInactive(cs, def);
isAbstract = CodeSystemUtilities.isNotSelectable(cs, def);
}
addCode(inc.getSystem(), c.getCode(), !Utilities.noString(c.getDisplay()) ? c.getDisplay() : def == null ? null : def.getDisplay(), c.hasDisplay() ? vsSrc.getLanguage() : cs.getLanguage(), null, mergeDesignations(def, convertDesignations(c.getDesignation())),
addCode(dwc, inc.getSystem(), c.getCode(), !Utilities.noString(c.getDisplay()) ? c.getDisplay() : def == null ? null : def.getDisplay(), c.hasDisplay() ? vsSrc.getLanguage() : cs.getLanguage(), null, mergeDesignations(def, convertDesignations(c.getDesignation())),
expParams, isAbstract, inactive, def == null ? null : def.getDefinition(), imports, noInactive, false, exp.getProperty(), def != null ? def.getProperty() : null, null, def == null ? null : def.getExtension(), c.getExtension());
}
}
if (inc.getFilter().size() > 1) {
canBeHeirarchy = false; // which will bt the case if we get around to supporting this
throw failTSE("Multiple filters not handled yet"); // need to and them, and this isn't done yet. But this shouldn't arise in non loinc and snomed value sets
}
if (inc.getFilter().size() == 1) {
if (inc.getFilter().size() > 0) {
if (inc.getFilter().size() > 1) {
dwc.setCanBeHeirarchy(false); // which will be the case if we get around to supporting this
}
if (cs.getContent() == CodeSystemContentMode.FRAGMENT) {
addFragmentWarning(exp, cs);
}
List<WorkingContext> filters = new ArrayList<>();
for (int i = 1; i < inc.getFilter().size(); i++) {
WorkingContext wc = new WorkingContext();
filters.add(wc);
processFilter(inc, exp, expParams, imports, cs, noInactive, inc.getFilter().get(i), wc, null);
}
ConceptSetFilterComponent fc = inc.getFilter().get(0);
if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISA) {
// special: all codes in the target code system under the value
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def == null)
throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, null, new AllConceptsFilter(), noInactive, exp.getProperty());
} else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISNOTA) {
// special: all codes in the target code system that are not under the value
ConceptDefinitionComponent defEx = getConceptForCode(cs.getConcept(), fc.getValue());
if (defEx == null)
throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
for (ConceptDefinitionComponent def : cs.getConcept()) {
addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, defEx, new AllConceptsFilter(), noInactive, exp.getProperty());
}
} else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.DESCENDENTOF) {
// special: all codes in the target code system under the value
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def == null)
throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, inc.getSystem(), c, null, expParams, imports, null, new AllConceptsFilter(), noInactive, exp.getProperty());
if (def.hasUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK)) {
List<ConceptDefinitionComponent> children = (List<ConceptDefinitionComponent>) def.getUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK);
for (ConceptDefinitionComponent c : children)
addCodeAndDescendents(cs, inc.getSystem(), c, null, expParams, imports, null, new AllConceptsFilter(), noInactive, exp.getProperty());
}
WorkingContext wc = dwc;
processFilter(inc, exp, expParams, imports, cs, noInactive, fc, wc, filters);
}
}
} else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) {
// gg; note: wtf is this: if the filter is display=v, look up the code 'v', and see if it's diplsay is 'v'?
canBeHeirarchy = false;
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def != null) {
if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {
if (def.getDisplay().contains(fc.getValue())) {
addCode(inc.getSystem(), def.getCode(), def.getDisplay(), cs.getLanguage(), null, def.getDesignation(), expParams, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def),
def.getDefinition(), imports, noInactive, false, exp.getProperty(), def.getProperty(), null, def.getExtension(), null);
}
private void processFilter(ConceptSetComponent inc, ValueSetExpansionComponent exp, Parameters expParams,
List<ValueSet> imports, CodeSystem cs, boolean noInactive, ConceptSetFilterComponent fc, WorkingContext wc, List<WorkingContext> filters)
throws ETooCostly {
if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISA) {
// special: all codes in the target code system under the value
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def == null)
throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, null, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters);
} else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISNOTA) {
// special: all codes in the target code system that are not under the value
ConceptDefinitionComponent defEx = getConceptForCode(cs.getConcept(), fc.getValue());
if (defEx == null)
throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
for (ConceptDefinitionComponent def : cs.getConcept()) {
addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, defEx, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters);
}
} else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.DESCENDENTOF) {
// special: all codes in the target code system under the value
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def == null)
throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(wc, cs, inc.getSystem(), c, null, expParams, imports, null, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters);
if (def.hasUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK)) {
List<ConceptDefinitionComponent> children = (List<ConceptDefinitionComponent>) def.getUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK);
for (ConceptDefinitionComponent c : children)
addCodeAndDescendents(wc, cs, inc.getSystem(), c, null, expParams, imports, null, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters);
}
} else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) {
// gg; note: wtf is this: if the filter is display=v, look up the code 'v', and see if it's diplsay is 'v'?
dwc.setCanBeHeirarchy(false);
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def != null) {
if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {
if (def.getDisplay().contains(fc.getValue()) && passesOtherFilters(filters, cs, def.getCode())) {
addCode(wc, inc.getSystem(), def.getCode(), def.getDisplay(), cs.getLanguage(), null, def.getDesignation(), expParams, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def),
def.getDefinition(), imports, noInactive, false, exp.getProperty(), def.getProperty(), null, def.getExtension(), null);
}
}
} else if (isDefinedProperty(cs, fc.getProperty())) {
for (ConceptDefinitionComponent def : cs.getConcept()) {
addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, null, new PropertyFilter(fc, getPropertyDefinition(cs, fc.getProperty())), noInactive, exp.getProperty());
}
} else if ("code".equals(fc.getProperty()) && fc.getOp() == FilterOperator.REGEX) {
for (ConceptDefinitionComponent def : cs.getConcept()) {
addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, null, new RegexFilter(fc.getValue()), noInactive, exp.getProperty());
}
} else {
throw fail("Search by property[" + fc.getProperty() + "] and op[" + fc.getOp() + "] is not supported yet");
}
} else if (isDefinedProperty(cs, fc.getProperty())) {
for (ConceptDefinitionComponent def : cs.getConcept()) {
addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, null, new PropertyFilter(allErrors, fc, getPropertyDefinition(cs, fc.getProperty())), noInactive, exp.getProperty(), filters);
}
} else if ("code".equals(fc.getProperty()) && fc.getOp() == FilterOperator.REGEX) {
for (ConceptDefinitionComponent def : cs.getConcept()) {
addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, null, new RegexFilter(allErrors, fc.getValue()), noInactive, exp.getProperty(), filters);
}
} else {
throw fail("Filter by property[" + fc.getProperty() + "] and op[" + fc.getOp() + "] is not supported yet");
}
}
@ -1039,4 +968,16 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
this.checkCodesWhenExpanding = checkCodesWhenExpanding;
}
private boolean passesOtherFilters(List<WorkingContext> otherFilters, CodeSystem cs, String code) {
if (otherFilters == null) {
return true;
}
String key = key(cs.getUrl(), code);
for (WorkingContext wc : otherFilters) {
if (!wc.getMap().containsKey(key)) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,77 @@
package org.hl7.fhir.r5.terminologies.expansion;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
/**
* Some value sets are just too big to expand. Instead of an expanded value set,
* you get back an interface that can test membership - usually on a server somewhere
*
* @author Grahame
*/
public class ValueSetExpansionOutcome {
private ValueSet valueset;
private String error;
private TerminologyServiceErrorClass errorClass;
private String txLink;
private List<String> allErrors = new ArrayList<>();
public ValueSetExpansionOutcome(ValueSet valueset) {
super();
this.valueset = valueset;
this.error = null;
}
public ValueSetExpansionOutcome(ValueSet valueset, String error, TerminologyServiceErrorClass errorClass) {
super();
this.valueset = valueset;
this.error = error;
this.errorClass = errorClass;
allErrors.add(error);
}
public ValueSetExpansionOutcome(String error, TerminologyServiceErrorClass errorClass) {
this.valueset = null;
this.error = error;
this.errorClass = errorClass;
allErrors.add(error);
}
public ValueSetExpansionOutcome(String error, TerminologyServiceErrorClass errorClass, List<String> errList) {
this.valueset = null;
this.error = error;
this.errorClass = errorClass;
this.allErrors.addAll(errList);
if (!allErrors.contains(error)) {
allErrors.add(error);
}
if (!errList.contains(error)) {
errList.add(error);
}
}
public ValueSet getValueset() {
return valueset;
}
public String getError() {
return error;
}
public TerminologyServiceErrorClass getErrorClass() {
return errorClass;
}
public String getTxLink() {
return txLink;
}
public ValueSetExpansionOutcome setTxLink(String txLink) {
this.txLink = txLink;
return this;
}
public List<String> getAllErrors() {
return allErrors;
}
public boolean isOk() {
return (allErrors.isEmpty() || (allErrors.size() == 1 && allErrors.get(0) == null)) && error == null;
}
}

View File

@ -0,0 +1,81 @@
package org.hl7.fhir.r5.terminologies.expansion;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
class WorkingContext {
private List<ValueSetExpansionContainsComponent> codes = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private List<ValueSetExpansionContainsComponent> roots = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>();
private Set<String> excludeKeys = new HashSet<String>();
private Set<String> excludeSystems = new HashSet<String>();
private boolean canBeHeirarchy = true;
private int offset;
private int count;
private int total;
public List<ValueSetExpansionContainsComponent> getCodes() {
return codes;
}
public List<ValueSetExpansionContainsComponent> getRoots() {
return roots;
}
public Map<String, ValueSetExpansionContainsComponent> getMap() {
return map;
}
public Set<String> getExcludeKeys() {
return excludeKeys;
}
public Set<String> getExcludeSystems() {
return excludeSystems;
}
public boolean isCanBeHeirarchy() {
return canBeHeirarchy;
}
public void setCanBeHeirarchy(boolean canBeHeirarchy) {
this.canBeHeirarchy = canBeHeirarchy;
}
public int getOffset() {
return offset;
}
public void setOffset(int offset) {
this.offset = offset;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public void incTotal() {
total++;
}
}

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r5.terminologies;
package org.hl7.fhir.r5.terminologies.providers;
import java.util.List;
@ -8,8 +8,6 @@ import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionPropertyComponent;
import org.hl7.fhir.r5.terminologies.providers.CodeSystemProviderExtension;
import org.hl7.fhir.r5.terminologies.providers.ColorRGBProvider;
/**
* For special code systems where the code system resource isn't enough, but we can support them internall
@ -32,6 +30,6 @@ public abstract class CodeSystemProvider {
List<ValueSet> imports, Parameters expParams, List<Extension> extensions, boolean noInactive,
List<ValueSetExpansionPropertyComponent> vsProps) throws CodeSystemProviderExtension;
protected abstract Boolean checkCode(String code);
public abstract Boolean checkCode(String code);
}

View File

@ -9,7 +9,6 @@ import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionPropertyComponent;
import org.hl7.fhir.r5.terminologies.CodeSystemProvider;
public class ColorRGBProvider extends CodeSystemProvider {
@ -21,7 +20,7 @@ public class ColorRGBProvider extends CodeSystemProvider {
}
@Override
protected Boolean checkCode(String code) {
public Boolean checkCode(String code) {
return code.matches("^\\#[0-9a-fA-F]{6}$");
}

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r5.terminologies;
package org.hl7.fhir.r5.terminologies.providers;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.Coding;

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r5.terminologies;
package org.hl7.fhir.r5.terminologies.providers;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.Coding;
@ -8,11 +8,12 @@ public class URICodeSystem extends SpecialCodeSystem {
@Override
public ConceptDefinitionComponent findConcept(Coding code) {
if (Utilities.isAbsoluteUrl(code.getCode())) {
return new ConceptDefinitionComponent(code.getCode());
} else {
return null;
}
throw new Error("This is used");
// if (Utilities.isAbsoluteUrl(code.getCode())) {
// return new ConceptDefinitionComponent(code.getCode());
// } else {
// return null;
// }
}
}

View File

@ -0,0 +1,9 @@
package org.hl7.fhir.r5.terminologies.utilities;
public enum TerminologyServiceErrorClass {
UNKNOWN, NOSERVICE, SERVER_ERROR, VALUESET_UNSUPPORTED, CODESYSTEM_UNSUPPORTED, BLOCKED_BY_OPTIONS, INTERNAL_ERROR, BUSINESS_RULE, TOO_COSTLY;
public boolean isInfrastructure() {
return this == NOSERVICE || this == SERVER_ERROR || this == VALUESET_UNSUPPORTED;
}
}

View File

@ -0,0 +1,24 @@
package org.hl7.fhir.r5.terminologies.validation;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
public class ConceptReferencePair {
private ValueSet valueset;
private ConceptReferenceComponent cc;
public ConceptReferencePair(ValueSet valueset, ConceptReferenceComponent cc) {
this.valueset = valueset;
this.cc = cc;
}
public ValueSet getValueset() {
return valueset;
}
public ConceptReferenceComponent getCc() {
return cc;
}
}

View File

@ -0,0 +1,23 @@
package org.hl7.fhir.r5.terminologies.validation;
import java.util.List;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
public class VSCheckerException extends FHIRException {
private List<OperationOutcomeIssueComponent> issues;
public VSCheckerException(String message, List<OperationOutcomeIssueComponent> issues) {
super(message);
this.issues = issues;
}
public List<OperationOutcomeIssueComponent> getIssues() {
return issues;
}
private static final long serialVersionUID = -5889505119633054187L;
}

View File

@ -0,0 +1,43 @@
package org.hl7.fhir.r5.terminologies.validation;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
public class ValidationProcessInfo {
private TerminologyServiceErrorClass err;
private List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
public TerminologyServiceErrorClass getErr() {
return err;
}
public void setErr(TerminologyServiceErrorClass err) {
this.err = err;
}
public List<OperationOutcomeIssueComponent> getIssues() {
return issues;
}
public void addIssue(List<OperationOutcomeIssueComponent> issues) {
issues.addAll(issues);
}
public boolean hasErrors() {
for (OperationOutcomeIssueComponent issue : issues) {
if (issue.getSeverity() == IssueSeverity.FATAL || issue.getSeverity() == IssueSeverity.ERROR) {
return true;
}
}
return false;
}
public String summary() {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder("; ");
for (OperationOutcomeIssueComponent issue : issues) {
b.append(issue.getDetails().getText());
}
return b.toString();
}
}

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r5.terminologies;
package org.hl7.fhir.r5.terminologies.validation;
/*
Copyright (c) 2011+, HL7, Inc.
@ -54,12 +54,12 @@ import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionDesignationComponent;
import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r5.model.OperationOutcome.IssueType;
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.hl7.fhir.r5.model.PackageInformation;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r5.model.TerminologyCapabilities.TerminologyCapabilitiesCodeSystemComponent;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.Transport.ParameterComponent;
@ -70,10 +70,12 @@ import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.ValueSetCheckerSimple.ConceptReferencePair;
import org.hl7.fhir.r5.terminologies.ValueSetCheckerSimple.VersionInfo;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.providers.CodeSystemProvider;
import org.hl7.fhir.r5.terminologies.providers.SpecialCodeSystem;
import org.hl7.fhir.r5.terminologies.providers.URICodeSystem;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier.ValidationContextResourceProxy;
@ -87,117 +89,18 @@ import org.hl7.fhir.utilities.validation.ValidationOptions.ValueSetMode;
import com.google.j2objc.annotations.ReflectionSupport.Level;
public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChecker {
public class VersionInfo {
private String expansionVersion;
private String composeVersion;
public String getExpansionVersion() {
return expansionVersion;
}
public void setExpansionVersion(String expansionVersion) {
this.expansionVersion = expansionVersion;
}
public String getComposeVersion() {
return composeVersion;
}
public void setComposeVersion(String composeVersion) {
this.composeVersion = composeVersion;
}
public String getVersion(String system, String version) {
String fixedVersion = getVersionParameter("force-system-version", system);
if (fixedVersion != null) {
return fixedVersion;
}
String checkVersion = getVersionParameter("check-system-version", system);
if (version != null) {
if (checkVersion != null && !version.equals(checkVersion)) {
throw new FHIRException("Attempt to use version "+version+" of "+system+", when the expansion parameters limit the use to "+checkVersion);
}
return version;
}
if (expansionVersion != null) {
if (checkVersion != null && !expansionVersion.equals(checkVersion)) {
throw new FHIRException("Attempt to use version "+expansionVersion+" of "+system+", when the expansion parameters limit the use to "+checkVersion);
}
return expansionVersion;
}
if (composeVersion != null) {
if (checkVersion != null && !composeVersion.equals(checkVersion)) {
throw new FHIRException("Attempt to use version "+composeVersion+" of "+system+", when the expansion parameters limit the use to "+checkVersion);
}
return composeVersion;
}
return getVersionParameter("system-version", system);
}
private String getVersionParameter(String name, String system) {
if (expansionProfile != null) {
for (ParametersParameterComponent pc : expansionProfile.getParameter()) {
if (name.equals(pc.getName()) && pc.hasValue()) {
String v = pc.getValue().primitiveValue();
if (v != null && v.startsWith(system+"|")) {
return v.substring(system.length()+1);
}
}
}
}
return null;
}
}
public static class VSCheckerException extends FHIRException {
private List<OperationOutcomeIssueComponent> issues;
public VSCheckerException(String message, List<OperationOutcomeIssueComponent> issues) {
super(message);
this.issues = issues;
}
public List<OperationOutcomeIssueComponent> getIssues() {
return issues;
}
private static final long serialVersionUID = -5889505119633054187L;
}
public class ConceptReferencePair {
private ValueSet valueset;
private ConceptReferenceComponent cc;
public ConceptReferencePair(ValueSet valueset, ConceptReferenceComponent cc) {
this.valueset = valueset;
this.cc = cc;
}
public ValueSet getValueset() {
return valueset;
}
public ConceptReferenceComponent getCc() {
return cc;
}
}
public class ValueSetValidator {
private ValueSet valueset;
private IWorkerContext context;
private Map<String, ValueSetCheckerSimple> inner = new HashMap<>();
private Map<String, ValueSetValidator> inner = new HashMap<>();
private ValidationOptions options;
private ValidationContextCarrier localContext;
private List<CodeSystem> localSystems = new ArrayList<>();
private Parameters expansionProfile;
Parameters expansionProfile;
private TerminologyCapabilities txCaps;
public ValueSetCheckerSimple(ValidationOptions options, ValueSet source, IWorkerContext context, Parameters expansionProfile, TerminologyCapabilities txCaps) {
public ValueSetValidator(ValidationOptions options, ValueSet source, IWorkerContext context, Parameters expansionProfile, TerminologyCapabilities txCaps) {
this.valueset = source;
this.context = context;
this.options = options;
@ -205,7 +108,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
this.txCaps = txCaps;
}
public ValueSetCheckerSimple(ValidationOptions options, ValueSet source, IWorkerContext context, ValidationContextCarrier ctxt, Parameters expansionProfile, TerminologyCapabilities txCaps) {
public ValueSetValidator(ValidationOptions options, ValueSet source, IWorkerContext context, ValidationContextCarrier ctxt, Parameters expansionProfile, TerminologyCapabilities txCaps) {
this.valueset = source;
this.context = context;
this.options = options.copy();
@ -260,7 +163,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
if (!c.hasSystem()) {
info.addIssue(makeIssue(IssueSeverity.WARNING, IssueType.UNKNOWN, path, context.formatMessage(I18nConstants.CODING_HAS_NO_SYSTEM__CANNOT_VALIDATE)));
}
VersionInfo vi = new VersionInfo();
VersionInfo vi = new VersionInfo(this);
checkExpansion(c, vi);
checkInclude(c, vi);
CodeSystem cs = resolveCodeSystem(c.getSystem(), vi.getVersion(c.getSystem(), c.getVersion()));
@ -417,7 +320,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
ValidationResult res = null;
boolean inExpansion = false;
boolean inInclude = false;
VersionInfo vi = new VersionInfo();
VersionInfo vi = new VersionInfo(this);
String system = code.hasSystem() ? code.getSystem() : getValueSetSystemOrNull();
if (options.getValueSetMode() != ValueSetMode.CHECK_MEMERSHIP_ONLY) {
@ -966,7 +869,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
}
private boolean checkForCodeInValueSet(String code, String uri, Set<String> sys, List<String> problems) {
ValueSetCheckerSimple vs = getVs(uri);
ValueSetValidator vs = getVs(uri);
return vs.scanForCodeInValueSet(code, sys, problems);
}
@ -985,7 +888,6 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
return true;
}
@Override
public Boolean codeInValueSet(String system, String version, String code, ValidationProcessInfo info) throws FHIRException {
return codeInValueSet("code", system, version, code, info);
}
@ -994,7 +896,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
return false;
}
Boolean result = false;
VersionInfo vi = new VersionInfo();
VersionInfo vi = new VersionInfo(this);
if (valueset.hasExpansion()) {
return checkExpansion(new Coding(system, code, null), vi);
@ -1126,12 +1028,36 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
return codeInConceptFilter(cs, f, code);
else if ("code".equals(f.getProperty()) && f.getOp() == FilterOperator.REGEX)
return codeInRegexFilter(cs, f, code);
else {
else if (CodeSystemUtilities.hasPropertyDef(cs, f.getProperty())) {
return codeInPropertyFilter(cs, f, code);
} else {
System.out.println("todo: handle filters with property = "+f.getProperty()+" "+f.getOp().toCode());
throw new FHIRException(context.formatMessage(I18nConstants.UNABLE_TO_HANDLE_SYSTEM__FILTER_WITH_PROPERTY__, cs.getUrl(), f.getProperty(), f.getOp().toCode()));
}
}
private boolean codeInPropertyFilter(CodeSystem cs, ConceptSetFilterComponent f, String code) {
switch (f.getOp()) {
case EQUAL:
if (f.getValue() == null) {
return false;
}
DataType d = CodeSystemUtilities.getProperty(cs, code, f.getProperty());
return d != null && f.getValue().equals(d.primitiveValue());
case EXISTS:
return CodeSystemUtilities.getProperty(cs, code, f.getProperty()) != null;
case REGEX:
if (f.getValue() == null) {
return false;
}
d = CodeSystemUtilities.getProperty(cs, code, f.getProperty());
return d != null && d.primitiveValue() != null && d.primitiveValue().matches(f.getValue());
default:
System.out.println("todo: handle property filters with op = "+f.getOp());
throw new FHIRException(context.formatMessage(I18nConstants.UNABLE_TO_HANDLE_SYSTEM__PROPERTY_FILTER_WITH_OP__, cs.getUrl(), f.getOp()));
}
}
private boolean codeInRegexFilter(CodeSystem cs, ConceptSetFilterComponent f, String code) {
return code.matches(f.getValue());
}
@ -1147,16 +1073,16 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
}
}
private boolean codeInConceptIsAFilter(CodeSystem cs, ConceptSetFilterComponent f, String code, boolean rootOnly) {
if (!rootOnly && code.equals(f.getProperty())) {
private boolean codeInConceptIsAFilter(CodeSystem cs, ConceptSetFilterComponent f, String code, boolean excludeRoot) {
if (!excludeRoot && code.equals(f.getProperty())) {
return true;
}
ConceptDefinitionComponent cc = findCodeInConcept(cs.getConcept(), f.getValue());
if (cc == null) {
return false;
}
cc = findCodeInConcept(cc, code);
return cc != null;
ConceptDefinitionComponent cc2 = findCodeInConcept(cc, code);
return cc2 != null && cc2 != cc;
}
public boolean validateCodeInConceptList(String code, CodeSystem def, List<ConceptDefinitionComponent> list) {
@ -1182,18 +1108,18 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
return false;
}
private ValueSetCheckerSimple getVs(String url) {
private ValueSetValidator getVs(String url) {
if (inner.containsKey(url)) {
return inner.get(url);
}
ValueSet vs = context.fetchResource(ValueSet.class, url, valueset);
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, context, localContext, expansionProfile, txCaps);
ValueSetValidator vsc = new ValueSetValidator(options, vs, context, localContext, expansionProfile, txCaps);
inner.put(url, vsc);
return vsc;
}
private boolean inImport(String uri, String system, String version, String code) throws FHIRException {
ValueSetCheckerSimple vs = getVs(uri);
ValueSetValidator vs = getVs(uri);
if (vs == null) {
return false;
} else {

View File

@ -0,0 +1,79 @@
package org.hl7.fhir.r5.terminologies.validation;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
public class VersionInfo {
/**
*
*/
private final ValueSetValidator valueSetCheckerSimple;
/**
* @param valueSetCheckerSimple
*/
VersionInfo(ValueSetValidator valueSetCheckerSimple) {
this.valueSetCheckerSimple = valueSetCheckerSimple;
}
private String expansionVersion;
private String composeVersion;
public String getExpansionVersion() {
return expansionVersion;
}
public void setExpansionVersion(String expansionVersion) {
this.expansionVersion = expansionVersion;
}
public String getComposeVersion() {
return composeVersion;
}
public void setComposeVersion(String composeVersion) {
this.composeVersion = composeVersion;
}
public String getVersion(String system, String version) {
String fixedVersion = getVersionParameter("force-system-version", system);
if (fixedVersion != null) {
return fixedVersion;
}
String checkVersion = getVersionParameter("check-system-version", system);
if (version != null) {
if (checkVersion != null && !version.equals(checkVersion)) {
throw new FHIRException("Attempt to use version "+version+" of "+system+", when the expansion parameters limit the use to "+checkVersion);
}
return version;
}
if (expansionVersion != null) {
if (checkVersion != null && !expansionVersion.equals(checkVersion)) {
throw new FHIRException("Attempt to use version "+expansionVersion+" of "+system+", when the expansion parameters limit the use to "+checkVersion);
}
return expansionVersion;
}
if (composeVersion != null) {
if (checkVersion != null && !composeVersion.equals(checkVersion)) {
throw new FHIRException("Attempt to use version "+composeVersion+" of "+system+", when the expansion parameters limit the use to "+checkVersion);
}
return composeVersion;
}
return getVersionParameter("system-version", system);
}
private String getVersionParameter(String name, String system) {
if (this.valueSetCheckerSimple.expansionProfile != null) {
for (ParametersParameterComponent pc : this.valueSetCheckerSimple.expansionProfile.getParameter()) {
if (name.equals(pc.getName()) && pc.hasValue()) {
String v = pc.getValue().primitiveValue();
if (v != null && v.startsWith(system+"|")) {
return v.substring(system.length()+1);
}
}
}
}
return null;
}
}

View File

@ -51,7 +51,7 @@ import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpander;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities;

View File

@ -63,7 +63,7 @@ import org.hl7.fhir.r5.model.StructureMap.*;
import org.hl7.fhir.r5.model.TypeDetails.ProfiledType;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.renderers.TerminologyRenderer;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.FHIRLexer;
import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r5.utils.FHIRPathEngine;

View File

@ -20,10 +20,10 @@ import org.hl7.fhir.r5.model.Enumerations;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.terminologies.ValueSetCheckerSimple;
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
import org.hl7.fhir.r5.terminologies.ValueSetExpanderSimple;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpander;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.validation.ValueSetValidator;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.validation.ValidationOptions;
@ -54,7 +54,7 @@ public class SimpleWorkerContextTests {
ToolingClientLogger txLog;
@Mock
TerminologyClient terminologyClient;
ITerminologyClient terminologyClient;
@Mock
TerminologyCache.CacheToken cacheToken;
@ -63,13 +63,13 @@ public class SimpleWorkerContextTests {
IWorkerContext.ValidationResult expectedValidationResult;
@Mock
ValueSetExpander.ValueSetExpansionOutcome expectedExpansionResult;
ValueSetExpansionOutcome expectedExpansionResult;
@Mock
ValueSetCheckerSimple valueSetCheckerSimple;
ValueSetValidator valueSetCheckerSimple;
@Mock
ValueSetExpanderSimple valueSetExpanderSimple;
ValueSetExpander valueSetExpanderSimple;
@Mock
Parameters pIn;
@ -90,7 +90,7 @@ public class SimpleWorkerContextTests {
public void beforeEach() {
context.txCache = terminologyCache;
context.expParameters = expParameters;
context.txClient = terminologyClient;
context.tcc.setClient(terminologyClient);
context.txLog = txLog;
}
@ -270,7 +270,7 @@ public class SimpleWorkerContextTests {
Mockito.doReturn(cacheToken).when(terminologyCache).generateExpandToken(argThat(new ValueSetMatcher(vs)),eq(true));
Mockito.doReturn(expectedExpansionResult).when(terminologyCache).getExpansion(cacheToken);
ValueSetExpander.ValueSetExpansionOutcome actualExpansionResult = context.expandVS(inc, true, false);
ValueSetExpansionOutcome actualExpansionResult = context.expandVS(inc, true, false);
assertEquals(expectedExpansionResult, actualExpansionResult);
@ -300,7 +300,7 @@ public class SimpleWorkerContextTests {
Mockito.doReturn(expectedValueSet).when(terminologyClient).expandValueset(argThat(new ValueSetMatcher(vs)),
argThat(new ParametersMatcher(pInWithDependentResources)), eq(params));
ValueSetExpander.ValueSetExpansionOutcome actualExpansionResult = context.expandVS(inc, true, false);
ValueSetExpansionOutcome actualExpansionResult = context.expandVS(inc, true, false);
assertEquals(expectedValueSet, actualExpansionResult.getValueset());
@ -319,7 +319,7 @@ public class SimpleWorkerContextTests {
Parameters pIn = new Parameters();
ValueSetExpander.ValueSetExpansionOutcome actualExpansionResult = context.expandVS(vs, true, true, true, pIn);
ValueSetExpansionOutcome actualExpansionResult = context.expandVS(vs, true, true, true, pIn);
assertEquals(expectedExpansionResult, actualExpansionResult);
@ -345,7 +345,7 @@ public class SimpleWorkerContextTests {
Mockito.doReturn(valueSetExpanderSimple).when(context).constructValueSetExpanderSimple();
ValueSetExpander.ValueSetExpansionOutcome actualExpansionResult = context.expandVS(vs, true, true, true, pIn);
ValueSetExpansionOutcome actualExpansionResult = context.expandVS(vs, true, true, true, pIn);
assertEquals(expectedExpansionResult, actualExpansionResult);
@ -374,7 +374,7 @@ public class SimpleWorkerContextTests {
Mockito.doReturn(expectedValueSet).when(terminologyClient).expandValueset(eq(vs), argThat(new ParametersMatcher(pInWithDependentResources)), eq(params));
ValueSetExpander.ValueSetExpansionOutcome actualExpansionResult = context.expandVS(vs, true, true, true, pIn);
ValueSetExpansionOutcome actualExpansionResult = context.expandVS(vs, true, true, true, pIn);
assertEquals(expectedValueSet, actualExpansionResult.getValueset());

View File

@ -28,7 +28,7 @@ import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.utilities.tests.ResourceLoaderTests;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.junit.jupiter.api.Test;
@ -129,7 +129,7 @@ public class TerminologyCacheTests implements ResourceLoaderTests {
terminologyCacheA.cacheValidation(codeableConceptTokenA, codeableConceptResultA, true);
TerminologyCache.CacheToken expansionTokenA = terminologyCacheA.generateExpandToken(valueSet, true);
ValueSetExpander.ValueSetExpansionOutcome expansionOutcomeA = new ValueSetExpander.ValueSetExpansionOutcome(valueSet);
ValueSetExpansionOutcome expansionOutcomeA = new ValueSetExpansionOutcome(valueSet);
terminologyCacheA.cacheExpansion(expansionTokenA, expansionOutcomeA, true);
// Check that the in-memory cache is returning what we put in
@ -165,7 +165,7 @@ public class TerminologyCacheTests implements ResourceLoaderTests {
assertEquals(a.getMessage(), b.getMessage());
}
private void assertExpansionOutcomeEquals(ValueSetExpander.ValueSetExpansionOutcome a, ValueSetExpander.ValueSetExpansionOutcome b) {
private void assertExpansionOutcomeEquals(ValueSetExpansionOutcome a, ValueSetExpansionOutcome b) {
assertEquals(a.getValueset().getUrl(), b.getValueset().getUrl());
}

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.r5.terminology;
package org.hl7.fhir.r5.terminologies;
import org.hl7.fhir.r5.terminologies.JurisdictionUtilities;
import org.junit.jupiter.params.ParameterizedTest;

View File

@ -22,7 +22,7 @@ import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ITypeParser;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.VersionUtilities;

View File

@ -1,21 +0,0 @@
{
"code": {
"coding": [
{
"code": "dummyCode"
}
]
},
"valueSet": {
"resourceType": "ValueSet"
},
"langs": "[]",
"useServer": "true",
"useClient": "true",
"guessSystem": "true",
"valueSetMode": "ALL_CHECKS",
"versionFlexible": "false",
"profile":{
"resourceType":"Parameters"
}
}

View File

@ -1,23 +0,0 @@
{
"code": {
"coding": [
{
"system": "dummySystem",
"version": "dummyVersion",
"code": "dummyCode"
}
]
},
"valueSet": {
"resourceType": "ValueSet"
},
"langs": "[]",
"useServer": "true",
"useClient": "true",
"guessSystem": "true",
"valueSetMode": "ALL_CHECKS",
"versionFlexible": "false",
"profile":{
"resourceType":"Parameters"
}
}

View File

@ -1,17 +0,0 @@
{
"code": {
"code": "dummyCode"
},
"valueSet": {
"resourceType": "ValueSet"
},
"langs": "[]",
"useServer": "true",
"useClient": "true",
"guessSystem": "true",
"valueSetMode": "ALL_CHECKS",
"versionFlexible": "false",
"profile":{
"resourceType":"Parameters"
}
}

View File

@ -1,19 +0,0 @@
{
"code": {
"system": "dummySystem",
"version": "dummyVersion",
"code": "dummyCode"
},
"valueSet": {
"resourceType": "ValueSet"
},
"langs": "[]",
"useServer": "true",
"useClient": "true",
"guessSystem": "true",
"valueSetMode": "ALL_CHECKS",
"versionFlexible": "false",
"profile":{
"resourceType":"Parameters"
}
}

View File

@ -1,28 +0,0 @@
{
"hierarchical": false,
"valueSet": {
"resourceType": "ValueSet",
"compose": {
"include": [
{
"system": "dummyIncludeSystem",
"version": "dummyIncludeVersion"
}
],
"exclude": [
{
"system": "dummyExcludeSystem",
"version": "dummyExcludeVersion"
}
]
},
"expansion": {
"contains": [
{
"system": "dummyContainsSystem",
"version": "dummyContainsVersion"
}
]
}
}
}

View File

@ -1,28 +0,0 @@
{
"hierarchical": true,
"valueSet": {
"resourceType": "ValueSet",
"compose": {
"include": [
{
"system": "dummyIncludeSystem",
"version": "dummyIncludeVersion"
}
],
"exclude": [
{
"system": "dummyExcludeSystem",
"version": "dummyExcludeVersion"
}
]
},
"expansion": {
"contains": [
{
"system": "dummyContainsSystem",
"version": "dummyContainsVersion"
}
]
}
}
}

View File

@ -560,6 +560,7 @@ public class I18nConstants {
public static final String UNABLE_TO_FIND_RESOURCE__AT__RESOLVING_DISCRIMINATOR__FROM_ = "Unable_to_find_resource__at__resolving_discriminator__from_";
public static final String UNABLE_TO_FIND__RESOLVING_DISCRIMINATOR__FROM_ = "Unable_to_find__resolving_discriminator__from_";
public static final String UNABLE_TO_HANDLE_SYSTEM__CONCEPT_FILTER_WITH_OP__ = "Unable_to_handle_system__concept_filter_with_op__";
public static final String UNABLE_TO_HANDLE_SYSTEM__PROPERTY_FILTER_WITH_OP__ = "UNABLE_TO_HANDLE_SYSTEM__PROPERTY_FILTER_WITH_OP__";
public static final String UNABLE_TO_HANDLE_SYSTEM__FILTER_WITH_PROPERTY__ = "Unable_to_handle_system__filter_with_property__";
public static final String UNABLE_TO_LOCATE_THE_PROFILE__IN_ORDER_TO_VALIDATE_AGAINST_IT = "Unable_to_locate_the_profile__in_order_to_validate_against_it";
public static final String UNABLE_TO_PROCESS_REQUEST_FOR_RESOURCE_FOR___ = "Unable_to_process_request_for_resource_for___";

View File

@ -16,7 +16,8 @@ public class FhirSettingsPOJO {
protected static final String TX_SERVER_PROD = "http://tx.fhir.org";
protected static final String TX_SERVER_DEV = "http://tx-dev.fhir.org";
protected static final String TX_SERVER_LOCAL = "http://local.fhir.org";
// protected static final String TX_SERVER_LOCAL = "http://local.fhir.org";
protected static final String TX_SERVER_LOCAL = "http://localhost";
private String fhirDirectory;
private Map<String, String> apiKeys;

View File

@ -471,6 +471,7 @@ None_of_the_provided_codes_are_in_the_value_set_one = The provided code {2} is n
None_of_the_provided_codes_are_in_the_value_set_other = None of the provided codes [{2}] are in the value set ''{1}''
Coding_has_no_system__cannot_validate = Coding has no system - cannot validate
Unable_to_handle_system__concept_filter_with_op__ = Unable to handle system {0} concept filter with op = {1}
UNABLE_TO_HANDLE_SYSTEM__PROPERTY_FILTER_WITH_OP__ = Unable to handle system {0} property filter with op = {1}
Unable_to_handle_system__filter_with_property__ = Unable to handle system {0} filter with property = {1}, op = {2}
Unable_to_resolve_system__value_set_has_include_with_no_system = Unable to resolve system - value set {0} include #{1} has no system
UNABLE_TO_RESOLVE_SYSTEM_SYSTEM_IS_INDETERMINATE = The code system {1} referred to from value set {0} has a grammar, and the code might be valid in it

View File

@ -369,12 +369,12 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
}
engine.setVersion(version);
engine.setIgLoader(new IgLoader(engine.getPcm(), engine.getContext(), engine.getVersion(), engine.isDebug()));
if (THO) {
loadTx(engine);
}
if (VersionUtilities.isR5Plus(version)) {
engine.loadPackage("hl7.fhir.uv.extensions", null);
}
// if (THO) {
// loadTx(engine);
// }
// if (VersionUtilities.isR5Plus(version)) {
// engine.loadPackage("hl7.fhir.uv.extensions", "1.0.0");
// }
return engine;
}
@ -393,7 +393,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
pid = "hl7.terminology.r5";
}
if (pid != null) {
engine.loadPackage(pid, null);
engine.loadPackage(pid, "5.0.0");
}
}

View File

@ -224,7 +224,7 @@ public class ValidatorCli {
final String version = Params.getParam(args, Params.VERSION);
final String tx = Params.getParam(args, Params.TERMINOLOGY);
final String filter = Params.getParam(args, Params.FILTER);
boolean ok = new TxTester(new InternalTxLoader(source, output)).setOutput(output).execute(tx, version, filter);
boolean ok = new TxTester(new InternalTxLoader(source, output), tx).setOutput(output).execute(version, filter);
System.exit(ok ? 1 : 0);
} else {
final String testModuleParam = Params.getParam(args, Params.TEST_MODULES);

View File

@ -433,7 +433,7 @@ public class CliContext {
@JsonProperty("sv")
public CliContext setSv(String sv) {
if (sv != null && sv.startsWith("R")) {
if (sv != null && (sv.startsWith("R") || sv.startsWith("r"))) {
this.sv = VersionUtilities.versionFromCode(sv.toLowerCase());
} else {
this.sv = sv;

View File

@ -19,7 +19,7 @@ import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
@ -258,7 +258,7 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, IV
String root = getRoot(p, url);
if (root != null) {
TerminologyClient c;
ITerminologyClient c;
c = TerminologyClientFactory.makeClient("source", root, "fhir/validator", context.getVersion());
return c.read(p[p.length - 2], p[p.length - 1]);
} else {

View File

@ -143,7 +143,7 @@ import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.renderers.DataRenderer;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.utils.BuildExtensions;
import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r5.utils.FHIRPathEngine;

View File

@ -20,10 +20,10 @@ import org.hl7.fhir.r5.model.Questionnaire;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemAnswerOptionComponent;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent;
import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemType;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.model.TimeType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.XVerExtensionManager;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;

View File

@ -29,8 +29,8 @@ import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.ConceptMapUtilities;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.XVerExtensionManager;
import org.hl7.fhir.r5.utils.structuremap.ResolvedGroup;

View File

@ -10,7 +10,7 @@ import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.utils.XVerExtensionManager;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;

View File

@ -13,10 +13,9 @@ public class FHIRPathExpressionFixer {
// return expr;
// }
//TODO is this expression below correct? @grahamegrieve
// if ("probability is decimal implies (probability as decimal) <= 100".equals(expr)) {
// return "probability.empty() or ((probability is decimal) implies ((probability as decimal) <= 100))";
// }
if ("probability is decimal implies (probability as decimal) <= 100".equals(expr)) {
return "(probability.exists() and (probability is decimal)) implies ((probability as decimal) <= 100)";
}
if ("enableWhen.count() > 2 implies enableBehavior.exists()".equals(expr)) {
return "enableWhen.count() >= 2 implies enableBehavior.exists()";
}

View File

@ -32,7 +32,7 @@ import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.OperationOutcome;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.test.utils.CompareUtilities;
import org.hl7.fhir.r5.utils.client.EFhirClientException;
import org.hl7.fhir.utilities.FhirPublication;
@ -57,19 +57,20 @@ public class TxTester {
private ITxTesterLoader loader;
private String error;
private String output;
private ITerminologyClient tx;
public TxTester(ITxTesterLoader loader) {
public TxTester(ITxTesterLoader loader, String server) {
super();
this.server = server;
this.loader = loader;
}
public static void main(String[] args) throws Exception {
new TxTester(new InternalTxLoader(args[0])).execute(args[1], args[2], args[3]);
new TxTester(new InternalTxLoader(args[0]), args[1]).execute(args[2], args[3]);
}
public boolean execute(String server, String version, String filter) throws IOException, URISyntaxException {
this.server = server;
public boolean execute(String version, String filter) throws IOException, URISyntaxException {
if (output == null) {
output = Utilities.path("[tmp]", serverId());
}
@ -89,7 +90,7 @@ public class TxTester {
json.add("date", new SimpleDateFormat("EEE, MMM d, yyyy HH:mmZ", new Locale("en", "US")).format(Calendar.getInstance().getTime()) + timezone());
try {
JsonObject tests = loadTests();
TerminologyClient tx = connectToServer();
ITerminologyClient tx = connectToServer();
boolean ok = checkClient(tx);
for (JsonObject suite : tests.getJsonObjects("suites")) {
ok = runSuite(suite, tx, filter, json.forceArray("suites")) && ok;
@ -121,7 +122,7 @@ public class TxTester {
return offset;
}
private boolean checkClient(TerminologyClient tx) {
private boolean checkClient(ITerminologyClient tx) {
tx.getCapabilitiesStatementQuick();
tx.getTerminologyCapabilities();
return true;
@ -132,17 +133,18 @@ public class TxTester {
return JsonParser.parseObject(loader.loadContent("test-cases.json"));
}
private TerminologyClient connectToServer() throws URISyntaxException {
private ITerminologyClient connectToServer() throws URISyntaxException {
System.out.println("Connect to "+server);
return TerminologyClientFactory.makeClient("Test-Server", server, "Tools/Java", FhirPublication.R4);
}
public String executeTest(JsonObject suite, JsonObject test, String server) throws URISyntaxException, FHIRFormatError, FileNotFoundException, IOException {
this.server = server;
public String executeTest(JsonObject suite, JsonObject test) throws URISyntaxException, FHIRFormatError, FileNotFoundException, IOException {
error = null;
TerminologyClient tx = connectToServer();
checkClient(tx);
if (tx == null) {
tx = connectToServer();
checkClient(tx);
}
List<Resource> setup = loadSetupResources(suite);
if (runTest(test, tx, setup, "*", null)) {
return null;
@ -150,7 +152,7 @@ public class TxTester {
return error;
}
}
private boolean runSuite(JsonObject suite, TerminologyClient tx, String filter, JsonArray output) throws FHIRFormatError, FileNotFoundException, IOException {
private boolean runSuite(JsonObject suite, ITerminologyClient tx, String filter, JsonArray output) throws FHIRFormatError, FileNotFoundException, IOException {
System.out.println("Group "+suite.asString("name"));
JsonObject outputS = new JsonObject();
if (output != null) {
@ -165,7 +167,7 @@ public class TxTester {
return ok;
}
private boolean runTest(JsonObject test, TerminologyClient tx, List<Resource> setup, String filter, JsonArray output) throws FHIRFormatError, DefinitionException, FileNotFoundException, FHIRException, IOException {
private boolean runTest(JsonObject test, ITerminologyClient tx, List<Resource> setup, String filter, JsonArray output) throws FHIRFormatError, DefinitionException, FileNotFoundException, FHIRException, IOException {
JsonObject outputT = new JsonObject();
if (output != null) {
output.add(outputT);
@ -229,7 +231,7 @@ public class TxTester {
return new URI(server).getHost();
}
private String expand(TerminologyClient tx, List<Resource> setup, Parameters p, String resp, String fp, Parameters profile) throws IOException {
private String expand(ITerminologyClient tx, List<Resource> setup, Parameters p, String resp, String fp, Parameters profile) throws IOException {
for (Resource r : setup) {
p.addParameter().setName("tx-resource").setResource(r);
}
@ -253,7 +255,7 @@ public class TxTester {
return diff;
}
private String validate(TerminologyClient tx, List<Resource> setup, Parameters p, String resp, String fp, Parameters profile) throws IOException {
private String validate(ITerminologyClient tx, List<Resource> setup, Parameters p, String resp, String fp, Parameters profile) throws IOException {
for (Resource r : setup) {
p.addParameter().setName("tx-resource").setResource(r);
}

View File

@ -48,8 +48,8 @@ public class ExternalTerminologyServiceTests implements ITxTesterLoader {
private JsonObject test;
}
private static final String SERVER = FhirSettings.getTxFhirDevelopment();
// private static final String SERVER = FhirSettings.getTxFhirLocal();
// private static final String SERVER = FhirSettings.getTxFhirDevelopment();
private static final String SERVER = FhirSettings.getTxFhirLocal();
@Parameters(name = "{index}: id {0}")
public static Iterable<Object[]> data() throws IOException {
@ -80,6 +80,7 @@ public class ExternalTerminologyServiceTests implements ITxTesterLoader {
private static org.hl7.fhir.utilities.json.model.JsonObject manifest;
private JsonObjectPair setup;
private String version = "5.0.0";
private static TxTester tester;
public ExternalTerminologyServiceTests(String name, JsonObjectPair setup) {
this.setup = setup;
@ -89,7 +90,10 @@ public class ExternalTerminologyServiceTests implements ITxTesterLoader {
@Test
public void test() throws Exception {
if (SERVER != null) {
String err = new TxTester(this).executeTest(setup.suite, setup.test, SERVER);
if (tester == null) {
tester = new TxTester(this, SERVER);
}
String err = tester.executeTest(setup.suite, setup.test);
Assertions.assertTrue(err == null, err);
} else {
Assertions.assertTrue(true);

View File

@ -35,8 +35,7 @@ import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.test.utils.CompareUtilities;
import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.terminology.tests.TerminologyServiceTests.JsonObjectPair;

View File

@ -34,7 +34,7 @@ public class TestUtilities {
public static ValidationEngine getValidationEngineNoTxServer(java.lang.String src, FhirPublication version, java.lang.String vString) throws Exception {
TestingUtilities.injectCorePackageLoader();
// TestingUtilities.injectCorePackageLoader();
final ValidationEngine validationEngine = new ValidationEngine.ValidationEngineBuilder()
.withCanRunWithoutTerminologyServer(true)

View File

@ -19,7 +19,7 @@
<properties>
<hapi_fhir_version>6.4.1</hapi_fhir_version>
<validator_test_case_version>1.3.4</validator_test_case_version>
<validator_test_case_version>1.3.5-SNAPSHOT</validator_test_case_version>
<jackson_version>2.14.0</jackson_version>
<junit_jupiter_version>5.9.2</junit_jupiter_version>
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>