Merge pull request #1545 from hapifhir/2024-01-gg-more-tx-work3

2024 01 gg more tx work3
This commit is contained in:
Grahame Grieve 2024-01-18 00:58:47 +11:00 committed by GitHub
commit 79f3fbb93c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
149 changed files with 6462 additions and 5657 deletions

View File

@ -1,7 +1,12 @@
## Validator Changes
* Fix issues with multiple terminology servers (should be live soon)
* Rework terminology system & cache to support new terminology server ecosystem
* Handle value set with exclusion filters when expanding
* Fix to consistent error messages when value sets can't be resolved
## Other code changes
* no changes
* Fix bug processing empty structure maps
* Performance improvements to common IO routines, with test cases - see https://blog.qligier.ch/posts/2024-improving-perf-ig-publisher-io/ (thanks to Quentin Ligier)

View File

@ -27,7 +27,7 @@ public class BOMRemover {
String s = Utilities.stripBOM(src);
if (!s.equals(src)) {
System.out.println("Remove BOM from "+f.getAbsolutePath());
TextFile.stringToFile(s, f, false);
TextFile.stringToFile(s, f);
}
}
}

View File

@ -6,6 +6,7 @@ import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientR5;
import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
@ -23,27 +24,28 @@ public class TerminologyClientFactory implements ITerminologyClientFactory {
this.v = version;
}
public ITerminologyClient makeClient(String id, String url, String userAgent) throws URISyntaxException {
@Override
public ITerminologyClient makeClient(String id, String url, String userAgent, ToolingClientLogger logger) throws URISyntaxException {
if (v == null)
return new TerminologyClientR5(id, checkEndsWith("/r4", url), userAgent);
return new TerminologyClientR5(id, checkEndsWith("/r4", url), userAgent).setLogger(logger);
v = VersionUtilities.getMajMin(v);
if (VersionUtilities.isR2Ver(v)) {
return new TerminologyClientR2(id, checkEndsWith("/r2", url), userAgent);
return new TerminologyClientR2(id, checkEndsWith("/r2", url), userAgent).setLogger(logger);
}
if (VersionUtilities.isR2BVer(v)) {
return new TerminologyClientR3(id, checkEndsWith("/r3", url), userAgent); // r3 is the least worst match
return new TerminologyClientR3(id, checkEndsWith("/r3", url), userAgent).setLogger(logger); // r3 is the least worst match
}
if (VersionUtilities.isR3Ver(v)) {
return new TerminologyClientR3(id, checkEndsWith("/r3", url), userAgent); // r3 is the least worst match
return new TerminologyClientR3(id, checkEndsWith("/r3", url), userAgent).setLogger(logger); // r3 is the least worst match
}
if (VersionUtilities.isR4Ver(v)) {
return new TerminologyClientR4(id, checkEndsWith("/r4", url), userAgent);
return new TerminologyClientR4(id, checkEndsWith("/r4", url), userAgent).setLogger(logger);
}
if (VersionUtilities.isR4BVer(v)) {
return new TerminologyClientR4(id, checkEndsWith("/r4", url), userAgent);
return new TerminologyClientR4(id, checkEndsWith("/r4", url), userAgent).setLogger(logger);
}
if (VersionUtilities.isR5Plus(v)) {
return new TerminologyClientR5(id, checkEndsWith("/r4", url), userAgent); // r4 for now, since the terminology is currently the same
return new TerminologyClientR5(id, checkEndsWith("/r4", url), userAgent).setLogger(logger); // r4 for now, since the terminology is currently the same
}
throw new Error("The version " + v + " is not currently supported");
}

View File

@ -131,6 +131,11 @@ public class TerminologyClientR2 implements ITerminologyClient {
return this;
}
@Override
public ToolingClientLogger getLogger() {
return client.getLogger();
}
@Override
public ITerminologyClient setLogger(ToolingClientLogger txLog) {
client.setLogger(txLog);
@ -227,4 +232,10 @@ public class TerminologyClientR2 implements ITerminologyClient {
public int getUseCount() {
return client.getUseCount();
}
@Override
public Bundle search(String type, String criteria) {
org.hl7.fhir.dstu2.model.Bundle result = client.search(type, criteria);
return result == null ? null : (Bundle) VersionConvertorFactory_10_50.convertResource(result);
}
}

View File

@ -128,6 +128,11 @@ public class TerminologyClientR3 implements ITerminologyClient {
return this;
}
@Override
public ToolingClientLogger getLogger() {
return client.getLogger();
}
@Override
public ITerminologyClient setLogger(ToolingClientLogger txLog) {
client.setLogger(txLog);
@ -230,4 +235,11 @@ public class TerminologyClientR3 implements ITerminologyClient {
return client.getUseCount();
}
@Override
public Bundle search(String type, String criteria) {
org.hl7.fhir.dstu3.model.Bundle result = client.search(type, criteria);
return result == null ? null : (Bundle) VersionConvertorFactory_30_50.convertResource(result);
}
}

View File

@ -138,6 +138,11 @@ public class TerminologyClientR4 implements ITerminologyClient {
return this;
}
@Override
public ToolingClientLogger getLogger() {
return client.getLogger();
}
@Override
public ITerminologyClient setLogger(ToolingClientLogger txLog) {
client.setLogger(txLog);
@ -242,5 +247,11 @@ public class TerminologyClientR4 implements ITerminologyClient {
return client.getUseCount();
}
@Override
public Bundle search(String type, String criteria) {
org.hl7.fhir.r4.model.Bundle result = client.search(type, criteria);
return result == null ? null : (Bundle) VersionConvertorFactory_40_50.convertResource(result);
}
}

View File

@ -53,14 +53,14 @@ public class TerminologyClientFactoryTest {
@ParameterizedTest
@MethodSource("data")
public void testMakeClient(String url, FhirPublication fhirPublication, String expectedAddress) throws URISyntaxException {
ITerminologyClient terminologyClient = new TerminologyClientFactory(fhirPublication).makeClient("id", url, "dummyUserAgent");
ITerminologyClient terminologyClient = new TerminologyClientFactory(fhirPublication).makeClient("id", url, "dummyUserAgent", null);
assertEquals(expectedAddress, terminologyClient.getAddress());
}
@Test
public void testMakeClientDstu1Fails() throws URISyntaxException {
assertThrows(Error.class, () -> {
ITerminologyClient terminologyClient = new TerminologyClientFactory(FhirPublication.DSTU1).makeClient("id", "urldoesnotmatter", "dummyUserAgent");
ITerminologyClient terminologyClient = new TerminologyClientFactory(FhirPublication.DSTU1).makeClient("id", "urldoesnotmatter", "dummyUserAgent", null);
}
);
}
@ -68,7 +68,7 @@ public class TerminologyClientFactoryTest {
@Test
public void testMakeClientNullFails() throws URISyntaxException {
assertThrows(Error.class, () -> {
ITerminologyClient terminologyClient = new TerminologyClientFactory(FhirPublication.NULL).makeClient("id", "urldoesnotmatter", "dummyUserAgent");
ITerminologyClient terminologyClient = new TerminologyClientFactory(FhirPublication.NULL).makeClient("id", "urldoesnotmatter", "dummyUserAgent", null);
}
);
}

View File

@ -925,4 +925,9 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
useCount++;
}
public Bundle search(String type, String criteria) {
recordUse();
return fetchFeed(Utilities.pathURL(base, type+criteria));
}
}

View File

@ -654,5 +654,10 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
useCount++;
}
public Bundle search(String type, String criteria) {
recordUse();
return fetchFeed(Utilities.pathURL(base, type+criteria));
}
}

View File

@ -372,7 +372,9 @@ public class FhirRequestBuilder {
headerMap.keySet().forEach(key -> headerMap.get(key).forEach(value -> headerList.add(key + ":" + value)));
try {
if (logger != null) {
logger.logResponse(Integer.toString(responseCode), headerList, responseBody);
}
} catch (Exception e) {
System.out.println("Error parsing response body passed in to logger ->\n" + e.getLocalizedMessage());
}

View File

@ -87,7 +87,7 @@ public class TerminologyClientR4 implements TerminologyClient {
@Override
public void setTimeout(int i) {
client.setTimeoutNormal(i); // #FIXME
client.setTimeoutNormal(i);
}
@Override

View File

@ -43,7 +43,9 @@ public class FhirLoggingInterceptor implements Interceptor {
request.body().writeTo(buf);
cnt = buf.readByteArray();
}
if (logger != null) {
logger.logRequest(request.method(), request.url().toString(), hdrs, cnt);
}
// Log Response
Response response = null;
@ -61,7 +63,9 @@ public class FhirLoggingInterceptor implements Interceptor {
Map<String, List<String>> headerMap = response.headers().toMultimap();
headerMap.keySet().forEach(key -> headerMap.get(key).forEach(value -> headerList.add(key + ":" + value)));
if (logger != null) {
logger.logResponse(Integer.toString(response.code()), headerList, bodyBytes);
}
// Reading byte[] clears body. Need to recreate.
ResponseBody body = ResponseBody.create(bodyBytes, contentType);

View File

@ -25,8 +25,10 @@ import org.hl7.fhir.utilities.settings.FhirSettings;
import okhttp3.Authenticator;
import okhttp3.Credentials;
import okhttp3.Headers;
import okhttp3.Headers.Builder;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class FhirRequestBuilder {
@ -259,10 +261,11 @@ public class FhirRequestBuilder {
protected <T extends Resource> T unmarshalReference(Response response, String format) {
T resource = null;
OperationOutcome error = null;
byte[] body = null;
if (response.body() != null) {
try {
byte[] body = response.body().bytes();
body = response.body().bytes();
resource = (T) getParser(format).parse(body);
if (resource instanceof OperationOutcome && hasError((OperationOutcome) resource)) {
error = (OperationOutcome) resource;
@ -276,13 +279,14 @@ public class FhirRequestBuilder {
if (error != null) {
String s = ResourceUtilities.getErrorDescription(error);
System.out.println(s);
System.out.println("Error from "+source+": " + s);
throw new EFhirClientException("Error from "+source+": " + ResourceUtilities.getErrorDescription(error), error);
}
return resource;
}
/**
* Unmarshalls Bundle from response stream.
*/
@ -291,6 +295,7 @@ public class FhirRequestBuilder {
OperationOutcome error = null;
try {
byte[] body = response.body().bytes();
TextFile.bytesToFile(body, "/Users/grahamegrieve/temp/http-body.txt");
String contentType = response.header("Content-Type");
if (body != null) {
if (contentType.contains(ResourceFormat.RESOURCE_XML.getHeader())

View File

@ -127,7 +127,7 @@ public abstract class ParserBase {
public abstract void compose(Element e, OutputStream destination, OutputStyle style, String base)
throws FHIRException, IOException;
// FIXME: i18n should be done here
// TODO: i18n should be done here ?
public void logError(int line, int col, String path, IssueType type, String message, IssueSeverity level)
throws FHIRFormatError {
if (errors != null) {

View File

@ -153,7 +153,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
}
}
if (result == null) {
warnings.add(0, context.formatMessage(I18nConstants.UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_,
warnings.add(0, context.formatMessage(I18nConstants.UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_CS,
valueset.getUrl(), b.toString()));
} else if (!result) {
errors.add(0, context.formatMessagePlural(code.getCoding().size(),

View File

@ -110,7 +110,7 @@ public class TestingUtilities extends BaseTestingUtilities {
if (!Utilities.noString(s))
return s;
s = "C:\\work\\org.hl7.fhir\\build";
// FIXME: change this back
// #TODO - what should we do with this?
s = "/Users/jamesagnew/git/fhir";
if (new File(s).exists())
return s;

View File

@ -236,10 +236,6 @@ public interface IResourceValidator {
public void setWantCheckSnapshotUnchanged(boolean wantCheckSnapshotUnchanged);
// FIXME: don't need that, gets never used?
// public String getValidationLanguage();
// public void setValidationLanguage(String value);
/**
* It's common to see references such as Patient/234234 - these usually mean a
* reference to a Patient resource. But there's no actual technical rule that it

View File

@ -364,11 +364,11 @@ public class NPMPackageGenerator {
// also, for cache management on current builds, generate a little manifest
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(packageManifest);
TextFile.stringToFile(json, Utilities.changeFileExt(destFile, ".manifest.json"), false);
TextFile.stringToFile(json, Utilities.changeFileExt(destFile, ".manifest.json"));
}
private void buildIndexJson() throws IOException {
byte[] content = TextFile.stringToBytes(indexer.build(), false);
byte[] content = TextFile.stringToBytes(indexer.build());
addFile(Category.RESOURCE, ".index.json", content);
content = TextFile.fileToBytes(indexdb);
new File(indexdb).delete();

View File

@ -38,7 +38,9 @@ public class FhirLoggingInterceptor implements Interceptor {
request.body().writeTo(buf);
cnt = buf.readByteArray();
}
if (logger != null) {
logger.logRequest(request.method(), request.url().toString(), hdrs, cnt);
}
// Log Response
Response response = null;
@ -56,7 +58,9 @@ public class FhirLoggingInterceptor implements Interceptor {
Map<String, List<String>> headerMap = response.headers().toMultimap();
headerMap.keySet().forEach(key -> headerMap.get(key).forEach(value -> headerList.add(key + ":" + value)));
if (logger != null) {
logger.logResponse(Integer.toString(response.code()), headerList, bodyBytes);
}
// Reading byte[] clears body. Need to recreate.
ResponseBody body = ResponseBody.create(bodyBytes, contentType);

View File

@ -10,4 +10,6 @@ public class BindingResolution {
}
public String display;
public String url;
public String uri;
public boolean external;
}

View File

@ -2722,8 +2722,8 @@ public class ProfileUtilities extends TranslatingUtilities {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "illegal attempt to change the binding on "+derived.getPath()+" from "+base.getBinding().getStrength().toCode()+" to "+derived.getBinding().getStrength().toCode(), ValidationMessage.IssueSeverity.ERROR));
// throw new DefinitionException("StructureDefinition "+pn+" at "+derived.getPath()+": illegal attempt to change a binding from "+base.getBinding().getStrength().toCode()+" to "+derived.getBinding().getStrength().toCode());
else if (base.hasBinding() && derived.hasBinding() && base.getBinding().getStrength() == BindingStrength.REQUIRED && base.getBinding().hasValueSet() && derived.getBinding().hasValueSet()) {
ValueSet baseVs = context.fetchResource(ValueSet.class, base.getBinding().getValueSet(), srcSD);
ValueSet contextVs = context.fetchResource(ValueSet.class, derived.getBinding().getValueSet(), derivedSrc);
ValueSet baseVs = context.findTxResource(ValueSet.class, base.getBinding().getValueSet(), srcSD);
ValueSet contextVs = context.findTxResource(ValueSet.class, derived.getBinding().getValueSet(), derivedSrc);
if (baseVs == null) {
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+base.getPath(), "Binding "+base.getBinding().getValueSet()+" could not be located", ValidationMessage.IssueSeverity.WARNING));
} else if (contextVs == null) {

View File

@ -48,6 +48,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
@ -124,6 +125,7 @@ import org.hl7.fhir.r5.terminologies.utilities.TerminologyOperationContext.Termi
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.utilities.ValidationResult;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache.CacheToken;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache.SourcedValueSet;
import org.hl7.fhir.r5.terminologies.validation.VSCheckerException;
import org.hl7.fhir.r5.terminologies.validation.ValueSetValidator;
import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
@ -136,6 +138,7 @@ import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.client.EFhirClientException;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.TranslationServices;
@ -231,7 +234,7 @@ 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
protected TerminologyClientManager terminologyClientManager = new TerminologyClientManager(null);
protected final TerminologyClientManager terminologyClientManager = new TerminologyClientManager(null, UUID.randomUUID().toString());
private boolean minimalMemory = false;
private Map<String, Map<String, ResourceProxy>> allResourcesById = new HashMap<String, Map<String, ResourceProxy>>();
@ -269,7 +272,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
private final Set<String> codeSystemsUsed = new HashSet<>();
protected ToolingClientLogger txLog;
private boolean canRunWithoutTerminology;
protected boolean canRunWithoutTerminology;
protected boolean noTerminologyServer;
private int expandCodesLimit = 1000;
protected ILoggingService logger = new SystemOutLoggingService();
@ -278,7 +281,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
private Map<String, PackageInformation> packages = new HashMap<>();
@Getter
protected TerminologyCache txCache;
protected TerminologyCache txCache = new TerminologyCache(this, null);
protected TimeTracker clock;
private boolean tlogging = true;
private IWorkerContextManager.ICanonicalResourceLocator locator;
@ -774,12 +777,11 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (noTerminologyServer) {
return false;
}
if (terminologyClientManager.getTxCapabilities() == null) {
if (terminologyClientManager != null) {
try {
logger.logMessage("Terminology server: Check for supported code systems for "+system);
final TerminologyCapabilities capabilityStatement = txCache.hasTerminologyCapabilities() ? txCache.getTerminologyCapabilities() : terminologyClientManager.getMasterClient().getTerminologyCapabilities();
txCache.cacheTerminologyCapabilities(capabilityStatement);
setTxCaps(capabilityStatement);
if (terminologyClientManager.supportsSystem(system)) {
supportedCodeSystems.add(system);
}
} catch (Exception e) {
if (canRunWithoutTerminology) {
noTerminologyServer = true;
@ -851,7 +853,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
Set<String> systems = findRelevantSystems(vs);
TerminologyClientContext tc = terminologyClientManager.chooseServer(systems, true);
if (tc == null) {
res = new ValueSetExpansionOutcome("No server available", TerminologyServiceErrorClass.INTERNAL_ERROR, true);
return new ValueSetExpansionOutcome("No server available", TerminologyServiceErrorClass.INTERNAL_ERROR, true);
}
Parameters p = constructParameters(tc, vs, hierarchical);
for (ConceptSetComponent incl : vs.getCompose().getInclude()) {
@ -1373,15 +1375,15 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
protected ValueSetExpander constructValueSetExpanderSimple(ValidationOptions options) {
return new ValueSetExpander(this, new TerminologyOperationContext(this, options));
return new ValueSetExpander(this, new TerminologyOperationContext(this, options)).setDebug(logger.isDebugLogging());
}
protected ValueSetValidator constructValueSetCheckerSimple(ValidationOptions options, ValueSet vs, ValidationContextCarrier ctxt) {
return new ValueSetValidator(this, new TerminologyOperationContext(this, options), options, vs, ctxt, expParameters, terminologyClientManager.getTxCapabilities());
return new ValueSetValidator(this, new TerminologyOperationContext(this, options), options, vs, ctxt, expParameters, terminologyClientManager);
}
protected ValueSetValidator constructValueSetCheckerSimple( ValidationOptions options, ValueSet vs) {
return new ValueSetValidator(this, new TerminologyOperationContext(this, options), options, vs, expParameters, terminologyClientManager.getTxCapabilities());
return new ValueSetValidator(this, new TerminologyOperationContext(this, options), options, vs, expParameters, terminologyClientManager);
}
protected Parameters constructParameters(TerminologyClientContext tcd, ValueSet vs, boolean hierarchical) {
@ -1645,8 +1647,11 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
protected void addServerValidationParameters(TerminologyClientContext terminologyClientContext, ValueSet vs, Parameters pin, ValidationOptions options) {
boolean cache = false;
if (vs != null) {
if (terminologyClientManager.isTxCaching() && terminologyClientManager.getCacheId() != null && vs.getUrl() != null && terminologyClientContext.getCached().contains(vs.getUrl()+"|"+ vs.getVersion())) {
pin.addParameter().setName("url").setValue(new UriType(vs.getUrl()+(vs.hasVersion() ? "|"+ vs.getVersion() : "")));
if (terminologyClientContext != null && terminologyClientContext.isTxCaching() && terminologyClientContext.getCacheId() != null && vs.getUrl() != null && terminologyClientContext.getCached().contains(vs.getUrl()+"|"+ vs.getVersion())) {
pin.addParameter().setName("url").setValue(new UriType(vs.getUrl()));
if (vs.hasVersion()) {
pin.addParameter().setName("valueSetVersion").setValue(new StringType(vs.getVersion()));
}
} else if (options.getVsAsUrl()){
pin.addParameter().setName("url").setValue(new UriType(vs.getUrl()));
} else {
@ -1705,7 +1710,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
private boolean checkAddToParams(TerminologyClientContext tc, Parameters pin, CanonicalResource cr) {
boolean cache = false;
boolean addToParams = false;
if (terminologyClientManager.usingCache()) {
if (tc.usingCache()) {
if (!tc.alreadyCached(cr)) {
tc.addToCache(cr);
if (logger.isDebugLogging()) {
@ -1856,11 +1861,15 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
return "item";
}
protected void initTS(String cachePath) throws IOException {
if (cachePath != null && !new File(cachePath).exists()) {
Utilities.createDirectory(cachePath);
}
public void initTxCache(String cachePath) throws FileNotFoundException, FHIRException, IOException {
if (cachePath != null) {
txCache = new TerminologyCache(lock, cachePath);
initTxCache(txCache);
}
}
public void initTxCache(TerminologyCache cache) {
txCache = cache;
terminologyClientManager.setCache(txCache);
}
@ -1868,10 +1877,6 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
txCache.removeCS(url);
}
public void clearTS() {
txCache.clear();
}
public boolean isCanRunWithoutTerminology() {
return canRunWithoutTerminology;
}
@ -2024,6 +2029,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (codeSystems.has(uri)) {
return (T) codeSystems.get(uri, version, pvlist);
}
if (systems.has(uri)) {
return (T) systems.get(uri, version, pvlist);
}
if (operations.has(uri)) {
return (T) operations.get(uri, version, pvlist);
}
@ -2082,6 +2090,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
return (T) structures.get(uri, version, pvlist);
} else if (class_ == StructureMap.class) {
return (T) transforms.get(uri, version, pvlist);
} else if (class_ == NamingSystem.class) {
return (T) systems.get(uri, version, pvlist);
} else if (class_ == ValueSet.class) {
return (T) valueSets.get(uri, version, pvlist);
} else if (class_ == CodeSystem.class) {
@ -2988,33 +2998,10 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
return terminologyClientManager.getCacheId();
}
public void setCacheId(String cacheId) {
terminologyClientManager.setCacheId(cacheId);
}
public TerminologyCapabilities getTxCaps() {
return terminologyClientManager.getTxCapabilities();
}
public void setTxCaps(TerminologyCapabilities txCaps) {
this.terminologyClientManager.setTxCapabilities(txCaps);
if (txCaps != null) {
for (TerminologyCapabilitiesExpansionParameterComponent t : terminologyClientManager.getTxCapabilities().getExpansion().getParameter()) {
if ("cache-id".equals(t.getName())) {
terminologyClientManager.setTxCaching(true);
}
}
for (TerminologyCapabilitiesCodeSystemComponent tccs : terminologyClientManager.getTxCapabilities().getCodeSystem()) {
supportedCodeSystems.add(tccs.getUri());
}
}
}
public TimeTracker clock() {
return clock;
}
public int countAllCaches() {
return codeSystems.size() + valueSets.size() + maps.size() + transforms.size() + structures.size() + measures.size() + libraries.size() +
guides.size() + capstmts.size() + searchParameters.size() + questionnaires.size() + operations.size() + plans.size() +
@ -3165,7 +3152,69 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
binaries.clear();
validationCache.clear();
txCache.clear();
txCache.unload();
}
private <T extends Resource> T doFindTxResource(Class<T> class_, String canonical) {
// well, we haven't found it locally. We're going look it up
if (class_ == ValueSet.class) {
SourcedValueSet svs = null;
if (txCache.hasValueSet(canonical)) {
svs = txCache.getValueSet(canonical);
} else {
svs = terminologyClientManager.findValueSetOnServer(canonical);
txCache.cacheValueSet(canonical, svs);
}
if (svs != null) {
String web = ToolingExtensions.readStringExtension(svs.getVs(), ToolingExtensions.EXT_WEB_SOURCE);
if (web == null) {
web = Utilities.pathURL(svs.getServer(), "ValueSet", svs.getVs().getIdBase());
}
svs.getVs().setWebPath(web);
svs.getVs().setUserData("External.Link", svs.getServer()); // so we can render it differently
}
if (svs == null) {
return null;
} else {
cacheResource(svs.getVs());
return (T) svs.getVs();
}
} else {
throw new Error("Not supported");
}
}
public <T extends Resource> T findTxResource(Class<T> class_, String canonical, Resource sourceOfReference) {
if (canonical == null) {
return null;
}
T result = fetchResource(class_, canonical, sourceOfReference);
if (result == null) {
result = doFindTxResource(class_, canonical);
}
return result;
}
public <T extends Resource> T findTxResource(Class<T> class_, String canonical) {
if (canonical == null) {
return null;
}
T result = fetchResource(class_, canonical);
if (result == null) {
result = doFindTxResource(class_, canonical);
}
return result;
}
public <T extends Resource> T findTxResource(Class<T> class_, String canonical, String version) {
if (canonical == null) {
return null;
}
T result = fetchResource(class_, canonical, version);
if (result == null) {
result = doFindTxResource(class_, canonical+"|"+version);
}
return result;
}
}

View File

@ -627,4 +627,17 @@ public interface IWorkerContext {
public Set<String> urlsForOid(boolean codeSystem, String oid);
/**
* this first does a fetch resource, and if nothing is found, looks in the
* terminology eco-system for a matching definition for the resource
*
* usually used (and so far only tested with) ValueSet.class
*
* @param value
* @return
*/
public <T extends Resource> T findTxResource(Class<T> class_, String canonical, Resource sourceOfReference);
public <T extends Resource> T findTxResource(Class<T> class_, String canonical);
public <T extends Resource> T findTxResource(Class<T> class_, String canonical, String version);
}

View File

@ -41,6 +41,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@ -51,6 +52,7 @@ import org.apache.commons.io.IOUtils;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy;
import org.hl7.fhir.r5.context.ILoggingService.LogCategory;
import org.hl7.fhir.r5.formats.IParser;
@ -64,6 +66,7 @@ import org.hl7.fhir.r5.model.StructureMap.StructureMapModelMode;
import org.hl7.fhir.r5.model.StructureMap.StructureMapStructureComponent;
import org.hl7.fhir.r5.terminologies.JurisdictionUtilities;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientContext;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientR5;
@ -237,7 +240,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
}
private SimpleWorkerContext build(SimpleWorkerContext context) throws IOException {
context.initTS(terminologyCachePath);
context.initTxCache(terminologyCachePath);
context.setUserAgent(userAgent);
context.setLogger(loggingService);
context.cacheResource(new org.hl7.fhir.r5.formats.JsonParser().parse(MagicResources.spdxCodesAsData()));
@ -247,7 +250,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
public SimpleWorkerContext fromPackage(NpmPackage pi) throws IOException, FHIRException {
SimpleWorkerContext context = getSimpleWorkerContextInstance();
context.setAllowLoadingDuplicates(allowLoadingDuplicates);
context.terminologyClientManager = new TerminologyClientManager(TerminologyClientR5.factory());
context.terminologyClientManager.setFactory(TerminologyClientR5.factory());
context.loadFromPackage(pi, null);
return build(context);
}
@ -328,30 +331,48 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
loadBytes(name, stream);
}
public String connectToTSServer(ITerminologyClientFactory factory, ITerminologyClient client, String log) {
try {
txLog("Connect to "+client.getAddress());
public void connectToTSServer(ITerminologyClientFactory factory, ITerminologyClient client) {
terminologyClientManager.setFactory(factory);
if (txLog == null) {
txLog = client.getLogger();
}
TerminologyClientContext tcc = terminologyClientManager.setMasterClient(client);
txLog("Connect to "+client.getAddress());
try {
tcc.initialize();
} catch (Exception e) {
if (canRunWithoutTerminology) {
noTerminologyServer = true;
logger.logMessage("==============!! Running without terminology server !! ==============");
if (terminologyClientManager.getMasterClient() != null) {
logger.logMessage("txServer = "+ terminologyClientManager.getMasterClient().getId());
logger.logMessage("Error = "+e.getMessage()+"");
}
logger.logMessage("=====================================================================");
} else {
e.printStackTrace();
throw new TerminologyServiceException(e);
}
}
}
public void connectToTSServer(ITerminologyClientFactory factory, String address, String software, String log) {
try {
terminologyClientManager.setFactory(factory);
terminologyClientManager.setMasterClient(client);
if (log != null && (log.endsWith(".htm") || log.endsWith(".html"))) {
txLog = new HTMLClientLogger(log);
} else {
txLog = new TextClientLogger(log);
}
terminologyClientManager.setLogger(txLog);
terminologyClientManager.setUserAgent(userAgent);
ITerminologyClient client = factory.makeClient("tx-server", address, software, txLog);
// txFactory.makeClient("Tx-Server", txServer, "fhir/publisher", null)
// terminologyClientManager.setLogger(txLog);
// terminologyClientManager.setUserAgent(userAgent);
connectToTSServer(factory, client);
final CapabilityStatement capabilitiesStatementQuick = txCache.hasCapabilityStatement() ? txCache.getCapabilityStatement() : terminologyClientManager.getMasterClient().getCapabilitiesStatementQuick();
txCache.cacheCapabilityStatement(capabilitiesStatementQuick);
final TerminologyCapabilities capabilityStatement = txCache.hasTerminologyCapabilities() ? txCache.getTerminologyCapabilities() : terminologyClientManager.getMasterClient().getTerminologyCapabilities();
txCache.cacheTerminologyCapabilities(capabilityStatement);
setTxCaps(capabilityStatement);
return capabilitiesStatementQuick.getSoftware().getVersion();
} catch (Exception e) {
e.printStackTrace();
throw new FHIRException(formatMessage(canNoTS ? I18nConstants.UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER_USE_PARAMETER_TX_NA_TUN_RUN_WITHOUT_USING_TERMINOLOGY_SERVICES_TO_VALIDATE_LOINC_SNOMED_ICDX_ETC_ERROR__ : I18nConstants.UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER, e.getMessage(), client.getAddress()), e);
throw new FHIRException(formatMessage(canNoTS ? I18nConstants.UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER_USE_PARAMETER_TX_NA_TUN_RUN_WITHOUT_USING_TERMINOLOGY_SERVICES_TO_VALIDATE_LOINC_SNOMED_ICDX_ETC_ERROR__ : I18nConstants.UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER, e.getMessage(), address), e);
}
}

View File

@ -243,7 +243,7 @@ public class SHLParser extends ParserBase {
}
private void processContent(List<ValidatedFragment> res, List<ValidationMessage> errors, String path, String name, String jose, String ct) throws FHIRFormatError, DefinitionException, FHIRException, IOException {
ValidatedFragment bin = addNamedElement(res, "encrypted", "jose", TextFile.stringToBytes(jose, false));
ValidatedFragment bin = addNamedElement(res, "encrypted", "jose", TextFile.stringToBytes(jose));
byte[] cnt = null;
JWEObject jwe;
try {

View File

@ -58,26 +58,26 @@ public class FHIRLexer {
private boolean allowDoubleQuotes;
public FHIRLexer(String source, String name) throws FHIRLexerException {
this.source = source == null ? "" : source;
this.source = source == null ? "" : Utilities.stripBOM(source);
this.name = name == null ? "??" : name;
currentLocation = new SourceLocation(1, 1);
next();
}
public FHIRLexer(String source, int i) throws FHIRLexerException {
this.source = source;
this.source = Utilities.stripBOM(source);
this.cursor = i;
currentLocation = new SourceLocation(1, 1);
next();
}
public FHIRLexer(String source, int i, boolean allowDoubleQuotes) throws FHIRLexerException {
this.source = source;
this.source = Utilities.stripBOM(source);
this.cursor = i;
this.allowDoubleQuotes = allowDoubleQuotes;
currentLocation = new SourceLocation(1, 1);
next();
}
public FHIRLexer(String source, String name, boolean metadataFormat, boolean allowDoubleQuotes) throws FHIRLexerException {
this.source = source == null ? "" : source;
this.source = source == null ? "" : Utilities.stripBOM(source);
this.name = name == null ? "??" : name;
this.metadataFormat = metadataFormat;
this.allowDoubleQuotes = allowDoubleQuotes;

View File

@ -2555,7 +2555,7 @@ public class FHIRPathEngine {
private List<Base> opMemberOf(ExecutionContext context, List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException {
boolean ans = false;
String url = right.get(0).primitiveValue();
ValueSet vs = hostServices != null ? hostServices.resolveValueSet(this, context.appInfo, url) : worker.fetchResource(ValueSet.class, url);
ValueSet vs = hostServices != null ? hostServices.resolveValueSet(this, context.appInfo, url) : worker.findTxResource(ValueSet.class, url);
if (vs != null) {
for (Base l : left) {
if (Utilities.existsInList(l.fhirType(), "code", "string", "uri")) {
@ -2748,7 +2748,7 @@ public class FHIRPathEngine {
p = worker.getUcumService().multiply(pl, pr);
result.add(pairToQty(p));
} catch (UcumException e) {
throw new PathEngineException(e.getMessage(), null, expr.getOpStart(), expr.toString(), e); // #FIXME
throw new PathEngineException(e.getMessage(), null, expr.getOpStart(), expr.toString(), e); // #TODO: i18n
}
} else {
throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "*", left.get(0).fhirType(), right.get(0).fhirType());
@ -4620,7 +4620,7 @@ public class FHIRPathEngine {
}
String url = nl.get(0).primitiveValue();
ValueSet vs = hostServices != null ? hostServices.resolveValueSet(this, context.appInfo, url) : worker.fetchResource(ValueSet.class, url);
ValueSet vs = hostServices != null ? hostServices.resolveValueSet(this, context.appInfo, url) : worker.findTxResource(ValueSet.class, url);
if (vs == null) {
return new ArrayList<Base>();
}
@ -4977,10 +4977,10 @@ public class FHIRPathEngine {
tn = "FHIR."+expr.getParameters().get(0).getName();
}
if (!isKnownType(tn)) {
throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_INVALID_TYPE, tn), I18nConstants.FHIRPATH_INVALID_TYPE); // #FIXME
throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_INVALID_TYPE, tn), I18nConstants.FHIRPATH_INVALID_TYPE);
}
if (!doNotEnforceAsSingletonRule && focus.size() > 1) {
throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_AS_COLLECTION, focus.size(), expr.toString()), I18nConstants.FHIRPATH_AS_COLLECTION); // #FIXME
throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_AS_COLLECTION, focus.size(), expr.toString()), I18nConstants.FHIRPATH_AS_COLLECTION);
}
for (Base b : focus) {
@ -5020,7 +5020,7 @@ public class FHIRPathEngine {
tn = "FHIR."+expr.getParameters().get(0).getName();
}
if (!isKnownType(tn)) {
throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_INVALID_TYPE, tn), I18nConstants.FHIRPATH_INVALID_TYPE); // #FIXME
throw new PathEngineException(worker.formatMessage(I18nConstants.FHIRPATH_INVALID_TYPE, tn), I18nConstants.FHIRPATH_INVALID_TYPE);
}

View File

@ -142,6 +142,9 @@ public class OperationOutcome extends DomainResource implements IBaseOperationOu
default: return "?";
}
}
public boolean isHigherThan(IssueSeverity other) {
return this.ordinal() < other.ordinal();
}
}
public static class IssueSeverityEnumFactory implements EnumFactory<IssueSeverity> {

View File

@ -259,7 +259,11 @@ public class AdditionalBindingsRenderer {
if (binding.compare!=null && binding.valueSet.equals(binding.compare.valueSet))
valueset.style(STYLE_UNCHANGED);
if (br.url != null) {
valueset.ah(determineUrl(br.url), binding.valueSet).tx(br.display);
XhtmlNode a = valueset.ah(determineUrl(br.url), br.uri).tx(br.display);
if (br.external) {
a.tx(" ");
a.img("external.png", null);
}
} else {
valueset.span(null, binding.valueSet).tx(br.display);
}
@ -411,7 +415,7 @@ public class AdditionalBindingsRenderer {
return; // what should happen?
}
BindingResolution br = pkp.resolveBinding(profile, b.getValueSet(), corePath);
XhtmlNode a = children.ahOrCode(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, b.hasDocumentation() ? b.getDocumentation() : null);
XhtmlNode a = children.ahOrCode(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, b.hasDocumentation() ? b.getDocumentation() : br.uri);
if (b.hasDocumentation()) {
a.attribute("title", b.getDocumentation());
}

View File

@ -337,7 +337,7 @@ public class DataRenderer extends Renderer implements CodeResolver {
if ("fr-CA".equals(lang)) {
return "French (Canadian)"; // this one was omitted from the value set
}
ValueSet v = getContext().getWorker().fetchResource(ValueSet.class, "http://hl7.org/fhir/ValueSet/languages");
ValueSet v = getContext().getWorker().findTxResource(ValueSet.class, "http://hl7.org/fhir/ValueSet/languages");
if (v != null) {
ConceptReferenceComponent l = null;
for (ConceptReferenceComponent cc : v.getCompose().getIncludeFirstRep().getConcept()) {

View File

@ -324,7 +324,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
defn.getPieces().add(gen.new Piece(vs.getWebPath(), vs.present(), null));
}
} else {
ValueSet vs = context.getWorker().fetchResource(ValueSet.class, i.getAnswerValueSet(), q);
ValueSet vs = context.getWorker().findTxResource(ValueSet.class, i.getAnswerValueSet(), q);
if (vs == null || !vs.hasWebPath()) {
defn.getPieces().add(gen.new Piece(null, i.getAnswerValueSet(), null));
} else {
@ -513,7 +513,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
defn.getPieces().add(gen.new Piece(vs.getWebPath(), vs.present(), null));
}
} else {
ValueSet vs = context.getWorker().fetchResource(ValueSet.class, i.getAnswerValueSet(), q);
ValueSet vs = context.getWorker().findTxResource(ValueSet.class, i.getAnswerValueSet(), q);
if (vs == null || !vs.hasWebPath()) {
defn.getPieces().add(gen.new Piece(null, i.getAnswerValueSet(), null));
} else {
@ -753,7 +753,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
ans.ah(vs.getWebPath()).tx(vs.present());
}
} else {
ValueSet vs = context.getWorker().fetchResource(ValueSet.class, i.getAnswerValueSet(), q);
ValueSet vs = context.getWorker().findTxResource(ValueSet.class, i.getAnswerValueSet(), q);
if (vs == null || !vs.hasWebPath()) {
ans.tx(i.getAnswerValueSet());
} else {
@ -854,7 +854,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
vs.setUrl(q.getUrl()+"--"+q.getContained(i.getAnswerValueSet().substring(1)));
}
} else {
vs = context.getContext().fetchResource(ValueSet.class, i.getAnswerValueSet(), q);
vs = context.getContext().findTxResource(ValueSet.class, i.getAnswerValueSet(), q);
}
if (vs != null) {
ValueSetExpansionOutcome exp = context.getContext().expandVS(vs, true, false);
@ -975,7 +975,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
// content control
defn(tbl, "Max Length", qi.getMaxLength());
if (qi.hasAnswerValueSet()) {
defn(tbl, "Value Set", qi.getDefinition(), context.getWorker().fetchResource(ValueSet.class, qi.getAnswerValueSet(), q));
defn(tbl, "Value Set", qi.getDefinition(), context.getWorker().findTxResource(ValueSet.class, qi.getAnswerValueSet(), q));
}
if (qi.hasAnswerOption()) {
XhtmlNode tr = tbl.tr();

View File

@ -492,7 +492,7 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer {
// ans.ah(vs.getWebPath()).tx(vs.present());
// }
// } else {
// ValueSet vs = context.getWorker().fetchResource(ValueSet.class, i.getAnswerValueSet());
// ValueSet vs = context.getWorker().findTxResource(ValueSet.class, i.getAnswerValueSet());
// if (vs == null || !vs.hasWebPath()) {
// ans.tx(i.getAnswerValueSet());
// } else {
@ -579,7 +579,7 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer {
// vs.setUrl("urn:uuid:"+UUID.randomUUID().toString().toLowerCase());
// }
// } else {
// vs = context.getContext().fetchResource(ValueSet.class, i.getAnswerValueSet());
// vs = context.getContext().findTxResource(ValueSet.class, i.getAnswerValueSet());
// }
// if (vs != null) {
// ValueSetExpansionOutcome exp = context.getContext().expandVS(vs, true, false);
@ -707,7 +707,7 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer {
// // content control
// defn(tbl, "Max Length", qi.getMaxLength());
// if (qi.hasAnswerValueSet()) {
// defn(tbl, "Value Set", qi.getDefinition(), context.getWorker().fetchResource(ValueSet.class, qi.getAnswerValueSet()));
// defn(tbl, "Value Set", qi.getDefinition(), context.getWorker().findTxResource(ValueSet.class, qi.getAnswerValueSet()));
// }
// if (qi.hasAnswerOption()) {
// XhtmlNode tr = tbl.tr();

View File

@ -1550,7 +1550,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
c.addPiece(gen.new Piece("br"));
BindingResolution br = context.getPkp() == null ? makeNullBr(binding) : context.getPkp().resolveBinding(profile, binding, definition.getPath());
c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, translate("sd.table", "Binding")+": ", null).addStyle("font-weight:bold")));
c.getPieces().add(checkForNoChange(binding.getValueSetElement(), gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, br.display, null)));
c.getPieces().add(checkForNoChange(binding.getValueSetElement(), checkAddExternalFlag(br, gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, br.display, br.uri))));
if (binding.hasStrength()) {
c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(null, " (", null)));
c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(corePath+"terminologies.html#"+binding.getStrength().toCode(), egt(binding.getStrengthElement()), binding.getStrength().getDefinition())));
@ -1662,6 +1662,13 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
return c;
}
private Piece checkAddExternalFlag(BindingResolution br, Piece piece) {
if (br.external) {
piece.setTagImg("external.png");
}
return piece;
}
private boolean isAttr(SourcedElementDefinition ed) {
for (Enumeration<PropertyRepresentation> t : ed.getDefinition().getRepresentation()) {
if (t.getValue() == PropertyRepresentation.XMLATTR) {
@ -2375,10 +2382,11 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
c.addPiece(gen.new Piece("br"));
BindingResolution br = context.getPkp().resolveBinding(profile, binding, definition.getPath());
c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, "Binding: ", null).addStyle("font-weight:bold")));
c.getPieces().add(checkForNoChange(binding, gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, br.display, null)));
c.getPieces().add(checkForNoChange(binding, checkAddExternalFlag(br, gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, br.display, br.uri))));
if (binding.hasStrength()) {
c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, " (", null)));
c.getPieces().add(checkForNoChange(binding, gen.new Piece(corePath+"terminologies.html#"+binding.getStrength().toCode(), binding.getStrength().toCode(), binding.getStrength().getDefinition()))); c.getPieces().add(gen.new Piece(null, ")", null));
c.getPieces().add(checkForNoChange(binding, gen.new Piece(corePath+"terminologies.html#"+binding.getStrength().toCode(), binding.getStrength().toCode(), binding.getStrength().getDefinition())));
c.getPieces().add(gen.new Piece(null, ")", null));
}
if (binding.hasDescription() && MarkDownProcessor.isSimpleMarkdown(binding.getDescription())) {
c.getPieces().add(gen.new Piece(null, ": ", null));
@ -3088,7 +3096,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
c.addPiece(gen.new Piece("br"));
BindingResolution br = context.getPkp().resolveBinding(ed, ved.getBinding(), ved.getPath());
c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(null, translate("sd.table", "Binding")+": ", null).addStyle("font-weight:bold")));
c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, br.display, null)));
c.getPieces().add(checkForNoChange(ved.getBinding(), checkAddExternalFlag(br, gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !context.getPkp().prependLinks() ? br.url : corePath+br.url, br.display, br.uri))));
if (ved.getBinding().hasStrength()) {
c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(null, " (", null)));
c.getPieces().add(checkForNoChange(ved.getBinding(), gen.new Piece(corePath+"terminologies.html#"+ved.getBinding().getStrength().toCode(), egt(ved.getBinding().getStrengthElement()), ved.getBinding().getStrength().getDefinition())));
@ -3470,11 +3478,11 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
return false;
}
public XhtmlNode compareString(String newStr, Base source, String nLink, String name, Base parent, String oldStr, String oLink, int mode) {
public XhtmlNode compareString(String newStr, Base source, String nLink, String name, Base parent, String oldStr, String oLink, int mode, boolean externalN, boolean externalO) {
XhtmlNode x = new XhtmlNode(NodeType.Element, "div");
if (mode != GEN_MODE_KEY) {
if (newStr != null) {
renderStatus(source, x).ah(nLink).tx(newStr);
renderStatus(source, x).ah(nLink).txN(newStr).iff(externalN).txN(" ").img("external.png", null);
} else if (VersionComparisonAnnotation.hasDeleted(parent, name)) {
PrimitiveType p = (PrimitiveType) VersionComparisonAnnotation.getDeletedItem(parent, name);
renderStatus(p, x).tx(p.primitiveValue());
@ -3485,33 +3493,33 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
if (newStr==null || newStr.isEmpty()) {
return null;
} else {
renderStatus(source, x).ah(nLink).tx(newStr);
renderStatus(source, x).ah(nLink).txN(newStr).iff(externalN).txN(" ").img("external.png", null);
}
} else if (oldStr!=null && !oldStr.isEmpty() && (newStr==null || newStr.isEmpty())) {
if (mode == GEN_MODE_DIFF) {
return null;
} else {
removed(x).ah(oLink).tx(oldStr);
removed(x).ah(oLink).txN(oldStr).iff(externalO).txN(" ").img("external.png", null);
}
} else if (oldStr.equals(newStr)) {
if (mode==GEN_MODE_DIFF) {
return null;
} else {
unchanged(x).ah(nLink).tx(newStr);
unchanged(x).ah(nLink).txN(newStr).iff(externalN).txN(" ").img("external.png", null);
}
} else if (newStr.startsWith(oldStr)) {
unchanged(x).ah(oLink).tx(oldStr);
renderStatus(source, x).ah(nLink).tx(newStr.substring(oldStr.length()));
unchanged(x).ah(oLink).txN(oldStr).iff(externalO).txN(" ").img("external.png", null);
renderStatus(source, x).ah(nLink).txN(newStr.substring(oldStr.length())).iff(externalN).txN(" ").img("external.png", null);
} else {
// TODO: improve comparision in this fall-through case, by looking for matches in sub-paragraphs?
renderStatus(source, x).ah(nLink).tx(newStr);
removed(x).ah(oLink).tx(oldStr);
renderStatus(source, x).ah(nLink).txN(newStr).iff(externalN).txN(" ").img("external.png", null);
removed(x).ah(oLink).txN(oldStr).iff(externalO).txN(" ").img("external.png", null);
}
return x;
}
public boolean compareString(XhtmlNode x, String newStr, Base source, String nLink, String name, Base parent, String oldStr, String oLink, int mode) {
XhtmlNode x1 = compareString(newStr, source, nLink, name, parent, oldStr, oLink, mode);
public boolean compareString(XhtmlNode x, String newStr, Base source, String nLink, String name, Base parent, String oldStr, String oLink, int mode, boolean externalN, boolean externalO) {
XhtmlNode x1 = compareString(newStr, source, nLink, name, parent, oldStr, oLink, mode, externalN, externalO);
if (x1 == null) {
return false;
} else {
@ -3541,12 +3549,12 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
boolean slicedExtension = d.hasSliceName() && (d.getPath().endsWith(".extension") || d.getPath().endsWith(".modifierExtension"));
// int slicedExtensionMode = (mode == GEN_MODE_KEY) && slicedExtension ? GEN_MODE_SNAP : mode; // see ProfileUtilities.checkExtensionDoco / Task 3970
if (d.hasSliceName()) {
tableRow(tbl, "Slice Name", "profiling.html#slicing", strikethrough, compareString(d.getSliceName(), d.getSliceNameElement(), null, (compare != null ? compare.getSliceName() : null), d, null, "sliceName", mode));
tableRow(tbl, "Slice Constraining", "profiling.html#slicing", strikethrough, compareString(encodeValue(d.getSliceIsConstrainingElement()), d.getSliceIsConstrainingElement(), null, (compare != null ? encodeValue(compare.getSliceIsConstrainingElement()) : null), d, null, "sliceName", mode));
tableRow(tbl, "Slice Name", "profiling.html#slicing", strikethrough, compareString(d.getSliceName(), d.getSliceNameElement(), null, (compare != null ? compare.getSliceName() : null), d, null, "sliceName", mode, false, false));
tableRow(tbl, "Slice Constraining", "profiling.html#slicing", strikethrough, compareString(encodeValue(d.getSliceIsConstrainingElement()), d.getSliceIsConstrainingElement(), null, (compare != null ? encodeValue(compare.getSliceIsConstrainingElement()) : null), d, null, "sliceName", mode, false, false));
}
tableRow(tbl, "Definition", null, strikethrough, compareMarkdown(sd.getName(), d.getDefinitionElement(), (compare==null) || slicedExtension ? null : compare.getDefinitionElement(), mode));
tableRow(tbl, "Short", null, strikethrough, compareString(d.hasShort() ? d.getShort() : null, d.getShortElement(), null, "short", d, compare!= null && compare.hasShortElement() ? compare.getShort() : null, null, mode));
tableRow(tbl, "Short", null, strikethrough, compareString(d.hasShort() ? d.getShort() : null, d.getShortElement(), null, "short", d, compare!= null && compare.hasShortElement() ? compare.getShort() : null, null, mode, false, false));
tableRow(tbl, "Comments", null, strikethrough, compareMarkdown(sd.getName(), d.getCommentElement(), (compare==null) || slicedExtension ? null : compare.getCommentElement(), mode));
tableRow(tbl, "Note", null, strikethrough, businessIdWarning(sd.getName(), tail(d.getPath())));
tableRow(tbl, "Control", "conformance-rules.html#conformance", strikethrough, describeCardinality(d, compare, mode));
@ -3696,13 +3704,13 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
tableRow(tbl, "Summary", "search.html#summary", strikethrough, Boolean.toString(d.getIsSummary()));
}
tableRow(tbl, "Requirements", null, strikethrough, compareMarkdown(sd.getName(), d.getRequirementsElement(), (compare==null) || slicedExtension ? null : compare.getRequirementsElement(), mode));
tableRow(tbl, "Label", null, strikethrough, compareString(d.getLabel(), d.getLabelElement(), null, "label", d, (compare != null ? compare.getLabel() : null), null, mode));
tableRow(tbl, "Label", null, strikethrough, compareString(d.getLabel(), d.getLabelElement(), null, "label", d, (compare != null ? compare.getLabel() : null), null, mode, false, false));
tableRow(tbl, "Alternate Names", null, strikethrough, compareSimpleTypeLists(d.getAlias(), ((compare==null) || slicedExtension ? null : compare.getAlias()), mode));
tableRow(tbl, "Definitional Codes", null, strikethrough, compareDataTypeLists(d.getCode(), ((compare==null) || slicedExtension ? null : compare.getCode()), mode));
tableRow(tbl, "Min Value", null, strikethrough, compareString(d.hasMinValue() ? encodeValue(d.getMinValue()) : null, d.getMinValue(), null, "minValue", d, compare!= null && compare.hasMinValue() ? encodeValue(compare.getMinValue()) : null, null, mode));
tableRow(tbl, "Max Value", null, strikethrough, compareString(d.hasMaxValue() ? encodeValue(d.getMaxValue()) : null, d.getMaxValue(), null, "maxValue", d, compare!= null && compare.hasMaxValue() ? encodeValue(compare.getMaxValue()) : null, null, mode));
tableRow(tbl, "Max Length", null, strikethrough, compareString(d.hasMaxLength() ? toStr(d.getMaxLength()) : null, d.getMaxLengthElement(), null, "maxLength", d, compare!= null && compare.hasMaxLengthElement() ? toStr(compare.getMaxLength()) : null, null, mode));
tableRow(tbl, "Value Required", null, strikethrough, compareString(encodeValue(d.getMustHaveValueElement()), d.getMustHaveValueElement(), null, (compare != null ? encodeValue(compare.getMustHaveValueElement()) : null), d, null, "mustHaveValueElement", mode));
tableRow(tbl, "Min Value", null, strikethrough, compareString(d.hasMinValue() ? encodeValue(d.getMinValue()) : null, d.getMinValue(), null, "minValue", d, compare!= null && compare.hasMinValue() ? encodeValue(compare.getMinValue()) : null, null, mode, false, false));
tableRow(tbl, "Max Value", null, strikethrough, compareString(d.hasMaxValue() ? encodeValue(d.getMaxValue()) : null, d.getMaxValue(), null, "maxValue", d, compare!= null && compare.hasMaxValue() ? encodeValue(compare.getMaxValue()) : null, null, mode, false, false));
tableRow(tbl, "Max Length", null, strikethrough, compareString(d.hasMaxLength() ? toStr(d.getMaxLength()) : null, d.getMaxLengthElement(), null, "maxLength", d, compare!= null && compare.hasMaxLengthElement() ? toStr(compare.getMaxLength()) : null, null, mode, false, false));
tableRow(tbl, "Value Required", null, strikethrough, compareString(encodeValue(d.getMustHaveValueElement()), d.getMustHaveValueElement(), null, (compare != null ? encodeValue(compare.getMustHaveValueElement()) : null), d, null, "mustHaveValueElement", mode, false, false));
tableRow(tbl, "Value Alternatives", null, strikethrough, compareSimpleTypeLists(d.getValueAlternatives(), ((compare==null) || slicedExtension ? null : compare.getValueAlternatives()), mode));
tableRow(tbl, "Default Value", null, strikethrough, encodeValue(d.getDefaultValue(), "defaultValue", d, compare==null ? null : compare.getDefaultValue(), mode));
tableRow(tbl, "Meaning if Missing", null, strikethrough, d.getMeaningWhenMissing());
@ -3716,9 +3724,9 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
}
private XhtmlNode presentModifier(ElementDefinition d, int mode, ElementDefinition compare) throws FHIRException, IOException {
XhtmlNode x1 = compareString(encodeValue(d.getIsModifierElement()), d.getIsModifierElement(), null, "isModifier", d, compare == null ? null : encodeValue(compare.getIsModifierElement()), null, mode);
XhtmlNode x1 = compareString(encodeValue(d.getIsModifierElement()), d.getIsModifierElement(), null, "isModifier", d, compare == null ? null : encodeValue(compare.getIsModifierElement()), null, mode, false, false);
if (x1 != null) {
XhtmlNode x2 = compareString(encodeValue(d.getIsModifierReasonElement()), d.getIsModifierReasonElement(), null, "isModifierReason", d, compare == null ? null : encodeValue(compare.getIsModifierReasonElement()), null, mode);
XhtmlNode x2 = compareString(encodeValue(d.getIsModifierReasonElement()), d.getIsModifierReasonElement(), null, "isModifierReason", d, compare == null ? null : encodeValue(compare.getIsModifierReasonElement()), null, mode, false, false);
if (x2 != null) {
x1.tx(" because ");
x1.copyAllContent(x2);
@ -3860,7 +3868,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
DataType au = ToolingExtensions.getAllowedUnits(d);
if (au instanceof CanonicalType) {
String url = ((CanonicalType) au).asStringValue();
ValueSet vs = context.getContext().fetchResource(ValueSet.class, url);
ValueSet vs = context.getContext().findTxResource(ValueSet.class, url);
ret.tx("Value set ");
genCT(ret, url, vs);
return ret;
@ -3951,9 +3959,9 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
x.codeWithText("This element introduces a set of slices on ", ed.getPath(), ". The slices are ");
String newOrdered = sliceOrderString(slicing);
String oldOrdered = (compare==null || !compare.hasSlicing()) ? null : sliceOrderString(compare.getSlicing());
compareString(x, newOrdered, slicing.getOrderedElement(), null, null, null, oldOrdered, null, mode);
compareString(x, newOrdered, slicing.getOrderedElement(), null, null, null, oldOrdered, null, mode, false, false);
x.tx(" and ");
compareString(x, slicing.hasRules() ? slicing.getRules().getDisplay() : null, slicing.getRulesElement(), null, "rules", slicing, compare!=null && compare.hasSlicing() && compare.getSlicing().hasRules() ? compare.getSlicing().getRules().getDisplay() : null, null, mode);
compareString(x, slicing.hasRules() ? slicing.getRules().getDisplay() : null, slicing.getRulesElement(), null, "rules", slicing, compare!=null && compare.hasSlicing() && compare.getSlicing().hasRules() ? compare.getSlicing().getRules().getDisplay() : null, null, mode, false, false);
if (slicing.hasDiscriminator()) {
x.tx(", and can be differentiated using the following discriminators: ");
@ -4080,11 +4088,11 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
}
} else {
if (!(mode==GEN_MODE_DIFF && (d.getMin()==compare.getMin() || d.getMin()==0))) {
compareString(x, toStr(d.getMin()), d.getMinElement(), null, "min", d, toStr(compare.getMin()), null, mode);
compareString(x, toStr(d.getMin()), d.getMinElement(), null, "min", d, toStr(compare.getMin()), null, mode, false, false);
}
x.tx("..");
if (!(mode==GEN_MODE_DIFF && (d.getMax().equals(compare.getMax()) || "1".equals(d.getMax())))) {
compareString(x, d.getMax(), d.getMaxElement(), null, "max", d, compare.getMax(), null, mode);
compareString(x, d.getMax(), d.getMaxElement(), null, "max", d, compare.getMax(), null, mode, false, false);
}
}
XhtmlNode t = compareSimpleTypeLists(d.getCondition(), compare == null ? null : compare.getCondition(), mode);
@ -4199,9 +4207,9 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
boolean ts = false;
if (t.getWorkingCode().startsWith("xs:")) {
ts = compareString(x, t.getWorkingCode(), t, null, "code", t, compare==null ? null : compare.getWorkingCode(), null, mode);
ts = compareString(x, t.getWorkingCode(), t, null, "code", t, compare==null ? null : compare.getWorkingCode(), null, mode, false, false);
} else {
ts = compareString(x, t.getWorkingCode(), t, getTypeLink(t, sd), "code", t, compare==null ? null : compare.getWorkingCode(), compare==null ? null : getTypeLink(compare, sd), mode);
ts = compareString(x, t.getWorkingCode(), t, getTypeLink(t, sd), "code", t, compare==null ? null : compare.getWorkingCode(), compare==null ? null : getTypeLink(compare, sd), mode, false, false);
}
if ((!mustSupportOnly && (t.hasProfile() || (compare!=null && compare.hasProfile()))) || isMustSupport(t.getProfile())) {
@ -4314,7 +4322,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
private XhtmlNode displayBoolean(boolean value, BooleanType source, String name, Base parent, BooleanType compare, int mode) {
String newValue = value ? "true" : source.hasValue() ? "false" : null;
String oldValue = compare==null || compare.getValue()==null ? null : (compare.getValue()!=true ? null : "true");
return compareString(newValue, source, null, name, parent, oldValue, null, mode);
return compareString(newValue, source, null, name, parent, oldValue, null, mode, false, false);
}
@ -4402,16 +4410,32 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
}
}
private boolean isSimpleContent(XhtmlNode bindingDesc) {
return bindingDesc.getChildNodes().size() == 1 && bindingDesc.getChildNodes().get(0).isPara();
}
private void renderBinding(XhtmlNode span, ElementDefinitionBindingComponent binding, ElementDefinitionBindingComponent compare, String path, StructureDefinition sd, int mode) {
compareString(span, conf(binding), binding.getStrengthElement(), null, "strength", binding, compare == null ? null : conf(compare), null, mode);
compareString(span, conf(binding), binding.getStrengthElement(), null, "strength", binding, compare == null ? null : conf(compare), null, mode, false, false);
span.tx(" ");
BindingResolution br = context.getPkp().resolveBinding(sd, binding, path);
compareString(span, br.display, binding.getValueSetElement(), br.url, "valueSet", binding, compare == null ? null : compare.getValueSet(), null, mode);
compareString(span, br.display, binding.getValueSetElement(), br.url, "valueSet", binding, compare == null ? null : compare.getValueSet(), null, mode, br.external, false);
if (binding.hasStrength() || binding.hasValueSet()) {
span.br();
span.tx("(");
if (binding.hasStrength()) {
span.ah(Utilities.pathURL(corePath, "terminologies.html#"+binding.getStrength().toCode())).tx(binding.getStrength().toCode());
}
if (binding.hasStrength() && binding.hasValueSet()) {
span.tx(" ");
}
if (binding.hasValueSet()) {
span.tx("to ");
XhtmlNode ispan = span.spanClss("copy-text-inline");
ispan.code().tx(binding.getValueSet());
ispan.button("btn-copy", "Click to Copy URL").attribute("data-clipboard-text", binding.getValueSet());
}
span.tx(")");
}
}
private String stripPara(String s) {
@ -4459,7 +4483,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
private XhtmlNode encodeValue(DataType value, String name, Base parent, DataType compare, int mode) throws FHIRException, IOException {
String oldValue = encodeValue(compare);
String newValue = encodeValue(value);
return compareString(newValue, value, null, name, parent, oldValue, null, mode);
return compareString(newValue, value, null, name, parent, oldValue, null, mode, false, false);
}
private String encodeValue(DataType value) throws FHIRException, IOException {
@ -4508,7 +4532,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
}
}
return compareString(Utilities.escapeXml(newMap), null, null, "mapping", d, Utilities.escapeXml(oldMap), null, mode);
return compareString(Utilities.escapeXml(newMap), null, null, "mapping", d, Utilities.escapeXml(oldMap), null, mode, false, false);
}
private XhtmlNode compareSimpleTypeLists(List<? extends PrimitiveType> original, List<? extends PrimitiveType> compare, int mode) throws IOException {

View File

@ -287,7 +287,7 @@ public abstract class TerminologyRenderer extends ResourceRenderer {
}
CanonicalResource vs = (CanonicalResource) res;
if (vs == null)
vs = getContext().getWorker().fetchResource(ValueSet.class, value, source);
vs = getContext().getWorker().findTxResource(ValueSet.class, value, source);
if (vs == null)
vs = getContext().getWorker().fetchResource(StructureDefinition.class, value, source);
if (vs == null)

View File

@ -118,7 +118,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
re = new ConceptMapRenderInstructions(cm.present(), cm.getUrl(), false);
}
if (re != null) {
ValueSet vst = cm.hasTargetScope() ? getContext().getWorker().fetchResource(ValueSet.class, cm.hasTargetScopeCanonicalType() ? cm.getTargetScopeCanonicalType().getValue() : cm.getTargetScopeUriType().asStringValue(), cm) : null;
ValueSet vst = cm.hasTargetScope() ? getContext().getWorker().findTxResource(ValueSet.class, cm.hasTargetScopeCanonicalType() ? cm.getTargetScopeCanonicalType().getValue() : cm.getTargetScopeUriType().asStringValue(), cm) : null;
res.add(new UsedConceptMap(re, vst == null ? cm.getWebPath() : vst.getWebPath(), cm));
}
}
@ -141,7 +141,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
// Map<ConceptMap, String> mymaps = new HashMap<ConceptMap, String>();
// for (ConceptMap a : context.getWorker().findMapsForSource(vs.getUrl())) {
// String url = "";
// ValueSet vsr = context.getWorker().fetchResource(ValueSet.class, ((Reference) a.getTarget()).getReference());
// ValueSet vsr = context.getWorker().findTxResource(ValueSet.class, ((Reference) a.getTarget()).getReference());
// if (vsr != null)
// url = (String) vsr.getUserData("filename");
// mymaps.put(a, url);
@ -160,7 +160,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
// ConceptMap cm = (ConceptMap) r;
// if (((Reference) cm.getSource()).getReference().equals(cs.getValueSet())) {
// String url = "";
// ValueSet vsr = context.getWorker().fetchResource(ValueSet.class, ((Reference) cm.getTarget()).getReference());
// ValueSet vsr = context.getWorker().findTxResource(ValueSet.class, ((Reference) cm.getTarget()).getReference());
// if (vsr != null)
// url = (String) vsr.getUserData("filename");
// mymaps.put(cm, url);

View File

@ -68,10 +68,42 @@ import org.hl7.fhir.r5.utils.CanonicalResourceUtilities;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.StandardsStatus;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
public class ValueSetUtilities extends TerminologyUtilities {
public static class ValueSetSorter implements Comparator<ValueSet> {
@Override
public int compare(ValueSet o1, ValueSet o2) {
String url1 = o1.getUrl();
String url2 = o2.getUrl();
int c = compareString(url1, url2);
if (c == 0) {
String ver1 = o1.getVersion();
String ver2 = o2.getVersion();
c = VersionUtilities.compareVersions(ver1, ver2);
if (c == 0) {
String d1 = o1.getDateElement().asStringValue();
String d2 = o2.getDateElement().asStringValue();
c = compareString(url1, url2);
}
}
return c;
}
private int compareString(String s1, String s2) {
if (s1 == null) {
return s2 == null ? 0 : 1;
} else {
return s1.compareTo(s2);
}
}
}
public static boolean isServerSide(String url) {
return Utilities.existsInList(url, "http://hl7.org/fhir/sid/cvx");
}
@ -413,7 +445,7 @@ public class ValueSetUtilities extends TerminologyUtilities {
Set<String> systems = new HashSet<>();
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
for (CanonicalType ct : inc.getValueSet()) {
ValueSet vsr = ctxt.fetchResource(ValueSet.class, ct.asStringValue(), vs);
ValueSet vsr = ctxt.findTxResource(ValueSet.class, ct.asStringValue(), vs);
if (vsr != null) {
systems.addAll(listSystems(ctxt, vsr));
}

View File

@ -55,6 +55,7 @@ public interface ITerminologyClient {
Parameters validateCS(Parameters pin) throws FHIRException;
Parameters validateVS(Parameters pin) throws FHIRException;
ITerminologyClient setTimeoutFactor(int i) throws FHIRException;
ToolingClientLogger getLogger();
ITerminologyClient setLogger(ToolingClientLogger txLog) throws FHIRException;
int getRetryCount() throws FHIRException;
ITerminologyClient setRetryCount(int retryCount) throws FHIRException;
@ -69,4 +70,5 @@ public interface ITerminologyClient {
ITerminologyClient setContentLanguage(String lang);
String getUserAgent();
int getUseCount();
Bundle search(String type, String criteria);
}

View File

@ -1,18 +1,34 @@
package org.hl7.fhir.r5.terminologies.client;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.TerminologyCapabilities.TerminologyCapabilitiesCodeSystemComponent;
import org.hl7.fhir.r5.model.TerminologyCapabilities.TerminologyCapabilitiesExpansionParameterComponent;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientContext.TerminologyClientContextUseCount;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache;
public class TerminologyClientContext {
public enum TerminologyClientContextUseType {
expand, validate, readVS
}
public class TerminologyClientContextUseCount {
private int expands;
private int validates;
private int readVS;
public int getReadVS() {
return readVS;
}
public void setReadVS(int readVS) {
this.readVS = readVS;
}
public int getExpands() {
return expands;
}
@ -29,14 +45,21 @@ public class TerminologyClientContext {
}
private ITerminologyClient client;
Map<String, TerminologyClientContextUseCount> useCounts = new HashMap<>();
private boolean initialised = false;
private CapabilityStatement capabilitiesStatementQuick;
private TerminologyCapabilities txcaps;
private TerminologyCache txCache;
private Map<String, TerminologyClientContextUseCount> useCounts = new HashMap<>();
private boolean isTxCaching;
private final Set<String> cached = new HashSet<>();
private boolean master;
private String cacheId;
protected TerminologyClientContext(ITerminologyClient client, boolean master) {
protected TerminologyClientContext(ITerminologyClient client, String cacheId, boolean master) {
super();
this.client = client;
this.cacheId = cacheId;
this.master = master;
}
@ -52,24 +75,32 @@ public class TerminologyClientContext {
return client;
}
public void seeUse(String s, boolean expand) {
public void seeUse(String s, TerminologyClientContextUseType useType) {
TerminologyClientContextUseCount uc = useCounts.get(s);
if (uc == null) {
uc = new TerminologyClientContextUseCount();
useCounts.put(s,uc);
}
if (expand) {
switch (useType) {
case expand:
uc.expands++;
} else {
break;
case readVS:
uc.readVS++;
break;
case validate:
uc.validates++;
break;
default:
break;
}
}
public TerminologyCapabilities getTxcaps() {
public TerminologyCapabilities getTxCapabilities() {
return txcaps;
}
public void setTxcaps(TerminologyCapabilities txcaps) {
public void setTxCapabilities(TerminologyCapabilities txcaps) {
this.txcaps = txcaps;
}
@ -93,4 +124,65 @@ public class TerminologyClientContext {
return getClient().getUseCount();
}
public boolean isTxCaching() {
return isTxCaching;
}
public void setTxCaching(boolean isTxCaching) {
this.isTxCaching = isTxCaching;
}
public boolean usingCache() {
return isTxCaching && cacheId != null;
}
public String getCacheId() {
return cacheId;
}
public TerminologyCache getTxCache() {
return txCache;
}
public void setTxCache(TerminologyCache txCache) {
this.txCache = txCache;
}
public void initialize() throws IOException {
if (!initialised) {
// we don't cache the quick CS - we want to know that the server is with us.
capabilitiesStatementQuick = client.getCapabilitiesStatementQuick();
if (txCache != null && txCache.hasTerminologyCapabilities(getAddress())) {
txcaps = txCache.getTerminologyCapabilities(getAddress());
if (txcaps.getSoftware().hasVersion() && !txcaps.getSoftware().getVersion().equals(capabilitiesStatementQuick.getSoftware().getVersion())) {
txcaps = null;
}
}
if (txcaps == null) {
txcaps = client.getTerminologyCapabilities();
if (txCache != null) {
txCache.cacheTerminologyCapabilities(getAddress(), txcaps);
}
}
if (txcaps != null) {
for (TerminologyCapabilitiesExpansionParameterComponent t : txcaps.getExpansion().getParameter()) {
if ("cache-id".equals(t.getName())) {
setTxCaching(true);
break;
}
}
}
initialised = true;
}
}
public boolean supportsSystem(String system) throws IOException {
initialize();
for (TerminologyCapabilitiesCodeSystemComponent tccs : txcaps.getCodeSystem()) {
if (system.equals(tccs.getUri())) {
return true;
}
}
return false;
}
}

View File

@ -6,6 +6,7 @@ import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -13,10 +14,19 @@ import java.util.Map;
import java.util.Set;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.context.CanonicalResourceManager;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
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.ValueSet;
import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientContext.TerminologyClientContextUseType;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache.SourcedValueSet;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.json.model.JsonObject;
@ -28,15 +38,16 @@ public class TerminologyClientManager {
}
public interface ITerminologyClientFactory {
ITerminologyClient makeClient(String id, String url, String userAgent) throws URISyntaxException;
ITerminologyClient makeClient(String id, String url, String userAgent, ToolingClientLogger logger) throws URISyntaxException;
String getVersion();
}
public static final String UNRESOLVED_VALUESET = "--unknown--";
private static final boolean IGNORE_TX_REGISTRY = false;
private ITerminologyClientFactory factory;
private String cacheId;
private boolean isTxCaching;
private List<TerminologyClientContext> serverList = new ArrayList<>(); // clients by server address
private Map<String, TerminologyClientContext> serverMap = new HashMap<>(); // clients by server address
private Map<String, String> resMap = new HashMap<>(); // client resolution list
@ -50,30 +61,18 @@ public class TerminologyClientManager {
private String monitorServiceURL;
public TerminologyClientManager(ITerminologyClientFactory factory) {
public TerminologyClientManager(ITerminologyClientFactory factory, String cacheId) {
super();
this.factory = factory;
this.cacheId = cacheId;
}
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 void copy(TerminologyClientManager other) {
cacheId = other.cacheId;
isTxCaching = other.isTxCaching;
serverList.addAll(other.serverList);
serverMap.putAll(other.serverMap);
resMap.putAll(other.resMap);
@ -82,10 +81,6 @@ public class TerminologyClientManager {
usage = other.usage;
}
public boolean usingCache() {
return isTxCaching && cacheId != null;
}
public TerminologyClientContext chooseServer(Set<String> systems, boolean expand) throws TerminologyServiceException {
if (serverList.isEmpty()) {
@ -124,21 +119,25 @@ public class TerminologyClientManager {
TerminologyClientContext client = serverMap.get(server);
if (client == null) {
try {
client = new TerminologyClientContext(factory.makeClient("id"+(serverList.size()+1), server, getMasterClient().getUserAgent()), false);
client = new TerminologyClientContext(factory.makeClient("id"+(serverList.size()+1), server, getMasterClient().getUserAgent(), getMasterClient().getLogger()), cacheId, false);
} catch (URISyntaxException e) {
throw new TerminologyServiceException(e);
}
client.setTxCache(cache);
serverList.add(client);
serverMap.put(server, client);
}
client.seeUse(s, expand);
client.seeUse(s, expand ? TerminologyClientContextUseType.expand : TerminologyClientContextUseType.validate);
return client;
}
private String decideWhichServer(String url) {
if (IGNORE_TX_REGISTRY) {
return getMasterClient().getAddress();
}
if (expParameters != null) {
if (!url.contains("|")) {
// the client hasn''t specified an explicit version, but the expansion parameters might
// the client hasn't specified an explicit version, but the expansion parameters might
for (ParametersParameterComponent p : expParameters.getParameter()) {
if (Utilities.existsInList(p.getName(), "system-version", "force-system-version") && p.hasValuePrimitive() && p.getValue().primitiveValue().startsWith(url+"|")) {
url = p.getValue().primitiveValue();
@ -153,7 +152,7 @@ public class TerminologyClientManager {
}
}
}
String request = Utilities.pathURL(monitorServiceURL, "resolve?fhirVersion="+factory.getVersion()+"&url="+url);
String request = Utilities.pathURL(monitorServiceURL, "resolve?fhirVersion="+factory.getVersion()+"&url="+Utilities.URLEncode(url));
if (usage != null) {
request = request + "&usage="+usage;
}
@ -162,7 +161,7 @@ public class TerminologyClientManager {
for (JsonObject item : json.getJsonObjects("authoritative")) {
return item.asString("url");
}
for (JsonObject item : json.getJsonObjects("candidate")) {
for (JsonObject item : json.getJsonObjects("candidates")) {
return item.asString("url");
}
} catch (Exception e) {
@ -208,12 +207,14 @@ public class TerminologyClientManager {
}
}
public void setMasterClient(ITerminologyClient client) {
TerminologyClientContext details = new TerminologyClientContext(client, true);
public TerminologyClientContext setMasterClient(ITerminologyClient client) {
TerminologyClientContext details = new TerminologyClientContext(client, cacheId, true);
details.setTxCache(cache);
serverList.clear();
serverList.add(details);
serverMap.put(client.getAddress(), details);
monitorServiceURL = Utilities.pathURL(Utilities.getDirectoryForURL(client.getAddress()), "tx-reg");
return details;
}
public TerminologyClientContext getMaster() {
@ -232,17 +233,9 @@ public class TerminologyClientManager {
return map;
}
public TerminologyCapabilities getTxCapabilities() {
return hasClient() ? serverList.get(0).getTxcaps() : null;
}
public void setTxCapabilities(TerminologyCapabilities txCaps) {
serverList.get(0).setTxcaps(txCaps);
}
public void setFactory(ITerminologyClientFactory factory) {
this.factory = factory;
System.out.println("tcc factory version = "+factory.getVersion());
}
public void setCache(TerminologyCache cache) {
@ -296,8 +289,6 @@ public class TerminologyClientManager {
return monitorServiceURL;
}
public Parameters getExpansionParameters() {
return expParameters;
}
@ -314,4 +305,93 @@ public class TerminologyClientManager {
this.usage = usage;
}
public SourcedValueSet findValueSetOnServer(String canonical) {
if (IGNORE_TX_REGISTRY || getMasterClient() == null) {
return null;
}
String request = Utilities.pathURL(monitorServiceURL, "resolve?fhirVersion="+factory.getVersion()+"&valueSet="+Utilities.URLEncode(canonical));
if (usage != null) {
request = request + "&usage="+usage;
}
String server = null;
try {
JsonObject json = JsonParser.parseObjectFromUrl(request);
for (JsonObject item : json.getJsonObjects("authoritative")) {
if (server == null) {
server = item.asString("url");
}
}
for (JsonObject item : json.getJsonObjects("candidates")) {
if (server == null) {
server = item.asString("url");
}
}
if (server == null) {
return null;
}
if (server.contains("://tx.fhir.org")) {
try {
server = server.replace("tx.fhir.org", new URL(getMasterClient().getAddress()).getHost());
} catch (MalformedURLException e) {
}
}
TerminologyClientContext client = serverMap.get(server);
if (client == null) {
try {
client = new TerminologyClientContext(factory.makeClient("id"+(serverList.size()+1), server, getMasterClient().getUserAgent(), getMasterClient().getLogger()), cacheId, false);
} catch (URISyntaxException e) {
throw new TerminologyServiceException(e);
}
client.setTxCache(cache);
serverList.add(client);
serverMap.put(server, client);
}
client.seeUse(canonical, TerminologyClientContextUseType.readVS);
String criteria = canonical.contains("|") ?
"?_format=json&url="+Utilities.URLEncode(canonical.substring(0, canonical.lastIndexOf("|")))+"&version="+Utilities.URLEncode(canonical.substring(canonical.lastIndexOf("|")+1)):
"?_format=json&url="+Utilities.URLEncode(canonical);
request = Utilities.pathURL(client.getAddress(), "ValueSet"+ criteria);
Bundle bnd = client.getClient().search("ValueSet", criteria);
String rid = null;
if (bnd.getEntry().size() == 0) {
return null;
} else if (bnd.getEntry().size() > 1) {
List<ValueSet> vslist = new ArrayList<>();
for (BundleEntryComponent be : bnd.getEntry()) {
if (be.hasResource() && be.getResource() instanceof ValueSet) {
vslist.add((ValueSet) be.getResource());
}
}
Collections.sort(vslist, new ValueSetUtilities.ValueSetSorter());
rid = vslist.get(vslist.size()-1).getIdBase();
} else {
if (bnd.getEntryFirstRep().hasResource() && bnd.getEntryFirstRep().getResource() instanceof ValueSet) {
rid = bnd.getEntryFirstRep().getResource().getIdBase();
}
}
if (rid == null) {
return null;
}
ValueSet vs = (ValueSet) client.getClient().read("ValueSet", rid);
return new SourcedValueSet(server, vs);
} catch (Exception e) {
e.printStackTrace();
String msg = "Error resolving valueSet "+canonical+": "+e.getMessage()+" ("+request+")";
if (!internalErrors.contains(msg)) {
internalErrors.add(msg);
}
e.printStackTrace();
return null;
}
}
public boolean supportsSystem(String system) throws IOException {
for (TerminologyClientContext client : serverList) {
if (client.supportsSystem(system)) {
return true;
}
}
return false;
}
}

View File

@ -57,7 +57,7 @@ public class TerminologyClientR5 implements ITerminologyClient {
public static class TerminologyClientR5Factory implements ITerminologyClientFactory {
@Override
public ITerminologyClient makeClient(String id, String url, String userAgent) throws URISyntaxException {
public ITerminologyClient makeClient(String id, String url, String userAgent, ToolingClientLogger logger) throws URISyntaxException {
return new TerminologyClientR5(id, checkEndsWith("/r4", url), userAgent);
}
@ -146,6 +146,11 @@ public class TerminologyClientR5 implements ITerminologyClient {
return this;
}
@Override
public ToolingClientLogger getLogger() {
return client.getLogger();
}
@Override
public ITerminologyClient setLogger(ToolingClientLogger txLog) {
client.setLogger(txLog);
@ -246,4 +251,9 @@ public class TerminologyClientR5 implements ITerminologyClient {
return new TerminologyClientR5Factory();
}
@Override
public Bundle search(String type, String criteria) {
return client.search(type, criteria);
}
}

View File

@ -166,6 +166,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
private boolean checkCodesWhenExpanding;
private boolean includeAbstract = true;
private boolean debug;
private AcceptLanguageHeader langs;
private List<Token> designations = new ArrayList<>();
@ -522,6 +523,32 @@ public class ValueSetExpander extends ValueSetProcessBase {
}
private void excludeCodeAndDescendents(WorkingContext wc, CodeSystem cs, String system, ConceptDefinitionComponent def, Parameters expParams, List<ValueSet> filters,
ConceptDefinitionComponent exclusion, ConceptFilter filterFunc, List<WorkingContext> otherFilters, ValueSetExpansionComponent exp) throws FHIRException, ETooCostly {
opContext.deadCheck();
def.checkNoModifiers("Code in Code System", "expanding");
if (exclusion != null) {
if (exclusion.getCode().equals(def.getCode()))
return; // excluded.
}
boolean abs = CodeSystemUtilities.isNotSelectable(cs, def);
if ((includeAbstract || !abs) && filterFunc.includeConcept(cs, def) && passesOtherFilters(otherFilters, cs, def.getCode())) {
for (String code : getCodesForConcept(def, expParams)) {
if (!(filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code, exp)))
excludeCode(wc, system, code);
}
}
for (ConceptDefinitionComponent c : def.getConcept()) {
excludeCodeAndDescendents(wc, cs, system, c, expParams, filters, exclusion, filterFunc, otherFilters, exp);
}
if (def.hasUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK)) {
List<ConceptDefinitionComponent> children = (List<ConceptDefinitionComponent>) def.getUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK);
for (ConceptDefinitionComponent c : children)
excludeCodeAndDescendents(wc, cs, system, c, expParams, filters, exclusion, filterFunc, otherFilters, exp);
}
}
private List<String> getCodesForConcept(ConceptDefinitionComponent focus, Parameters expParams) {
List<String> codes = new ArrayList<>();
codes.add(focus.getCode());
@ -590,8 +617,20 @@ public class ValueSetExpander extends ValueSetProcessBase {
excludeCode(wc, exc.getSystem(), c.getCode());
}
if (exc.getFilter().size() > 0)
throw fail("not done yet - multiple filters");
if (exc.getFilter().size() > 0) {
if (cs.getContent() == CodeSystemContentMode.FRAGMENT) {
addFragmentWarning(exp, cs);
}
List<WorkingContext> filters = new ArrayList<>();
for (int i = 1; i < exc.getFilter().size(); i++) {
WorkingContext wc1 = new WorkingContext();
filters.add(wc1);
processFilter(exc, exp, expParams, null, cs, false, exc.getFilter().get(i), wc1, null, true);
}
ConceptSetFilterComponent fc = exc.getFilter().get(0);
WorkingContext wc1 = dwc;
processFilter(exc, exp, expParams, null, cs, false, fc, wc1, filters, false);
}
}
private void excludeCodes(WorkingContext wc, ValueSetExpansionComponent expand) {
@ -634,7 +673,9 @@ public class ValueSetExpander extends ValueSetProcessBase {
} catch (ETooCostly e) {
return new ValueSetExpansionOutcome(e.getMessage(), TerminologyServiceErrorClass.TOO_COSTLY, allErrors, false);
} catch (Exception e) {
if (debug) {
e.printStackTrace();
}
// well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
// that might fail too, but it might not, later.
return new ValueSetExpansionOutcome(e.getMessage(), TerminologyServiceErrorClass.UNKNOWN, allErrors, e instanceof EFhirClientException || e instanceof TerminologyServiceException);
@ -814,7 +855,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
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);
ValueSet vs = context.findTxResource(ValueSet.class, value, valueSet);
if (vs == null) {
if (context.fetchResource(CodeSystem.class, value, valueSet) != null) {
throw fail("Cannot include value set "+value+" because it's actually a code system");
@ -861,7 +902,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
private ValueSet importValueSetForExclude(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);
ValueSet vs = context.findTxResource(ValueSet.class, value, valueSet);
if (vs == null) {
if (context.fetchResource(CodeSystem.class, value, valueSet) != null) {
throw fail("Cannot include value set "+value+" because it's actually a code system");
@ -1083,16 +1124,16 @@ public class ValueSetExpander extends ValueSetProcessBase {
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);
processFilter(inc, exp, expParams, imports, cs, noInactive, inc.getFilter().get(i), wc, null, false);
}
ConceptSetFilterComponent fc = inc.getFilter().get(0);
WorkingContext wc = dwc;
processFilter(inc, exp, expParams, imports, cs, noInactive, fc, wc, filters);
processFilter(inc, exp, expParams, imports, cs, noInactive, fc, wc, filters, false);
}
}
private void processFilter(ConceptSetComponent inc, ValueSetExpansionComponent exp, Parameters expParams,
List<ValueSet> imports, CodeSystem cs, boolean noInactive, ConceptSetFilterComponent fc, WorkingContext wc, List<WorkingContext> filters)
private void processFilter(ConceptSetComponent inc, ValueSetExpansionComponent exp, Parameters expParams, List<ValueSet> imports, CodeSystem cs, boolean noInactive,
ConceptSetFilterComponent fc, WorkingContext wc, List<WorkingContext> filters, boolean exclude)
throws ETooCostly {
opContext.deadCheck();
if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISA) {
@ -1100,27 +1141,44 @@ public class ValueSetExpander extends ValueSetProcessBase {
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def == null)
throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
if (exclude) {
excludeCodeAndDescendents(wc, cs, inc.getSystem(), def, null, imports, null, new AllConceptsFilter(allErrors), filters, exp);
} else {
addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, null, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters, exp);
}
} 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()) {
if (exclude) {
excludeCodeAndDescendents(wc, cs, inc.getSystem(), def, null, imports, defEx, new AllConceptsFilter(allErrors), filters, exp);
} else {
addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, defEx, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters, exp);
}
}
} 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())
if (exclude) {
excludeCodeAndDescendents(wc, cs, inc.getSystem(), c, null, imports, null, new AllConceptsFilter(allErrors), filters, exp);
} else {
addCodeAndDescendents(wc, cs, inc.getSystem(), c, null, expParams, imports, null, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters, exp);
}
if (def.hasUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK)) {
List<ConceptDefinitionComponent> children = (List<ConceptDefinitionComponent>) def.getUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK);
for (ConceptDefinitionComponent c : children)
for (ConceptDefinitionComponent c : children) {
if (exclude) {
excludeCodeAndDescendents(wc, cs, inc.getSystem(), c, null, imports, null, new AllConceptsFilter(allErrors), filters, exp);
} else {
addCodeAndDescendents(wc, cs, inc.getSystem(), c, null, expParams, imports, null, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters, exp);
}
}
}
} 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 display is 'v'?
@ -1131,20 +1189,32 @@ public class ValueSetExpander extends ValueSetProcessBase {
if (def.getDisplay().contains(fc.getValue()) && passesOtherFilters(filters, cs, def.getCode())) {
for (String code : getCodesForConcept(def, expParams)) {
opContext.deadCheck();
ValueSetExpansionContainsComponent t = addCode(wc, inc.getSystem(), code, def.getDisplay(), cs.getLanguage(), null, def.getDesignation(), expParams, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def),
if (exclude) {
excludeCode(wc, inc.getSystem(), code);
} else {
addCode(wc, inc.getSystem(), code, def.getDisplay(), cs.getLanguage(), null, def.getDesignation(), expParams, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def),
imports, noInactive, false, exp.getProperty(), makeCSProps(def.getDefinition(), def.getProperty()), cs, null, def.getExtension(), null, exp);
}
}
}
}
}
} else if (isDefinedProperty(cs, fc.getProperty())) {
for (ConceptDefinitionComponent def : cs.getConcept()) {
if (exclude) {
excludeCodeAndDescendents(wc, cs, inc.getSystem(), def, null, imports, null, new PropertyFilter(allErrors, fc, getPropertyDefinition(cs, fc.getProperty())), filters, exp);
} else {
addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, null, new PropertyFilter(allErrors, fc, getPropertyDefinition(cs, fc.getProperty())), noInactive, exp.getProperty(), filters, exp);
}
}
} else if ("code".equals(fc.getProperty()) && fc.getOp() == FilterOperator.REGEX) {
for (ConceptDefinitionComponent def : cs.getConcept()) {
if (exclude) {
excludeCodeAndDescendents(wc, cs, inc.getSystem(), def, null, imports, null, new RegexFilter(allErrors, fc.getValue()), filters, exp);
} else {
addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, null, new RegexFilter(allErrors, fc.getValue()), noInactive, exp.getProperty(), filters, exp);
}
}
} else {
throw fail("Filter by property[" + fc.getProperty() + "] and op[" + fc.getOp() + "] is not supported yet");
}
@ -1258,4 +1328,15 @@ public class ValueSetExpander extends ValueSetProcessBase {
}
return true;
}
public boolean isDebug() {
return debug;
}
public ValueSetExpander setDebug(boolean debug) {
this.debug = debug;
return this;
}
}

View File

@ -32,6 +32,7 @@ package org.hl7.fhir.r5.terminologies.utilities;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@ -43,7 +44,9 @@ import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.context.ILoggingService.LogCategory;
import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
@ -52,9 +55,14 @@ 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.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache.SourcedValueSet;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.IniFile;
import org.hl7.fhir.utilities.StringPair;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.json.model.JsonNull;
import org.hl7.fhir.utilities.json.model.JsonProperty;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationOptions;
@ -74,6 +82,44 @@ import com.google.gson.JsonPrimitive;
*/
public class TerminologyCache {
public static class SourcedValueSet {
private String server;
private ValueSet vs;
public SourcedValueSet(String server, ValueSet vs) {
super();
this.server = server;
this.vs = vs;
}
public String getServer() {
return server;
}
public ValueSet getVs() {
return vs;
}
}
public static class SourcedValueSetEntry {
private String server;
private String filename;
public SourcedValueSetEntry(String server, String filename) {
super();
this.server = server;
this.filename = filename;
}
public String getServer() {
return server;
}
public String getFilename() {
return filename;
}
}
public static final boolean TRANSIENT = false;
public static final boolean PERMANENT = true;
private static final String NAME_FOR_NO_SYSTEM = "all-systems";
@ -82,6 +128,7 @@ public class TerminologyCache {
private static final String CACHE_FILE_EXTENSION = ".cache";
private static final String CAPABILITY_STATEMENT_TITLE = ".capabilityStatement";
private static final String TERMINOLOGY_CAPABILITIES_TITLE = ".terminologyCapabilities";
private static final String FIXED_CACHE_VERSION = "3"; // last change: when multiple servers were introduced
private SystemNameKeyGenerator systemNameKeyGenerator = new SystemNameKeyGenerator();
@ -189,9 +236,11 @@ public class TerminologyCache {
@Getter private int requestCount;
@Getter private int hitCount;
@Getter private int networkCount;
private CapabilityStatement capabilityStatementCache = null;
private TerminologyCapabilities terminologyCapabilitiesCache = null;
private Map<String, CapabilityStatement> capabilityStatementCache = new HashMap<>();
private Map<String, TerminologyCapabilities> terminologyCapabilitiesCache = new HashMap<>();
private Map<String, NamedCache> caches = new HashMap<String, NamedCache>();
private Map<String, SourcedValueSetEntry> vsCache = new HashMap<>();
private Map<String, String> serverMap = new HashMap<>();
@Getter @Setter private static boolean noCaching;
@Getter @Setter private static boolean cacheErrors;
@ -201,54 +250,105 @@ public class TerminologyCache {
public TerminologyCache(Object lock, String folder) throws FileNotFoundException, IOException, FHIRException {
super();
this.lock = lock;
if (folder == null) {
folder = Utilities.path("[tmp]", "default-tx-cache");
}
this.folder = folder;
requestCount = 0;
hitCount = 0;
networkCount = 0;
if (folder != null) {
File f = new File(folder);
if (!f.exists()) {
Utilities.createDirectory(folder);
}
if (!f.exists()) {
throw new IOException("Unable to create terminology cache at "+folder);
}
checkVersion();
load();
}
private void checkVersion() throws IOException {
File verFile = new File(Utilities.path(folder, "version.ctl"));
if (verFile.exists()) {
String ver = TextFile.fileToString(verFile);
if (!ver.equals(FIXED_CACHE_VERSION)) {
System.out.println("Terminology Cache Version has changed from 1 to "+FIXED_CACHE_VERSION+", so clearing txCache");
clear();
}
TextFile.stringToFile(FIXED_CACHE_VERSION, verFile);
} else {
TextFile.stringToFile(FIXED_CACHE_VERSION, verFile);
}
}
public boolean hasCapabilityStatement() {
return capabilityStatementCache != null;
public String getServerId(String address) throws IOException {
if (serverMap.containsKey(address)) {
return serverMap.get(address);
}
String id = address.replace("http://", "").replace("https://", "").replace("/", ".");
int i = 1;
while (serverMap.containsValue(id)) {
i++;
id = address.replace("https:", "").replace("https:", "").replace("/", ".")+i;
}
serverMap.put(address, id);
if (folder != null) {
IniFile ini = new IniFile(Utilities.path(folder, "servers.ini"));
ini.setStringProperty("servers", id, address, null);
ini.save();
}
return id;
}
public CapabilityStatement getCapabilityStatement() {
return capabilityStatementCache;
}
public void cacheCapabilityStatement(CapabilityStatement capabilityStatement) {
if (noCaching) {
return;
}
this.capabilityStatementCache = capabilityStatement;
save(capabilityStatementCache, CAPABILITY_STATEMENT_TITLE);
}
public boolean hasTerminologyCapabilities() {
return terminologyCapabilitiesCache != null;
}
public TerminologyCapabilities getTerminologyCapabilities() {
return terminologyCapabilitiesCache;
}
public void cacheTerminologyCapabilities(TerminologyCapabilities terminologyCapabilities) {
if (noCaching) {
return;
}
this.terminologyCapabilitiesCache = terminologyCapabilities;
save(terminologyCapabilitiesCache, TERMINOLOGY_CAPABILITIES_TITLE);
}
public void clear() {
public void unload() {
// not useable after this is called
caches.clear();
vsCache.clear();
}
private void clear() throws IOException {
Utilities.clearDirectory(folder);
caches.clear();
vsCache.clear();
}
public boolean hasCapabilityStatement(String address) {
return capabilityStatementCache.containsKey(address);
}
public CapabilityStatement getCapabilityStatement(String address) {
return capabilityStatementCache.get(address);
}
public void cacheCapabilityStatement(String address, CapabilityStatement capabilityStatement) throws IOException {
if (noCaching) {
return;
}
this.capabilityStatementCache.put(address, capabilityStatement);
save(capabilityStatement, CAPABILITY_STATEMENT_TITLE+"."+getServerId(address));
}
public boolean hasTerminologyCapabilities(String address) {
return terminologyCapabilitiesCache.containsKey(address);
}
public TerminologyCapabilities getTerminologyCapabilities(String address) {
return terminologyCapabilitiesCache.get(address);
}
public void cacheTerminologyCapabilities(String address, TerminologyCapabilities terminologyCapabilities) throws IOException {
if (noCaching) {
return;
}
this.terminologyCapabilitiesCache.put(address, terminologyCapabilities);
save(terminologyCapabilities, TERMINOLOGY_CAPABILITIES_TITLE+"."+getServerId(address));
}
public CacheToken generateValidationToken(ValidationOptions options, Coding code, ValueSet vs, Parameters expParameters) {
try {
CacheToken ct = new CacheToken();
@ -614,14 +714,19 @@ public class TerminologyCache {
private void loadCapabilityCache(String fn) {
try {
String src = TextFile.fileToString(Utilities.path(folder, fn));
String serverId = Utilities.getFileNameForName(fn).replace(CACHE_FILE_EXTENSION, "");
serverId = serverId.substring(serverId.indexOf(".")+1);
serverId = serverId.substring(serverId.indexOf(".")+1);
String address = getServerForId(serverId);
if (address != null) {
JsonObject o = (JsonObject) new com.google.gson.JsonParser().parse(src);
Resource resource = new JsonParser().parse(o);
if (fn.startsWith(CAPABILITY_STATEMENT_TITLE)) {
this.capabilityStatementCache = (CapabilityStatement) resource;
this.capabilityStatementCache.put(address, (CapabilityStatement) resource);
} else if (fn.startsWith(TERMINOLOGY_CAPABILITIES_TITLE)) {
this.terminologyCapabilitiesCache = (TerminologyCapabilities) resource;
this.terminologyCapabilitiesCache.put(address, (TerminologyCapabilities) resource);
}
}
} catch (Exception e) {
e.printStackTrace();
@ -629,6 +734,15 @@ public class TerminologyCache {
}
}
private String getServerForId(String serverId) {
for (String n : serverMap.keySet()) {
if (serverMap.get(n).equals(serverId)) {
return n;
}
}
return null;
}
private CacheEntry getCacheEntry(String request, String resultString) throws IOException {
CacheEntry ce = new CacheEntry();
ce.persistent = true;
@ -703,7 +817,14 @@ public class TerminologyCache {
}
}
private void load() throws FHIRException {
private void load() throws FHIRException, IOException {
IniFile ini = new IniFile(Utilities.path(folder, "servers.ini"));
if (ini.hasSection("servers")) {
for (String n : ini.getPropertyNames("servers")) {
serverMap.put(ini.getStringProperty("servers", n), n);
}
}
for (String fn : new File(folder).list()) {
if (fn.endsWith(CACHE_FILE_EXTENSION) && !fn.equals("validation" + CACHE_FILE_EXTENSION)) {
try {
@ -717,6 +838,22 @@ public class TerminologyCache {
}
}
}
try {
File f = new File(Utilities.path(folder, "vs-externals.json"));
if (f.exists()) {
org.hl7.fhir.utilities.json.model.JsonObject json = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(f);
for (JsonProperty p : json.getProperties()) {
if (p.getValue().isJsonNull()) {
vsCache.put(p.getName(), null);
} else {
org.hl7.fhir.utilities.json.model.JsonObject j = p.getValue().asJsonObject();
vsCache.put(p.getName(), new SourcedValueSetEntry(j.asString("server"), j.asString("filename")));
}
}
}
} catch (Exception e) {
System.out.println("Error loading vs external cache: "+e.getMessage());
}
}
private String loadJS(JsonElement e) {
@ -819,5 +956,55 @@ public class TerminologyCache {
return servers;
}
public boolean hasValueSet(String canonical) {
return vsCache.containsKey(canonical);
}
public SourcedValueSet getValueSet(String canonical) {
SourcedValueSetEntry sp = vsCache.get(canonical);
if (sp == null) {
return null;
} else {
try {
return new SourcedValueSet(sp.getServer(), sp.getFilename() == null ? null : (ValueSet) new JsonParser().parse(new FileInputStream(Utilities.path(folder, sp.getFilename()))));
} catch (Exception e) {
return null;
}
}
}
public void cacheValueSet(String canonical, SourcedValueSet svs) {
if (canonical == null) {
return;
}
try {
if (svs == null) {
vsCache.put(canonical, null);
} else {
String uuid = Utilities.makeUuidLC();
String fn = "vs-"+uuid+".json";
new JsonParser().compose(new FileOutputStream(Utilities.path(folder, fn)), svs.getVs());
vsCache.put(canonical, new SourcedValueSetEntry(svs.getServer(), fn));
}
org.hl7.fhir.utilities.json.model.JsonObject j = new org.hl7.fhir.utilities.json.model.JsonObject();
for (String k : vsCache.keySet()) {
SourcedValueSetEntry sve = vsCache.get(k);
if (sve == null) {
j.add(k, new JsonNull());
} else {
org.hl7.fhir.utilities.json.model.JsonObject e = new org.hl7.fhir.utilities.json.model.JsonObject();
e.set("server", sve.getServer());
if (sve.getFilename() != null) {
e.set("filename", sve.getFilename());
}
j.add(k, e);
}
}
org.hl7.fhir.utilities.json.parser.JsonParser.compose(j, new File(Utilities.path(folder, "vs-externals.json")), true);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -148,6 +148,21 @@ public class ValidationResult {
return CommaSeparatedStringBuilder.join("; ", messages);
}
public String getTrimmedMessage() {
List<String> toTrim = new ArrayList<>();
for (OperationOutcomeIssueComponent iss : getIssues()) {
toTrim.add(iss.getDetails().getText());
}
List<String> trimmed = new ArrayList<>();
trimmed.addAll(messages);
trimmed.removeAll(toTrim);
if (trimmed.size() == 0) {
return null;
}
Collections.sort(trimmed);
return CommaSeparatedStringBuilder.join("; ", trimmed);
}
public boolean IsNoService() {
return errorClass == TerminologyServiceErrorClass.NOSERVICE;
}

View File

@ -57,4 +57,23 @@ public class ValidationProcessInfo {
Collections.sort(msgs);
return msgs;
}
public boolean hasMessage(String msg) {
for (OperationOutcomeIssueComponent iss : issues) {
if (msg.equals(iss.getDetails().getText())) {
return true;
}
}
return false;
}
public boolean hasNotFound(String system) {
for (OperationOutcomeIssueComponent iss : issues) {
if (iss.getDetails().hasCoding("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", "not-found") &&
iss.getDetails().hasText() && iss.getDetails().getText().contains(system)) {
return true;
}
}
return false;
}
}

View File

@ -1,5 +1,7 @@
package org.hl7.fhir.r5.terminologies.validation;
import java.io.IOException;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
@ -58,6 +60,7 @@ 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.Extension;
import org.hl7.fhir.r5.model.NamingSystem;
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r5.model.OperationOutcome.IssueType;
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
@ -75,6 +78,7 @@ 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.CodeSystemUtilities;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.providers.CodeSystemProvider;
import org.hl7.fhir.r5.terminologies.providers.SpecialCodeSystem;
@ -130,27 +134,28 @@ public class ValueSetValidator extends ValueSetProcessBase {
private ValidationContextCarrier localContext;
private List<CodeSystem> localSystems = new ArrayList<>();
protected Parameters expansionProfile;
private TerminologyCapabilities txCaps;
private TerminologyClientManager tcm;
private Set<String> unknownSystems;
private Set<String> unknownValueSets = new HashSet<>();
private boolean throwToServer;
public ValueSetValidator(IWorkerContext context, TerminologyOperationContext opContext, ValidationOptions options, ValueSet source, Parameters expansionProfile, TerminologyCapabilities txCaps) {
public ValueSetValidator(IWorkerContext context, TerminologyOperationContext opContext, ValidationOptions options, ValueSet source, Parameters expansionProfile, TerminologyClientManager tcm) {
super(context, opContext);
this.valueset = source;
this.options = options;
this.expansionProfile = expansionProfile;
this.txCaps = txCaps;
this.tcm = tcm;
analyseValueSet();
}
public ValueSetValidator(IWorkerContext context, TerminologyOperationContext opContext, ValidationOptions options, ValueSet source, ValidationContextCarrier ctxt, Parameters expansionProfile, TerminologyCapabilities txCaps) {
public ValueSetValidator(IWorkerContext context, TerminologyOperationContext opContext, ValidationOptions options, ValueSet source, ValidationContextCarrier ctxt, Parameters expansionProfile, TerminologyClientManager tcm) {
super(context, opContext);
this.valueset = source;
this.options = options.copy();
this.options.setEnglishOk(true);
this.localContext = ctxt;
this.expansionProfile = expansionProfile;
this.txCaps = txCaps;
this.tcm = tcm;
analyseValueSet();
}
@ -249,16 +254,20 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
} else {
res = context.validateCode(options.withNoClient(), c, null);
if (res.isOk()) {
vcc.addCoding(new Coding().setCode(res.getCode()).setVersion(res.getVersion()).setSystem(res.getSystem()).setDisplay(res.getDisplay()));
}
for (OperationOutcomeIssueComponent iss : res.getIssues()) {
if (iss.getSeverity() == org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR && iss.getDetails().hasCoding("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", "not-found")) {
iss.setSeverity(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.WARNING);
res.setSeverity(IssueSeverity.WARNING);
}
iss.resetPath("Coding", path+".coding["+i+"]");
}
if (res.isInactive()) {
String msg = context.formatMessage(I18nConstants.STATUS_CODE_WARNING_CODE, "not active", c.getCode());
res.getIssues().addAll(makeIssue(IssueSeverity.INFORMATION, IssueType.INVALID, path+".coding["+i+"].code", msg, OpIssueCode.CodeRule, res.getServer()));
}
if (res.isOk()) {
vcc.addCoding(new Coding().setCode(res.getCode()).setVersion(res.getVersion()).setSystem(res.getSystem()).setDisplay(res.getDisplay()));
}
}
} else {
c.setUserData("cs", cs);
@ -314,8 +323,12 @@ public class ValueSetValidator extends ValueSetProcessBase {
i++;
}
if (result == null) {
msg = context.formatMessage(I18nConstants.UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getVersionedUrl(), b.toString());
info.getIssues().addAll(makeIssue(IssueSeverity.WARNING, unknownSystems.isEmpty() ? IssueType.CODEINVALID : IssueType.NOTFOUND, cpath.size() == 1 ? cpath.get(0) : path, msg, OpIssueCode.VSProcessing, null));
if (!unknownValueSets.isEmpty()) {
msg = context.formatMessage(I18nConstants.UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_VS, valueset.getVersionedUrl(), CommaSeparatedStringBuilder.join(", ", unknownValueSets));
} else {
msg = context.formatMessage(I18nConstants.UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_CS, valueset.getVersionedUrl(), b.toString());
}
info.getIssues().addAll(makeIssue(IssueSeverity.WARNING, unknownSystems.isEmpty() && unknownValueSets.isEmpty() ? IssueType.CODEINVALID : IssueType.NOTFOUND, null, msg, OpIssueCode.VSProcessing, null));
} else if (!result) {
// to match Ontoserver
OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR, org.hl7.fhir.r5.model.OperationOutcome.IssueType.CODEINVALID);
@ -629,9 +642,18 @@ public class ValueSetValidator extends ValueSetProcessBase {
res.setErrorClass(info.getErr());
}
if (ok == null) {
String m = context.formatMessage(I18nConstants.UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getVersionedUrl(), CommaSeparatedStringBuilder.join(",", unknownSystems));
String m = null;
if (!unknownSystems.isEmpty()) {
m = context.formatMessage(I18nConstants.UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_CS, valueset.getVersionedUrl(), CommaSeparatedStringBuilder.join(",", unknownSystems));
} else if (!unknownValueSets.isEmpty()) {
res.addMessage(info.getIssues().get(0).getDetails().getText());
m = context.formatMessage(I18nConstants.UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_VS, valueset.getVersionedUrl(), CommaSeparatedStringBuilder.join(",", unknownValueSets));
} else {
// not sure why we'd get to here?
m = context.formatMessage(I18nConstants.UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getVersionedUrl());
}
res.addMessage(m);
res.getIssues().addAll(makeIssue(IssueSeverity.WARNING, IssueType.NOTFOUND, path, m, OpIssueCode.VSProcessing, null));
res.getIssues().addAll(makeIssue(IssueSeverity.WARNING, IssueType.NOTFOUND, null, m, OpIssueCode.VSProcessing, null));
res.setUnknownSystems(unknownSystems);
res.setSeverity(IssueSeverity.ERROR); // back patching for display logic issue
res.setErrorClass(TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED);
@ -698,13 +720,14 @@ public class ValueSetValidator extends ValueSetProcessBase {
return true;
}
if (txCaps != null) {
for (TerminologyCapabilitiesCodeSystemComponent tccs : txCaps.getCodeSystem()) {
if (system.equals(tccs.getUri())) {
try {
if (tcm.supportsSystem(system)) {
return true;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
@ -951,7 +974,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
}
for (CanonicalType url : inc.getValueSet()) {
ConceptReferencePair cc = getVs(url.asStringValue()).findValueSetRef(system, code);
ConceptReferencePair cc = getVs(url.asStringValue(), null).findValueSetRef(system, code);
if (cc != null) {
return cc;
}
@ -1155,7 +1178,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
private boolean checkForCodeInValueSet(String code, String uri, Set<String> sys, List<StringWithCode> problems) {
ValueSetValidator vs = getVs(uri);
ValueSetValidator vs = getVs(uri, null);
return vs.scanForCodeInValueSet(code, sys, problems);
}
@ -1177,7 +1200,7 @@ public class ValueSetValidator extends ValueSetProcessBase {
public Boolean codeInValueSet(String path, String system, String version, String code, ValidationProcessInfo info) throws FHIRException {
if (valueset == null) {
return false;
return null;
}
opContext.deadCheck();
checkCanonical(info.getIssues(), path, valueset, valueset);
@ -1226,7 +1249,11 @@ public class ValueSetValidator extends ValueSetProcessBase {
}
}
} else {
ok = inImport(path, vsi.getValueSet().get(0).getValue(), system, version, code, info);
Boolean bok = inImport(path, vsi.getValueSet().get(0).getValue(), system, version, code, info);
if (bok == null) {
return bok;
}
ok = bok;
for (int i = 1; i < vsi.getValueSet().size(); i++) {
UriType uri = vsi.getValueSet().get(i);
ok = ok && inImport(path, uri.getValue(), system, version, code, info);
@ -1263,12 +1290,15 @@ public class ValueSetValidator extends ValueSetProcessBase {
if (res.getErrorClass() == TerminologyServiceErrorClass.UNKNOWN || res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED || res.getErrorClass() == TerminologyServiceErrorClass.VALUESET_UNSUPPORTED) {
if (info != null && res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
// server didn't know the code system either - we'll take it face value
info.addIssue(makeIssue(IssueSeverity.WARNING, IssueType.UNKNOWN, path, context.formatMessage(I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, system), OpIssueCode.NotFound, null));
if (!info.hasNotFound(system)) {
String msg = context.formatMessage(I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, system);
info.addIssue(makeIssue(IssueSeverity.WARNING, IssueType.UNKNOWN, path, msg, OpIssueCode.NotFound, null));
for (ConceptReferenceComponent cc : vsi.getConcept()) {
if (cc.getCode().equals(code)) {
return true;
}
}
}
info.setErr(TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED);
return null;
}
@ -1423,24 +1453,28 @@ public class ValueSetValidator extends ValueSetProcessBase {
return false;
}
private ValueSetValidator getVs(String url) {
private ValueSetValidator getVs(String url, ValidationProcessInfo info) {
if (inner.containsKey(url)) {
return inner.get(url);
}
ValueSet vs = context.fetchResource(ValueSet.class, url, valueset);
ValueSetValidator vsc = new ValueSetValidator(context, opContext.copy(), options, vs, localContext, expansionProfile, txCaps);
ValueSet vs = context.findTxResource(ValueSet.class, url, valueset);
if (vs == null && info != null) {
unknownValueSets.add(url);
info.addIssue(makeIssue(IssueSeverity.ERROR, IssueType.NOTFOUND, null, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_VALUE_SET_, url), OpIssueCode.NotFound, null));
}
ValueSetValidator vsc = new ValueSetValidator(context, opContext.copy(), options, vs, localContext, expansionProfile, tcm);
vsc.setThrowToServer(throwToServer);
inner.put(url, vsc);
return vsc;
}
private boolean inImport(String path, String uri, String system, String version, String code, ValidationProcessInfo info) throws FHIRException {
ValueSetValidator vs = getVs(uri);
private Boolean inImport(String path, String uri, String system, String version, String code, ValidationProcessInfo info) throws FHIRException {
ValueSetValidator vs = getVs(uri, info);
if (vs == null) {
return false;
} else {
Boolean ok = vs.codeInValueSet(path, system, version, code, info);
return ok != null && ok;
return ok;
}
}

View File

@ -140,7 +140,7 @@ public class TestingUtilities extends BaseTestingUtilities {
if (!Utilities.noString(s))
return s;
s = "C:\\work\\org.hl7.fhir\\build";
// FIXME: change this back
// #TODO - what should we do with this?
s = "/Users/jamesagnew/git/fhir";
if (new File(s).exists())
return s;

View File

@ -380,11 +380,11 @@ public class NPMPackageGenerator {
TextFile.bytesToFile(OutputStream.toByteArray(), destFile);
// also, for cache management on current builds, generate a little manifest
String json = JsonParser.compose(packageManifest, true);
TextFile.stringToFile(json, Utilities.changeFileExt(destFile, ".manifest.json"), false);
TextFile.stringToFile(json, Utilities.changeFileExt(destFile, ".manifest.json"));
}
private void buildIndexJson() throws IOException {
byte[] content = TextFile.stringToBytes(indexer.build(), false);
byte[] content = TextFile.stringToBytes(indexer.build());
addFile(Category.RESOURCE, ".index.json", content);
content = TextFile.fileToBytes(indexdb);
new File(indexdb).delete();

View File

@ -268,6 +268,7 @@ public class ToolingExtensions {
public static final String EXT_SEARCH_PARAMETER_BASE = "http://hl7.org/fhir/tools/StructureDefinition/searchparameter-base-type";
public static final String EXT_ISSUE_SLICE_INFO = "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-slicetext";
public static final String EXT_ISSUE_SERVER = "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server";
public static final String EXT_WEB_SOURCE = "http://hl7.org/fhir/tools/StructureDefinition/web-source";
// specific extension helpers

View File

@ -658,7 +658,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
try {
getCapabilitiesStatementQuick();
} catch (Throwable e) {
//FIXME This is creepy. Shouldn't we report this at some level?
//#TODO This is creepy. Shouldn't we report this at some level?
}
}
return capabilities == null ? null : capabilities.getSoftware().getVersion();

View File

@ -38,7 +38,9 @@ public class FhirLoggingInterceptor implements Interceptor {
request.body().writeTo(buf);
cnt = buf.readByteArray();
}
if (logger != null) {
logger.logRequest(request.method(), request.url().toString(), hdrs, cnt);
}
// Log Response
Response response = null;
@ -56,7 +58,9 @@ public class FhirLoggingInterceptor implements Interceptor {
Map<String, List<String>> headerMap = response.headers().toMultimap();
headerMap.keySet().forEach(key -> headerMap.get(key).forEach(value -> headerList.add(key + ":" + value)));
if (logger != null) {
logger.logResponse(Integer.toString(response.code()), headerList, bodyBytes);
}
// Reading byte[] clears body. Need to recreate.
ResponseBody body = ResponseBody.create(bodyBytes, contentType);

View File

@ -97,7 +97,7 @@ public class FHIRPathHostServices implements FHIRPathEngine.IEvaluationContext {
@Override
public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) {
return structureMapUtilities.getWorker().fetchResource(ValueSet.class, url);
return structureMapUtilities.getWorker().findTxResource(ValueSet.class, url);
}
@Override

View File

@ -2119,6 +2119,7 @@ public class StructureMapUtilities {
StructureMapAnalysis result = new StructureMapAnalysis();
TransformContext context = new TransformContext(appInfo);
VariablesForProfiling vars = new VariablesForProfiling(this, false, false);
if (map.hasGroup()) {
StructureMapGroupComponent start = map.getGroup().get(0);
for (StructureMapGroupInputComponent t : start.getInput()) {
PropertyWithType ti = resolveType(map, t.getType(), t.getMode());
@ -2127,7 +2128,6 @@ public class StructureMapUtilities {
else
vars.add(VariableMode.OUTPUT, t.getName(), createProfile(map, result.profiles, ti, start.getName(), start));
}
result.summary = new XhtmlNode(NodeType.Element, "table").setAttribute("class", "grid");
XhtmlNode tr = result.summary.addTag("tr");
tr.addTag("td").addTag("b").addText("Source");
@ -2135,6 +2135,7 @@ public class StructureMapUtilities {
log("Start Profiling Transform " + map.getUrl());
analyseGroup("", context, map, vars, start, result);
}
ProfileUtilities pu = new ProfileUtilities(worker, null, pkp);
for (StructureDefinition sd : result.getProfiles())
pu.cleanUpDifferential(sd);

View File

@ -95,7 +95,7 @@ public class SimpleWorkerContextTests {
public void beforeEach() {
Mockito.doReturn(DUMMY_URL).when(terminologyClient).getAddress();
context.txCache = terminologyCache;
context.initTxCache(terminologyCache);
context.expParameters = expParameters;
context.terminologyClientManager.setMasterClient(terminologyClient);
context.txLog = txLog;
@ -427,46 +427,41 @@ public class SimpleWorkerContextTests {
@Test
public void testInitializationWithCache() {
Mockito.doReturn(true).when(terminologyCache).hasTerminologyCapabilities();
Mockito.doReturn(true).when(terminologyCache).hasCapabilityStatement();
Mockito.doReturn(terminologyCapabilities).when(terminologyCache).getTerminologyCapabilities();
Mockito.doReturn(capabilitiesStatement).when(terminologyCache).getCapabilityStatement();
String actual = context.connectToTSServer(new TerminologyClientR5Factory(), terminologyClient, null);
assertEquals("dummyVersion", actual);
Mockito.verify(terminologyCache).getTerminologyCapabilities();
Mockito.verify(terminologyCache).getCapabilityStatement();
Mockito.verify(terminologyClient, times(0)).getTerminologyCapabilities();
Mockito.verify(terminologyClient, times(0)).getCapabilitiesStatementQuick();
Mockito.verify(context).setTxCaps(terminologyCapabilities);
// String address = "/...";
//
// Mockito.doReturn(true).when(terminologyCache).hasTerminologyCapabilities(address);
//// Mockito.doReturn(true).when(terminologyCache).hasCapabilityStatement();
//
// Mockito.doReturn(terminologyCapabilities).when(terminologyCache).getTerminologyCapabilities(address);
//// Mockito.doReturn(capabilitiesStatement).when(terminologyCache).getCapabilityStatement();
//
// context.connectToTSServer(new TerminologyClientR5Factory(), terminologyClient);
//
// Mockito.verify(terminologyCache).getTerminologyCapabilities(address);
// Mockito.verify(terminologyClient).getCapabilitiesStatementQuick();
//
// Mockito.verify(terminologyCache, times(0)).getCapabilityStatement(address);
// Mockito.verify(terminologyClient, times(0)).getTerminologyCapabilities();
}
@Test
public void testInitializationWithClient() {
// String address = "/...";
//
// Mockito.doReturn(false).when(terminologyCache).hasTerminologyCapabilities(address);
//// Mockito.doReturn(false).when(terminologyCache).hasCapabilityStatement();
//
// Mockito.doReturn(terminologyCapabilities).when(terminologyClient).getTerminologyCapabilities();
// Mockito.doReturn(capabilitiesStatement).when(terminologyClient).getCapabilitiesStatementQuick();
//
// context.connectToTSServer(new TerminologyClientR5Factory(), terminologyClient);
//
// Mockito.verify(terminologyCache, times(0)).getTerminologyCapabilities(address);
// Mockito.verify(terminologyCache, times(0)).getCapabilityStatement(address);
//
// Mockito.verify(terminologyClient).getTerminologyCapabilities();
// Mockito.verify(terminologyClient).getCapabilitiesStatementQuick();
Mockito.doReturn(false).when(terminologyCache).hasTerminologyCapabilities();
Mockito.doReturn(false).when(terminologyCache).hasCapabilityStatement();
Mockito.doReturn(terminologyCapabilities).when(terminologyClient).getTerminologyCapabilities();
Mockito.doReturn(capabilitiesStatement).when(terminologyClient).getCapabilitiesStatementQuick();
String actual = context.connectToTSServer(new TerminologyClientR5Factory(), terminologyClient, null);
assertEquals("dummyVersion", actual);
Mockito.verify(terminologyCache, times(0)).getTerminologyCapabilities();
Mockito.verify(terminologyCache, times(0)).getCapabilityStatement();
Mockito.verify(terminologyClient).getTerminologyCapabilities();
Mockito.verify(terminologyClient).getCapabilitiesStatementQuick();
Mockito.verify(context).setTxCaps(terminologyCapabilities);
}
public static Stream<Arguments> zipSlipData() {

View File

@ -31,6 +31,7 @@ import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache;
import org.hl7.fhir.r5.terminologies.utilities.ValidationResult;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.tests.ResourceLoaderTests;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.junit.jupiter.api.Test;
@ -92,6 +93,8 @@ public class TerminologyCacheTests implements ResourceLoaderTests {
@Test
public void testCachePersistence() throws IOException, URISyntaxException {
String address = "/...";
Object lock = new Object();
Path tempCacheDirectory = createTempCacheDirectory();
ValueSet valueSet = new ValueSet();
@ -117,8 +120,8 @@ public class TerminologyCacheTests implements ResourceLoaderTests {
// Add dummy results to the cache
TerminologyCache terminologyCacheA = new TerminologyCache(lock, tempCacheDirectory.toString());
terminologyCacheA.cacheTerminologyCapabilities(terminologyCapabilities);
terminologyCacheA.cacheCapabilityStatement(capabilityStatement);
terminologyCacheA.cacheTerminologyCapabilities(address, terminologyCapabilities);
terminologyCacheA.cacheCapabilityStatement(address, capabilityStatement);
ValidationResult codingResultA = new ValidationResult(ValidationMessage.IssueSeverity.INFORMATION, "dummyInfo", null);
TerminologyCache.CacheToken codingTokenA = terminologyCacheA.generateValidationToken(CacheTestUtils.validationOptions,
@ -136,8 +139,8 @@ public class TerminologyCacheTests implements ResourceLoaderTests {
terminologyCacheA.cacheExpansion(expansionTokenA, expansionOutcomeA, true);
// Check that the in-memory cache is returning what we put in
{
assertEquals(terminologyCapabilities, terminologyCacheA.getTerminologyCapabilities());
assertEquals(capabilityStatement, terminologyCacheA.getCapabilityStatement());
assertEquals(terminologyCapabilities, terminologyCacheA.getTerminologyCapabilities(address));
assertEquals(capabilityStatement, terminologyCacheA.getCapabilityStatement(address));
assertValidationResultEquals(codingResultA, terminologyCacheA.getValidation(codingTokenA));
assertValidationResultEquals(codeableConceptResultA, terminologyCacheA.getValidation(codeableConceptTokenA));
@ -148,8 +151,8 @@ public class TerminologyCacheTests implements ResourceLoaderTests {
{
TerminologyCache terminologyCacheB = new TerminologyCache(lock, tempCacheDirectory.toString());
assertCanonicalResourceEquals(terminologyCapabilities, terminologyCacheB.getTerminologyCapabilities());
assertCanonicalResourceEquals(capabilityStatement, terminologyCacheB.getCapabilityStatement());
assertCanonicalResourceEquals(terminologyCapabilities, terminologyCacheB.getTerminologyCapabilities(address));
assertCanonicalResourceEquals(capabilityStatement, terminologyCacheB.getCapabilityStatement(address));
assertValidationResultEquals(codingResultA, terminologyCacheB.getValidation(terminologyCacheA.generateValidationToken(CacheTestUtils.validationOptions, coding, valueSet, new Parameters())));
assertValidationResultEquals(codeableConceptResultA, terminologyCacheB.getValidation(terminologyCacheA.generateValidationToken(CacheTestUtils.validationOptions, concept, valueSet, new Parameters())));

View File

@ -0,0 +1,6 @@
{
"http://somewhere/something-else" : null,
"http://loinc.org/vs/LL715-4" : null,
"http://hl7.org/fhir/us/vrdr/ValueSet/vrdr-PlaceOfDeath" : null,
"http://somewhere/something" : null
}

View File

@ -43,8 +43,11 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
@ -59,230 +62,141 @@ public class TextFile {
public static List<String> readAllLines(String path) throws IOException
{
List<String> result = new ArrayList<String>();
File file = new CSFile(path);
FileInputStream fs = new FileInputStream(file);
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(fs,"UTF-8"));
while( reader.ready() )
result.add(reader.readLine());
reader.close();
return result;
} finally {
fs.close();
}
final File file = new CSFile(path);
return Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
}
public static void writeAllLines(String path, List<String> lines) throws IOException
{
File file = new CSFile(path);
FileOutputStream s = new FileOutputStream(file);
OutputStreamWriter sw = new OutputStreamWriter(s, "UTF-8");
for( String line : lines )
sw.write(line + "\r\n");
sw.flush();
s.close();
final File file = new CSFile(path);
Files.write(file.toPath(), lines, StandardCharsets.UTF_8);
}
public static void stringToFile(String content, File file) throws IOException {
FileOutputStream fs = new FileOutputStream(file);
try {
OutputStreamWriter sw = new OutputStreamWriter(fs, "UTF-8");
sw.write('\ufeff'); // Unicode BOM, translates to UTF-8 with the configured outputstreamwriter
sw.write(content);
sw.flush();
sw.close();
} finally {
fs.close();
}
public static void stringToStream(final String content, final OutputStream stream) throws IOException {
stream.write(content.getBytes(StandardCharsets.UTF_8));
}
public static void stringToStream(String content, OutputStream stream, boolean bom) throws IOException {
OutputStreamWriter sw = new OutputStreamWriter(stream, "UTF-8");
if (bom) {
sw.write('\ufeff'); // Unicode BOM, translates to UTF-8 with the configured outputstreamwriter
}
sw.write(content);
sw.flush();
sw.close();
public static byte[] stringToBytes(final String content) throws IOException {
return content.getBytes(StandardCharsets.UTF_8);
}
public static byte[] stringToBytes(String content, boolean bom) throws IOException {
ByteArrayOutputStream bs = new ByteArrayOutputStream();
OutputStreamWriter sw = new OutputStreamWriter(bs, "UTF-8");
if (bom)
sw.write('\ufeff'); // Unicode BOM, translates to UTF-8 with the configured outputstreamwriter
sw.write(content);
sw.flush();
sw.close();
return bs.toByteArray();
}
public static void stringToFile(String content, String path) throws IOException {
File file = new CSFile(path);
public static void stringToFile(final String content, final String path) throws IOException {
final File file = new CSFile(path);
stringToFile(content, file);
}
public static void stringToFile(String content, File file, boolean bom) throws IOException {
FileOutputStream fs = new FileOutputStream(file);
OutputStreamWriter sw = new OutputStreamWriter(fs, "UTF-8");
if (bom)
sw.write('\ufeff'); // Unicode BOM, translates to UTF-8 with the configured outputstreamwriter
sw.write(content);
sw.flush();
sw.close();
}
public static void stringToFile(String content, String path, boolean bom) throws IOException {
File file = new CSFile(path);
stringToFile(content, file, bom);
}
public static void stringToFileNoPrefix(String content, String path) throws IOException {
File file = new CSFile(path);
OutputStreamWriter sw = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
sw.write(content);
sw.flush();
sw.close();
}
public static String fileToString(File f) throws FileNotFoundException, IOException {
FileInputStream fs = new FileInputStream(f);
try {
return streamToString(fs);
} finally {
fs.close();
public static void stringToFile(final String content, final File file) throws IOException {
try (final OutputStream output = Files.newOutputStream(file.toPath())) {
output.write(content.getBytes(StandardCharsets.UTF_8));
}
}
public static String fileToString(String src) throws FileNotFoundException, IOException {
CSFile f = new CSFile(src);
public static void stringToFileWithBOM(final String content, final File file) throws IOException {
try (final OutputStream output = Files.newOutputStream(file.toPath())) {
output.write(new byte[]{(byte)239, (byte)187, (byte)191});
output.write(content.getBytes(StandardCharsets.UTF_8));
}
}
public static void stringToFileWithBOM(final String content, final String path) throws IOException {
final File file = new CSFile(path);
stringToFileWithBOM(content, file);
}
public static String fileToString(final File f) throws FileNotFoundException, IOException {
// Files.readString(Path) will fail on invalid UTF-8 byte sequences, so we use Files.readAllBytes() instead.
// This would happen when reading an XSLX file, for example.
return new String(Files.readAllBytes(f.toPath()), StandardCharsets.UTF_8);
}
public static String fileToString(final String src) throws FileNotFoundException, IOException {
final CSFile f = new CSFile(src);
if (!f.exists()) {
throw new IOException("File "+src+" not found");
}
FileInputStream fs = new FileInputStream(f);
try {
return streamToString(fs);
} finally {
fs.close();
}
return fileToString(f);
}
public static String streamToString(InputStream input) throws IOException {
InputStreamReader sr = new InputStreamReader(input, "UTF-8");
StringBuilder b = new StringBuilder();
//while (sr.ready()) { Commented out by Claude Nanjo (1/14/2014) - sr.ready() always returns false - please remove if change does not impact other areas of codebase
int i = -1;
while((i = sr.read()) > -1) {
String s = Character.toString(i);
b.append(s);
}
sr.close();
return b.toString().replace("\uFEFF", "");
public static String streamToString(final InputStream input) throws IOException {
return new String(input.readAllBytes(), StandardCharsets.UTF_8).replace("\uFEFF", "");
}
public static byte[] streamToBytes(InputStream input) throws IOException {
public static byte[] streamToBytes(final InputStream input) throws IOException {
if (input == null) {
return null;
}
ByteArrayOutputStream r = new ByteArrayOutputStream(2048);
byte[] read = new byte[512];
for (int i; -1 != (i = input.read(read)); r.write(read, 0, i));
final byte[] read = input.readAllBytes();
input.close();
return r.toByteArray();
return read;
}
public static byte[] streamToBytesNoClose(InputStream input) throws IOException {
public static byte[] streamToBytesNoClose(final InputStream input) throws IOException {
if (input == null) {
return null;
}
ByteArrayOutputStream r = new ByteArrayOutputStream(2048);
byte[] read = new byte[512];
for (int i; -1 != (i = input.read(read)); r.write(read, 0, i));
return r.toByteArray();
return input.readAllBytes();
}
public static void bytesToFile(byte[] bytes, String path) throws IOException {
File file = new CSFile(path);
OutputStream sw = new FileOutputStream(file);
public static void bytesToFile(final byte[] bytes, final String path) throws IOException {
try (final OutputStream sw = new FileOutputStream(new CSFile(path))) {
sw.write(bytes);
sw.flush();
sw.close();
}
}
public static void bytesToFile(byte[] bytes, File f) throws IOException {
OutputStream sw = new FileOutputStream(f);
public static void bytesToFile(final byte[] bytes, final File f) throws IOException {
try (final OutputStream sw = new FileOutputStream(f)) {
sw.write(bytes);
sw.flush();
sw.close();
}
}
public static void appendBytesToFile(byte[] bytes, String path) throws IOException {
public static void appendBytesToFile(final byte[] bytes, final String path) throws IOException {
byte[] linebreak = new byte[] {13, 10};
Files.write(Paths.get(path), linebreak, StandardOpenOption.APPEND);
Files.write(Paths.get(path), bytes, StandardOpenOption.APPEND);
}
public static byte[] fileToBytes(String srcFile) throws FileNotFoundException, IOException {
FileInputStream fs = new FileInputStream(new CSFile(srcFile));
try {
return streamToBytes(fs);
} finally {
fs.close();
}
public static byte[] fileToBytes(final String srcFile) throws FileNotFoundException, IOException {
final File f = new CSFile(srcFile);
return Files.readAllBytes(f.toPath());
}
/**
*
* fileToBytes insists in case correctness to ensure that stuff works across linux and windows, but it's not always appropriate to ceheck case (e.g. validator parameters)
* fileToBytes insists in case correctness to ensure that stuff works across linux and windows, but it's not always appropriate to check case (e.g. validator parameters)
*
* @param srcFile
* @return
* @throws FileNotFoundException
* @throws IOException
*/
public static byte[] fileToBytesNCS(String srcFile) throws FileNotFoundException, IOException {
FileInputStream fs = new FileInputStream(new File(srcFile));
try {
return streamToBytes(fs);
} finally {
fs.close();
}
public static byte[] fileToBytesNCS(final String srcFile) throws FileNotFoundException, IOException {
return Files.readAllBytes(Path.of(srcFile));
}
public static byte[] fileToBytes(File file) throws FileNotFoundException, IOException {
FileInputStream fs = new FileInputStream(file);
try {
return streamToBytes(fs);
} finally {
fs.close();
}
public static byte[] fileToBytes(final File file) throws FileNotFoundException, IOException {
return Files.readAllBytes(file.toPath());
}
public static String bytesToString(byte[] bs) throws IOException {
return streamToString(new ByteArrayInputStream(bs));
public static String bytesToString(final byte[] bs) throws IOException {
return new String(bs, StandardCharsets.UTF_8);
}
public static String bytesToString(byte[] bs, boolean removeBOM) throws IOException {
public static String bytesToString(final byte[] bs, final boolean removeBOM) throws IOException {
final String read = new String(bs, StandardCharsets.UTF_8);
if (removeBOM)
return streamToString(new ByteArrayInputStream(bs)).replace("\uFEFF", "");
return read.replace("\uFEFF", "");
else
return streamToString(new ByteArrayInputStream(bs));
return read;
}
public static void streamToFile(InputStream stream, String filename) throws IOException {
byte[] cnt = streamToBytes(stream);
bytesToFile(cnt, filename);
public static void streamToFile(final InputStream stream, final String filename) throws IOException {
Files.copy(stream, Path.of(filename), StandardCopyOption.REPLACE_EXISTING);
stream.close();
}
public static void streamToFileNoClose(InputStream stream, String filename) throws IOException {
byte[] cnt = streamToBytesNoClose(stream);
bytesToFile(cnt, filename);
public static void streamToFileNoClose(final InputStream stream, final String filename) throws IOException {
Files.copy(stream, Path.of(filename), StandardCopyOption.REPLACE_EXISTING);
}
}

View File

@ -1,11 +1,13 @@
package org.hl7.fhir.utilities;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.StringJoiner;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.VersionUtilities.SemVer;
/*
Copyright (c) 2011+, HL7, Inc.
@ -40,6 +42,71 @@ import org.hl7.fhir.exceptions.FHIRException;
public class VersionUtilities {
public static class SemVerSorter implements Comparator<String> {
@Override
public int compare(String s1, String s2) {
return compareVersions(s1, s2);
}
}
public static class SemVer {
private String major;
private String minor;
private String patch;
private String label;
public SemVer(String ver) {
String[] p = ver.split("\\.");
if (p.length > 0) {
major = p[0];
}
if (p.length > 1) {
minor = p[1];
}
if (p.length > 2) {
patch = p[2];
if (patch.contains("-")) {
label = patch.substring(patch.indexOf("-")+1);
patch = patch.substring(0, patch.indexOf("-"));
}
}
}
private int compareString(String s1, String s2) {
if (s1 == null) {
return s2 == null ? 0 : 1;
} else {
return s1.compareTo(s2);
}
}
private int compareInteger(String s1, String s2) {
if (s1 == null) {
return s2 == null ? 0 : 1;
} else {
return Integer.compare(Integer.parseInt(s1), Integer.parseInt(s2));
}
}
public int compareTo(SemVer sv2) {
int c = compareInteger(major, sv2.major);
if (c == 0) {
c = compareInteger(minor, sv2.minor);
}
if (c == 0) {
c = compareInteger(patch, sv2.patch);
}
if (c == 0) {
c = compareString(label, sv2.label);
}
return c;
}
}
public static final String[] SUPPORTED_MAJOR_VERSIONS = {"1.0", "1.4", "3.0", "4.0", "5.0", "6.0"};
public static final String[] SUPPORTED_VERSIONS = {"1.0.2", "1.4.0", "3.0.2", "4.0.1", "4.1.0", "4.3.0", "5.0.0", "6.0.0"};
@ -650,5 +717,17 @@ public class VersionUtilities {
return version != null && version.startsWith("6.");
}
public static int compareVersions(String ver1, String ver2) {
if (ver1 == null) {
return ver2 == null ? 0 : 1;
} else if (isSemVer(ver1) && isSemVer(ver2)) {
SemVer sv1 = new SemVer(ver1);
SemVer sv2 = new SemVer(ver2);
return sv1.compareTo(sv2);
} else {
return ver1.compareTo(ver2);
}
}
}

View File

@ -266,6 +266,8 @@ public class I18nConstants {
public static final String NODE_TYPE__IS_NOT_ALLOWED = "Node_type__is_not_allowed";
public static final String NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_ = "None_of_the_provided_codes_are_in_the_value_set";
public static final String NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_ONE = "None_of_the_provided_codes_are_in_the_value_set_one";
public static final String UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_CS = "UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_CS";
public static final String UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_VS = "UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_VS";
public static final String UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_ = "UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_";
public static final String NOT_DONE_YET = "Not_done_yet";
public static final String NOT_DONE_YET_CANT_FETCH_ = "not_done_yet_cant_fetch_";

View File

@ -191,7 +191,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
File packagesIniFile = new File(packagesIniPath);
if (!(packagesIniFile.exists()))
packagesIniFile.createNewFile();
TextFile.stringToFile("[cache]\r\nversion=" + CACHE_VERSION + "\r\n\r\n[urls]\r\n\r\n[local]\r\n\r\n", packagesIniPath, false);
TextFile.stringToFile("[cache]\r\nversion=" + CACHE_VERSION + "\r\n\r\n[urls]\r\n\r\n[local]\r\n\r\n", packagesIniPath);
createIniFile();
for (File f : cacheFolder.listFiles()) {
if (f.isDirectory() && Utilities.isValidUUID(f.getName())) {
@ -541,7 +541,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
npm.getNpm().remove("version");
npm.getNpm().add("version", v);
}
TextFile.stringToFile(JsonParser.compose(npm.getNpm(), true), Utilities.path(cacheFolder, id + "#" + v, "package", "package.json"), false);
TextFile.stringToFile(JsonParser.compose(npm.getNpm(), true), Utilities.path(cacheFolder, id + "#" + v, "package", "package.json"));
}
pck = loadPackageInfo(packRoot);
} catch (Exception e) {

View File

@ -1125,7 +1125,7 @@ public class NpmPackage {
byte[] cnt = indexer.build().getBytes(StandardCharsets.UTF_8);
TextFile.bytesToFile(cnt, Utilities.path(dir.getAbsolutePath(), n, ".index.json"));
}
byte[] cnt = TextFile.stringToBytes(JsonParser.compose(npm, true), false);
byte[] cnt = TextFile.stringToBytes(JsonParser.compose(npm, true));
TextFile.bytesToFile(cnt, Utilities.path(dir.getAbsolutePath(), "package", "package.json"));
}
@ -1185,7 +1185,7 @@ public class NpmPackage {
tar.closeArchiveEntry();
}
}
byte[] cnt = TextFile.stringToBytes(JsonParser.compose(npm, true), false);
byte[] cnt = TextFile.stringToBytes(JsonParser.compose(npm, true));
TarArchiveEntry entry = new TarArchiveEntry("package/package.json");
entry.setSize(cnt.length);
tar.putArchiveEntry(entry);

View File

@ -23,10 +23,12 @@ public class CacheVerificationLogger implements ToolingClientLogger {
System.err.println("Header: " + header);
}
}
if (body != null) {
System.err.println("Body");
System.err.println("----");
System.err.println(new String(body, StandardCharsets.UTF_8));
}
}
requests++;
}

View File

@ -141,6 +141,7 @@ public class HierarchicalTableGenerator extends TranslatingUtilities {
private String text;
private String hint;
private String style;
private String tagImg;
private Map<String, String> attributes;
private XhtmlNodeList children;
@ -231,6 +232,16 @@ public class HierarchicalTableGenerator extends TranslatingUtilities {
attributes.put(name, value);
return this;
}
public String getTagImg() {
return tagImg;
}
public Piece setTagImg(String tagImg) {
this.tagImg = tagImg;
return this;
}
}
public class Cell {
@ -671,7 +682,7 @@ public class HierarchicalTableGenerator extends TranslatingUtilities {
model.setAlternating(true);
if (mode == TableGenerationMode.XML) {
model.setDocoImg(help16AsData()); // #FIXME
model.setDocoImg(help16AsData());
} else {
model.setDocoImg(Utilities.pathURL(prefix, "help16.png"));
}
@ -879,6 +890,14 @@ public class HierarchicalTableGenerator extends TranslatingUtilities {
a.addChildren(p.getChildren());
}
addStyle(a, p);
if (p.getTagImg() != null) {
a.tx(" ");
a.img(p.getTagImg(), null);
}
if (p.hasChildren()) {
tc.getChildNodes().addAll(p.getChildren());
}
} else {
if (!Utilities.noString(p.getHint())) {
XhtmlNode s = addStyle(tc.addTag("span"), p);
@ -893,6 +912,10 @@ public class HierarchicalTableGenerator extends TranslatingUtilities {
if (p.hasChildren()) {
tc.getChildNodes().addAll(p.getChildren());
}
if (p.getTagImg() != null) {
tc.tx(" ");
tc.img(p.getTagImg(), null);
}
}
}
if (makeTargets && !Utilities.noString(anchor))

View File

@ -181,8 +181,12 @@ public abstract class XhtmlFluent {
}
public XhtmlNode img(String src, String alt) {
if (alt == null) {
return addTag("img").attribute("src", src);
} else {
return addTag("img").attribute("src", src).attribute("alt", alt);
}
}
public XhtmlNode img(String src, String alt, String title) {
return addTag("img").attribute("src", src).attribute("alt", alt).attribute("title", title);
@ -276,6 +280,18 @@ public abstract class XhtmlFluent {
}
}
// differs from tx because it returns the owner node, not the created text
public XhtmlFluent txN(String cnt) {
addText(cnt);
return this;
}
public XhtmlFluent iff(boolean test) {
if (test) {
return this;
} else {
return new XhtmlNode(NodeType.Element, "span"); // which will never be connected
}
}
}

View File

@ -783,11 +783,6 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
return setAttribute("colspan", Integer.toString(n));
}
// differs from tx because it returns the owner node, not the created text
public XhtmlNode txN(String cnt) {
addText(cnt);
return this;
}
@Override
@ -939,4 +934,30 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml {
return x;
}
// differs from tx because it returns the owner node, not the created text
public XhtmlNode txN(String cnt) {
addText(cnt);
return this;
}
public XhtmlNode iff(boolean test) {
if (test) {
return this;
} else {
return new XhtmlNode(NodeType.Element, "span"); // which will never be connected
}
}
public XhtmlNode button(String class_, String title) {
XhtmlNode btn = addTag("button");
btn.attribute("class", class_);
if (title != null) {
btn.attribute("title", title);
}
return btn;
}
}

View File

@ -126,7 +126,7 @@ public class XLSXmlNormaliser {
String s = TextFile.fileToString(dest);
s = s.replaceAll("\r\n","\n");
s = replaceSignificantEoln(s);
TextFile.stringToFile(s, dest, false);
TextFile.stringToFile(s, dest);
new File(dest).setLastModified(time);
} catch (Exception e) {
System.out.println("The file "+dest+" is still open in Excel, and you will have to run the build after closing Excel before committing");

View File

@ -192,8 +192,8 @@ Terminology_TX_System_Relative = Coding.system must be an absolute reference, no
Terminology_TX_System_Unknown = Unknown Code System ''{0}''
Terminology_TX_System_ValueSet = Invalid System URI: {0} - cannot use a value set URI as a system
Terminology_TX_System_ValueSet2 = The Coding references a value set, not a code system (''{0}'')
Terminology_TX_ValueSet_NotFound = ValueSet {0} not found
Terminology_TX_ValueSet_NotFound_CS = Found a reference to a CodeSystem ({0}) where a ValueSet belongs
Terminology_TX_ValueSet_NotFound = ValueSet ''{0}'' not found
Terminology_TX_ValueSet_NotFound_CS = Found a reference to a CodeSystem ''{0}'' where a ValueSet belongs
Type_Specific_Checks_DT_Base64_Valid = The value ''{0}'' is not a valid Base64 value
Type_Specific_Checks_DT_Boolean_Value = Boolean values must be ''true'' or ''false''
Type_Specific_Checks_DT_Code_WS = The code ''{0}'' is not valid (whitespace rules)
@ -453,12 +453,12 @@ no_url_in_expand_value_set = No url in expand value set
no_value_set = ValueSet has no url property
No_Parameters_provided_to_expandVS = No Parameters provided to expandVS
No_Expansion_Parameters_provided = No Expansion Parameters provided
Unable_to_resolve_value_Set_ = Unable to resolve value Set {0}
Unable_to_resolve_value_Set_ = A definition for the value Set ''{0}'' could not be found
Delimited_versions_have_exact_match_for_delimiter____vs_ = Delimited versions have exact match for delimiter ''{0}'' : {1} vs {2}
Duplicate_Resource_ = Duplicate Resource {0} of type {3} (existing version {2}, new version {1})
DUPLICATE_RESOURCE_VERSION = Duplicate Resource {0} Version {1} of type {2}
Error_expanding_ValueSet_running_without_terminology_services = Error expanding ValueSet: running without terminology services
Error_validating_code_running_without_terminology_services = Error validating code: running without terminology services
Error_validating_code_running_without_terminology_services = Unable to validate code ''{0}'' in system ''{1}'' because the validator is running without terminology services
Unable_to_validate_code_without_using_server = Unable to validate code without using server because: {0}
UNABLE_TO_VALIDATE_LOCALLY = Unable to validate code locally: {0}
Profile___Error_generating_snapshot = Profile {0} ({1}). Error generating snapshot
@ -471,10 +471,10 @@ Version_mismatch_The_context_has_version__loaded_and_the_new_content_being_loade
Error_reading__from_package__ = Error reading {0} from package {1}#{2}: {3}
Error_parsing_ = Error parsing {0}:{1}
Unable_to_connect_to_terminology_server_Use_parameter_tx_na_tun_run_without_using_terminology_services_to_validate_LOINC_SNOMED_ICDX_etc_Error__ = Unable to connect to terminology server at {1}. Use parameter ''-tx n/a'' to run without using terminology services to validate LOINC, SNOMED, ICD-X etc. Error = {0}
Display_Name_for__should_be_one_of__instead_of_one = Wrong Display Name ''{4}'' for {1}#{2} - should be {3} (for the language(s) ''{5}'')
Display_Name_for__should_be_one_of__instead_of_other = Wrong Display Name ''{4}'' for {1}#{2} - should be one of {0} choices: {3} (for the language(s) ''{5}'')
Display_Name_WS_for__should_be_one_of__instead_of_one = Wrong whitespace in Display Name ''{4}'' for {1}#{2} - should be {3} (for the language(s) ''{5}'')
Display_Name_WS_for__should_be_one_of__instead_of_other = Wrong whitespace in Display Name ''{4}'' for {1}#{2} - should be one of {0} choices: {3} (for the language(s) ''{5}'')
Display_Name_for__should_be_one_of__instead_of_one = Wrong Display Name ''{4}'' for {1}#{2}. Valid display is {3} (for the language(s) ''{5}'')
Display_Name_for__should_be_one_of__instead_of_other = Wrong Display Name ''{4}'' for {1}#{2}. Valid display is one of {0} choices: {3} (for the language(s) ''{5}'')
Display_Name_WS_for__should_be_one_of__instead_of_one = Wrong whitespace in Display Name ''{4}'' for {1}#{2}. Valid display is {3} (for the language(s) ''{5}'')
Display_Name_WS_for__should_be_one_of__instead_of_other = Wrong whitespace in Display Name ''{4}'' for {1}#{2}. Valid display is one of {0} choices: {3} (for the language(s) ''{5}'')
Unknown_Code_in = Unknown code ''{0}'' in the CodeSystem ''{1}''
Unknown_Code_in_Version = Unknown code ''{0}'' in the CodeSystem ''{1}'' version ''{2}''
UNKNOWN_CODE_IN_FRAGMENT = Unknown Code ''{0}'' in the system ''{1}'' version ''{2}'' - note that the code system is labeled as a fragment, so the code may be valid in some other fragment
@ -684,7 +684,9 @@ RENDER_BUNDLE_DOCUMENT_CONTENT = Additional Document Content
RENDER_BUNDLE_HEADER_DOC_ENTRY_URD = {0}. {1} ({2}/{3})
RENDER_BUNDLE_HEADER_DOC_ENTRY_U = {0}. {1}
RENDER_BUNDLE_HEADER_DOC_ENTRY_RD = {0}. {2}/{3}
UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_ = Unable to check whether the code is in the value set {0} because the code system {1} was not found
UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_CS = Unable to check whether the code is in the value set ''{0}'' because the code system {1} was not found
UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_VS = Unable to check whether the code is in the value set ''{0}'' because the value set {1} was not found
UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET = Unable to check whether the code is in the value set ''{0}''
TERMINOLOGY_TX_SYSTEM_WRONG_HTML = The code system reference {0} is wrong - the code system reference cannot be to an HTML page. This may be the correct reference: {1}
TERMINOLOGY_TX_SYSTEM_WRONG_BUILD = The code system reference {0} is wrong - the code system reference cannot be a reference to build.fhir.org. This may be the correct reference: {1}
FHIRPATH_BAD_DATE = Unable to parse Date {0}

View File

@ -0,0 +1,129 @@
package org.hl7.fhir.utilities;
import org.junit.jupiter.api.*;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* Test bench for {@link TextFile}.
*
* @author Quentin Ligier
**/
class TextFileTest {
private static final String SAMPLE_CONTENT = "Line 1\nLine 2\nLine 3";
private static final List<String> SAMPLE_CONTENT_LINES = List.of("Line 1", "Line 2", "Line 3");
private static final String BOM = "\uFEFF";
private static File readFile;
private final static List<File> createdFiles = new ArrayList<>(4);
@BeforeAll
static void setUp() throws IOException {
readFile = createTempFile();
readFile.deleteOnExit();
Files.writeString(readFile.toPath(), SAMPLE_CONTENT);
}
@AfterAll
static void tearDown() throws IOException {
for (final var file : createdFiles) {
Files.deleteIfExists(file.toPath());
}
}
@Test
void testReadAllLines() throws IOException {
final var readLines = TextFile.readAllLines(readFile.getAbsolutePath());
assertEquals(3, readLines.size());
assertEquals(SAMPLE_CONTENT_LINES, readLines);
}
@Test
void testBytesToString1() throws IOException {
final var converted = TextFile.bytesToString(SAMPLE_CONTENT.getBytes(StandardCharsets.UTF_8));
assertEquals(SAMPLE_CONTENT, converted);
}
@Test
void testBytesToString2() throws IOException {
final var bytesWithoutBom = SAMPLE_CONTENT.getBytes(StandardCharsets.UTF_8);
final var bomBytes = BOM.getBytes(StandardCharsets.UTF_8);
final var bytesWithBom = Arrays.copyOf(bomBytes, bomBytes.length + bytesWithoutBom.length);
System.arraycopy(bytesWithoutBom, 0, bytesWithBom, bomBytes.length, bytesWithoutBom.length);
var converted = TextFile.bytesToString(bytesWithoutBom, true);
assertEquals(SAMPLE_CONTENT, converted);
converted = TextFile.bytesToString(bytesWithoutBom, false);
assertEquals(SAMPLE_CONTENT, converted);
converted = TextFile.bytesToString(bytesWithBom, true);
assertEquals(SAMPLE_CONTENT, converted);
converted = TextFile.bytesToString(bytesWithBom, false);
assertEquals(BOM + SAMPLE_CONTENT, converted);
}
@Test
void testFileToString1() throws IOException {
final var read = TextFile.fileToString(readFile);
assertEquals(SAMPLE_CONTENT, read);
}
@Test
void testFileToString2() throws IOException {
final var read = TextFile.fileToString(readFile.getAbsolutePath());
assertEquals(SAMPLE_CONTENT, read);
}
@Test
void testFileToBytes1() throws IOException {
final var read = TextFile.fileToBytes(readFile);
assertArrayEquals(SAMPLE_CONTENT.getBytes(StandardCharsets.UTF_8), read);
}
@Test
void testFileToBytesNCS() throws IOException {
final var read = TextFile.fileToBytesNCS(readFile.getAbsolutePath());
assertArrayEquals(SAMPLE_CONTENT.getBytes(StandardCharsets.UTF_8), read);
}
@Test
void testFileToBytes2() throws IOException {
final var read = TextFile.fileToBytes(readFile.getAbsolutePath());
assertArrayEquals(SAMPLE_CONTENT.getBytes(StandardCharsets.UTF_8), read);
}
@Test
void testStringToFile() throws IOException {
final var writeFile = createTempFile();
TextFile.stringToFileWithBOM(SAMPLE_CONTENT, writeFile);
assertEquals(BOM + SAMPLE_CONTENT, Files.readString(writeFile.toPath()));
TextFile.stringToFile(SAMPLE_CONTENT, writeFile);
assertEquals(SAMPLE_CONTENT, Files.readString(writeFile.toPath()));
}
@Test
void testWriteAllLines() throws IOException {
final var writeFile = createTempFile();
TextFile.writeAllLines(writeFile.getAbsolutePath(), SAMPLE_CONTENT_LINES);
assertEquals(SAMPLE_CONTENT_LINES, Files.readAllLines(writeFile.toPath()));
}
private static File createTempFile() throws IOException {
final var file = Files.createTempFile("test_fhir_utilities_", ".txt").toFile();
file.deleteOnExit();
createdFiles.add(file);
return file;
}
}

View File

@ -978,11 +978,11 @@ public class BaseValidator implements IValidationContextResourceLoader {
return null;
} else {
long t = System.nanoTime();
ValueSet fr = context.fetchResource(ValueSet.class, reference, src);
ValueSet fr = context.findTxResource(ValueSet.class, reference, src);
if (fr == null) {
if (!Utilities.isAbsoluteUrl(reference)) {
reference = resolve(uri, reference);
fr = context.fetchResource(ValueSet.class, reference, src);
fr = context.findTxResource(ValueSet.class, reference, src);
}
}
if (fr == null) {

View File

@ -554,7 +554,7 @@ public class IgLoader implements IValidationEngineLoader {
private String readInfoVersion(ByteProvider bs) throws IOException {
String is = TextFile.bytesToString(bs.getBytes());
is = is.trim();
IniFile ini = new IniFile(new ByteArrayInputStream(TextFile.stringToBytes(is, false)));
IniFile ini = new IniFile(new ByteArrayInputStream(TextFile.stringToBytes(is)));
return ini.getStringProperty("FHIR", "version");
}
@ -863,7 +863,9 @@ public class IgLoader implements IValidationEngineLoader {
}
private void log(String s) {
if (isDebug()) System.out.println(s);
if (isDebug()) {
System.out.println(s);
}
}
@Override

View File

@ -475,7 +475,6 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
protected void initContext(TimeTracker tt) throws IOException {
context.setCanNoTS(true);
context.setCacheId(UUID.randomUUID().toString());
context.setAllowLoadingDuplicates(true); // because of Forge
context.setExpansionParameters(makeExpProfile());
if (tt != null) {
@ -524,7 +523,8 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
} else {
try {
TerminologyClientFactory factory = new TerminologyClientFactory(version);
return context.connectToTSServer(factory, factory.makeClient("Tx-Server", url, context.getUserAgent()), log);
context.connectToTSServer(factory, url, context.getUserAgent(), log);
return "Connected to Terminology Server at "+url;
} catch (Exception e) {
if (context.isCanRunWithoutTerminology()) {
return "n/a: Running without Terminology Server (error: " + e.getMessage() + ")";
@ -927,7 +927,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
else if (fn.endsWith(".json") && !fn.endsWith("template.json"))
new org.hl7.fhir.dstu3.formats.JsonParser().setOutputStyle(org.hl7.fhir.dstu3.formats.IParser.OutputStyle.PRETTY).compose(s, res);
else if (fn.endsWith(".txt") || fn.endsWith(".map") || fn.endsWith(".fml"))
TextFile.stringToStream(org.hl7.fhir.dstu3.utils.StructureMapUtilities.render((org.hl7.fhir.dstu3.model.StructureMap) res), s, false);
TextFile.stringToStream(org.hl7.fhir.dstu3.utils.StructureMapUtilities.render((org.hl7.fhir.dstu3.model.StructureMap) res), s);
else
throw new FHIRException("Unsupported format for " + fn);
} else if (VersionUtilities.isR4Ver(version)) {
@ -937,7 +937,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
else if (fn.endsWith(".json") && !fn.endsWith("template.json"))
new org.hl7.fhir.r4.formats.JsonParser().setOutputStyle(org.hl7.fhir.r4.formats.IParser.OutputStyle.PRETTY).compose(s, res);
else if (fn.endsWith(".txt") || fn.endsWith(".map") || fn.endsWith(".fml"))
TextFile.stringToStream(org.hl7.fhir.r4.utils.StructureMapUtilities.render((org.hl7.fhir.r4.model.StructureMap) res), s, false);
TextFile.stringToStream(org.hl7.fhir.r4.utils.StructureMapUtilities.render((org.hl7.fhir.r4.model.StructureMap) res), s);
else
throw new FHIRException("Unsupported format for " + fn);
} else if (VersionUtilities.isR2BVer(version)) {
@ -962,7 +962,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
else if (fn.endsWith(".json") && !fn.endsWith("template.json"))
new JsonParser().setOutputStyle(org.hl7.fhir.r5.formats.IParser.OutputStyle.PRETTY).compose(s, r);
else if (fn.endsWith(".txt") || fn.endsWith(".map") || fn.endsWith(".fml"))
TextFile.stringToStream(StructureMapUtilities.render((org.hl7.fhir.r5.model.StructureMap) r), s, false);
TextFile.stringToStream(StructureMapUtilities.render((org.hl7.fhir.r5.model.StructureMap) r), s);
else
throw new FHIRException("Unsupported format for " + fn);
} else

View File

@ -363,8 +363,6 @@ public class ValidatorCli {
private void readParamsAndExecuteTask(TimeTracker tt, TimeTracker.Session tts, CliContext cliContext, String[] params) throws Exception {
Display.printCliParamsAndInfo(params);
final CliTask cliTask = selectCliTask(cliContext, params);
if (cliTask instanceof ValidationEngineTask) {

View File

@ -289,7 +289,7 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, IV
@Nonnull
protected ITerminologyClient getTerminologyClient(String root) throws URISyntaxException {
return new TerminologyClientFactory(context.getVersion()).makeClient("source", root, Common.getValidatorUserAgent());
return new TerminologyClientFactory(context.getVersion()).makeClient("source", root, Common.getValidatorUserAgent(), null);
}
private String getRoot(String[] p, String url) {

View File

@ -537,7 +537,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
return null;
}
return context.fetchResource(ValueSet.class, url);
return context.findTxResource(ValueSet.class, url);
}
@Override
@ -1135,7 +1135,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
timeTracker.tx(t, "vc "+system+"#"+code+" '"+display+"'");
if (s == null)
return true;
ok = processTxIssues(errors, s, element, path, null, false) & ok;
ok = processTxIssues(errors, s, element, path, null, false, null) & ok;
if (s.isOk()) {
if (s.getMessage() != null && !s.messageIsInIssues()) {
@ -1175,6 +1175,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return false;
}
} else if (context.isNoTerminologyServer() && NO_TX_SYSTEM_EXEMPT.contains(system)) {
txWarning(errors, NO_RULE_DATE, null, IssueType.BUSINESSRULE, element.line(), element.col(), path, false, I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES, code, system);
return ok; // no checks in this case
} else if (startsWithButIsNot(system, "http://snomed.info/sct", "http://loinc.org", "http://unitsofmeasure.org", "http://www.nlm.nih.gov/research/umls/rxnorm")) {
rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_SYSTEM_INVALID, system);
@ -1399,7 +1400,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else {
checked = true;
ValidationResult vr = checkCodeOnServer(stack, valueset, cc);
bh.see(processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), false));
bh.see(processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), false, binding.getValueSet()));
if (!vr.isOk()) {
bindingsOk = false;
if (vr.getErrorClass() != null && vr.getErrorClass() == TerminologyServiceErrorClass.NOSERVICE) {
@ -1436,12 +1437,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
} else if (vr.getMessage() != null) {
if (vr.getTrimmedMessage() != null) {
if (vr.getSeverity() == IssueSeverity.INFORMATION) {
txHint(errors, "2023-07-03", vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage());
} else {
checkDisp = false;
if (!vr.messageIsInIssues()) {
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage());
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getTrimmedMessage());
}
}
} else {
@ -1478,7 +1479,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (cc.hasCoding()) {
long t = System.nanoTime();
ValidationResult vr = checkCodeOnServer(stack, null, cc);
bh.see(processTxIssues(errors, vr, element, path, org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, false));
bh.see(processTxIssues(errors, vr, element, path, org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, false, null));
timeTracker.tx(t, "vc "+cc.toString());
}
}
@ -1503,7 +1504,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
private boolean processTxIssues(List<ValidationMessage> errors, ValidationResult vr, Element element, String path,
org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity notFoundLevel, boolean ignoreCantInfer) {
org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity notFoundLevel, boolean ignoreCantInfer, String vsurl) {
boolean ok = true;
if (vr != null) {
for (OperationOutcomeIssueComponent iss : vr.getIssues()) {
@ -1511,9 +1512,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
!iss.getDetails().hasCoding("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", "this-code-not-in-vs")
&& !(ignoreCantInfer || iss.getDetails().hasCoding("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", "cannot-infer"))) {
OperationOutcomeIssueComponent i = iss.copy();
if (notFoundLevel != null && (i.getDetails().hasCoding("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", "not-found"))) {
if (notFoundLevel != null && i.getDetails().hasCoding("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", "not-found")) {
if (i.getSeverity().isHigherThan(notFoundLevel) || (vsurl != null && i.getDetails().getText().contains(vsurl))) {
i.setSeverity(notFoundLevel);
}
}
if (baseOptions.isDisplayWarningMode() && i.getSeverity() == org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR && i.getDetails().hasCoding("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", "invalid-display")) {
i.setSeverity(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.WARNING);
}
@ -1531,7 +1534,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
boolean ok = true;
if (isNotBlank(nextCoding.getCode()) && isNotBlank(nextCoding.getSystem()) && context.supportsSystem(nextCoding.getSystem(), baseOptions.getFhirVersion())) {
ValidationResult vr = checkCodeOnServer(stack, valueset, nextCoding);
ok = processTxIssues(errors, vr, element, path, null, false) && ok;
ok = processTxIssues(errors, vr, element, path, null, false, null) && ok;
if (vr.getSeverity() != null && !vr.messageIsInIssues()) {
if (vr.getSeverity() == IssueSeverity.INFORMATION) {
@ -1597,7 +1600,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// ignore this since we can't validate but it doesn't matter..
} else {
ValidationResult vr = checkCodeOnServer(stack, valueset, cc);
ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), false) && ok;
ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), false, binding.getValueSet()) && ok;
if (!vr.isOk()) {
bindingsOk = false;
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) {
@ -1646,7 +1649,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String nextVersion = nextCoding.getVersion();
if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem, baseOptions.getFhirVersion())) {
ValidationResult vr = checkCodeOnServer(stack, nextCode, nextSystem, nextVersion, null, false);
ok = (processTxIssues(errors, vr, element, path, null, false)) && ok;
ok = (processTxIssues(errors, vr, element, path, null, false, null)) && ok;
if (!vr.isOk()) {
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_NOTVALID, nextCode, nextSystem);
}
@ -1710,7 +1713,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (binding.getStrength() == BindingStrength.REQUIRED) {
removeTrackedMessagesForLocation(errors, element, path);
}
ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), false) && ok;
ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), false, binding.getValueSet()) && ok;
timeTracker.tx(t, "vc "+system+"#"+code+" '"+display+"'");
if (vr != null && !vr.isOk()) {
if (vr.IsNoService())
@ -1852,7 +1855,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
try {
long t = System.nanoTime();
ValidationResult vr = checkCodeOnServer(stack, valueset, cc);
ok = processTxIssues(errors, vr, element, path, null, false) && ok;
ok = processTxIssues(errors, vr, element, path, null, false, maxVSUrl) && ok;
timeTracker.tx(t, "vc "+cc.toString());
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1869,7 +1872,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
// private String describeValueSet(String url) {
// ValueSet vs = context.fetchResource(ValueSet.class, url);
// ValueSet vs = context.findTxResource(ValueSet.class, url);
// if (vs != null) {
// return "'"+vs.present()+"' ("+url+")";
// } else {
@ -1891,7 +1894,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
try {
long t = System.nanoTime();
ValidationResult vr = checkCodeOnServer(stack, valueset, c);
ok = processTxIssues(errors, vr, element, path, null, false) && ok;
ok = processTxIssues(errors, vr, element, path, null, false, maxVSUrl) && ok;
timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'");
if (!vr.isOk()) {
@ -1922,7 +1925,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
try {
long t = System.nanoTime();
ValidationResult vr = checkCodeOnServer(stack, valueset, value, baseOptions.withLanguage(stack.getWorkingLang()));
ok = processTxIssues(errors, vr, element, path, null, false) && ok;
ok = processTxIssues(errors, vr, element, path, null, false, maxVSUrl) && ok;
timeTracker.tx(t, "vc "+value);
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1999,7 +2002,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
checked = true;
vr = checkCodeOnServer(stack, valueset, c);
}
ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), binding.getStrength() == BindingStrength.EXTENSIBLE) && ok;
ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), binding.getStrength() == BindingStrength.EXTENSIBLE, binding.getValueSet()) && ok;
timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'");
if (binding.getStrength() == BindingStrength.REQUIRED) {
@ -3451,7 +3454,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
vr = checkCodeOnServer(stack, vs, value, options);
}
ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), binding.getStrength() == BindingStrength.EXTENSIBLE) && ok;
ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), binding.getStrength() == BindingStrength.EXTENSIBLE, binding.getValueSet()) && ok;
timeTracker.tx(t, "vc "+value+"");
if (binding.getStrength() == BindingStrength.REQUIRED) {
@ -7264,21 +7267,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
public ValidationResult checkCodeOnServer(NodeStack stack, ValueSet valueset, Coding c) {
codingObserver.seeCode(stack, c);
if (false) { // #FIXME
return checkForInactive(filterOutSpecials(stack.getLiteralPath(), valueset, context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withCheckValueSetOnly(), c, valueset)), c);
} else {
return checkForInactive(filterOutSpecials(stack.getLiteralPath(), valueset, context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()), c, valueset)), c);
}
}
public ValidationResult checkCodeOnServer(NodeStack stack, ValueSet valueset, CodeableConcept cc) {
codingObserver.seeCode(stack, cc);
if (false) { // #FIXME
return checkForInactive(filterOutSpecials(stack.getLiteralPath(), valueset, context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()).withCheckValueSetOnly(), cc, valueset)), cc);
} else {
return checkForInactive(filterOutSpecials(stack.getLiteralPath(), valueset, context.validateCode(baseOptions.withLanguage(stack.getWorkingLang()), cc, valueset)), cc);
}
}
private ValidationResult filterOutSpecials(String path, ValueSet vs, ValidationResult vr) {
// this is where we hack around problems in the infrastructure that lead to technically correct errors

View File

@ -194,10 +194,10 @@ public class ConceptMapValidator extends BaseValidator {
if (ref.contains("|")) {
res.url = ref.substring(0, ref.indexOf("|"));
res.version = ref.substring(ref.indexOf("|")+1);
res.vs = context.fetchResource(ValueSet.class, res.url, res.version);
res.vs = context.findTxResource(ValueSet.class, res.url, res.version);
} else {
res.url = ref;
res.vs = context.fetchResource(ValueSet.class, res.url);
res.vs = context.findTxResource(ValueSet.class, res.url);
}
return res;
}

View File

@ -894,7 +894,7 @@ public class StructureMapValidator extends BaseValidator {
ValueSet srcVS = null;
if (srcED != null) {
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, srcED.getBinding().hasValueSet() && srcED.getBinding().getStrength() == BindingStrength.REQUIRED, I18nConstants.SM_TARGET_TRANSLATE_BINDING_SOURCE)) {
srcVS = context.fetchResource(ValueSet.class, srcED.getBinding().getValueSet());
srcVS = context.findTxResource(ValueSet.class, srcED.getBinding().getValueSet());
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, srcVS != null, I18nConstants.SM_TARGET_TRANSLATE_BINDING_VS_SOURCE)) {
ValueSetExpansionOutcome vse = context.expandVS(srcVS, true, false);
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, vse.isOk(), I18nConstants.SM_TARGET_TRANSLATE_BINDING_VSE_SOURCE, vse.getError())) {
@ -913,7 +913,7 @@ public class StructureMapValidator extends BaseValidator {
}
if (srcED != null) {
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, tgtED.getBinding().hasValueSet() && tgtED.getBinding().getStrength() == BindingStrength.REQUIRED, I18nConstants.SM_TARGET_TRANSLATE_BINDING_TARGET)) {
ValueSet vs = context.fetchResource(ValueSet.class, tgtED.getBinding().getValueSet());
ValueSet vs = context.findTxResource(ValueSet.class, tgtED.getBinding().getValueSet());
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, vs != null, I18nConstants.SM_TARGET_TRANSLATE_BINDING_VS_TARGET)) {
ValueSetExpansionOutcome vse = context.expandVS(vs, true, false);
if (warning(errors, "2023-03-01", IssueType.INVALID, line, col, literalPath, vse.isOk(), I18nConstants.SM_TARGET_TRANSLATE_BINDING_VSE_TARGET, vse.getError())) {

View File

@ -123,7 +123,7 @@ public class ValueSetValidator extends BaseValidator {
int i = 0;
for (Element ve : valuesets) {
String v = ve.getValue();
ValueSet vs = context.fetchResource(ValueSet.class, v);
ValueSet vs = context.findTxResource(ValueSet.class, v);
if (vs == null) {
NodeStack ns = stack.push(ve, i, ve.getProperty().getDefinition(), ve.getProperty().getDefinition());

View File

@ -139,7 +139,7 @@ public class TxTester {
private ITerminologyClient connectToServer() throws URISyntaxException {
System.out.println("Connect to "+server);
return new TerminologyClientFactory(FhirPublication.R4).makeClient("Test-Server", server, "Tools/Java");
return new TerminologyClientFactory(FhirPublication.R4).makeClient("Test-Server", server, "Tools/Java", null);
}

View File

@ -89,6 +89,7 @@ public class TxTesterScrubbers {
public static void scrubOO(OperationOutcome po, boolean tight) {
scrubDR(po, tight);
po.getIssue().removeIf(i -> i.hasDiagnostics() & !i.hasDetails());
}
}

View File

@ -1,7 +1,9 @@
package org.hl7.fhir.validation.special;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.hl7.fhir.ParametersParameter;
import org.hl7.fhir.r5.model.Base;
@ -10,12 +12,14 @@ import org.hl7.fhir.r5.model.OperationOutcome;
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent;
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.utilities.CommaSeparatedStringBuilder;
public class TxTesterSorters {
@ -27,6 +31,17 @@ public class TxTesterSorters {
if (p.getResource() != null && p.getResource() instanceof OperationOutcome) {
Collections.sort(((OperationOutcome) p.getResource()).getIssue(), new TxTesterSorters.OperationIssueSorter());
}
if ("message".equals(p.getName()) && p.hasValuePrimitive()) {
String pv = p.getValue().primitiveValue();
if (pv.contains("; ")) {
List<String> bits = new ArrayList<>();
for (String s : pv.split("\\; ")) {
bits.add(s);
}
Collections.sort(bits);
p.setValue(new StringType(CommaSeparatedStringBuilder.join("; ", bits)));
}
}
}
}

View File

@ -125,7 +125,7 @@ public class UtilitiesXTests {
if (!Utilities.noString(s))
return s;
s = "C:\\work\\org.hl7.fhir\\build";
// FIXME: change this back
// TODO - what should we do here?
s = "/Users/jamesagnew/git/fhir";
if (new File(s).exists())
return s;

View File

@ -43,6 +43,7 @@ import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.json.model.JsonObject;
import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.hl7.fhir.validation.ValidationEngine;
@ -244,13 +245,32 @@ public class TerminologyServiceTests {
private void validate(ValidationEngine engine, String name, Resource req, String resp, String lang, String fp, JsonObject ext, boolean isCS) throws JsonSyntaxException, FileNotFoundException, IOException {
org.hl7.fhir.r5.model.Parameters p = (org.hl7.fhir.r5.model.Parameters) req;
ValueSet vs = null;
String vsurl = null;
if (!isCS) {
if (p.hasParameter("valueSetVersion")) {
vsurl = p.getParameterValue("url").primitiveValue()+"|"+p.getParameterValue("valueSetVersion").primitiveValue();
vs = engine.getContext().fetchResource(ValueSet.class, p.getParameterValue("url").primitiveValue(), p.getParameterValue("valueSetVersion").primitiveValue());
} else {
vsurl = p.getParameterValue("url").primitiveValue();
vs = engine.getContext().fetchResource(ValueSet.class, p.getParameterValue("url").primitiveValue());
}
}
ValidationResult vm = null;
String code = null;
String system = null;
String version = null;
String display = null;
CodeableConcept cc = null;
org.hl7.fhir.r5.model.Parameters res = null;
OperationOutcome oo = null;
if (vs == null && vsurl != null) {
String msg = engine.getContext().formatMessage(I18nConstants.UNABLE_TO_RESOLVE_VALUE_SET_, vsurl);
oo = new OperationOutcome();
CodeableConcept cct = oo.addIssue().setSeverity(IssueSeverity.ERROR).setCode(IssueType.NOTFOUND).getDetails();
cct.addCoding("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", "not-found", null);
cct.setText(msg);
} else {
ValidationOptions options = new ValidationOptions(FhirPublication.R5);
if (p.hasParameter("displayLanguage")) {
options = options.withLanguage(p.getParameterString("displayLanguage"));
@ -272,12 +292,6 @@ public class TerminologyServiceTests {
engine.getContext().getExpansionParameters().addParameter(pp.copy());
}
}
ValidationResult vm;
String code = null;
String system = null;
String version = null;
String display = null;
CodeableConcept cc = null;
if (p.hasParameter("code")) {
code = p.getParameterString("code");
system = p.getParameterString(isCS ? "url" : "system");
@ -299,9 +313,12 @@ public class TerminologyServiceTests {
} else {
throw new Error("validate not done yet for this steup");
}
if (vm.getSeverity() == org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity.FATAL) {
OperationOutcome oo = new OperationOutcome();
}
if (oo == null && vm != null && vm.getSeverity() == org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity.FATAL) {
oo = new OperationOutcome();
oo.getIssue().addAll(vm.getIssues());
}
if (oo != null) {
TxTesterSorters.sortOperationOutcome(oo);
TxTesterScrubbers.scrubOO(oo, false);
@ -314,7 +331,8 @@ public class TerminologyServiceTests {
}
Assertions.assertTrue(diff == null, diff);
} else {
org.hl7.fhir.r5.model.Parameters res = new org.hl7.fhir.r5.model.Parameters();
if (res == null) {
res = new org.hl7.fhir.r5.model.Parameters();
if (vm.getSystem() != null) {
res.addParameter("system", new UriType(vm.getSystem()));
} else if (system != null) {
@ -361,10 +379,12 @@ public class TerminologyServiceTests {
}
}
if (vm.getIssues().size() > 0) {
OperationOutcome oo = new OperationOutcome();
oo = new OperationOutcome();
oo.getIssue().addAll(vm.getIssues());
res.addParameter().setName("issues").setResource(oo);
}
}
TxTesterSorters.sortParameters(res);
TxTesterScrubbers.scrubParams(res);

View File

@ -186,7 +186,7 @@ public class ValidationEngineTests {
OperationOutcome op = ve.validate(FhirFormat.JSON, TestingUtilities.loadTestResourceStream("validator", "observation102.json"), null);
if (!TestUtilities.silent)
for (OperationOutcomeIssueComponent iss : op.getIssue()) {
System.out.println(" " + iss.getDetails().getText());
System.out.println(" "+iss.getSeverity().toCode()+": "+ iss.getDetails().getText());
}
int e = errors(op);
int w = warnings(op);
@ -265,12 +265,12 @@ public class ValidationEngineTests {
OperationOutcome op = ve.validate(FhirFormat.JSON, TestingUtilities.loadTestResourceStream("validator", "observation401_ucum.json"), profiles);
if (!TestUtilities.silent)
for (OperationOutcomeIssueComponent issue : op.getIssue())
System.out.println(" - " + issue.getDetails().getText());
System.out.println(" - "+issue.getSeverity().toCode()+": " + issue.getDetails().getText());
int e = errors(op);
int w = warnings(op);
int h = hints(op);
Assertions.assertEquals(0, e);
Assertions.assertEquals(5, w);
Assertions.assertEquals(6, w);
Assertions.assertEquals(2, h);
assertTrue(logger.verifyHasNoRequests(), "Unexpected request to TX server");
if (!TestUtilities.silent)

View File

@ -0,0 +1,6 @@
{
"http://somewhere/something-else" : null,
"http://loinc.org/vs/LL715-4" : null,
"http://hl7.org/fhir/us/vrdr/ValueSet/vrdr-PlaceOfDeath" : null,
"http://somewhere/something" : null
}

View File

@ -1,43 +0,0 @@
{
"resourceType" : "CapabilityStatement",
"id" : "FhirServer",
"meta" : {
"tag" : [{
"system" : "http://hl7.org/fhir/v3/ObservationValue",
"code" : "SUBSETTED",
"display" : "Subsetted"
}]
},
"extension" : [{
"url" : "http://hl7.org/fhir/3.0/StructureDefinition/extension-CapabilityStatement.acceptUnknown",
"valueCode" : "both"
}],
"url" : "http://tx-dev.fhir.org/r2/metadata",
"version" : "1.0.2-3.1.0",
"name" : "FHIR Reference Server Conformance Statement",
"status" : "active",
"date" : "2024-01-10T05:45:51.112Z",
"contact" : [{
"telecom" : [{
"system" : "other",
"value" : "http://healthintersections.com.au/"
}]
}],
"description" : "Standard Conformance Statement for the open source Reference FHIR Server provided by Health Intersections",
"kind" : "instance",
"software" : {
"name" : "Reference Server",
"version" : "3.1.0",
"releaseDate" : "2024-01-08T12:29:45.425Z"
},
"implementation" : {
"description" : "FHIR Server running at http://tx-dev.fhir.org/r2",
"url" : "http://tx-dev.fhir.org/r2"
},
"fhirVersion" : "1.0.2",
"format" : ["application/xml+fhir",
"application/json+fhir"],
"rest" : [{
"mode" : "server"
}]
}

View File

@ -1,44 +1,4 @@
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://acme.org/devices/clinical-codes",
"code" : "body-weight",
"display" : "Body Weight"
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"code" : "body-weight",
"severity" : "error",
"error" : "A definition for CodeSystem 'http://acme.org/devices/clinical-codes' could not be found, so the code cannot be validated",
"class" : "CODESYSTEM_UNSUPPORTED",
"server" : "http://tx-dev.fhir.org/r2",
"unknown-systems" : "http://acme.org/devices/clinical-codes",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
"valueUrl" : "http://tx-dev.fhir.org/r2"
}],
"severity" : "error",
"code" : "not-found",
"details" : {
"coding" : [{
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code" : "not-found"
}],
"text" : "A definition for CodeSystem 'http://acme.org/devices/clinical-codes' could not be found, so the code cannot be validated"
},
"location" : ["Coding.system"]
}]
}
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://acme.org/devices/clinical-codes",
"code" : "body-weight",
@ -79,3 +39,43 @@ v: {
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://acme.org/devices/clinical-codes",
"code" : "body-weight",
"display" : "Body Weight"
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"code" : "body-weight",
"severity" : "error",
"error" : "A definition for CodeSystem 'http://acme.org/devices/clinical-codes' could not be found, so the code cannot be validated",
"class" : "CODESYSTEM_UNSUPPORTED",
"server" : "http://tx-dev.fhir.org/r2",
"unknown-systems" : "http://acme.org/devices/clinical-codes",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
"valueUrl" : "http://tx-dev.fhir.org/r2"
}],
"severity" : "error",
"code" : "not-found",
"details" : {
"coding" : [{
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code" : "not-found"
}],
"text" : "A definition for CodeSystem 'http://acme.org/devices/clinical-codes' could not be found, so the code cannot be validated"
},
"location" : ["Coding.system"]
}]
}
}
-------------------------------------------------------------------------------------

View File

@ -3,7 +3,7 @@
"system" : "http://loinc.org",
"code" : "3141-9",
"display" : "Weight Measured"
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
@ -26,7 +26,7 @@ v: {
"system" : "http://loinc.org",
"code" : "3141-9",
"display" : "Weight Measured"
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",

View File

@ -0,0 +1,3 @@
[servers]
tx-dev.fhir.org.r2 = http://tx-dev.fhir.org/r2

View File

@ -3,7 +3,7 @@
"system" : "http://snomed.info/sct",
"code" : "27113001",
"display" : "Body weight"
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
@ -26,7 +26,7 @@ v: {
"system" : "http://snomed.info/sct",
"code" : "27113001",
"display" : "Body weight"
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",

View File

@ -2,7 +2,7 @@
{"code" : {
"system" : "http://unitsofmeasure.org",
"code" : "[lb_av]"
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
@ -24,7 +24,7 @@ v: {
{"code" : {
"system" : "http://unitsofmeasure.org",
"code" : "[lb_av]"
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",

View File

@ -1,70 +0,0 @@
{
"resourceType" : "CapabilityStatement",
"id" : "FhirServer",
"meta" : {
"tag" : [{
"system" : "http://hl7.org/fhir/v3/ObservationValue",
"code" : "SUBSETTED",
"display" : "Subsetted"
}]
},
"extension" : [{
"url" : "http://hl7.org/fhir/3.0/StructureDefinition/extension-CapabilityStatement.acceptUnknown",
"valueCode" : "both"
}],
"url" : "http://tx-dev.fhir.org/r3/metadata",
"version" : "3.0.2-3.1.0",
"name" : "FHIR Reference Server Conformance Statement",
"status" : "active",
"date" : "2024-01-10T05:46:10.065Z",
"contact" : [{
"telecom" : [{
"system" : "other",
"value" : "http://healthintersections.com.au/"
}]
}],
"kind" : "instance",
"instantiates" : ["http://hl7.org/fhir/CapabilityStatement/terminology-server"],
"software" : {
"name" : "Reference Server",
"version" : "3.1.0",
"releaseDate" : "2024-01-08T12:29:45.425Z"
},
"implementation" : {
"description" : "FHIR Server running at http://tx-dev.fhir.org/r3",
"url" : "http://tx-dev.fhir.org/r3"
},
"fhirVersion" : "3.0.2",
"format" : ["application/fhir+xml",
"application/fhir+json"],
"rest" : [{
"mode" : "server",
"security" : {
"cors" : true
},
"operation" : [{
"name" : "expand",
"definition" : "http://hl7.org/fhir/OperationDefinition/ValueSet-expand"
},
{
"name" : "lookup",
"definition" : "http://hl7.org/fhir/OperationDefinition/ValueSet-lookup"
},
{
"name" : "validate-code",
"definition" : "http://hl7.org/fhir/OperationDefinition/Resource-validate"
},
{
"name" : "translate",
"definition" : "http://hl7.org/fhir/OperationDefinition/ConceptMap-translate"
},
{
"name" : "closure",
"definition" : "http://hl7.org/fhir/OperationDefinition/ConceptMap-closure"
},
{
"name" : "versions",
"definition" : "http://tx-dev.fhir.org/r3/OperationDefinition/fso-versions"
}]
}]
}

View File

@ -0,0 +1,3 @@
[servers]
tx-dev.fhir.org.r3 = http://tx-dev.fhir.org/r3

View File

@ -1,70 +0,0 @@
{
"resourceType" : "CapabilityStatement",
"id" : "FhirServer",
"meta" : {
"tag" : [{
"system" : "http://hl7.org/fhir/v3/ObservationValue",
"code" : "SUBSETTED",
"display" : "Subsetted"
}]
},
"extension" : [{
"url" : "http://hl7.org/fhir/3.0/StructureDefinition/extension-CapabilityStatement.acceptUnknown",
"valueCode" : "both"
}],
"url" : "http://tx-dev.fhir.org/r3/metadata",
"version" : "3.0.2-3.1.0",
"name" : "FHIR Reference Server Conformance Statement",
"status" : "active",
"date" : "2024-01-10T05:46:15.222Z",
"contact" : [{
"telecom" : [{
"system" : "other",
"value" : "http://healthintersections.com.au/"
}]
}],
"kind" : "instance",
"instantiates" : ["http://hl7.org/fhir/CapabilityStatement/terminology-server"],
"software" : {
"name" : "Reference Server",
"version" : "3.1.0",
"releaseDate" : "2024-01-08T12:29:45.425Z"
},
"implementation" : {
"description" : "FHIR Server running at http://tx-dev.fhir.org/r3",
"url" : "http://tx-dev.fhir.org/r3"
},
"fhirVersion" : "3.0.2",
"format" : ["application/fhir+xml",
"application/fhir+json"],
"rest" : [{
"mode" : "server",
"security" : {
"cors" : true
},
"operation" : [{
"name" : "expand",
"definition" : "http://hl7.org/fhir/OperationDefinition/ValueSet-expand"
},
{
"name" : "lookup",
"definition" : "http://hl7.org/fhir/OperationDefinition/ValueSet-lookup"
},
{
"name" : "validate-code",
"definition" : "http://hl7.org/fhir/OperationDefinition/Resource-validate"
},
{
"name" : "translate",
"definition" : "http://hl7.org/fhir/OperationDefinition/ConceptMap-translate"
},
{
"name" : "closure",
"definition" : "http://hl7.org/fhir/OperationDefinition/ConceptMap-closure"
},
{
"name" : "versions",
"definition" : "http://tx-dev.fhir.org/r3/OperationDefinition/fso-versions"
}]
}]
}

View File

@ -1,4 +1,50 @@
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "29463-7",
"display" : "Body Weight"
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "Body weight",
"code" : "29463-7",
"system" : "http://loinc.org",
"version" : "2.74",
"server" : "http://tx-dev.fhir.org/r3",
"issues" : {
"resourceType" : "OperationOutcome"
}
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "3141-9",
"display" : "Body weight Measured"
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "Body weight Measured",
"code" : "3141-9",
"system" : "http://loinc.org",
"version" : "2.74",
"server" : "http://tx-dev.fhir.org/r3",
"issues" : {
"resourceType" : "OperationOutcome"
}
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "19935-6",
@ -20,51 +66,6 @@ v: {
"resourceType" : "OperationOutcome"
}
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "19935-6"
}, "url": "http://phr.kanta.fi/ValueSet/fiphr-vs-vitalsigns--0", "version": "0.03", "langs":"fi", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "ei käännetty",
"code" : "19935-6",
"system" : "http://loinc.org",
"version" : "2.74",
"server" : "http://tx-dev.fhir.org/r3",
"issues" : {
"resourceType" : "OperationOutcome"
}
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "19935-6",
"display" : "Maximum expiratory gas flow Respiratory system airway by Peak flow meter"
}, "url": "http://phr.kanta.fi/ValueSet/fiphr-vs-vitalsigns", "version": "0.03", "langs":"fi", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "ei käännetty",
"code" : "19935-6",
"system" : "http://loinc.org",
"version" : "2.74",
"server" : "http://tx-dev.fhir.org/r3",
"issues" : {
"resourceType" : "OperationOutcome"
}
}
-------------------------------------------------------------------------------------
{"code" : {
@ -307,7 +308,7 @@ v: {
"display" : "Allergies and adverse reactions Document",
"code" : "48765-2",
"severity" : "error",
"error" : "Wrong Display Name 'Allergies' for http://loinc.org#48765-2 - should be one of 28 choices: 'Allergies and adverse reactions Document', 'Allergies &or adverse reactions Doc', '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 变态反应与不良反应 文档.其他' (zh-CN), '杂项类文档' (zh-CN), '其他文档 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 杂项' (zh-CN), '杂项类' (zh-CN), '杂项试验 过敏反应' (zh-CN), '过敏' (zh-CN), 'Allergie e reazioni avverse Documentazione miscellanea Miscellanea Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Документ Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')",
"error" : "Wrong Display Name 'Allergies' for http://loinc.org#48765-2. Valid display is one of 28 choices: 'Allergies and adverse reactions Document', 'Allergies &or adverse reactions Doc', '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 变态反应与不良反应 文档.其他' (zh-CN), '杂项类文档' (zh-CN), '其他文档 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 杂项' (zh-CN), '杂项类' (zh-CN), '杂项试验 过敏反应' (zh-CN), '过敏' (zh-CN), 'Allergie e reazioni avverse Documentazione miscellanea Miscellanea Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Документ Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"issues" : {
@ -324,7 +325,7 @@ v: {
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code" : "invalid-display"
}],
"text" : "Wrong Display Name 'Allergies' for http://loinc.org#48765-2 - should be one of 28 choices: 'Allergies and adverse reactions Document', 'Allergies &or adverse reactions Doc', '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 变态反应与不良反应 文档.其他' (zh-CN), '杂项类文档' (zh-CN), '其他文档 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 杂项' (zh-CN), '杂项类' (zh-CN), '杂项试验 过敏反应' (zh-CN), '过敏' (zh-CN), 'Allergie e reazioni avverse Documentazione miscellanea Miscellanea Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Документ Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')"
"text" : "Wrong Display Name 'Allergies' for http://loinc.org#48765-2. Valid display is one of 28 choices: 'Allergies and adverse reactions Document', 'Allergies &or adverse reactions Doc', '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 变态反应与不良反应 文档.其他' (zh-CN), '杂项类文档' (zh-CN), '其他文档 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 杂项' (zh-CN), '杂项类' (zh-CN), '杂项试验 过敏反应' (zh-CN), '过敏' (zh-CN), 'Allergie e reazioni avverse Documentazione miscellanea Miscellanea Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Документ Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')"
},
"location" : ["CodeableConcept.coding[0].display"],
"expression" : ["CodeableConcept.coding[0].display"]
@ -509,7 +510,7 @@ v: {
"display" : "Plan of care note",
"code" : "18776-5",
"severity" : "error",
"error" : "Wrong Display Name 'Plan of care' for http://loinc.org#18776-5 - should be one of 30 choices: 'Plan of care note', 'Plan of care note', '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 事件发生的地方' (zh-CN), '场景' (zh-CN), '环境' (zh-CN), '背景 医疗服务(照护服务、护理服务、护理、照护、医疗照护、诊疗、诊疗服务、照顾、看护)计划(方案)记录 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 文档本体' (zh-CN), '临床文档本体' (zh-CN), '文档本体' (zh-CN), '文书本体' (zh-CN), '医疗文书本体' (zh-CN), '临床医疗文书本体 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 未加明确说明的角色 笔记' (zh-CN), '按语' (zh-CN), '注释' (zh-CN), '说明' (zh-CN), '票据' (zh-CN), '单据' (zh-CN), '证明书' (zh-CN) or 'Documentazione dell'ontologia Osservazione Piano di cura Punto nel tempo (episodio) Ruolo non specificato' (it-IT) (for the language(s) '--')",
"error" : "Wrong Display Name 'Plan of care' for http://loinc.org#18776-5. Valid display is one of 30 choices: 'Plan of care note', 'Plan of care note', '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 事件发生的地方' (zh-CN), '场景' (zh-CN), '环境' (zh-CN), '背景 医疗服务(照护服务、护理服务、护理、照护、医疗照护、诊疗、诊疗服务、照顾、看护)计划(方案)记录 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 文档本体' (zh-CN), '临床文档本体' (zh-CN), '文档本体' (zh-CN), '文书本体' (zh-CN), '医疗文书本体' (zh-CN), '临床医疗文书本体 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 未加明确说明的角色 笔记' (zh-CN), '按语' (zh-CN), '注释' (zh-CN), '说明' (zh-CN), '票据' (zh-CN), '单据' (zh-CN), '证明书' (zh-CN) or 'Documentazione dell'ontologia Osservazione Piano di cura Punto nel tempo (episodio) Ruolo non specificato' (it-IT) (for the language(s) '--')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"issues" : {
@ -526,7 +527,7 @@ v: {
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code" : "invalid-display"
}],
"text" : "Wrong Display Name 'Plan of care' for http://loinc.org#18776-5 - should be one of 30 choices: 'Plan of care note', 'Plan of care note', '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 事件发生的地方' (zh-CN), '场景' (zh-CN), '环境' (zh-CN), '背景 医疗服务(照护服务、护理服务、护理、照护、医疗照护、诊疗、诊疗服务、照顾、看护)计划(方案)记录 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 文档本体' (zh-CN), '临床文档本体' (zh-CN), '文档本体' (zh-CN), '文书本体' (zh-CN), '医疗文书本体' (zh-CN), '临床医疗文书本体 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 未加明确说明的角色 笔记' (zh-CN), '按语' (zh-CN), '注释' (zh-CN), '说明' (zh-CN), '票据' (zh-CN), '单据' (zh-CN), '证明书' (zh-CN) or 'Documentazione dell'ontologia Osservazione Piano di cura Punto nel tempo (episodio) Ruolo non specificato' (it-IT) (for the language(s) '--')"
"text" : "Wrong Display Name 'Plan of care' for http://loinc.org#18776-5. Valid display is one of 30 choices: 'Plan of care note', 'Plan of care note', '临床文档型' (zh-CN), '临床文档' (zh-CN), '文档' (zh-CN), '文书' (zh-CN), '医疗文书' (zh-CN), '临床医疗文书 事件发生的地方' (zh-CN), '场景' (zh-CN), '环境' (zh-CN), '背景 医疗服务(照护服务、护理服务、护理、照护、医疗照护、诊疗、诊疗服务、照顾、看护)计划(方案)记录 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 文档本体' (zh-CN), '临床文档本体' (zh-CN), '文档本体' (zh-CN), '文书本体' (zh-CN), '医疗文书本体' (zh-CN), '临床医疗文书本体 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 未加明确说明的角色 笔记' (zh-CN), '按语' (zh-CN), '注释' (zh-CN), '说明' (zh-CN), '票据' (zh-CN), '单据' (zh-CN), '证明书' (zh-CN) or 'Documentazione dell'ontologia Osservazione Piano di cura Punto nel tempo (episodio) Ruolo non specificato' (it-IT) (for the language(s) '--')"
},
"location" : ["CodeableConcept.coding[0].display"],
"expression" : ["CodeableConcept.coding[0].display"]
@ -596,7 +597,7 @@ v: {
"display" : "Review of systems Narrative - Reported",
"code" : "10187-3",
"severity" : "error",
"error" : "Wrong Display Name 'Review of systems Narrative Reporte' for http://loinc.org#10187-3 - should be one of 41 choices: 'Review of systems Narrative - Reported', 'Review of systems', '医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 历史纪录与体格检查 历史纪录与体格检查.历史记录' (zh-CN), '历史纪录与体格检查.历史记录类' (zh-CN), '历史纪录与体格检查.历史记录类别' (zh-CN), '历史纪录与体格检查.病史' (zh-CN), '历史纪录与体格检查.病史类' (zh-CN), '历史纪录与体格检查.病史类别' (zh-CN), '历史纪录与体格检查.病史记录' (zh-CN), '历史纪录与体格检查.病史记录类' (zh-CN), '历史纪录与体格检查.病史记录类别' (zh-CN), '历史纪录与体格检查小节.历史记录' (zh-CN), '历史纪录与体格检查小节.历史记录类' (zh-CN), '历史纪录与体格检查小节.历史记录类别' (zh-CN), '历史纪录与体格检查小节.病史' (zh-CN), '历史纪录与体格检查小节.病史类' (zh-CN), '历史纪录与体格检查小节.病史类别 历史纪录与体格检查小节 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 叙述' (zh-CN), '叙述性文字' (zh-CN), '报告' (zh-CN), '报告型' (zh-CN), '文字叙述' (zh-CN), '文本叙述型' (zh-CN), '文本描述' (zh-CN), '文本描述型 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 病史与体格检查 系统回顾' (zh-CN), '系统审核' (zh-CN), 'Anamnesi Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Анамнестические сведения' (ru-RU), 'Сообщенная третьим лицом информация Описательный Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')",
"error" : "Wrong Display Name 'Review of systems Narrative Reporte' for http://loinc.org#10187-3. Valid display is one of 41 choices: 'Review of systems Narrative - Reported', 'Review of systems', '医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 历史纪录与体格检查 历史纪录与体格检查.历史记录' (zh-CN), '历史纪录与体格检查.历史记录类' (zh-CN), '历史纪录与体格检查.历史记录类别' (zh-CN), '历史纪录与体格检查.病史' (zh-CN), '历史纪录与体格检查.病史类' (zh-CN), '历史纪录与体格检查.病史类别' (zh-CN), '历史纪录与体格检查.病史记录' (zh-CN), '历史纪录与体格检查.病史记录类' (zh-CN), '历史纪录与体格检查.病史记录类别' (zh-CN), '历史纪录与体格检查小节.历史记录' (zh-CN), '历史纪录与体格检查小节.历史记录类' (zh-CN), '历史纪录与体格检查小节.历史记录类别' (zh-CN), '历史纪录与体格检查小节.病史' (zh-CN), '历史纪录与体格检查小节.病史类' (zh-CN), '历史纪录与体格检查小节.病史类别 历史纪录与体格检查小节 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 叙述' (zh-CN), '叙述性文字' (zh-CN), '报告' (zh-CN), '报告型' (zh-CN), '文字叙述' (zh-CN), '文本叙述型' (zh-CN), '文本描述' (zh-CN), '文本描述型 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 病史与体格检查 系统回顾' (zh-CN), '系统审核' (zh-CN), 'Anamnesi Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Анамнестические сведения' (ru-RU), 'Сообщенная третьим лицом информация Описательный Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"issues" : {
@ -613,7 +614,7 @@ v: {
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code" : "invalid-display"
}],
"text" : "Wrong Display Name 'Review of systems Narrative Reporte' for http://loinc.org#10187-3 - should be one of 41 choices: 'Review of systems Narrative - Reported', 'Review of systems', '医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 历史纪录与体格检查 历史纪录与体格检查.历史记录' (zh-CN), '历史纪录与体格检查.历史记录类' (zh-CN), '历史纪录与体格检查.历史记录类别' (zh-CN), '历史纪录与体格检查.病史' (zh-CN), '历史纪录与体格检查.病史类' (zh-CN), '历史纪录与体格检查.病史类别' (zh-CN), '历史纪录与体格检查.病史记录' (zh-CN), '历史纪录与体格检查.病史记录类' (zh-CN), '历史纪录与体格检查.病史记录类别' (zh-CN), '历史纪录与体格检查小节.历史记录' (zh-CN), '历史纪录与体格检查小节.历史记录类' (zh-CN), '历史纪录与体格检查小节.历史记录类别' (zh-CN), '历史纪录与体格检查小节.病史' (zh-CN), '历史纪录与体格检查小节.病史类' (zh-CN), '历史纪录与体格检查小节.病史类别 历史纪录与体格检查小节 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 叙述' (zh-CN), '叙述性文字' (zh-CN), '报告' (zh-CN), '报告型' (zh-CN), '文字叙述' (zh-CN), '文本叙述型' (zh-CN), '文本描述' (zh-CN), '文本描述型 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 病史与体格检查 系统回顾' (zh-CN), '系统审核' (zh-CN), 'Anamnesi Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Анамнестические сведения' (ru-RU), 'Сообщенная третьим лицом информация Описательный Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')"
"text" : "Wrong Display Name 'Review of systems Narrative Reporte' for http://loinc.org#10187-3. Valid display is one of 41 choices: 'Review of systems Narrative - Reported', 'Review of systems', '医疗服务对象' (zh-CN), '客户' (zh-CN), '病人' (zh-CN), '病患' (zh-CN), '病号' (zh-CN), '超系统 - 病人 历史纪录与体格检查 历史纪录与体格检查.历史记录' (zh-CN), '历史纪录与体格检查.历史记录类' (zh-CN), '历史纪录与体格检查.历史记录类别' (zh-CN), '历史纪录与体格检查.病史' (zh-CN), '历史纪录与体格检查.病史类' (zh-CN), '历史纪录与体格检查.病史类别' (zh-CN), '历史纪录与体格检查.病史记录' (zh-CN), '历史纪录与体格检查.病史记录类' (zh-CN), '历史纪录与体格检查.病史记录类别' (zh-CN), '历史纪录与体格检查小节.历史记录' (zh-CN), '历史纪录与体格检查小节.历史记录类' (zh-CN), '历史纪录与体格检查小节.历史记录类别' (zh-CN), '历史纪录与体格检查小节.病史' (zh-CN), '历史纪录与体格检查小节.病史类' (zh-CN), '历史纪录与体格检查小节.病史类别 历史纪录与体格检查小节 发现是一个原子型临床观察指标,并不是作为印象的概括陈述。体格检查、病史、系统检查及其他此类观察指标的属性均为发现。它们的标尺对于编码型发现可能是名义型,而对于叙述型文本之中所报告的发现,则可能是叙述型。' (zh-CN), '发现物' (zh-CN), '所见' (zh-CN), '结果' (zh-CN), '结论 叙述' (zh-CN), '叙述性文字' (zh-CN), '报告' (zh-CN), '报告型' (zh-CN), '文字叙述' (zh-CN), '文本叙述型' (zh-CN), '文本描述' (zh-CN), '文本描述型 时刻' (zh-CN), '随机' (zh-CN), '随意' (zh-CN), '瞬间 病史与体格检查 系统回顾' (zh-CN), '系统审核' (zh-CN), 'Anamnesi Osservazione paziente Punto nel tempo (episodio)' (it-IT), 'Анамнестические сведения' (ru-RU), 'Сообщенная третьим лицом информация Описательный Точка во времени' (ru-RU) or 'Момент' (ru-RU) (for the language(s) '--')"
},
"location" : ["CodeableConcept.coding[0].display"],
"expression" : ["CodeableConcept.coding[0].display"]
@ -637,7 +638,7 @@ v: {
"display" : "LCDS v4.00 - Administrative information - discharge [CMS Assessment]",
"code" : "87504-7",
"severity" : "error",
"error" : "Wrong Display Name 'Administrative information' for http://loinc.org#87504-7 - should be 'LCDS v4.00 - Administrative information - discharge [CMS Assessment]' (for the language(s) '--')",
"error" : "Wrong Display Name 'Administrative information' for http://loinc.org#87504-7. Valid display is 'LCDS v4.00 - Administrative information - discharge [CMS Assessment]' (for the language(s) '--')",
"class" : "UNKNOWN",
"server" : "http://tx-dev.fhir.org/r3",
"issues" : {
@ -654,7 +655,7 @@ v: {
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code" : "invalid-display"
}],
"text" : "Wrong Display Name 'Administrative information' for http://loinc.org#87504-7 - should be 'LCDS v4.00 - Administrative information - discharge [CMS Assessment]' (for the language(s) '--')"
"text" : "Wrong Display Name 'Administrative information' for http://loinc.org#87504-7. Valid display is 'LCDS v4.00 - Administrative information - discharge [CMS Assessment]' (for the language(s) '--')"
},
"location" : ["CodeableConcept.coding[0].display"],
"expression" : ["CodeableConcept.coding[0].display"]
@ -688,9 +689,8 @@ v: {
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "29463-7",
"display" : "Body Weight"
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
"code" : "19935-6"
}, "url": "http://phr.kanta.fi/ValueSet/fiphr-vs-vitalsigns--0", "version": "0.03", "langs":"fi", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
@ -698,8 +698,8 @@ v: {
}]
}}####
v: {
"display" : "Body weight",
"code" : "29463-7",
"display" : "ei käännetty",
"code" : "19935-6",
"system" : "http://loinc.org",
"version" : "2.74",
"server" : "http://tx-dev.fhir.org/r3",
@ -712,9 +712,9 @@ v: {
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "3141-9",
"display" : "Body weight Measured"
}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
"code" : "19935-6",
"display" : "Maximum expiratory gas flow Respiratory system airway by Peak flow meter"
}, "url": "http://phr.kanta.fi/ValueSet/fiphr-vs-vitalsigns", "version": "0.03", "langs":"fi", "useServer":"true", "useClient":"true", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
@ -722,8 +722,8 @@ v: {
}]
}}####
v: {
"display" : "Body weight Measured",
"code" : "3141-9",
"display" : "ei käännetty",
"code" : "19935-6",
"system" : "http://loinc.org",
"version" : "2.74",
"server" : "http://tx-dev.fhir.org/r3",

Some files were not shown because too many files have changed in this diff Show More