From 5aefd6a268de9c8f38e078750009f5911247d8e6 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 11 Aug 2023 12:11:22 +1000 Subject: [PATCH 01/15] add flat mode to tests, and add experimental functionality --- .../fhir/r5/context/BaseWorkerContext.java | 8 +++++ .../fhir/r5/test/utils/CompareUtilities.java | 2 +- .../org/hl7/fhir/r5/utils/ElementVisitor.java | 22 +++++++------- .../fhir/utilities/i18n/I18nConstants.java | 9 ++++++ .../src/main/resources/Messages.properties | 9 ++++++ .../fhir/validation/cli/model/CliContext.java | 8 +++++ .../validation/cli/tasks/TxTestsTask.java | 2 +- .../hl7/fhir/validation/cli/utils/Params.java | 8 +++++ .../hl7/fhir/validation/special/TxTester.java | 30 ++++++++++++------- .../validation/special/TxTesterScrubbers.java | 6 ++-- .../ExternalTerminologyServiceTests.java | 7 +++-- .../tests/TerminologyServiceTests.java | 1 - .../5.0.0/all-systems.cache | 15 ++++++++++ 13 files changed, 97 insertions(+), 30 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index 2962e8908..401a126b5 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -1521,6 +1521,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.EXPIRED); iss.getDetails().setText(formatMessage(I18nConstants.MSG_RETIRED, ((PrimitiveType) p.getValue()).asStringValue())); issues.add(iss); + } else if (p.getName().equals("warning-experimental")) { + OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); + iss.getDetails().setText(formatMessage(I18nConstants.MSG_EXPERIMENTAL, ((PrimitiveType) p.getValue()).asStringValue())); + issues.add(iss); + } else if (p.getName().equals("warning-draft")) { + OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION, org.hl7.fhir.r5.model.OperationOutcome.IssueType.BUSINESSRULE); + iss.getDetails().setText(formatMessage(I18nConstants.MSG_DRAFT, ((PrimitiveType) p.getValue()).asStringValue())); + issues.add(iss); } else if (p.getName().equals("cause")) { try { IssueType it = IssueType.fromCode(((StringType) p.getValue()).getValue()); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/CompareUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/CompareUtilities.java index 2d19973a8..f1744648e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/CompareUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/CompareUtilities.java @@ -309,7 +309,7 @@ public class CompareUtilities extends BaseTestingUtilities { int expectedMin = countExpectedMin(expectedArray); if (actualArray.size() > expectedArray.size() || actualArray.size() < expectedMin) - return createNotEqualMessage("array properties count differs at " + path, Integer.toString(expectedArray.size()), Integer.toString(actualArray.size())); + return createNotEqualMessage("array item count differs at " + path, Integer.toString(expectedArray.size()), Integer.toString(actualArray.size())); int c = 0; for (int i = 0; i < expectedArray.size(); i++) { if (c >= actualArray.size()) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ElementVisitor.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ElementVisitor.java index 0ae4b1a56..b2e6504a8 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ElementVisitor.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ElementVisitor.java @@ -8,8 +8,8 @@ import org.hl7.fhir.r5.model.Resource; public class ElementVisitor { public interface IElementVisitor { - public void visit(Resource resource); - public void visit(Element element); + public void visit(Object context, Resource resource); + public void visit(Object context, Element element); } private IElementVisitor visitor; @@ -18,28 +18,28 @@ public class ElementVisitor { this.visitor = visitor; } - private void visitBase(Base base) { + private void visitBase(Object context, Base base) { for (Property p : base.children()) { if (p.hasValues()) { for (Base b : p.getValues()) { if (b instanceof Resource) { - visit((Resource) b); + visit(context, (Resource) b); } else { - visit((Element) b); + visit(context, (Element) b); } } } } } - public void visit(Resource res) { - visitor.visit(res); - visitBase(res); + public void visit(Object context, Resource res) { + visitor.visit(context, res); + visitBase(context, res); } - public void visit(Element e) { - visitor.visit(e); - visitBase(e); + public void visit(Object context, Element e) { + visitor.visit(context, e); + visitBase(context, e); } } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index c4494e86a..b1e238661 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -956,6 +956,15 @@ public class I18nConstants { public static final String MSG_RETIRED = "MSG_RETIRED"; public static final String INACTIVE_CODE_WARNING = "INACTIVE_CODE_WARNING"; public static final String SD_EXTENSION_URL_MISSING = "SD_EXTENSION_URL_MISSING"; + public static final String MSG_EXPERIMENTAL = "MSG_EXPERIMENTAL"; + public static final String MSG_DRAFT = "MSG_DRAFT"; + public static final String MSG_DEPENDS_ON_DEPRECATED = "MSG_DEPENDS_ON_DEPRECATED"; + public static final String MSG_DEPENDS_ON_WITHDRAWN = "MSG_DEPENDS_ON_WITHDRAWN"; + public static final String MSG_DEPENDS_ON_RETIRED = "MSG_DEPENDS_ON_RETIRED"; + public static final String MSG_DEPENDS_ON_EXPERIMENTAL = "MSG_DEPENDS_ON_EXPERIMENTAL"; + public static final String MSG_DEPENDS_ON_DRAFT = "MSG_DEPENDS_ON_DRAFT"; + public static final String MSG_DEPENDS_ON_EXTENSION = "MSG_DEPENDS_ON_EXTENSION"; + public static final String MSG_DEPENDS_ON_PROFILE = "MSG_DEPENDS_ON_PROFILE"; } diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index c77643f3d..2ab215b84 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -1012,5 +1012,14 @@ SD_EXTENSION_URL_MISSING = The value of Extension.url is not fixed to the extens MSG_DEPRECATED = Reference to deprecated item {0} MSG_WITHDRAWN = Reference to withdrawn item {0} MSG_RETIRED = Reference to retired item {0} +MSG_EXPERIMENTAL = Reference to experimental item {0} +MSG_DRAFT = Reference to draft item {0} INACTIVE_CODE_WARNING = The code ''{0}'' is valid but is not active SD_ED_TYPE_PROFILE_WRONG_TYPE = The type {0} is not in the list of allowed types {1} in the profile {2} +MSG_DEPENDS_ON_DEPRECATED = The {0} {1} is deprecated +MSG_DEPENDS_ON_WITHDRAWN = The {0} {1} is withdrawn +MSG_DEPENDS_ON_RETIRED = The {0} {1} is retired +MSG_DEPENDS_ON_EXPERIMENTAL = The {0} {1} is an experimental resource +MSG_DEPENDS_ON_DRAFT = The {0} {1} is a draft resource +MSG_DEPENDS_ON_EXTENSION = extension +MSG_DEPENDS_ON_PROFILE = profile diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java index e2d391c8e..27b2ac4b7 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java @@ -101,6 +101,8 @@ public class CliContext { private List sources = new ArrayList(); @JsonProperty("inputs") private List inputs = new ArrayList(); + @JsonProperty("modeParams") + private List modeParams = new ArrayList(); @JsonProperty("mode") private EngineMode mode = EngineMode.VALIDATION; @@ -427,6 +429,12 @@ public class CliContext { public List getInputs() { return inputs; } + + + @JsonProperty("modeParams") + public List getModeParams() { + return modeParams; + } @JsonProperty("sources") public CliContext setSources(List sources) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/tasks/TxTestsTask.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/tasks/TxTestsTask.java index 38246f127..87da870d5 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/tasks/TxTestsTask.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/tasks/TxTestsTask.java @@ -41,7 +41,7 @@ public class TxTestsTask extends StandaloneTask{ final String version = Params.getParam(args, Params.VERSION); final String tx = Params.getParam(args, Params.TERMINOLOGY); final String filter = Params.getParam(args, Params.FILTER); - boolean ok = new TxTester(new TxTester.InternalTxLoader(source, output), tx, false).setOutput(output).execute(version, filter); + boolean ok = new TxTester(new TxTester.InternalTxLoader(source, output), tx, false).setOutput(output).execute(version, cliContext.getModeParams(), filter); SystemExitManager.setError(ok ? 1 : 0); SystemExitManager.finish(); } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java index 0f73603c2..0e1a9a36e 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java @@ -99,6 +99,7 @@ public class Params { public static final String SOURCE = "-source"; public static final String INPUT = "-input"; public static final String FILTER = "-filter"; + public static final String MODE = "-mode"; private static final String FHIR_SETTINGS_PARAM = "-fhir-settings"; private static final String WATCH_MODE_PARAM = "-watch-mode"; private static final String WATCH_SCAN_DELAY = "-watch-scan-delay"; @@ -203,6 +204,13 @@ public class Params { String q = args[++i]; cliContext.setLevel(ValidationLevel.fromCode(q)); } + } else if (args[i].equals(MODE)) { + if (i + 1 == args.length) + throw new Error("Specified -mode without indicating mode"); + else { + String q = args[++i]; + cliContext.getModeParams().add(q); + } } else if (args[i].equals(INPUT)) { if (i + 1 == args.length) throw new Error("Specified -input without providing value"); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java index 38e89c5a8..70ac919b2 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java @@ -65,10 +65,10 @@ public class TxTester { } public static void main(String[] args) throws Exception { - new TxTester(new InternalTxLoader(args[0]), args[1], "true".equals(args[2])).execute(args[2], args[3]); + new TxTester(new InternalTxLoader(args[0]), args[1], "true".equals(args[2])).execute(args[2], new ArrayList<>(), args[3]); } - public boolean execute(String version, String filter) throws IOException, URISyntaxException { + public boolean execute(String version, List modes, String filter) throws IOException, URISyntaxException { if (output == null) { output = Utilities.path("[tmp]", serverId()); } @@ -77,6 +77,7 @@ public class TxTester { System.out.println(" Source for tests: "+loader.describe()); System.out.println(" Output Directory: "+output); System.out.println(" Term Service Url: "+server); + System.out.println(" Test Exec Modes: "+modes.toString()); if (version != null) { System.out.println(" Tx FHIR Version: "+version); } @@ -91,7 +92,7 @@ public class TxTester { ITerminologyClient tx = connectToServer(); boolean ok = checkClient(tx); for (JsonObject suite : tests.getJsonObjects("suites")) { - ok = runSuite(suite, tx, filter, json.forceArray("suites")) && ok; + ok = runSuite(suite, tx, modes, filter, json.forceArray("suites")) && ok; } TextFile.stringToFile(JsonParser.compose(json, true), Utilities.path(output, "test-results.json")); if (ok) { @@ -137,20 +138,20 @@ public class TxTester { } - public String executeTest(JsonObject suite, JsonObject test) throws URISyntaxException, FHIRFormatError, FileNotFoundException, IOException { + public String executeTest(JsonObject suite, JsonObject test, List modes) throws URISyntaxException, FHIRFormatError, FileNotFoundException, IOException { error = null; if (tx == null) { tx = connectToServer(); checkClient(tx); } List setup = loadSetupResources(suite); - if (runTest(test, tx, setup, "*", null)) { + if (runTest(test, tx, setup, modes, "*", null)) { return null; } else { return error; } } - private boolean runSuite(JsonObject suite, ITerminologyClient tx, String filter, JsonArray output) throws FHIRFormatError, FileNotFoundException, IOException { + private boolean runSuite(JsonObject suite, ITerminologyClient tx, List modes, String filter, JsonArray output) throws FHIRFormatError, FileNotFoundException, IOException { System.out.println("Group "+suite.asString("name")); JsonObject outputS = new JsonObject(); if (output != null) { @@ -160,12 +161,12 @@ public class TxTester { List setup = loadSetupResources(suite); boolean ok = true; for (JsonObject test : suite.getJsonObjects("tests")) { - ok = runTest(test, tx, setup, filter, outputS.forceArray("tests")) && ok; + ok = runTest(test, tx, setup, modes, filter, outputS.forceArray("tests")) && ok; } return ok; } - private boolean runTest(JsonObject test, ITerminologyClient tx, List setup, String filter, JsonArray output) throws FHIRFormatError, DefinitionException, FileNotFoundException, FHIRException, IOException { + private boolean runTest(JsonObject test, ITerminologyClient tx, List setup, List modes, String filter, JsonArray output) throws FHIRFormatError, DefinitionException, FileNotFoundException, FHIRException, IOException { JsonObject outputT = new JsonObject(); if (output != null) { output.add(outputT); @@ -175,9 +176,9 @@ public class TxTester { if (Utilities.noString(filter) || filter.equals("*") || test.asString("name").contains(filter)) { System.out.print(" Test "+test.asString("name")+": "); try { - Parameters req = (Parameters) loader.loadResource(test.asString("request")); + Parameters req = (Parameters) loader.loadResource(chooseParam(test, "request", modes)); - String fn = test.asString("response"); + String fn = chooseParam(test, "response", modes); String resp = TextFile.bytesToString(loader.loadContent(fn)); String fp = Utilities.path("[tmp]", serverId(), fn); File fo = new File(fp); @@ -217,6 +218,15 @@ public class TxTester { } } + private String chooseParam(JsonObject test, String name, List modes) { + for (String mode : modes) { + if (test.has(name+":"+mode)) { + return test.asString(name+":"+mode); + } + } + return test.asString(name); + } + private Parameters loadProfile(JsonObject test) throws FHIRFormatError, DefinitionException, FileNotFoundException, FHIRException, IOException { if (test.has("profile")) { return (Parameters) loader.loadResource(test.asString("profile")); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterScrubbers.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterScrubbers.java index b5e7f8729..f23d0fed5 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterScrubbers.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterScrubbers.java @@ -53,7 +53,7 @@ public class TxTesterScrubbers { } @Override - public void visit(Resource resource) { + public void visit(Object context, Resource resource) { if (resource instanceof DomainResource) { DomainResource dr = (DomainResource) resource; dr.getExtension().removeIf(ext -> !isManagedExtension(ext)); @@ -61,7 +61,7 @@ public class TxTesterScrubbers { } @Override - public void visit(Element element) { + public void visit(Object context, Element element) { element.getExtension().removeIf(ext -> !isManagedExtension(ext)); } } @@ -69,7 +69,7 @@ public class TxTesterScrubbers { public static void scrubDR(DomainResource dr, boolean tight) { dr.setText(null); dr.setMeta(null); - new ElementVisitor(new TxTesterScrubberVisitor(tight)).visit(dr); + new ElementVisitor(new TxTesterScrubberVisitor(tight)).visit(null, dr); } public static void scrubVS(ValueSet vs, boolean tight) { diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/ExternalTerminologyServiceTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/ExternalTerminologyServiceTests.java index 99ba5e71c..11eb70b1d 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/ExternalTerminologyServiceTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/ExternalTerminologyServiceTests.java @@ -46,8 +46,8 @@ public class ExternalTerminologyServiceTests implements ITxTesterLoader { private JsonObject test; } - private static final String SERVER = FhirSettings.getTxFhirDevelopment(); -// private static final String SERVER = FhirSettings.getTxFhirLocal(); +// private static final String SERVER = FhirSettings.getTxFhirDevelopment(); + private static final String SERVER = FhirSettings.getTxFhirLocal(); // private static final String SERVER = "https://r4.ontoserver.csiro.au/fhir"; @@ -81,6 +81,7 @@ public class ExternalTerminologyServiceTests implements ITxTesterLoader { private JsonObjectPair setup; private String version = "5.0.0"; private static TxTester tester; + private List modes = new ArrayList<>(); public ExternalTerminologyServiceTests(String name, JsonObjectPair setup) { this.setup = setup; @@ -93,7 +94,7 @@ public class ExternalTerminologyServiceTests implements ITxTesterLoader { if (tester == null) { tester = new TxTester(this, SERVER, true); } - String err = tester.executeTest(setup.suite, setup.test); + String err = tester.executeTest(setup.suite, setup.test, modes); Assertions.assertTrue(err == null, err); } else { Assertions.assertTrue(true); diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/TerminologyServiceTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/TerminologyServiceTests.java index d9fe67d7b..7035ce849 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/TerminologyServiceTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/TerminologyServiceTests.java @@ -117,7 +117,6 @@ public class TerminologyServiceTests { public void test() throws Exception { if (baseEngine == null) { baseEngine = TestUtilities.getValidationEngineNoTxServer("hl7.fhir.r5.core#5.0.0", FhirPublication.R5, "5.0.0"); - } ValidationEngine engine = new ValidationEngine(this.baseEngine); for (String s : setup.suite.forceArray("setup").asStrings()) { diff --git a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/all-systems.cache b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/all-systems.cache index f71ba85ea..438753f0f 100644 --- a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/all-systems.cache +++ b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/all-systems.cache @@ -143,3 +143,18 @@ v: { "system" : "http://unstats.un.org/unsd/methods/m49/m49.htm" } ------------------------------------------------------------------------------------- +{"code" : { + "code" : "json" +}, "url": "http://hl7.org/fhir/ValueSet/mimetypes", "version": "5.0.0", "langs":"[en]", "useServer":"true", "useClient":"true", "guessSystem":"true", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"false", "profile": { + "resourceType" : "Parameters", + "parameter" : [{ + "name" : "profile-url", + "valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891" + }] +}}#### +v: { + "display" : "json", + "code" : "json", + "system" : "urn:ietf:bcp:13" +} +------------------------------------------------------------------------------------- From b85b588825b47943cc2dee6c7f9aa37dbb49f4f4 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 11 Aug 2023 12:24:08 +1000 Subject: [PATCH 02/15] restructure validators to properly validate deprecated dependencies --- .../utils/validation/IResourceValidator.java | 4 ++ .../hl7/fhir/validation/BaseValidator.java | 69 ++++++++++++++++++- .../instance/InstanceValidator.java | 45 ++++++++---- .../instance/type/BundleValidator.java | 31 +++++---- .../instance/type/CodeSystemValidator.java | 11 +-- .../instance/type/ConceptMapValidator.java | 12 +--- .../instance/type/MeasureValidator.java | 14 +--- .../instance/type/QuestionnaireValidator.java | 10 +-- .../type/SearchParameterValidator.java | 11 +-- .../type/StructureDefinitionValidator.java | 20 +++--- .../instance/type/StructureMapValidator.java | 12 +--- .../instance/type/ValueSetValidator.java | 17 +---- 12 files changed, 140 insertions(+), 116 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IResourceValidator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IResourceValidator.java index 7f26bb8f9..038c05cd2 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IResourceValidator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IResourceValidator.java @@ -102,6 +102,10 @@ public interface IResourceValidator { boolean isForPublication(); IResourceValidator setForPublication(boolean forPublication); + public boolean isWarnOnDraftOrExperimental(); + + public IResourceValidator setWarnOnDraftOrExperimental(boolean warnOnDraftOrExperimental); + /** * Whether being unable to resolve a profile in found in Resource.meta.profile or ElementDefinition.type.profile or targetProfile is an error or just a warning */ diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index db1eda891..291693807 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -7,8 +7,10 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; /* Copyright (c) 2011+, HL7, Inc. @@ -51,16 +53,20 @@ import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.elementmodel.JsonParser; import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.model.Base; +import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.DomainResource; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; import org.hl7.fhir.r5.terminologies.ValueSetUtilities; +import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.r5.utils.XVerExtensionManager.XVerExtensionStatus; import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier.IValidationContextResourceLoader; import org.hl7.fhir.utilities.FhirPublication; +import org.hl7.fhir.utilities.StandardsStatus; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationMessage; @@ -144,18 +150,20 @@ public class BaseValidator implements IValidationContextResourceLoader { protected final String BUNDLE = "Bundle"; protected final String LAST_UPDATED = "lastUpdated"; - + protected BaseValidator parent; protected Source source; protected IWorkerContext context; protected TimeTracker timeTracker = new TimeTracker(); protected XVerExtensionManager xverManager; protected List trackedMessages = new ArrayList<>(); protected List messagesToRemove = new ArrayList<>(); - private ValidationLevel level = ValidationLevel.HINTS; + protected ValidationLevel level = ValidationLevel.HINTS; protected Coding jurisdiction; protected boolean allowExamples; protected boolean forPublication; protected boolean debug; + protected boolean warnOnDraftOrExperimental; + protected Set statusWarnings = new HashSet<>(); public BaseValidator(IWorkerContext context, XVerExtensionManager xverManager, boolean debug) { super(); @@ -167,6 +175,24 @@ public class BaseValidator implements IValidationContextResourceLoader { this.debug = debug; } + public BaseValidator(BaseValidator parent) { + super(); + this.parent = parent; + this.context = parent.context; + this.xverManager = parent.xverManager; + this.debug = parent.debug; + this.source = parent.source; + this.timeTracker = parent.timeTracker; + this.trackedMessages = parent.trackedMessages; + this.messagesToRemove = parent.messagesToRemove; + this.level = parent.level; + this.allowExamples = parent.allowExamples; + this.forPublication = parent.forPublication; + this.debug = parent.debug; + this.warnOnDraftOrExperimental = parent.warnOnDraftOrExperimental; + this.statusWarnings = parent.statusWarnings; + } + private boolean doingLevel(IssueSeverity error) { switch (error) { case ERROR: @@ -1277,5 +1303,42 @@ public class BaseValidator implements IValidationContextResourceLoader { public void setDebug(boolean debug) { this.debug = debug; } - + + + protected void checkDefinitionStatus(List errors, Element element, String path, StructureDefinition ex, CanonicalResource source, String type) { + String vurl = ex.getVersionedUrl(); + + StandardsStatus standardsStatus = ToolingExtensions.getStandardsStatus(ex); + if (standardsStatus == StandardsStatus.DEPRECATED) { + if (!statusWarnings.contains(vurl+":DEPRECATED")) { + statusWarnings.add(vurl+":DEPRECATED"); + hint(errors, "2023-08-10", IssueType.EXPIRED, element.line(), element.col(), path, false, I18nConstants.MSG_DEPENDS_ON_DEPRECATED, type, vurl); + } + } else if (standardsStatus == StandardsStatus.WITHDRAWN) { + if (!statusWarnings.contains(vurl+":WITHDRAWN")) { + statusWarnings.add(vurl+":WITHDRAWN"); + hint(errors, "2023-08-10", IssueType.EXPIRED, element.line(), element.col(), path, false, I18nConstants.MSG_DEPENDS_ON_WITHDRAWN, type, vurl); + } + } else if (ex.getStatus() == PublicationStatus.RETIRED) { + if (!statusWarnings.contains(vurl+":RETIRED")) { + statusWarnings.add(vurl+":RETIRED"); + hint(errors, "2023-08-10", IssueType.EXPIRED, element.line(), element.col(), path, false, I18nConstants.MSG_DEPENDS_ON_RETIRED, type, vurl); + } + } else if (false && warnOnDraftOrExperimental && source != null) { + // for now, this is disabled; these warnings are just everywhere, and it's an intractible problem. + // working this through QA in IG publisher + if (ex.getExperimental() && !source.getExperimental()) { + if (!statusWarnings.contains(vurl+":Experimental")) { + statusWarnings.add(vurl+":Experimental"); + hint(errors, "2023-08-10", IssueType.BUSINESSRULE, element.line(), element.col(), path, false, I18nConstants.MSG_DEPENDS_ON_EXPERIMENTAL, type, vurl); + } + } else if (ex.getStatus() == PublicationStatus.DRAFT && source.getStatus() != PublicationStatus.DRAFT) { + if (!statusWarnings.contains(vurl+":Draft")) { + statusWarnings.add(vurl+":Draft"); + hint(errors, "2023-08-10", IssueType.BUSINESSRULE, element.line(), element.col(), path, false, I18nConstants.MSG_DEPENDS_ON_DRAFT, type, vurl); + } + } + } + } + } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index fa6ada3b0..d5f10b009 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -52,6 +52,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Stack; import java.util.UUID; import javax.annotation.Nonnull; @@ -114,6 +115,7 @@ import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; import org.hl7.fhir.r5.model.Enumeration; import org.hl7.fhir.r5.model.Enumerations.BindingStrength; import org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode; +import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; import org.hl7.fhir.r5.model.ExpressionNode; import org.hl7.fhir.r5.model.ExpressionNode.CollectionStatus; import org.hl7.fhir.r5.model.Extension; @@ -171,6 +173,7 @@ import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.SIDUtilities; +import org.hl7.fhir.utilities.StandardsStatus; import org.hl7.fhir.utilities.UnicodeUtilities; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities.DecimalStatus; @@ -1938,9 +1941,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else { rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", def.getIsModifier() == isModifier, I18nConstants.EXTENSION_EXT_MODIFIER_MISMATCHN); } - // two questions + // 1. can this extension be used here? checkExtensionContext(hostContext.getAppContext(), errors, resource, container, ex, containerStack, hostContext, isModifier); + checkDefinitionStatus(errors, element, path, ex, profile, context.formatMessage(I18nConstants.MSG_DEPENDS_ON_EXTENSION)); if (isModifier) rule(errors, NO_RULE_DATE, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(), I18nConstants.EXTENSION_EXT_MODIFIER_Y, url); @@ -1964,8 +1968,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return ex; } - - private boolean hasExtensionSlice(StructureDefinition profile, String sliceName) { for (ElementDefinition ed : profile.getSnapshot().getElement()) { if (ed.getPath().equals("Extension.extension.url") && ed.hasFixed() && sliceName.equals(ed.getFixed().primitiveValue())) { @@ -5139,31 +5141,31 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat public boolean checkSpecials(ValidatorHostContext hostContext, List errors, Element element, NodeStack stack, boolean checkSpecials, PercentageTracker pct, ValidationMode mode) { // specific known special validations if (element.getType().equals(BUNDLE)) { - return new BundleValidator(context, debug, serverBase, this, xverManager, jurisdiction).validateBundle(errors, element, stack, checkSpecials, hostContext, pct, mode); + return new BundleValidator(this, serverBase).validateBundle(errors, element, stack, checkSpecials, hostContext, pct, mode); } else if (element.getType().equals("Observation")) { return validateObservation(errors, element, stack); } else if (element.getType().equals("Questionnaire")) { - return new QuestionnaireValidator(context, debug, myEnableWhenEvaluator, fpe, timeTracker, questionnaireMode, xverManager, jurisdiction).validateQuestionannaire(errors, element, element, stack); + return new QuestionnaireValidator(this, myEnableWhenEvaluator, fpe, questionnaireMode).validateQuestionannaire(errors, element, element, stack); } else if (element.getType().equals("QuestionnaireResponse")) { - return new QuestionnaireValidator(context, debug, myEnableWhenEvaluator, fpe, timeTracker, questionnaireMode, xverManager, jurisdiction).validateQuestionannaireResponse(hostContext, errors, element, stack); + return new QuestionnaireValidator(this, myEnableWhenEvaluator, fpe, questionnaireMode).validateQuestionannaireResponse(hostContext, errors, element, stack); } else if (element.getType().equals("Measure")) { - return new MeasureValidator(context, debug, timeTracker, xverManager, jurisdiction, this).validateMeasure(hostContext, errors, element, stack); + return new MeasureValidator(this).validateMeasure(hostContext, errors, element, stack); } else if (element.getType().equals("MeasureReport")) { - return new MeasureValidator(context, debug, timeTracker, xverManager, jurisdiction, this).validateMeasureReport(hostContext, errors, element, stack); + return new MeasureValidator(this).validateMeasureReport(hostContext, errors, element, stack); } else if (element.getType().equals("CapabilityStatement")) { return validateCapabilityStatement(errors, element, stack); } else if (element.getType().equals("CodeSystem")) { - return new CodeSystemValidator(context, debug, timeTracker, this, xverManager, jurisdiction).validateCodeSystem(errors, element, stack, baseOptions.withLanguage(stack.getWorkingLang())); + return new CodeSystemValidator(this).validateCodeSystem(errors, element, stack, baseOptions.withLanguage(stack.getWorkingLang())); } else if (element.getType().equals("ConceptMap")) { - return new ConceptMapValidator(context, debug, timeTracker, this, xverManager, jurisdiction).validateConceptMap(errors, element, stack, baseOptions.withLanguage(stack.getWorkingLang())); + return new ConceptMapValidator(this).validateConceptMap(errors, element, stack, baseOptions.withLanguage(stack.getWorkingLang())); } else if (element.getType().equals("SearchParameter")) { - return new SearchParameterValidator(context, debug, timeTracker, fpe, xverManager, jurisdiction).validateSearchParameter(errors, element, stack); + return new SearchParameterValidator(this, fpe).validateSearchParameter(errors, element, stack); } else if (element.getType().equals("StructureDefinition")) { - return new StructureDefinitionValidator(context, debug, timeTracker, fpe, wantCheckSnapshotUnchanged, xverManager, jurisdiction, forPublication).validateStructureDefinition(errors, element, stack); + return new StructureDefinitionValidator(this, fpe, wantCheckSnapshotUnchanged).validateStructureDefinition(errors, element, stack); } else if (element.getType().equals("StructureMap")) { - return new StructureMapValidator(context, debug, timeTracker, fpe, xverManager,profileUtilities, jurisdiction).validateStructureMap(errors, element, stack); + return new StructureMapValidator(this, fpe, profileUtilities).validateStructureMap(errors, element, stack); } else if (element.getType().equals("ValueSet")) { - return new ValueSetValidator(context, debug, timeTracker, this, xverManager, jurisdiction, allowExamples).validateValueSet(errors, element, stack); + return new ValueSetValidator(this).validateValueSet(errors, element, stack); } else { return true; } @@ -6699,9 +6701,22 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat codingObserver.setCheckIPSCodes(checkIPSCodes); } - public InstanceValidator setForPublication(boolean forPublication) { this.forPublication = forPublication; + if (forPublication) { + warnOnDraftOrExperimental = true; + } return this; } + + public boolean isWarnOnDraftOrExperimental() { + return warnOnDraftOrExperimental; + } + + public InstanceValidator setWarnOnDraftOrExperimental(boolean warnOnDraftOrExperimental) { + this.warnOnDraftOrExperimental = warnOnDraftOrExperimental; + return this; + } + + } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java index e115d7cb7..b804e6558 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java @@ -33,13 +33,10 @@ import org.hl7.fhir.validation.instance.utils.ValidatorHostContext; public class BundleValidator extends BaseValidator { public final static String URI_REGEX3 = "((http|https)://([A-Za-z0-9\\\\\\.\\:\\%\\$]*\\/)*)?(Account|ActivityDefinition|AllergyIntolerance|AdverseEvent|Appointment|AppointmentResponse|AuditEvent|Basic|Binary|BodySite|Bundle|CapabilityStatement|CarePlan|CareTeam|ChargeItem|Claim|ClaimResponse|ClinicalImpression|CodeSystem|Communication|CommunicationRequest|CompartmentDefinition|Composition|ConceptMap|Condition (aka Problem)|Consent|Contract|Coverage|DataElement|DetectedIssue|Device|DeviceComponent|DeviceMetric|DeviceRequest|DeviceUseStatement|DiagnosticReport|DocumentManifest|DocumentReference|EligibilityRequest|EligibilityResponse|Encounter|Endpoint|EnrollmentRequest|EnrollmentResponse|EpisodeOfCare|ExpansionProfile|ExplanationOfBenefit|FamilyMemberHistory|Flag|Goal|GraphDefinition|Group|GuidanceResponse|HealthcareService|ImagingManifest|ImagingStudy|Immunization|ImmunizationRecommendation|ImplementationGuide|Library|Linkage|List|Location|Measure|MeasureReport|Media|Medication|MedicationAdministration|MedicationDispense|MedicationRequest|MedicationStatement|MessageDefinition|MessageHeader|NamingSystem|NutritionOrder|Observation|OperationDefinition|OperationOutcome|Organization|Parameters|Patient|PaymentNotice|PaymentReconciliation|Person|PlanDefinition|Practitioner|PractitionerRole|Procedure|ProcedureRequest|ProcessRequest|ProcessResponse|Provenance|Questionnaire|QuestionnaireResponse|ReferralRequest|RelatedPerson|RequestGroup|ResearchStudy|ResearchSubject|RiskAssessment|Schedule|SearchParameter|Sequence|ServiceDefinition|Slot|Specimen|StructureDefinition|StructureMap|Subscription|Substance|SupplyDelivery|SupplyRequest|Task|TestScript|TestReport|ValueSet|VisionPrescription)\\/[A-Za-z0-9\\-\\.]{1,64}(\\/_history\\/[A-Za-z0-9\\-\\.]{1,64})?"; private String serverBase; - private InstanceValidator validator; - public BundleValidator(IWorkerContext context, boolean debug, String serverBase, InstanceValidator validator, XVerExtensionManager xverManager, Coding jurisdiction) { - super(context, xverManager, debug); + public BundleValidator(BaseValidator parent, String serverBase) { + super(parent); this.serverBase = serverBase; - this.validator = validator; - this.jurisdiction = jurisdiction; } public boolean validateBundle(List errors, Element bundle, NodeStack stack, boolean checkSpecials, ValidatorHostContext hostContext, PercentageTracker pct, ValidationMode mode) { @@ -118,19 +115,19 @@ public class BundleValidator extends BaseValidator { String rtype = entry.getNamedChild(RESOURCE).fhirType(); int rcount = counter.containsKey(rtype) ? counter.get(rtype)+1 : 0; counter.put(rtype, rcount); - for (BundleValidationRule bvr : validator.getBundleValidationRules()) { + for (BundleValidationRule bvr : validator().getBundleValidationRules()) { if (meetsRule(bvr, rtype, rcount, count)) { - StructureDefinition defn = validator.getContext().fetchResource(StructureDefinition.class, bvr.getProfile()); + StructureDefinition defn = context.fetchResource(StructureDefinition.class, bvr.getProfile()); if (defn == null) { - throw new Error(validator.getContext().formatMessage(I18nConstants.BUNDLE_RULE_PROFILE_UNKNOWN, bvr.getRule(), bvr.getProfile())); + throw new Error(context.formatMessage(I18nConstants.BUNDLE_RULE_PROFILE_UNKNOWN, bvr.getRule(), bvr.getProfile())); } else { Element res = entry.getNamedChild(RESOURCE); NodeStack rstack = estack.push(res, -1, null, null); - if (validator.isCrumbTrails()) { + if (validator().isCrumbTrails()) { res.addMessage(signpost(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, res.line(), res.col(), stack.getLiteralPath(), I18nConstants.VALIDATION_VAL_PROFILE_SIGNPOST_BUNDLE_PARAM, defn.getUrl())); } stack.resetIds(); - ok = validator.startInner(hostContext, errors, res, res, defn, rstack, false, pct, mode) && ok; + ok = validator().startInner(hostContext, errors, res, res, defn, rstack, false, pct, mode) && ok; } } } @@ -142,6 +139,10 @@ public class BundleValidator extends BaseValidator { return ok; } + private InstanceValidator validator() { + return (InstanceValidator) parent; + } + private boolean validateLink(List errors, Element bundle, List links, Element link, NodeStack stack, String type, List entries) { switch (type) { case "document": return validateDocumentLink(errors, bundle, links, link, stack, entries); @@ -802,22 +803,22 @@ public class BundleValidator extends BaseValidator { public boolean meetsRule(BundleValidationRule bvr, String rtype, int rcount, int count) { if (bvr.getRule() == null) { - throw new Error(validator.getContext().formatMessage(I18nConstants.BUNDLE_RULE_NONE)); + throw new Error(context.formatMessage(I18nConstants.BUNDLE_RULE_NONE)); } String rule = bvr.getRule(); String t = rule.contains(":") ? rule.substring(0, rule.indexOf(":")) : Utilities.isInteger(rule) ? null : rule; String index = rule.contains(":") ? rule.substring(rule.indexOf(":")+1) : Utilities.isInteger(rule) ? rule : null; if (Utilities.noString(t) && Utilities.noString(index)) { - throw new Error(validator.getContext().formatMessage(I18nConstants.BUNDLE_RULE_NONE)); + throw new Error(context.formatMessage(I18nConstants.BUNDLE_RULE_NONE)); } if (!Utilities.noString(t)) { - if (!validator.getContext().getResourceNames().contains(t)) { - throw new Error(validator.getContext().formatMessage(I18nConstants.BUNDLE_RULE_UNKNOWN, t)); + if (!context.getResourceNames().contains(t)) { + throw new Error(context.formatMessage(I18nConstants.BUNDLE_RULE_UNKNOWN, t)); } } if (!Utilities.noString(index)) { if (!Utilities.isInteger(index)) { - throw new Error(validator.getContext().formatMessage(I18nConstants.BUNDLE_RULE_INVALID_INDEX, index)); + throw new Error(context.formatMessage(I18nConstants.BUNDLE_RULE_INVALID_INDEX, index)); } } if (t == null) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/CodeSystemValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/CodeSystemValidator.java index 0a178ce6b..0d60ae90e 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/CodeSystemValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/CodeSystemValidator.java @@ -21,15 +21,8 @@ import org.hl7.fhir.validation.instance.utils.NodeStack; public class CodeSystemValidator extends BaseValidator { - private InstanceValidator parent; - - public CodeSystemValidator(IWorkerContext context, boolean debug, TimeTracker timeTracker, InstanceValidator parent, XVerExtensionManager xverManager, Coding jurisdiction) { - super(context, xverManager, debug); - source = Source.InstanceValidator; - this.timeTracker = timeTracker; - this.jurisdiction = jurisdiction; - this.parent = parent; - + public CodeSystemValidator(BaseValidator parent) { + super(parent); } public boolean validateCodeSystem(List errors, Element cs, NodeStack stack, ValidationOptions options) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ConceptMapValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ConceptMapValidator.java index 9fdaa0fcc..f911bdc59 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ConceptMapValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ConceptMapValidator.java @@ -47,15 +47,9 @@ public class ConceptMapValidator extends BaseValidator { } - - private InstanceValidator parent; - - public ConceptMapValidator(IWorkerContext context, boolean debug, TimeTracker timeTracker, InstanceValidator parent, XVerExtensionManager xverManager, Coding jurisdiction) { - super(context, xverManager, debug); - source = Source.InstanceValidator; - this.timeTracker = timeTracker; - this.jurisdiction = jurisdiction; - this.parent = parent; + + public ConceptMapValidator(BaseValidator parent) { + super(parent); } public boolean validateConceptMap(List errors, Element cm, NodeStack stack, ValidationOptions options) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/MeasureValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/MeasureValidator.java index e35cec9e0..b4f690b36 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/MeasureValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/MeasureValidator.java @@ -11,7 +11,6 @@ import java.util.List; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.elementmodel.JsonParser; import org.hl7.fhir.r5.elementmodel.ObjectConverter; @@ -26,31 +25,22 @@ import org.hl7.fhir.r5.model.Measure.MeasureGroupStratifierComponent; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.renderers.DataRenderer; import org.hl7.fhir.r5.utils.ToolingExtensions; -import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.utilities.FhirPublication; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; -import org.hl7.fhir.utilities.validation.ValidationMessage.Source; import org.hl7.fhir.utilities.xml.XMLUtil; import org.hl7.fhir.validation.BaseValidator; -import org.hl7.fhir.validation.TimeTracker; -import org.hl7.fhir.validation.instance.InstanceValidator; import org.hl7.fhir.validation.instance.utils.NodeStack; import org.hl7.fhir.validation.instance.utils.ValidatorHostContext; import org.w3c.dom.Document; public class MeasureValidator extends BaseValidator { - private InstanceValidator parent; - public MeasureValidator(IWorkerContext context, boolean debug, TimeTracker timeTracker, XVerExtensionManager xverManager, Coding jurisdiction, InstanceValidator parent) { - super(context, xverManager, debug); - source = Source.InstanceValidator; - this.timeTracker = timeTracker; - this.jurisdiction = jurisdiction; - this.parent = parent; + public MeasureValidator(BaseValidator parent) { + super(parent); } public boolean validateMeasure(ValidatorHostContext hostContext, List errors, Element element, NodeStack stack) throws FHIRException { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/QuestionnaireValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/QuestionnaireValidator.java index 37486a9bc..82baf3be9 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/QuestionnaireValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/QuestionnaireValidator.java @@ -9,7 +9,6 @@ import java.util.List; import java.util.Map; import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.elementmodel.ObjectConverter; @@ -26,7 +25,6 @@ import org.hl7.fhir.r5.model.TimeType; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass; import org.hl7.fhir.r5.utils.FHIRPathEngine; -import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier; import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier.ValidationContextResourceProxy; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; @@ -39,7 +37,6 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.Source; import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.validation.BaseValidator; -import org.hl7.fhir.validation.TimeTracker; import org.hl7.fhir.validation.cli.utils.QuestionnaireMode; import org.hl7.fhir.validation.instance.EnableWhenEvaluator; import org.hl7.fhir.validation.instance.EnableWhenEvaluator.QStack; @@ -128,14 +125,11 @@ public class QuestionnaireValidator extends BaseValidator { private FHIRPathEngine fpe; private QuestionnaireMode questionnaireMode; - public QuestionnaireValidator(IWorkerContext context, boolean debug, EnableWhenEvaluator myEnableWhenEvaluator, FHIRPathEngine fpe, TimeTracker timeTracker, QuestionnaireMode questionnaireMode, XVerExtensionManager xverManager, Coding jurisdiction) { - super(context, xverManager, debug); - source = Source.InstanceValidator; + public QuestionnaireValidator(BaseValidator parent, EnableWhenEvaluator myEnableWhenEvaluator, FHIRPathEngine fpe, QuestionnaireMode questionnaireMode) { + super(parent); this.myEnableWhenEvaluator = myEnableWhenEvaluator; this.fpe = fpe; - this.timeTracker = timeTracker; this.questionnaireMode = questionnaireMode; - this.jurisdiction = jurisdiction; } public boolean validateQuestionannaire(List errors, Element element, Element element2, NodeStack stack) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/SearchParameterValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/SearchParameterValidator.java index 258ae05d3..09ea98441 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/SearchParameterValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/SearchParameterValidator.java @@ -5,22 +5,18 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.elementmodel.Element; -import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.ExpressionNode; import org.hl7.fhir.r5.model.ExpressionNode.Kind; import org.hl7.fhir.r5.model.ExpressionNode.Operation; import org.hl7.fhir.r5.model.SearchParameter; import org.hl7.fhir.r5.utils.FHIRPathEngine; -import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.Source; import org.hl7.fhir.validation.BaseValidator; -import org.hl7.fhir.validation.TimeTracker; import org.hl7.fhir.validation.instance.utils.NodeStack; public class SearchParameterValidator extends BaseValidator { @@ -36,12 +32,9 @@ public class SearchParameterValidator extends BaseValidator { private FHIRPathEngine fpe; - public SearchParameterValidator(IWorkerContext context, boolean debug, TimeTracker timeTracker, FHIRPathEngine fpe, XVerExtensionManager xverManager, Coding jurisdiction) { - super(context, xverManager, debug); - source = Source.InstanceValidator; + public SearchParameterValidator(BaseValidator parent, FHIRPathEngine fpe) { + super (parent); this.fpe = fpe; - this.timeTracker = timeTracker; - this.jurisdiction = jurisdiction; } public boolean validateSearchParameter(List errors, Element cs, NodeStack stack) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java index 6ed6958fb..959da55a4 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java @@ -16,7 +16,6 @@ import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; -import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.elementmodel.Manager; @@ -28,6 +27,7 @@ import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; +import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; import org.hl7.fhir.r5.model.ExpressionNode; import org.hl7.fhir.r5.model.Extension; import org.hl7.fhir.r5.model.Resource; @@ -37,16 +37,13 @@ import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.ToolingExtensions; -import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; -import org.hl7.fhir.utilities.validation.ValidationMessage.Source; import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.validation.BaseValidator; -import org.hl7.fhir.validation.TimeTracker; import org.hl7.fhir.validation.instance.utils.NodeStack; public class StructureDefinitionValidator extends BaseValidator { @@ -63,14 +60,10 @@ public class StructureDefinitionValidator extends BaseValidator { private FHIRPathEngine fpe; private boolean wantCheckSnapshotUnchanged; - public StructureDefinitionValidator(IWorkerContext context, boolean debug, TimeTracker timeTracker, FHIRPathEngine fpe, boolean wantCheckSnapshotUnchanged, XVerExtensionManager xverManager, Coding jurisdiction, boolean forPublication) { - super(context, xverManager, debug); - source = Source.InstanceValidator; + public StructureDefinitionValidator(BaseValidator parent, FHIRPathEngine fpe, boolean wantCheckSnapshotUnchanged) { + super(parent); this.fpe = fpe; - this.timeTracker = timeTracker; this.wantCheckSnapshotUnchanged = wantCheckSnapshotUnchanged; - this.jurisdiction = jurisdiction; - this.forPublication = forPublication; } public boolean validateStructureDefinition(List errors, Element src, NodeStack stack) { @@ -831,7 +824,7 @@ public class StructureDefinitionValidator extends BaseValidator { } else { for (Element profile : profiles) { - ok = validateTypeProfile(errors, profile, code, stack.push(profile, -1, null, null), path) && ok; + ok = validateTypeProfile(errors, profile, code, stack.push(profile, -1, null, null), path, sd) && ok; } profiles = type.getChildrenByName("targetProfile"); for (Element profile : profiles) { @@ -906,7 +899,7 @@ public class StructureDefinitionValidator extends BaseValidator { return codes; } - private boolean validateTypeProfile(List errors, Element profile, String code, NodeStack stack, String path) { + private boolean validateTypeProfile(List errors, Element profile, String code, NodeStack stack, String path, StructureDefinition source) { boolean ok = true; String p = profile.primitiveValue(); StructureDefinition sd = context.fetchResource(StructureDefinition.class, p); @@ -921,6 +914,7 @@ public class StructureDefinitionValidator extends BaseValidator { ok = rule(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path) && ok; } else { if (t.getType().equals("Extension")) { + checkDefinitionStatus(errors, profile, path, sd, source, context.formatMessage(I18nConstants.MSG_DEPENDS_ON_EXTENSION)); boolean isModifierDefinition = checkIsModifierExtension(sd); boolean isModifierContext = path.endsWith(".modifierExtension"); if (isModifierDefinition) { @@ -928,6 +922,8 @@ public class StructureDefinitionValidator extends BaseValidator { } else { ok = rule(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.getLiteralPath(), !isModifierContext, I18nConstants.SD_ED_TYPE_PROFILE_IS_MODIFIER, p, t, code, path) && ok; } + } else { + checkDefinitionStatus(errors, profile, path, sd, source, context.formatMessage(I18nConstants.MSG_DEPENDS_ON_PROFILE)); } } } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureMapValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureMapValidator.java index 95debcec8..3601d6cb2 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureMapValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureMapValidator.java @@ -6,7 +6,6 @@ import java.util.List; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; import org.hl7.fhir.r5.context.ContextUtilities; -import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.ConceptMap; @@ -29,7 +28,6 @@ import org.hl7.fhir.r5.terminologies.ConceptMapUtilities; import org.hl7.fhir.r5.terminologies.ValueSetUtilities; import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; import org.hl7.fhir.r5.utils.FHIRPathEngine; -import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.r5.utils.structuremap.ResolvedGroup; import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; @@ -38,9 +36,7 @@ import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; -import org.hl7.fhir.utilities.validation.ValidationMessage.Source; import org.hl7.fhir.validation.BaseValidator; -import org.hl7.fhir.validation.TimeTracker; import org.hl7.fhir.validation.instance.utils.NodeStack; public class StructureMapValidator extends BaseValidator { @@ -300,15 +296,11 @@ public class StructureMapValidator extends BaseValidator { private ContextUtilities cu; private List imports = new ArrayList<>(); - public StructureMapValidator(IWorkerContext context, boolean debug, TimeTracker timeTracker, FHIRPathEngine fpe, XVerExtensionManager xverManager, ProfileUtilities profileUtilities, Coding jurisdiction) { - super(context, xverManager, debug); - source = Source.InstanceValidator; + public StructureMapValidator(BaseValidator parent, FHIRPathEngine fpe, ProfileUtilities profileUtilities) { + super(parent); this.fpe = fpe; - this.timeTracker = timeTracker; - this.jurisdiction = jurisdiction; this.profileUtilities = profileUtilities; this.cu = new ContextUtilities(context); - } public boolean isAbstractType(List list) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java index 86ebb6b4f..881188cf5 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java @@ -3,7 +3,6 @@ package org.hl7.fhir.validation.instance.type; import java.util.ArrayList; import java.util.List; -import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext.CodingValidationRequest; import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; import org.hl7.fhir.r5.elementmodel.Element; @@ -11,16 +10,13 @@ import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass; -import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; -import org.hl7.fhir.utilities.validation.ValidationMessage.Source; import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.validation.BaseValidator; -import org.hl7.fhir.validation.TimeTracker; import org.hl7.fhir.validation.codesystem.CodeSystemChecker; import org.hl7.fhir.validation.codesystem.GeneralCodeSystemChecker; import org.hl7.fhir.validation.codesystem.SnomedCTChecker; @@ -54,15 +50,8 @@ public class ValueSetValidator extends BaseValidator { } - private InstanceValidator parent; - - public ValueSetValidator(IWorkerContext context, boolean debug, TimeTracker timeTracker, InstanceValidator parent, XVerExtensionManager xverManager, Coding jurisdiction, boolean allowExamples) { - super(context, xverManager, debug); - source = Source.InstanceValidator; - this.timeTracker = timeTracker; - this.parent = parent; - this.jurisdiction = jurisdiction; - this.allowExamples = allowExamples; + public ValueSetValidator(InstanceValidator parent) { + super(parent); } public boolean validateValueSet(List errors, Element vs, NodeStack stack) { @@ -169,7 +158,7 @@ public class ValueSetValidator extends BaseValidator { } cc++; } - if (parent.isValidateValueSetCodesOnTxServer() && batch.size() > 0 & !context.isNoTerminologyServer()) { + if (((InstanceValidator) parent).isValidateValueSetCodesOnTxServer() && batch.size() > 0 & !context.isNoTerminologyServer()) { long t = System.currentTimeMillis(); if (parent.isDebug()) { System.out.println(" : Validate "+batch.size()+" codes from "+system+" for "+vsid); From d9df51fa63c51f7869344ca965d0a3fa88d384bd Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 11 Aug 2023 12:24:25 +1000 Subject: [PATCH 03/15] Add test case for PE Engine based on issue #1343 --- .../src/test/java/org/hl7/fhir/r5/profiles/PEModelTest1343.java | 1 - 1 file changed, 1 deletion(-) diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PEModelTest1343.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PEModelTest1343.java index 23b84de31..a394fc140 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PEModelTest1343.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/profiles/PEModelTest1343.java @@ -28,7 +28,6 @@ public class PEModelTest1343 { } } - @Test public void testPatientCreate() throws Exception { CreatePatientFromProfile("http://interopsante.org/fhir/StructureDefinition/FrPatient"); From 9625f9b81860c17a6b31e4efeccadc08a93c49fa Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 11 Aug 2023 12:24:38 +1000 Subject: [PATCH 04/15] remove spurious logging --- .../src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java | 1 - 1 file changed, 1 deletion(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java index b0b8747e3..26a617f6e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java @@ -6325,7 +6325,6 @@ public class FHIRPathEngine { SourcedChildDefinitions childDefinitions = profileUtilities.getChildMap(sd, element.getElement()); for (ElementDefinition t : childDefinitions.getList()) { if (t.getPath().endsWith(".extension") && t.hasSliceName()) { - System.out.println("t: "+t.getId()); StructureDefinition exsd = (t.getType() == null || t.getType().isEmpty() || t.getType().get(0).getProfile().isEmpty()) ? null : worker.fetchResource(StructureDefinition.class, t.getType().get(0).getProfile().get(0).getValue(), profile); while (exsd != null && !exsd.getBaseDefinition().equals("http://hl7.org/fhir/StructureDefinition/Extension")) { From 1208d691b247fa70bd8f5b51ad12998cfde39347 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 11 Aug 2023 12:25:01 +1000 Subject: [PATCH 05/15] Fix loading issues in test mode for simplifier packages --- .../java/org/hl7/fhir/r5/context/SimpleWorkerContext.java | 6 +++++- .../main/java/org/hl7/fhir/utilities/npm/NpmPackage.java | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java index 002d4dadb..86efc6b4a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java @@ -498,7 +498,11 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon for (PackageResourceInformation pri : pi.listIndexedResources(types)) { if (!pri.getFilename().contains("ig-r4") && (loader == null || loader.wantLoad(pi, pri))) { try { - registerResourceFromPackage(new PackageResourceLoader(pri, loader), new PackageInformation(pi)); + if (!pri.hasId()) { + loadDefinitionItem(pri.getFilename(), new FileInputStream(pri.getFilename()), loader, null, new PackageInformation(pi)); + } else { + registerResourceFromPackage(new PackageResourceLoader(pri, loader), new PackageInformation(pi)); + } t++; } catch (FHIRException e) { throw new FHIRException(formatMessage(I18nConstants.ERROR_READING__FROM_PACKAGE__, pri.getFilename(), pi.name(), pi.version(), e.getMessage()), e); diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java index c6882c553..84f1d5c7a 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/NpmPackage.java @@ -144,6 +144,9 @@ public class NpmPackage { public String getSupplements() { return supplements; } + public boolean hasId() { + return !Utilities.noString(id); + } } public class IndexVersionSorter implements Comparator { From b7fd419509fcf6ffbdabeec2b49c0c67b5638684 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 11 Aug 2023 12:25:37 +1000 Subject: [PATCH 06/15] Check experimental / draft in mini terminology service --- .../expansion/ValueSetExpander.java | 10 +++++----- .../utilities/ValueSetProcessBase.java | 18 ++++++++++++++++-- .../validation/ValueSetValidator.java | 8 ++++---- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java index 8f5d6e3af..b5b01b7a9 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java @@ -286,7 +286,7 @@ public class ValueSetExpander extends ValueSetProcessBase { private boolean filterContainsCode(List filters, String system, String code, ValueSetExpansionComponent exp) { for (ValueSet vse : filters) { - checkCanonical(exp, vse); + checkCanonical(exp, vse, focus); if (expansionContainsCode(vse.getExpansion().getContains(), system, code)) return true; } @@ -503,7 +503,7 @@ public class ValueSetExpander extends ValueSetProcessBase { focus.setExpansion(new ValueSet.ValueSetExpansionComponent()); focus.getExpansion().setTimestampElement(DateTimeType.now()); focus.getExpansion().setIdentifier(Factory.createUUID()); - checkCanonical(focus.getExpansion(), focus); + checkCanonical(focus.getExpansion(), focus, focus); for (ParametersParameterComponent p : expParams.getParameter()) { if (Utilities.existsInList(p.getName(), "includeDesignations", "excludeNested", "activeOnly", "offset", "count")) { focus.getExpansion().addParameter().setName(p.getName()).setValue(p.getValue()); @@ -643,7 +643,7 @@ public class ValueSetExpander extends ValueSetProcessBase { throw fail("Unable to find imported value set " + value); } } - checkCanonical(exp, vs); + checkCanonical(exp, vs, focus); if (noInactive) { expParams = expParams.copy(); expParams.addParameter("activeOnly", true); @@ -736,7 +736,7 @@ public class ValueSetExpander extends ValueSetProcessBase { if (imports.isEmpty()) // though this is not supposed to be the case return; ValueSet base = imports.get(0); - checkCanonical(exp, base); + checkCanonical(exp, base, focus); imports.remove(0); base.checkNoModifiers("Imported ValueSet", "expanding"); copyImportContains(base.getExpansion().getContains(), null, expParams, imports, noInactive, base.getExpansion().getProperty(), base, exp); @@ -798,7 +798,7 @@ public class ValueSetExpander extends ValueSetProcessBase { else throw failTSE("Unable to find code system " + inc.getSystem().toString()); } - checkCanonical(exp, cs); + checkCanonical(exp, cs, focus); cs.checkNoModifiers("Code System", "expanding"); if (cs.getContent() != CodeSystemContentMode.COMPLETE && cs.getContent() != CodeSystemContentMode.FRAGMENT) throw failTSE("Code system " + inc.getSystem().toString() + " is incomplete"); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/utilities/ValueSetProcessBase.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/utilities/ValueSetProcessBase.java index 567556b45..49b37c4c9 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/utilities/ValueSetProcessBase.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/utilities/ValueSetProcessBase.java @@ -113,7 +113,7 @@ public class ValueSetProcessBase { return list; } - public void checkCanonical(List issues, String path, CanonicalResource resource) { + public void checkCanonical(List issues, String path, CanonicalResource resource, CanonicalResource source) { if (resource != null) { StandardsStatus standardsStatus = ToolingExtensions.getStandardsStatus(resource); if (standardsStatus == StandardsStatus.DEPRECATED) { @@ -122,6 +122,11 @@ public class ValueSetProcessBase { addToIssues(issues, makeStatusIssue(path, "withdrawn", I18nConstants.MSG_WITHDRAWN, resource)); } else if (resource.getStatus() == PublicationStatus.RETIRED) { addToIssues(issues, makeStatusIssue(path, "retired", I18nConstants.MSG_RETIRED, resource)); + } else if (resource.getExperimental() && !source.getExperimental()) { + addToIssues(issues, makeStatusIssue(path, "experimental", I18nConstants.MSG_EXPERIMENTAL, resource)); + } else if ((resource.getStatus() == PublicationStatus.DRAFT || standardsStatus == StandardsStatus.DRAFT) + && !(source.getStatus() == PublicationStatus.DRAFT || ToolingExtensions.getStandardsStatus(source) == StandardsStatus.DRAFT)) { + addToIssues(issues, makeStatusIssue(path, "draft", I18nConstants.MSG_DRAFT, resource)); } } } @@ -150,7 +155,7 @@ public class ValueSetProcessBase { } } - public void checkCanonical(ValueSetExpansionComponent params, CanonicalResource resource) { + public void checkCanonical(ValueSetExpansionComponent params, CanonicalResource resource, ValueSet source) { if (resource != null) { StandardsStatus standardsStatus = ToolingExtensions.getStandardsStatus(resource); if (standardsStatus == StandardsStatus.DEPRECATED) { @@ -165,6 +170,15 @@ public class ValueSetProcessBase { if (!params.hasParameterValue("warning-retired", resource.getVersionedUrl())) { params.addParameter("warning-retired", new UriType(resource.getVersionedUrl())); } + } else if (resource.getExperimental() && !source.getExperimental()) { + if (!params.hasParameterValue("warning-experimental", resource.getVersionedUrl())) { + params.addParameter("warning-experimental", new UriType(resource.getVersionedUrl())); + } + } else if ((resource.getStatus() == PublicationStatus.DRAFT || standardsStatus == StandardsStatus.DRAFT) + && !(source.getStatus() == PublicationStatus.DRAFT || ToolingExtensions.getStandardsStatus(source) == StandardsStatus.DRAFT)) { + if (!params.hasParameterValue("warning-draft", resource.getVersionedUrl())) { + params.addParameter("warning-draft", new UriType(resource.getVersionedUrl())); + } } } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/validation/ValueSetValidator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/validation/ValueSetValidator.java index df425b3fc..be8748f7e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/validation/ValueSetValidator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/validation/ValueSetValidator.java @@ -351,7 +351,7 @@ public class ValueSetValidator extends ValueSetProcessBase { List issues = new ArrayList<>(); ValidationProcessInfo info = new ValidationProcessInfo(issues); VersionInfo vi = new VersionInfo(this); - checkCanonical(issues, path, valueset); + checkCanonical(issues, path, valueset, valueset); String system = code.hasSystem() ? code.getSystem() : getValueSetSystemOrNull(); if (options.getValueSetMode() != ValueSetMode.CHECK_MEMERSHIP_ONLY) { @@ -412,7 +412,7 @@ public class ValueSetValidator extends ValueSetProcessBase { } } } else { - checkCanonical(issues, path, cs); + checkCanonical(issues, path, cs, valueset); } if (cs != null && cs.hasSupplements()) { String msg = context.formatMessage(I18nConstants.CODESYSTEM_CS_NO_SUPPLEMENT, cs.getUrl()); @@ -975,7 +975,7 @@ public class ValueSetValidator extends ValueSetProcessBase { if (valueset == null) { return false; } - checkCanonical(info.getIssues(), path, valueset); + checkCanonical(info.getIssues(), path, valueset, valueset); Boolean result = false; VersionInfo vi = new VersionInfo(this); @@ -1083,7 +1083,7 @@ public class ValueSetValidator extends ValueSetProcessBase { return null; } } else { - checkCanonical(info.getIssues(), path, cs); + checkCanonical(info.getIssues(), path, cs, valueset); if (valueset.getCompose().hasInactive() && !valueset.getCompose().getInactive()) { if (CodeSystemUtilities.isInactive(cs, code)) { return false; From 991a2defee365a0b45091e6553d57cc35056f4c0 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 11 Aug 2023 12:26:21 +1000 Subject: [PATCH 07/15] Start working on showing changes when rendering --- .../comparison/CanonicalResourceComparer.java | 150 ++++++++++++++++-- .../r5/comparison/CodeSystemComparer.java | 57 +++++-- .../r5/comparison/ComparisonRenderer.java | 2 + .../fhir/r5/comparison/ComparisonSession.java | 20 +++ .../fhir/r5/comparison/StructuralMatch.java | 4 +- .../fhir/r5/comparison/ValueSetComparer.java | 76 ++++++--- .../VersionComparisonAnnotation.java | 143 +++++++++++++++++ .../main/java/org/hl7/fhir/r5/model/Base.java | 17 +- .../fhir/r5/renderers/ValueSetRenderer.java | 18 ++- .../hl7/fhir/utilities/xhtml/XhtmlNode.java | 5 + 10 files changed, 435 insertions(+), 57 deletions(-) create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/VersionComparisonAnnotation.java diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CanonicalResourceComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CanonicalResourceComparer.java index da811905f..d4b8ed94c 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CanonicalResourceComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CanonicalResourceComparer.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Set; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r5.comparison.CanonicalResourceComparer.ChangeAnalysisState; import org.hl7.fhir.r5.comparison.ResourceComparer.MessageCounts; import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.CanonicalType; @@ -31,11 +32,26 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNode; public abstract class CanonicalResourceComparer extends ResourceComparer { + public enum ChangeAnalysisState { + Unknown, NotChanged, Changed, CannotEvaluate; + + boolean noteable() { + return this == Changed || this == CannotEvaluate; + } + } + + public abstract class CanonicalResourceComparison extends ResourceComparison { protected T left; protected T right; protected T union; protected T intersection; + + private ChangeAnalysisState changedMetadata = ChangeAnalysisState.Unknown; + private ChangeAnalysisState changedDefinitions = ChangeAnalysisState.Unknown; + private ChangeAnalysisState changedContent = ChangeAnalysisState.Unknown; + private ChangeAnalysisState changedContentInterpretation = ChangeAnalysisState.Unknown; + protected Map> metadata = new HashMap<>(); public CanonicalResourceComparison(T left, T right) { @@ -80,6 +96,59 @@ public abstract class CanonicalResourceComparer extends ResourceComparer { this.intersection = intersection; } + private ChangeAnalysisState updateState(ChangeAnalysisState newState, ChangeAnalysisState oldState) { + switch (newState) { + case CannotEvaluate: + return ChangeAnalysisState.CannotEvaluate; + case Changed: + if (oldState != ChangeAnalysisState.CannotEvaluate) { + return ChangeAnalysisState.Changed; + } + break; + case NotChanged: + if (oldState == ChangeAnalysisState.Unknown) { + return ChangeAnalysisState.NotChanged; + } + break; + case Unknown: + default: + break; + } + return oldState; + } + + public void updatedMetadataState(ChangeAnalysisState state) { + changedMetadata = updateState(state, changedMetadata); + } + + public void updateDefinitionsState(ChangeAnalysisState state) { + changedDefinitions = updateState(state, changedDefinitions); + } + + public void updateContentState(ChangeAnalysisState state) { + changedContent = updateState(state, changedContent); + } + + public void updateContentInterpretationState(ChangeAnalysisState state) { + changedContentInterpretation = updateState(state, changedContentInterpretation); + } + + public void updatedMetadataState(boolean state) { + changedMetadata = updateState(state ? ChangeAnalysisState.Changed : ChangeAnalysisState.NotChanged, changedMetadata); + } + + public void updateDefinitionsState(boolean state) { + changedDefinitions = updateState(state ? ChangeAnalysisState.Changed : ChangeAnalysisState.NotChanged, changedDefinitions); + } + + public void updateContentState(boolean state) { + changedContent = updateState(state ? ChangeAnalysisState.Changed : ChangeAnalysisState.NotChanged, changedContent); + } + + public void updateContentInterpretationState(boolean state) { + changedContentInterpretation = updateState(state ? ChangeAnalysisState.Changed : ChangeAnalysisState.NotChanged, changedContentInterpretation); + } + @Override protected String toTable() { String s = ""; @@ -96,34 +165,75 @@ public abstract class CanonicalResourceComparer extends ResourceComparer { sm.countMessages(cnts); } } + + protected String changeSummary() { + if (!(changedMetadata.noteable() || changedDefinitions.noteable() || changedContent.noteable() || changedContentInterpretation.noteable())) { + return null; + }; + CommaSeparatedStringBuilder bc = new CommaSeparatedStringBuilder(); + if (changedMetadata == ChangeAnalysisState.CannotEvaluate) { + bc.append("Metadata"); + } + if (changedDefinitions == ChangeAnalysisState.CannotEvaluate) { + bc.append("Definitions"); + } + if (changedContent == ChangeAnalysisState.CannotEvaluate) { + bc.append("Content"); + } + if (changedContentInterpretation == ChangeAnalysisState.CannotEvaluate) { + bc.append("Interpretation"); + } + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + if (changedMetadata == ChangeAnalysisState.Changed) { + b.append("Metadata"); + } + if (changedDefinitions == ChangeAnalysisState.Changed) { + b.append("Definitions"); + } + if (changedContent == ChangeAnalysisState.Changed) { + b.append("Content"); + } + if (changedContentInterpretation == ChangeAnalysisState.Changed) { + b.append("Interpretation"); + } + return (bc.length() == 0 ? "" : "Error Checking: "+bc.toString()+"; ")+ "Changed: "+b.toString(); + } } public CanonicalResourceComparer(ComparisonSession session) { super(session); } - protected void compareMetadata(CanonicalResource left, CanonicalResource right, Map> comp, CanonicalResourceComparison res) { - comparePrimitives("url", left.getUrlElement(), right.getUrlElement(), comp, IssueSeverity.ERROR, res); - comparePrimitives("version", left.getVersionElement(), right.getVersionElement(), comp, IssueSeverity.ERROR, res); - comparePrimitives("name", left.getNameElement(), right.getNameElement(), comp, IssueSeverity.INFORMATION, res); - comparePrimitives("title", left.getTitleElement(), right.getTitleElement(), comp, IssueSeverity.INFORMATION, res); - comparePrimitives("status", left.getStatusElement(), right.getStatusElement(), comp, IssueSeverity.INFORMATION, res); - comparePrimitives("experimental", left.getExperimentalElement(), right.getExperimentalElement(), comp, IssueSeverity.WARNING, res); - comparePrimitives("date", left.getDateElement(), right.getDateElement(), comp, IssueSeverity.INFORMATION, res); - comparePrimitives("publisher", left.getPublisherElement(), right.getPublisherElement(), comp, IssueSeverity.INFORMATION, res); - comparePrimitives("description", left.getDescriptionElement(), right.getDescriptionElement(), comp, IssueSeverity.NULL, res); - comparePrimitives("purpose", left.getPurposeElement(), right.getPurposeElement(), comp, IssueSeverity.NULL, res); - comparePrimitives("copyright", left.getCopyrightElement(), right.getCopyrightElement(), comp, IssueSeverity.INFORMATION, res); - compareCodeableConceptList("jurisdiction", left.getJurisdiction(), right.getJurisdiction(), comp, IssueSeverity.INFORMATION, res, res.getUnion().getJurisdiction(), res.getIntersection().getJurisdiction()); + protected boolean compareMetadata(CanonicalResource left, CanonicalResource right, Map> comp, CanonicalResourceComparison res) { + var changed = false; + changed = comparePrimitives("url", left.getUrlElement(), right.getUrlElement(), comp, IssueSeverity.ERROR, res) || changed; + if (session.getForVersion() == null) { + changed = comparePrimitives("version", left.getVersionElement(), right.getVersionElement(), comp, IssueSeverity.ERROR, res) || changed; + } + changed = comparePrimitives("name", left.getNameElement(), right.getNameElement(), comp, IssueSeverity.INFORMATION, res) || changed; + changed = comparePrimitives("title", left.getTitleElement(), right.getTitleElement(), comp, IssueSeverity.INFORMATION, res) || changed; + changed = comparePrimitives("status", left.getStatusElement(), right.getStatusElement(), comp, IssueSeverity.INFORMATION, res) || changed; + changed = comparePrimitives("experimental", left.getExperimentalElement(), right.getExperimentalElement(), comp, IssueSeverity.WARNING, res) || changed; + if (session.getForVersion() == null) { + changed = comparePrimitives("date", left.getDateElement(), right.getDateElement(), comp, IssueSeverity.INFORMATION, res) || changed; + } + changed = comparePrimitives("publisher", left.getPublisherElement(), right.getPublisherElement(), comp, IssueSeverity.INFORMATION, res) || changed; + changed = comparePrimitives("description", left.getDescriptionElement(), right.getDescriptionElement(), comp, IssueSeverity.NULL, res) || changed; + changed = comparePrimitives("purpose", left.getPurposeElement(), right.getPurposeElement(), comp, IssueSeverity.NULL, res) || changed; + changed = comparePrimitives("copyright", left.getCopyrightElement(), right.getCopyrightElement(), comp, IssueSeverity.INFORMATION, res) || changed; + changed = compareCodeableConceptList("jurisdiction", left.getJurisdiction(), right.getJurisdiction(), comp, IssueSeverity.INFORMATION, res, res.getUnion().getJurisdiction(), res.getIntersection().getJurisdiction()) || changed; + return changed; } - protected void compareCodeableConceptList(String name, List left, List right, Map> comp, IssueSeverity level, CanonicalResourceComparison res, List union, List intersection ) { + protected boolean compareCodeableConceptList(String name, List left, List right, Map> comp, IssueSeverity level, CanonicalResourceComparison res, List union, List intersection ) { + boolean result = false; List matchR = new ArrayList<>(); StructuralMatch combined = new StructuralMatch(); for (CodeableConcept l : left) { CodeableConcept r = findCodeableConceptInList(right, l); if (r == null) { union.add(l); + result = true; combined.getChildren().add(new StructuralMatch(gen(l), vm(IssueSeverity.INFORMATION, "Removed the item '"+gen(l)+"'", fhirType()+"."+name, res.getMessages()))); } else { matchR.add(r); @@ -131,15 +241,20 @@ public abstract class CanonicalResourceComparer extends ResourceComparer { intersection.add(r); StructuralMatch sm = new StructuralMatch(gen(l), gen(r)); combined.getChildren().add(sm); + if (sm.isDifferent()) { + result = true; + } } } for (CodeableConcept r : right) { if (!matchR.contains(r)) { union.add(r); + result = true; combined.getChildren().add(new StructuralMatch(vm(IssueSeverity.INFORMATION, "Added the item '"+gen(r)+"'", fhirType()+"."+name, res.getMessages()), gen(r))); } } - comp.put(name, combined); + comp.put(name, combined); + return result; } @@ -233,7 +348,7 @@ public abstract class CanonicalResourceComparer extends ResourceComparer { } @SuppressWarnings("rawtypes") - protected void comparePrimitives(String name, PrimitiveType l, PrimitiveType r, Map> comp, IssueSeverity level, CanonicalResourceComparison res) { + protected boolean comparePrimitives(String name, PrimitiveType l, PrimitiveType r, Map> comp, IssueSeverity level, CanonicalResourceComparison res) { StructuralMatch match = null; if (l.isEmpty() && r.isEmpty()) { match = new StructuralMatch<>(null, null, null); @@ -255,7 +370,8 @@ public abstract class CanonicalResourceComparer extends ResourceComparer { res.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, fhirType()+"."+name, "Values for "+name+" differ: '"+l.primitiveValue()+"' vs '"+r.primitiveValue()+"'", level)); } } - comp.put(name, match); + comp.put(name, match); + return match.isDifferent(); } protected abstract String fhirType(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CodeSystemComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CodeSystemComparer.java index ced654a50..ad74339d9 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CodeSystemComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CodeSystemComparer.java @@ -33,7 +33,6 @@ public class CodeSystemComparer extends CanonicalResourceComparer { private StructuralMatch combined; private Map propMap = new HashMap<>(); // right to left; left retains it's name - public CodeSystemComparison(CodeSystem left, CodeSystem right) { super(left, right); combined = new StructuralMatch(); // base @@ -54,9 +53,15 @@ public class CodeSystemComparer extends CanonicalResourceComparer { @Override protected String summary() { - return "CodeSystem: "+left.present()+" vs "+right.present(); + String res = "CodeSystem: "+left.present()+" vs "+right.present(); + String ch = changeSummary(); + if (ch != null) { + res = res + ". "+ch; + } + return res; } + @Override protected String fhirType() { return "CodeSystem"; @@ -114,14 +119,18 @@ public class CodeSystemComparer extends CanonicalResourceComparer { cs1.setDate(new Date()); cs1.getProperty().addAll(cs.getProperty()); - compareMetadata(left, right, res.getMetadata(), res); - comparePrimitives("caseSensitive", left.getCaseSensitiveElement(), right.getCaseSensitiveElement(), res.getMetadata(), IssueSeverity.ERROR, res); - comparePrimitives("hierarchyMeaning", left.getHierarchyMeaningElement(), right.getHierarchyMeaningElement(), res.getMetadata(), IssueSeverity.ERROR, res); - comparePrimitives("compositional", left.getCompositionalElement(), right.getCompositionalElement(), res.getMetadata(), IssueSeverity.WARNING, res); - comparePrimitives("versionNeeded", left.getVersionNeededElement(), right.getVersionNeededElement(), res.getMetadata(), IssueSeverity.INFORMATION, res); - comparePrimitives("content", left.getContentElement(), right.getContentElement(), res.getMetadata(), IssueSeverity.WARNING, res); - - compareConcepts(left.getConcept(), right.getConcept(), res.getCombined(), res.getUnion().getConcept(), res.getIntersection().getConcept(), res.getUnion(), res.getIntersection(), res, "CodeSystem.concept"); + boolean ch = compareMetadata(left, right, res.getMetadata(), res); + ch = comparePrimitives("versionNeeded", left.getVersionNeededElement(), right.getVersionNeededElement(), res.getMetadata(), IssueSeverity.INFORMATION, res) || ch; + ch = comparePrimitives("compositional", left.getCompositionalElement(), right.getCompositionalElement(), res.getMetadata(), IssueSeverity.WARNING, res) || ch; + res.updatedMetadataState(ch); + ch = false; + ch = comparePrimitives("caseSensitive", left.getCaseSensitiveElement(), right.getCaseSensitiveElement(), res.getMetadata(), IssueSeverity.ERROR, res) || ch; + ch = comparePrimitives("hierarchyMeaning", left.getHierarchyMeaningElement(), right.getHierarchyMeaningElement(), res.getMetadata(), IssueSeverity.ERROR, res) || ch; + ch = comparePrimitives("content", left.getContentElement(), right.getContentElement(), res.getMetadata(), IssueSeverity.WARNING, res); + + ch = compareConcepts(left.getConcept(), right.getConcept(), res.getCombined(), res.getUnion().getConcept(), res.getIntersection().getConcept(), res.getUnion(), res.getIntersection(), res, "CodeSystem.concept") || ch; + res.updateDefinitionsState(ch); + return res; } @@ -153,13 +162,15 @@ public class CodeSystemComparer extends CanonicalResourceComparer { } - private void compareConcepts(List left, List right, StructuralMatch combined, + private boolean compareConcepts(List left, List right, StructuralMatch combined, List union, List intersection, CodeSystem csU, CodeSystem csI, CodeSystemComparison res, String path) { + boolean result = false; List matchR = new ArrayList<>(); for (ConceptDefinitionComponent l : left) { ConceptDefinitionComponent r = findInList(right, l); if (r == null) { union.add(l); + res.updateContentState(true); combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed this concept", path))); } else { matchR.add(r); @@ -168,17 +179,23 @@ public class CodeSystemComparer extends CanonicalResourceComparer { union.add(cdM); intersection.add(cdI); StructuralMatch sm = new StructuralMatch(l, r); - compare(sm.getMessages(), l, r, path+".where(code='"+l.getCode()+"')", res); + if (compare(sm.getMessages(), l, r, path+".where(code='"+l.getCode()+"')", res)) { + result = true; + } combined.getChildren().add(sm); - compareConcepts(l.getConcept(), r.getConcept(), sm, cdM.getConcept(), cdI.getConcept(), csU, csI, res, path+".where(code='"+l.getCode()+"').concept"); + if (compareConcepts(l.getConcept(), r.getConcept(), sm, cdM.getConcept(), cdI.getConcept(), csU, csI, res, path+".where(code='"+l.getCode()+"').concept")) { + result = true; + } } } for (ConceptDefinitionComponent r : right) { if (!matchR.contains(r)) { union.add(r); + res.updateContentState(true); combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r)); } } + return result; } private ConceptDefinitionComponent findInList(List list, ConceptDefinitionComponent item) { @@ -190,24 +207,30 @@ public class CodeSystemComparer extends CanonicalResourceComparer { return null; } - private void compare(List msgs, ConceptDefinitionComponent l, ConceptDefinitionComponent r, String path, CodeSystemComparison res) { - compareStrings(path, msgs, l.getDisplay(), r.getDisplay(), "display", IssueSeverity.WARNING, res); - compareStrings(path, msgs, l.getDefinition(), r.getDefinition(), "definition", IssueSeverity.INFORMATION, res); + private boolean compare(List msgs, ConceptDefinitionComponent l, ConceptDefinitionComponent r, String path, CodeSystemComparison res) { + boolean result = false; + result = compareStrings(path, msgs, l.getDisplay(), r.getDisplay(), "display", IssueSeverity.WARNING, res) || result; + result = compareStrings(path, msgs, l.getDefinition(), r.getDefinition(), "definition", IssueSeverity.INFORMATION, res) || result; + return result; } - private void compareStrings(String path, List msgs, String left, String right, String name, IssueSeverity level, CodeSystemComparison res) { + private boolean compareStrings(String path, List msgs, String left, String right, String name, IssueSeverity level, CodeSystemComparison res) { if (!Utilities.noString(right)) { if (Utilities.noString(left)) { msgs.add(vmI(level, "Value for "+name+" added", path)); + return true; } else if (!left.equals(right)) { if (level != IssueSeverity.NULL) { res.getMessages().add(new ValidationMessage(Source.ProfileComparer, IssueType.INFORMATIONAL, path+"."+name, "Changed value for "+name+": '"+left+"' vs '"+right+"'", level)); } msgs.add(vmI(level, name+" changed from left to right", path)); + return true; } } else if (!Utilities.noString(left)) { msgs.add(vmI(level, "Value for "+name+" removed", path)); + return true; } + return false; } private ConceptDefinitionComponent merge(ConceptDefinitionComponent l, ConceptDefinitionComponent r, List destProps, CodeSystemComparison res) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java index be29d518d..feeeaf077 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java @@ -175,6 +175,7 @@ public class ComparisonRenderer implements IEvaluationContext { vars.put("rightId", new StringType(comp.getRight().getId())); vars.put("leftUrl", new StringType(comp.getLeft().getUrl())); vars.put("rightUrl", new StringType(comp.getRight().getUrl())); + vars.put("summary", new StringType(comp.summary())); vars.put("errors", new StringType(new XhtmlComposer(true).compose(cs.renderErrors(comp)))); vars.put("metadata", new StringType(new XhtmlComposer(true).compose(cs.renderMetadata(comp, "", "")))); vars.put("concepts", new StringType(new XhtmlComposer(true).compose(cs.renderConcepts(comp, "", "")))); @@ -198,6 +199,7 @@ public class ComparisonRenderer implements IEvaluationContext { vars.put("rightId", new StringType(comp.getRight().getId())); vars.put("leftUrl", new StringType(comp.getLeft().getUrl())); vars.put("rightUrl", new StringType(comp.getRight().getUrl())); + vars.put("summary", new StringType(comp.summary())); vars.put("errors", new StringType(new XhtmlComposer(true).compose(cs.renderErrors(comp)))); vars.put("metadata", new StringType(new XhtmlComposer(true).compose(cs.renderMetadata(comp, "", "")))); vars.put("compose", new StringType(new XhtmlComposer(true).compose(cs.renderCompose(comp, "", "")))); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonSession.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonSession.java index 75abf98d9..522e2e7ac 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonSession.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonSession.java @@ -32,6 +32,8 @@ public class ComparisonSession { private String sessiondId; private int count; private boolean debug; + private String forVersion; + private boolean annotate; private String title; private ProfileKnowledgeProvider pkpLeft; private ProfileKnowledgeProvider pkpRight; @@ -164,4 +166,22 @@ public class ComparisonSession { public ProfileKnowledgeProvider getPkpRight() { return pkpRight; } + + public String getForVersion() { + return forVersion; + } + + public void setForVersion(String forVersion) { + this.forVersion = forVersion; + } + + public boolean isAnnotate() { + return annotate; + } + + public void setAnnotate(boolean annotate) { + this.annotate = annotate; + } + + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructuralMatch.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructuralMatch.java index 7e96e8115..6ea63ebe3 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructuralMatch.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructuralMatch.java @@ -114,6 +114,8 @@ public class StructuralMatch { return this; } - + public boolean isDifferent() { + return (left == null) != (right == null) || !messages.isEmpty(); + } } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ValueSetComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ValueSetComparer.java index c0261c3f2..9d1cbdfb0 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ValueSetComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ValueSetComparer.java @@ -8,6 +8,7 @@ import java.util.List; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.comparison.ResourceComparer.MessageCounts; +import org.hl7.fhir.r5.comparison.VersionComparisonAnnotation.AnotationType; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.Element; @@ -67,7 +68,12 @@ public class ValueSetComparer extends CanonicalResourceComparer { @Override protected String summary() { - return "ValueSet: "+left.present()+" vs "+right.present(); + String res = "ValueSet: "+left.present()+" vs "+right.present(); + String ch = changeSummary(); + if (ch != null) { + res = res + ". "+ch; + } + return res; } @Override @@ -118,28 +124,34 @@ public class ValueSetComparer extends CanonicalResourceComparer { vs1.setStatus(left.getStatus()); vs1.setDate(new Date()); - compareMetadata(left, right, res.getMetadata(), res); - comparePrimitives("immutable", left.getImmutableElement(), right.getImmutableElement(), res.getMetadata(), IssueSeverity.WARNING, res); + var ch = compareMetadata(left, right, res.getMetadata(), res); + var def = false; + ch = comparePrimitives("immutable", left.getImmutableElement(), right.getImmutableElement(), res.getMetadata(), IssueSeverity.WARNING, res) || ch; if (left.hasCompose() || right.hasCompose()) { - comparePrimitives("compose.lockedDate", left.getCompose().getLockedDateElement(), right.getCompose().getLockedDateElement(), res.getMetadata(), IssueSeverity.WARNING, res); - comparePrimitives("compose.inactive", left.getCompose().getInactiveElement(), right.getCompose().getInactiveElement(), res.getMetadata(), IssueSeverity.WARNING, res); + ch = comparePrimitives("compose.lockedDate", left.getCompose().getLockedDateElement(), right.getCompose().getLockedDateElement(), res.getMetadata(), IssueSeverity.WARNING, res) || ch; + def = comparePrimitives("compose.inactive", left.getCompose().getInactiveElement(), right.getCompose().getInactiveElement(), res.getMetadata(), IssueSeverity.WARNING, res) || def; } - - compareCompose(left.getCompose(), right.getCompose(), res, res.getUnion().getCompose(), res.getIntersection().getCompose()); + res.updatedMetadataState(ch); + + def = compareCompose(left.getCompose(), right.getCompose(), res, res.getUnion().getCompose(), res.getIntersection().getCompose()) || def; + res.updateDefinitionsState(def); compareExpansions(left, right, res); return res; } - - - private void compareCompose(ValueSetComposeComponent left, ValueSetComposeComponent right, ValueSetComparison res, ValueSetComposeComponent union, ValueSetComposeComponent intersection) { + private boolean compareCompose(ValueSetComposeComponent left, ValueSetComposeComponent right, ValueSetComparison res, ValueSetComposeComponent union, ValueSetComposeComponent intersection) { + boolean def = false; // first, the includes List matchR = new ArrayList<>(); for (ConceptSetComponent l : left.getInclude()) { ConceptSetComponent r = findInList(right.getInclude(), l, left.getInclude()); if (r == null) { union.getInclude().add(l); + res.updateContentState(true); res.getIncludes().getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed Include", "ValueSet.compose.include"))); + if (session.isAnnotate()) { + VersionComparisonAnnotation.markDeleted(right, session.getForVersion(), "include", l); + } } else { matchR.add(r); ConceptSetComponent csM = new ConceptSetComponent(); @@ -148,13 +160,17 @@ public class ValueSetComparer extends CanonicalResourceComparer { intersection.getInclude().add(csI); StructuralMatch sm = new StructuralMatch(l, r); res.getIncludes().getChildren().add(sm); - compareDefinitions(l, r, sm, csM, csI); + def = compareDefinitions(l, r, sm, csM, csI, res) || def; } } for (ConceptSetComponent r : right.getInclude()) { if (!matchR.contains(r)) { union.getInclude().add(r); - res.getIncludes().getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added Include", "ValueSet.compose.include"), r)); + res.updateContentState(true); + res.getIncludes().getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added Include", "ValueSet.compose.include"), r)); + if (session.isAnnotate()) { + VersionComparisonAnnotation.markAdded(r, session.getForVersion()); + } } } @@ -164,6 +180,7 @@ public class ValueSetComparer extends CanonicalResourceComparer { ConceptSetComponent r = findInList(right.getExclude(), l, left.getExclude()); if (r == null) { union.getExclude().add(l); + res.updateContentState(true); res.getExcludes().getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed Exclude", "ValueSet.compose.exclude"))); } else { matchR.add(r); @@ -173,15 +190,17 @@ public class ValueSetComparer extends CanonicalResourceComparer { intersection.getExclude().add(csI); StructuralMatch sm = new StructuralMatch(l, r); res.getExcludes().getChildren().add(sm); - compareDefinitions(l, r, sm, csM, csI); + def = compareDefinitions(l, r, sm, csM, csI, res) || def; } } for (ConceptSetComponent r : right.getExclude()) { if (!matchR.contains(r)) { union.getExclude().add(r); + res.updateContentState(true); res.getExcludes().getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added Exclude", "ValueSet.compose.exclude"), r)); } } + return def; } private ConceptSetComponent findInList(List matches, ConceptSetComponent item, List source) { @@ -218,13 +237,15 @@ public class ValueSetComparer extends CanonicalResourceComparer { } - private void compareDefinitions(ConceptSetComponent left, ConceptSetComponent right, StructuralMatch combined, ConceptSetComponent union, ConceptSetComponent intersection) { + private boolean compareDefinitions(ConceptSetComponent left, ConceptSetComponent right, StructuralMatch combined, ConceptSetComponent union, ConceptSetComponent intersection, ValueSetComparison res) { + boolean def = false; // system must match, but the rest might not. we're going to do the full comparison whatever, so the outcome looks consistent to the user List matchVSR = new ArrayList<>(); for (CanonicalType l : left.getValueSet()) { CanonicalType r = findInList(right.getValueSet(), l, left.getValueSet()); if (r == null) { union.getValueSet().add(l); + res.updateContentState(true); combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed ValueSet", "ValueSet.compose.include.valueSet"))); } else { matchVSR.add(r); @@ -234,8 +255,10 @@ public class ValueSetComparer extends CanonicalResourceComparer { StructuralMatch sm = new StructuralMatch(l, r, null); combined.getChildren().add(sm); } else { + // it's not possible to get here? union.getValueSet().add(l); union.getValueSet().add(r); + res.updateContentState(true); StructuralMatch sm = new StructuralMatch(l, r, vmI(IssueSeverity.INFORMATION, "Values are different", "ValueSet.compose.include.valueSet")); combined.getChildren().add(sm); } @@ -244,6 +267,7 @@ public class ValueSetComparer extends CanonicalResourceComparer { for (CanonicalType r : right.getValueSet()) { if (!matchVSR.contains(r)) { union.getValueSet().add(r); + res.updateContentState(true); combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Add ValueSet", "ValueSet.compose.include.valueSet"), r)); } } @@ -253,6 +277,7 @@ public class ValueSetComparer extends CanonicalResourceComparer { ConceptReferenceComponent r = findInList(right.getConcept(), l, left.getConcept()); if (r == null) { union.getConcept().add(l); + res.updateContentState(true); combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed this Concept", "ValueSet.compose.include.concept"))); } else { matchCR.add(r); @@ -263,12 +288,14 @@ public class ValueSetComparer extends CanonicalResourceComparer { intersection.getConcept().add(ci); StructuralMatch sm = new StructuralMatch(l, r); combined.getChildren().add(sm); - compareConcepts(l, r, sm, cu, ci); + def = compareConcepts(l, r, sm, cu, ci) || def; } else { + // not that it's possible to get here? union.getConcept().add(l); union.getConcept().add(r); StructuralMatch sm = new StructuralMatch(l, r, vmI(IssueSeverity.INFORMATION, "Concepts are different", "ValueSet.compose.include.concept")); combined.getChildren().add(sm); + res.updateContentState(true); compareConcepts(l, r, sm, null, null); } } @@ -276,6 +303,7 @@ public class ValueSetComparer extends CanonicalResourceComparer { for (ConceptReferenceComponent r : right.getConcept()) { if (!matchCR.contains(r)) { union.getConcept().add(r); + res.updateContentState(true); combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this Concept", "ValueSet.compose.include.concept"), r)); } } @@ -285,6 +313,7 @@ public class ValueSetComparer extends CanonicalResourceComparer { ConceptSetFilterComponent r = findInList(right.getFilter(), l, left.getFilter()); if (r == null) { union.getFilter().add(l); + res.updateContentState(true); combined.getChildren().add(new StructuralMatch(l, vmI(IssueSeverity.INFORMATION, "Removed this item", "ValueSet.compose.include.filter"))); } else { matchFR.add(r); @@ -295,11 +324,14 @@ public class ValueSetComparer extends CanonicalResourceComparer { intersection.getFilter().add(ci); StructuralMatch sm = new StructuralMatch(l, r); combined.getChildren().add(sm); - compareFilters(l, r, sm, cu, ci); + if (!compareFilters(l, r, sm, cu, ci)) { + res.updateContentState(true); + } } else { union.getFilter().add(l); union.getFilter().add(r); StructuralMatch sm = new StructuralMatch(l, r, vmI(IssueSeverity.INFORMATION, "Codes are different", "ValueSet.compose.include.filter")); + res.updateContentState(true); combined.getChildren().add(sm); compareFilters(l, r, sm, null, null); } @@ -308,12 +340,15 @@ public class ValueSetComparer extends CanonicalResourceComparer { for (ConceptSetFilterComponent r : right.getFilter()) { if (!matchFR.contains(r)) { union.getFilter().add(r); + res.updateContentState(true); combined.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this item", "ValueSet.compose.include.filter"), r)); } } + return def; } - private void compareConcepts(ConceptReferenceComponent l, ConceptReferenceComponent r, StructuralMatch sm, ConceptReferenceComponent cu, ConceptReferenceComponent ci) { + private boolean compareConcepts(ConceptReferenceComponent l, ConceptReferenceComponent r, StructuralMatch sm, ConceptReferenceComponent cu, ConceptReferenceComponent ci) { + boolean def = false; sm.getChildren().add(new StructuralMatch(l.getCodeElement(), r.getCodeElement(), l.getCode().equals(r.getCode()) ? null : vmI(IssueSeverity.INFORMATION, "Codes do not match", "ValueSet.compose.include.concept"))); if (ci != null) { ci.setCode(l.getCode()); @@ -325,24 +360,28 @@ public class ValueSetComparer extends CanonicalResourceComparer { ci.setDisplay(r.getDisplay()); cu.setDisplay(r.getDisplay()); } + def = !l.getDisplay().equals(r.getDisplay()); } else if (l.hasDisplay()) { sm.getChildren().add(new StructuralMatch(l.getDisplayElement(), null, vmI(IssueSeverity.INFORMATION, "Display Removed", "ValueSet.compose.include.concept"))); if (ci != null) { ci.setDisplay(l.getDisplay()); cu.setDisplay(l.getDisplay()); } + def = true; } else if (r.hasDisplay()) { sm.getChildren().add(new StructuralMatch(null, r.getDisplayElement(), vmI(IssueSeverity.INFORMATION, "Display added", "ValueSet.compose.include.concept"))); if (ci != null) { ci.setDisplay(r.getDisplay()); cu.setDisplay(r.getDisplay()); } + def = true; } else { sm.getChildren().add(new StructuralMatch(null, null, vmI(IssueSeverity.INFORMATION, "No Display", "ValueSet.compose.include.concept"))); } + return def; } - private void compareFilters(ConceptSetFilterComponent l, ConceptSetFilterComponent r, StructuralMatch sm, ConceptSetFilterComponent cu, ConceptSetFilterComponent ci) { + private boolean compareFilters(ConceptSetFilterComponent l, ConceptSetFilterComponent r, StructuralMatch sm, ConceptSetFilterComponent cu, ConceptSetFilterComponent ci) { sm.getChildren().add(new StructuralMatch(l.getPropertyElement(), r.getPropertyElement(), l.getProperty().equals(r.getProperty()) ? null : vmI(IssueSeverity.INFORMATION, "Properties do not match", "ValueSet.compose.include.concept"))); sm.getChildren().add(new StructuralMatch(l.getOpElement(), r.getOpElement(), l.getOp().equals(r.getOp()) ? null : vmI(IssueSeverity.INFORMATION, "Filter Operations do not match", "ValueSet.compose.include.concept"))); sm.getChildren().add(new StructuralMatch(l.getValueElement(), r.getValueElement(), l.getValue().equals(r.getValue()) ? null : vmI(IssueSeverity.INFORMATION, "Values do not match", "ValueSet.compose.include.concept"))); @@ -354,6 +393,7 @@ public class ValueSetComparer extends CanonicalResourceComparer { cu.setOp(l.getOp()); cu.setValue(l.getValue()); } + return !l.getProperty().equals(r.getProperty()); } private CanonicalType findInList(List matches, CanonicalType item, List source) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/VersionComparisonAnnotation.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/VersionComparisonAnnotation.java new file mode 100644 index 000000000..d94f88925 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/VersionComparisonAnnotation.java @@ -0,0 +1,143 @@ +package org.hl7.fhir.r5.comparison; + +import java.util.List; +import java.util.Map; +import java.util.ArrayList; +import java.util.HashMap; + +import org.hl7.fhir.r5.model.Base; +import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; +import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent; +import org.hl7.fhir.utilities.xhtml.XhtmlNode; + +public class VersionComparisonAnnotation { + + public enum AnotationType { + Added, Changed, Deleted; + } + + public static final String USER_DATA_NAME = "versoin-annotation"; + + private AnotationType type; +// private String comment; +// private String link; + private Map> deletedChildren; + + private String version; + + private VersionComparisonAnnotation(AnotationType type, String version) { + super(); + this.type = type; + this.version = version; + } +// +// private VersionComparisonAnnotation(AnotationType type, String comment) { +// super(); +// this.type = type; +// this.comment = comment; +// } +// private VersionComparisonAnnotation(AnotationType type, String comment, String link) { +// super(); +// this.type = type; +// this.comment = comment; +// this.link = link; +// } +// + public static void markAdded(Base focus, String version) { + focus.setUserData(USER_DATA_NAME, new VersionComparisonAnnotation(AnotationType.Added, version)); + } + + public static void markDeleted(Base parent, String name, String version, Base other) { + VersionComparisonAnnotation vca = null; + if (parent.hasUserData(USER_DATA_NAME)) { + vca = (VersionComparisonAnnotation) parent.getUserData(USER_DATA_NAME); + assert vca.type != AnotationType.Added; + } else { + vca = new VersionComparisonAnnotation(AnotationType.Changed, version); + parent.setUserData(USER_DATA_NAME, vca); + } + if (vca.deletedChildren == null) { + vca.deletedChildren = new HashMap<>(); + } + if (!vca.deletedChildren.containsKey(name)) { + vca.deletedChildren.put(name, new ArrayList<>()); + } + other.setUserData(USER_DATA_NAME, new VersionComparisonAnnotation(AnotationType.Deleted, version)); + vca.deletedChildren.get(name).add(other); + } + + public AnotationType getType() { + return type; + } + public void setType(AnotationType type) { + this.type = type; + } + +// public String getComment() { +// return comment; +// } +// public void setComment(String comment) { +// this.comment = comment; +// } +// public String getLink() { +// return link; +// } +// public void setLink(String link) { +// this.link = link; +// } + + public static XhtmlNode render(Base b, XhtmlNode x) { + if (b.hasUserData(USER_DATA_NAME)) { + VersionComparisonAnnotation self = (VersionComparisonAnnotation) b.getUserData(USER_DATA_NAME); + return self.render(x); + } else { + return x; + } + } + + private XhtmlNode render(XhtmlNode x) { + switch (type) { + case Added: + XhtmlNode span = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", null); + span.img("icon-change-add.png", "This content has been added since version "+version); + span.tx(" Added:"); + return x; + case Changed: + return x; + case Deleted: + span = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", null); + span.img("icon-change-remove.png", "This content has been removed since version "+version); + span.tx(" Removed:"); + return x.strikethrough(); + default: + return x; + } + } + + public static boolean hasDeleted(Base base, String... names) { + boolean result = false; + if (base.hasUserData(USER_DATA_NAME)) { + VersionComparisonAnnotation self = (VersionComparisonAnnotation) base.getUserData(USER_DATA_NAME); + for (String name : names) { + if (self.deletedChildren != null && self.deletedChildren.containsKey(name)) { + result = true; + } + } + } + return result; + } + + public static List getDeleted(Base base, String... names) { + List result = new ArrayList<>(); + if (base.hasUserData(USER_DATA_NAME)) { + VersionComparisonAnnotation self = (VersionComparisonAnnotation) base.getUserData(USER_DATA_NAME); + for (String name : names) { + if (self.deletedChildren != null && self.deletedChildren.containsKey(name)) { + result.addAll(self.deletedChildren.get(name)); + } + } + } + return result; + } + +} \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Base.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Base.java index 22a320ace..c04102fa8 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Base.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Base.java @@ -88,6 +88,17 @@ public abstract class Base implements Serializable, IBase, IElement { } + private static ThreadLocal copyUserData = new ThreadLocal<>(); + + public static boolean isCopyUserData() { + Boolean res = copyUserData.get(); + return res != null && res; + } + + public static void setCopyUserData(boolean value) { + copyUserData.set(value); + } + /** * User appended data items - allow users to add extra information to the class */ @@ -456,7 +467,11 @@ public abstract class Base implements Serializable, IBase, IElement { public abstract Base copy(); - public void copyValues(Base dst) { + public void copyValues(Base dst) { + if (isCopyUserData() && userData != null) { + dst.userData = new HashMap<>(); + dst.userData.putAll(userData); + } } /** diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java index ccc11cc96..95983dc20 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java @@ -14,8 +14,10 @@ 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.comparison.VersionComparisonAnnotation; import org.hl7.fhir.r5.context.IWorkerContext.CodingValidationRequest; import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; +import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.BooleanType; import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.CodeSystem; @@ -924,7 +926,7 @@ public class ValueSetRenderer extends TerminologyRenderer { generateCopyright(x, vs); } int index = 0; - if (vs.getCompose().getInclude().size() == 1 && vs.getCompose().getExclude().size() == 0) { + if (vs.getCompose().getInclude().size() == 1 && vs.getCompose().getExclude().size() == 0 && !VersionComparisonAnnotation.hasDeleted(vs.getCompose(), "include", "exclude")) { hasExtensions = genInclude(x.ul(), vs.getCompose().getInclude().get(0), "Include", langs, doDesignations, maps, designations, index, vs) || hasExtensions; } else { XhtmlNode p = x.para(); @@ -934,7 +936,11 @@ public class ValueSetRenderer extends TerminologyRenderer { hasExtensions = genInclude(ul, inc, "Include", langs, doDesignations, maps, designations, index, vs) || hasExtensions; index++; } - if (vs.getCompose().hasExclude()) { + for (Base inc : VersionComparisonAnnotation.getDeleted(vs.getCompose(), "include")) { + genInclude(ul, (ConceptSetComponent) inc, "Include", langs, doDesignations, maps, designations, index, vs); + index++; + } + if (vs.getCompose().hasExclude() || VersionComparisonAnnotation.hasDeleted(vs.getCompose(), "exclude")) { p = x.para(); p.tx("This value set excludes codes based on the following rules:"); ul = x.ul(); @@ -942,6 +948,10 @@ public class ValueSetRenderer extends TerminologyRenderer { hasExtensions = genInclude(ul, exc, "Exclude", langs, doDesignations, maps, designations, index, vs) || hasExtensions; index++; } + for (Base inc : VersionComparisonAnnotation.getDeleted(vs.getCompose(), "exclude")) { + genInclude(ul, (ConceptSetComponent) inc, "Exclude", langs, doDesignations, maps, designations, index, vs); + index++; + } } } @@ -1131,6 +1141,8 @@ public class ValueSetRenderer extends TerminologyRenderer { boolean hasExtensions = false; XhtmlNode li; li = ul.li(); + li = VersionComparisonAnnotation.render(inc, li); + Map definitions = new HashMap<>(); if (inc.hasSystem()) { @@ -1144,7 +1156,7 @@ public class ValueSetRenderer extends TerminologyRenderer { addCsRef(inc, li, e); if (inc.hasVersion()) { li.addText(" version "); - li.code(inc.getVersion()); + li.code(inc.getVersion()); } // for performance reasons, we do all the fetching in one batch diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java index ea1a6efd1..b26c53617 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java @@ -867,4 +867,9 @@ public class XhtmlNode extends XhtmlFluent implements IBaseXhtml { return res; } + + public XhtmlNode strikethrough() { + return addTag("s"); + } + } \ No newline at end of file From 6041660e9b194e72ebb9622e9eb2df6a83a6ee55 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 11 Aug 2023 14:16:09 +1000 Subject: [PATCH 08/15] Improved error messages on server failure --- .../dstu3/utils/client/FHIRToolingClient.java | 1 + .../fhir/dstu3/utils/client/network/Client.java | 14 ++++++++++++-- .../utils/client/network/FhirRequestBuilder.java | 14 ++++++++------ .../client/network/FhirRequestBuilderTests.java | 2 +- .../fhir/r4/utils/client/FHIRToolingClient.java | 1 + .../hl7/fhir/r4/utils/client/network/Client.java | 14 ++++++++++++-- .../utils/client/network/FhirRequestBuilder.java | 16 +++++++++------- .../fhir/r4b/utils/client/FHIRToolingClient.java | 1 + .../fhir/r4b/utils/client/network/Client.java | 14 ++++++++++++-- .../utils/client/network/FhirRequestBuilder.java | 14 ++++++++------ .../fhir/r5/utils/client/FHIRToolingClient.java | 1 + .../hl7/fhir/r5/utils/client/network/Client.java | 13 +++++++++++-- .../utils/client/network/FhirRequestBuilder.java | 16 +++++++++------- 13 files changed, 86 insertions(+), 35 deletions(-) diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/FHIRToolingClient.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/FHIRToolingClient.java index 94bd3e766..ed3377e14 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/FHIRToolingClient.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/FHIRToolingClient.java @@ -93,6 +93,7 @@ public class FHIRToolingClient { public void initialize(String baseServiceUrl) throws URISyntaxException { base = baseServiceUrl; + client.setBase(base); resourceAddress = new ResourceAddress(baseServiceUrl); this.allowedVersions = supportableVersions(); this.maxResultSetSize = -1; diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/Client.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/Client.java index f66067069..a0635aadf 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/Client.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/Client.java @@ -22,6 +22,16 @@ public class Client { private ToolingClientLogger logger; private int retryCount; private long timeout = DEFAULT_TIMEOUT; + private String base; + + public String getBase() { + return base; + } + + public void setBase(String base) { + this.base = base; + } + public ToolingClientLogger getLogger() { return logger; @@ -167,7 +177,7 @@ public class Client { String message, int retryCount, long timeout) throws IOException { - return new FhirRequestBuilder(request) + return new FhirRequestBuilder(request, base) .withLogger(logger) .withResourceFormat(resourceFormat) .withRetryCount(retryCount) @@ -183,7 +193,7 @@ public class Client { String message, int retryCount, long timeout) throws IOException { - return new FhirRequestBuilder(request) + return new FhirRequestBuilder(request, base) .withLogger(logger) .withResourceFormat(resourceFormat) .withRetryCount(retryCount) diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/FhirRequestBuilder.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/FhirRequestBuilder.java index 3ac6dfb10..af2f370b8 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/FhirRequestBuilder.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/FhirRequestBuilder.java @@ -59,9 +59,11 @@ public class FhirRequestBuilder { * {@link ToolingClientLogger} for log output. */ private ToolingClientLogger logger = null; + private String source; - public FhirRequestBuilder(Request.Builder httpRequest) { + public FhirRequestBuilder(Request.Builder httpRequest, String source) { this.httpRequest = httpRequest; + this.source = source; } /** @@ -265,9 +267,9 @@ public class FhirRequestBuilder { error = (OperationOutcome) resource; } } catch (IOException ioe) { - throw new EFhirClientException("Error reading Http Response: " + ioe.getMessage(), ioe); + throw new EFhirClientException("Error reading Http Response from "+source+": " + ioe.getMessage(), ioe); } catch (Exception e) { - throw new EFhirClientException("Error parsing response message: " + e.getMessage(), e); + throw new EFhirClientException("Error parsing response message from "+source+": " + e.getMessage(), e); } } @@ -301,12 +303,12 @@ public class FhirRequestBuilder { } } } catch (IOException ioe) { - throw new EFhirClientException("Error reading Http Response", ioe); + throw new EFhirClientException("Error reading Http Response from "+source+": "+ioe.getMessage(), ioe); } catch (Exception e) { - throw new EFhirClientException("Error parsing response message", e); + throw new EFhirClientException("Error parsing response message from "+source+":"+e.getMessage(), e); } if (error != null) { - throw new EFhirClientException("Error from server: " + ResourceUtilities.getErrorDescription(error), error); + throw new EFhirClientException("Error from "+source+": " + ResourceUtilities.getErrorDescription(error), error); } return feed; } diff --git a/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/utils/client/network/FhirRequestBuilderTests.java b/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/utils/client/network/FhirRequestBuilderTests.java index d75c03928..13a901ce7 100644 --- a/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/utils/client/network/FhirRequestBuilderTests.java +++ b/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/utils/client/network/FhirRequestBuilderTests.java @@ -47,7 +47,7 @@ public class FhirRequestBuilderTests { final Request.Builder requestBuilder = new Request.Builder() .url(DUMMY_URL); - final FhirRequestBuilder fhirRequestBuilder = Mockito.spy(new FhirRequestBuilder(requestBuilder)); + final FhirRequestBuilder fhirRequestBuilder = Mockito.spy(new FhirRequestBuilder(requestBuilder, "http://local/local")); @Mock OkHttpClient client; diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/FHIRToolingClient.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/FHIRToolingClient.java index e127858ff..e57e6b559 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/FHIRToolingClient.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/FHIRToolingClient.java @@ -93,6 +93,7 @@ public class FHIRToolingClient { public void initialize(String baseServiceUrl) throws URISyntaxException { base = baseServiceUrl; + client.setBase(base); resourceAddress = new ResourceAddress(baseServiceUrl); this.maxResultSetSize = -1; } diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/Client.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/Client.java index 3f3c13628..1b750c796 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/Client.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/Client.java @@ -23,6 +23,16 @@ public class Client { private FhirLoggingInterceptor fhirLoggingInterceptor; private int retryCount; private long timeout = DEFAULT_TIMEOUT; + private String base; + + public String getBase() { + return base; + } + + public void setBase(String base) { + this.base = base; + } + public ToolingClientLogger getLogger() { return logger; @@ -131,7 +141,7 @@ public class Client { public Bundle executeBundleRequest(Request.Builder request, String resourceFormat, Headers headers, String message, int retryCount, long timeout) throws IOException { - return new FhirRequestBuilder(request).withLogger(fhirLoggingInterceptor).withResourceFormat(resourceFormat) + return new FhirRequestBuilder(request, base).withLogger(fhirLoggingInterceptor).withResourceFormat(resourceFormat) .withRetryCount(retryCount).withMessage(message) .withHeaders(headers == null ? new Headers.Builder().build() : headers) .withTimeout(timeout, TimeUnit.MILLISECONDS).executeAsBatch(); @@ -139,7 +149,7 @@ public class Client { public ResourceRequest executeFhirRequest(Request.Builder request, String resourceFormat, Headers headers, String message, int retryCount, long timeout) throws IOException { - return new FhirRequestBuilder(request).withLogger(fhirLoggingInterceptor).withResourceFormat(resourceFormat) + return new FhirRequestBuilder(request, base).withLogger(fhirLoggingInterceptor).withResourceFormat(resourceFormat) .withRetryCount(retryCount).withMessage(message) .withHeaders(headers == null ? new Headers.Builder().build() : headers) .withTimeout(timeout, TimeUnit.MILLISECONDS).execute(); diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirRequestBuilder.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirRequestBuilder.java index 7cefc67c0..4a17e709a 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirRequestBuilder.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirRequestBuilder.java @@ -58,9 +58,11 @@ public class FhirRequestBuilder { * {@link FhirLoggingInterceptor} for log output. */ private FhirLoggingInterceptor logger = null; + private String source; - public FhirRequestBuilder(Request.Builder httpRequest) { + public FhirRequestBuilder(Request.Builder httpRequest, String source) { this.httpRequest = httpRequest; + this.source = source; } /** @@ -264,9 +266,9 @@ public class FhirRequestBuilder { error = (OperationOutcome) resource; } } catch (IOException ioe) { - throw new EFhirClientException("Error reading Http Response: " + ioe.getMessage(), ioe); + throw new EFhirClientException("Error reading Http Response from "+source+": " + ioe.getMessage(), ioe); } catch (Exception e) { - throw new EFhirClientException("Error parsing response message: " + e.getMessage(), e); + throw new EFhirClientException("Error parsing response message from "+source+": " + e.getMessage(), e); } } @@ -296,17 +298,17 @@ public class FhirRequestBuilder { else if (rf instanceof OperationOutcome && hasError((OperationOutcome) rf)) { error = (OperationOutcome) rf; } else { - throw new EFhirClientException("Error reading server response: a resource was returned instead"); + throw new EFhirClientException("Error reading server response from "+source+": a resource was returned instead"); } } } } catch (IOException ioe) { - throw new EFhirClientException("Error reading Http Response", ioe); + throw new EFhirClientException("Error reading Http Response from "+source+":"+ioe.getMessage(), ioe); } catch (Exception e) { - throw new EFhirClientException("Error parsing response message", e); + throw new EFhirClientException("Error parsing response message from "+source+":"+e.getMessage(), e); } if (error != null) { - throw new EFhirClientException("Error from server: " + ResourceUtilities.getErrorDescription(error), error); + throw new EFhirClientException("Error from "+source+": " + ResourceUtilities.getErrorDescription(error), error); } return feed; } diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/FHIRToolingClient.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/FHIRToolingClient.java index 82d8b2215..3bb65d634 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/FHIRToolingClient.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/FHIRToolingClient.java @@ -110,6 +110,7 @@ public class FHIRToolingClient { public void initialize(String baseServiceUrl) throws URISyntaxException { base = baseServiceUrl; + client.setBase(base); resourceAddress = new ResourceAddress(baseServiceUrl); this.maxResultSetSize = -1; checkCapabilities(); diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/network/Client.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/network/Client.java index 986c11c89..6cf5fa10f 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/network/Client.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/network/Client.java @@ -23,6 +23,16 @@ public class Client { private int retryCount; private long timeout = DEFAULT_TIMEOUT; private byte[] payload; + private String base; + + public String getBase() { + return base; + } + + public void setBase(String base) { + this.base = base; + } + public ToolingClientLogger getLogger() { return logger; @@ -135,7 +145,7 @@ public class Client { public Bundle executeBundleRequest(Request.Builder request, String resourceFormat, Headers headers, String message, int retryCount, long timeout) throws IOException { - return new FhirRequestBuilder(request).withLogger(fhirLoggingInterceptor).withResourceFormat(resourceFormat) + return new FhirRequestBuilder(request, base).withLogger(fhirLoggingInterceptor).withResourceFormat(resourceFormat) .withRetryCount(retryCount).withMessage(message) .withHeaders(headers == null ? new Headers.Builder().build() : headers) .withTimeout(timeout, TimeUnit.MILLISECONDS).executeAsBatch(); @@ -143,7 +153,7 @@ public class Client { public ResourceRequest executeFhirRequest(Request.Builder request, String resourceFormat, Headers headers, String message, int retryCount, long timeout) throws IOException { - return new FhirRequestBuilder(request).withLogger(fhirLoggingInterceptor).withResourceFormat(resourceFormat) + return new FhirRequestBuilder(request, base).withLogger(fhirLoggingInterceptor).withResourceFormat(resourceFormat) .withRetryCount(retryCount).withMessage(message) .withHeaders(headers == null ? new Headers.Builder().build() : headers) .withTimeout(timeout, TimeUnit.MILLISECONDS).execute(); diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/network/FhirRequestBuilder.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/network/FhirRequestBuilder.java index d2a4347cf..3fc062aab 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/network/FhirRequestBuilder.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/network/FhirRequestBuilder.java @@ -52,9 +52,11 @@ public class FhirRequestBuilder { * {@link FhirLoggingInterceptor} for log output. */ private FhirLoggingInterceptor logger = null; + private String source; - public FhirRequestBuilder(Request.Builder httpRequest) { + public FhirRequestBuilder(Request.Builder httpRequest, String source) { this.httpRequest = httpRequest; + this.source = source; } /** @@ -253,9 +255,9 @@ public class FhirRequestBuilder { error = (OperationOutcome) resource; } } catch (IOException ioe) { - throw new EFhirClientException("Error reading Http Response: " + ioe.getMessage(), ioe); + throw new EFhirClientException("Error reading Http Response from "+source+": " + ioe.getMessage(), ioe); } catch (Exception e) { - throw new EFhirClientException("Error parsing response message: " + e.getMessage(), e); + throw new EFhirClientException("Error parsing response message from "+source+": " + e.getMessage(), e); } } @@ -290,12 +292,12 @@ public class FhirRequestBuilder { } } } catch (IOException ioe) { - throw new EFhirClientException("Error reading Http Response", ioe); + throw new EFhirClientException("Error reading Http Response from "+source+": "+ioe.getMessage(), ioe); } catch (Exception e) { - throw new EFhirClientException("Error parsing response message", e); + throw new EFhirClientException("Error parsing response message from "+source+": "+e.getMessage(), e); } if (error != null) { - throw new EFhirClientException("Error from server: " + ResourceUtilities.getErrorDescription(error), error); + throw new EFhirClientException("Error from "+source+": " + ResourceUtilities.getErrorDescription(error), error); } return feed; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java index 005b2f71e..3feb89629 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java @@ -112,6 +112,7 @@ public class FHIRToolingClient { public void initialize(String baseServiceUrl) throws URISyntaxException { base = baseServiceUrl; + client.setBase(base); resourceAddress = new ResourceAddress(baseServiceUrl); this.maxResultSetSize = -1; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/Client.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/Client.java index b719907d9..45cc9829d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/Client.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/Client.java @@ -23,6 +23,15 @@ public class Client { private int retryCount; private long timeout = DEFAULT_TIMEOUT; private byte[] payload; + private String base; + + public String getBase() { + return base; + } + + public void setBase(String base) { + this.base = base; + } public ToolingClientLogger getLogger() { return logger; @@ -174,7 +183,7 @@ public class Client { String message, int retryCount, long timeout) throws IOException { - return new FhirRequestBuilder(request) + return new FhirRequestBuilder(request, base) .withLogger(fhirLoggingInterceptor) .withResourceFormat(resourceFormat) .withRetryCount(retryCount) @@ -190,7 +199,7 @@ public class Client { String message, int retryCount, long timeout) throws IOException { - return new FhirRequestBuilder(request) + return new FhirRequestBuilder(request, base) .withLogger(fhirLoggingInterceptor) .withResourceFormat(resourceFormat) .withRetryCount(retryCount) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilder.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilder.java index 315a26d23..8e8c2e06b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilder.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirRequestBuilder.java @@ -50,9 +50,11 @@ public class FhirRequestBuilder { * {@link FhirLoggingInterceptor} for log output. */ private FhirLoggingInterceptor logger = null; + private String source; - public FhirRequestBuilder(Request.Builder httpRequest) { + public FhirRequestBuilder(Request.Builder httpRequest, String source) { this.httpRequest = httpRequest; + this.source = source; } /** @@ -249,14 +251,14 @@ public class FhirRequestBuilder { error = (OperationOutcome) resource; } } catch (IOException ioe) { - throw new EFhirClientException("Error reading Http Response: " + ioe.getMessage(), ioe); + throw new EFhirClientException("Error reading Http Response from "+source+": " + ioe.getMessage(), ioe); } catch (Exception e) { - throw new EFhirClientException("Error parsing response message: " + e.getMessage(), e); + throw new EFhirClientException("Error parsing response message from "+source+": " + e.getMessage(), e); } } if (error != null) { - throw new EFhirClientException("Error from server: " + ResourceUtilities.getErrorDescription(error), error); + throw new EFhirClientException("Error from "+source+": " + ResourceUtilities.getErrorDescription(error), error); } return resource; @@ -284,12 +286,12 @@ public class FhirRequestBuilder { } } } catch (IOException ioe) { - throw new EFhirClientException("Error reading Http Response", ioe); + throw new EFhirClientException("Error reading Http Response from "+source+":"+ioe.getMessage(), ioe); } catch (Exception e) { - throw new EFhirClientException("Error parsing response message", e); + throw new EFhirClientException("Error parsing response message from "+source+": "+e.getMessage(), e); } if (error != null) { - throw new EFhirClientException("Error from server: " + ResourceUtilities.getErrorDescription(error), error); + throw new EFhirClientException("Error from "+source+": " + ResourceUtilities.getErrorDescription(error), error); } return feed; } From 4a11c5ffe106ffd8a8329d06741966e3862f901e Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 11 Aug 2023 14:16:15 +1000 Subject: [PATCH 09/15] fix failing test --- .../resources/testUtilities/json/actualDiffArraySize.json.error | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.r5/src/test/resources/testUtilities/json/actualDiffArraySize.json.error b/org.hl7.fhir.r5/src/test/resources/testUtilities/json/actualDiffArraySize.json.error index b8aee7189..5ad083e15 100644 --- a/org.hl7.fhir.r5/src/test/resources/testUtilities/json/actualDiffArraySize.json.error +++ b/org.hl7.fhir.r5/src/test/resources/testUtilities/json/actualDiffArraySize.json.error @@ -1,3 +1,3 @@ -array properties count differs at .expectedArray +array item count differs at .expectedArray Expected :2 Actual :1 \ No newline at end of file From 9c65d7a20f697af568baa6d1b46954767bcd8ffd Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 11 Aug 2023 14:16:29 +1000 Subject: [PATCH 10/15] Fix for NPE validating codes --- .../terminologies/utilities/ValueSetProcessBase.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/utilities/ValueSetProcessBase.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/utilities/ValueSetProcessBase.java index 49b37c4c9..551711e65 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/utilities/ValueSetProcessBase.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/utilities/ValueSetProcessBase.java @@ -122,11 +122,13 @@ public class ValueSetProcessBase { addToIssues(issues, makeStatusIssue(path, "withdrawn", I18nConstants.MSG_WITHDRAWN, resource)); } else if (resource.getStatus() == PublicationStatus.RETIRED) { addToIssues(issues, makeStatusIssue(path, "retired", I18nConstants.MSG_RETIRED, resource)); - } else if (resource.getExperimental() && !source.getExperimental()) { - addToIssues(issues, makeStatusIssue(path, "experimental", I18nConstants.MSG_EXPERIMENTAL, resource)); - } else if ((resource.getStatus() == PublicationStatus.DRAFT || standardsStatus == StandardsStatus.DRAFT) - && !(source.getStatus() == PublicationStatus.DRAFT || ToolingExtensions.getStandardsStatus(source) == StandardsStatus.DRAFT)) { - addToIssues(issues, makeStatusIssue(path, "draft", I18nConstants.MSG_DRAFT, resource)); + } else if (source != null) { + if (resource.getExperimental() && !source.getExperimental()) { + addToIssues(issues, makeStatusIssue(path, "experimental", I18nConstants.MSG_EXPERIMENTAL, resource)); + } else if ((resource.getStatus() == PublicationStatus.DRAFT || standardsStatus == StandardsStatus.DRAFT) + && !(source.getStatus() == PublicationStatus.DRAFT || ToolingExtensions.getStandardsStatus(source) == StandardsStatus.DRAFT)) { + addToIssues(issues, makeStatusIssue(path, "draft", I18nConstants.MSG_DRAFT, resource)); + } } } } From ec3186848612dd619c1336eed55d21e14c1ca699 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 11 Aug 2023 14:16:50 +1000 Subject: [PATCH 11/15] improve version annotation presentation --- .../comparison/VersionComparisonAnnotation.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/VersionComparisonAnnotation.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/VersionComparisonAnnotation.java index d94f88925..590a6a470 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/VersionComparisonAnnotation.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/VersionComparisonAnnotation.java @@ -16,7 +16,7 @@ public class VersionComparisonAnnotation { Added, Changed, Deleted; } - public static final String USER_DATA_NAME = "versoin-annotation"; + public static final String USER_DATA_NAME = "version-annotation"; private AnotationType type; // private String comment; @@ -47,7 +47,7 @@ public class VersionComparisonAnnotation { focus.setUserData(USER_DATA_NAME, new VersionComparisonAnnotation(AnotationType.Added, version)); } - public static void markDeleted(Base parent, String name, String version, Base other) { + public static void markDeleted(Base parent, String version, String name, Base other) { VersionComparisonAnnotation vca = null; if (parent.hasUserData(USER_DATA_NAME)) { vca = (VersionComparisonAnnotation) parent.getUserData(USER_DATA_NAME); @@ -98,15 +98,18 @@ public class VersionComparisonAnnotation { private XhtmlNode render(XhtmlNode x) { switch (type) { case Added: - XhtmlNode span = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", null); - span.img("icon-change-add.png", "This content has been added since version "+version); + XhtmlNode span = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been added since "+version); + span.img("icon-change-add.png", "icon"); span.tx(" Added:"); return x; case Changed: + span = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been changed since version "+version); + span.img("icon-change-edit.png", "icon"); + span.tx(" Changed:"); return x; case Deleted: - span = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", null); - span.img("icon-change-remove.png", "This content has been removed since version "+version); + span = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", "This content has been removed since version "+version); + span.img("icon-change-remove.png", "icon"); span.tx(" Removed:"); return x.strikethrough(); default: From 369d01646e8b9efffdec5574e40f24a41607efe4 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 11 Aug 2023 16:29:44 +1000 Subject: [PATCH 12/15] fix sorting --- .../org/hl7/fhir/validation/special/TxTesterSorters.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterSorters.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterSorters.java index 30e42ce36..f82e6b0fc 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterSorters.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterSorters.java @@ -124,7 +124,11 @@ public class TxTesterSorters { public int compare(ValueSetExpansionParameterComponent o1, ValueSetExpansionParameterComponent o2) { Collections.sort(o1.getExtension(), new ExtensionSorter()); Collections.sort(o2.getExtension(), new ExtensionSorter()); - return o1.getName().compareTo(o2.getName()); + int res = o1.getName().compareTo(o2.getName()); + if (res == 0) { + res = o1.getValue().primitiveValue().compareTo(o2.getValue().primitiveValue()); + } + return res; } } From a5ff5b50a81274dbc954d8b0f2022c30d22c9485 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 11 Aug 2023 22:27:50 +1000 Subject: [PATCH 13/15] rework how extensions are handled after discussion with Michael --- .../expansion/ValueSetExpander.java | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java index b5b01b7a9..1dd1107e5 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java @@ -97,6 +97,7 @@ import org.hl7.fhir.r5.model.Parameters; import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.r5.model.PrimitiveType; import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.model.StringType; import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.ValueSet; @@ -134,7 +135,6 @@ public class ValueSetExpander extends ValueSetProcessBase { private boolean includeAbstract = true; public ValueSetExpander(IWorkerContext context) { - super(); this.context = context; } @@ -149,7 +149,7 @@ public class ValueSetExpander extends ValueSetProcessBase { } private ValueSetExpansionContainsComponent addCode(WorkingContext wc, String system, String code, String display, String dispLang, ValueSetExpansionContainsComponent parent, List designations, Parameters expParams, - boolean isAbstract, boolean inactive, String definition, List filters, boolean noInactive, boolean deprecated, List vsProp, + boolean isAbstract, boolean inactive, List filters, boolean noInactive, boolean deprecated, List vsProp, List csProps, List expProps, List csExtList, List vsExtList, ValueSetExpansionComponent exp) throws ETooCostly { if (filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code, exp)) @@ -168,9 +168,6 @@ public class ValueSetExpander extends ValueSetProcessBase { if (deprecated) { ValueSetUtilities.setDeprecated(vsProp, n); } - if (expParams.getParameterBool("includeDefinition") && definition != null) { - n.addExtension(Extensions.makeVSConceptDefinition(definition)); - } if (ExtensionsUtils.hasExtension(csExtList, "http://hl7.org/fhir/StructureDefinition/codesystem-label")) { ValueSetUtilities.addProperty(focus, n, "http://hl7.org/fhir/concept-properties#label", "label", ExtensionsUtils.getExtensionValue(csExtList, "http://hl7.org/fhir/StructureDefinition/codesystem-label")); } @@ -318,7 +315,7 @@ public class ValueSetExpander extends ValueSetProcessBase { ValueSetExpansionContainsComponent np = null; for (String code : getCodesForConcept(focus, expParams)) { ValueSetExpansionContainsComponent t = addCode(wc, focus.getSystem(), code, focus.getDisplay(), vsSrc.getLanguage(), parent, - convert(focus.getDesignation()), expParams, focus.getAbstract(), focus.getInactive(), focus.getExtensionString(ToolingExtensions.EXT_DEFINITION), filters, noInactive, false, vsProps, null, focus.getProperty(), null, focus.getExtension(), exp); + convert(focus.getDesignation()), expParams, focus.getAbstract(), focus.getInactive(), filters, noInactive, false, vsProps, makeCSProps(focus.getExtensionString(ToolingExtensions.EXT_DEFINITION), null), focus.getProperty(), null, focus.getExtension(), exp); if (np == null) { np = t; } @@ -327,6 +324,17 @@ public class ValueSetExpander extends ValueSetProcessBase { addCodeAndDescendents(wc, c, np, expParams, filters, noInactive, vsProps, vsSrc, exp); } + private List makeCSProps(String definition, List list) { + List res = new ArrayList<>(); + if (!Utilities.noString(definition)) { + res.add(new ConceptPropertyComponent("definition", new StringType(definition))); + } + if (list != null) { + res.addAll(list); + } + return res; + } + private List getCodesForConcept(ValueSetExpansionContainsComponent focus, Parameters expParams) { List codes = new ArrayList<>(); codes.add(focus.getCode()); @@ -363,7 +371,7 @@ public class ValueSetExpander extends ValueSetProcessBase { boolean dep = CodeSystemUtilities.isDeprecated(cs, def, false); if ((includeAbstract || !abs) && filterFunc.includeConcept(cs, def) && passesOtherFilters(otherFilters, cs, def.getCode())) { for (String code : getCodesForConcept(def, expParams)) { - ValueSetExpansionContainsComponent t = addCode(wc, system, code, def.getDisplay(), cs.getLanguage(), parent, def.getDesignation(), expParams, abs, inc, def.getDefinition(), filters, noInactive, dep, vsProps, def.getProperty(), null, def.getExtension(), null, exp); + ValueSetExpansionContainsComponent t = addCode(wc, system, code, def.getDisplay(), cs.getLanguage(), parent, def.getDesignation(), expParams, abs, inc, filters, noInactive, dep, vsProps, makeCSProps(def.getDefinition(), def.getProperty()), null, def.getExtension(), null, exp); if (np == null) { np = t; } @@ -719,8 +727,8 @@ public class ValueSetExpander extends ValueSetProcessBase { private void copyImportContains(List list, ValueSetExpansionContainsComponent parent, Parameters expParams, List filter, boolean noInactive, List vsProps, ValueSet vsSrc, ValueSetExpansionComponent exp) throws FHIRException, ETooCostly { for (ValueSetExpansionContainsComponent c : list) { c.checkNoModifiers("Imported Expansion in Code System", "expanding"); - ValueSetExpansionContainsComponent np = addCode(dwc, c.getSystem(), c.getCode(), c.getDisplay(), vsSrc.getLanguage(), parent, null, expParams, c.getAbstract(), c.getInactive(), c.getExtensionString(ToolingExtensions.EXT_DEFINITION), - filter, noInactive, false, vsProps, null, c.getProperty(), null, c.getExtension(), exp); + ValueSetExpansionContainsComponent np = addCode(dwc, c.getSystem(), c.getCode(), c.getDisplay(), vsSrc.getLanguage(), parent, null, expParams, c.getAbstract(), c.getInactive(), + filter, noInactive, false, vsProps, makeCSProps(c.getExtensionString(ToolingExtensions.EXT_DEFINITION), null), c.getProperty(), null, c.getExtension(), exp); copyImportContains(c.getContains(), np, expParams, filter, noInactive, vsProps, vsSrc, exp); } } @@ -850,7 +858,7 @@ public class ValueSetExpander extends ValueSetProcessBase { isAbstract = CodeSystemUtilities.isNotSelectable(cs, def); } addCode(dwc, inc.getSystem(), c.getCode(), !Utilities.noString(c.getDisplay()) ? c.getDisplay() : def == null ? null : def.getDisplay(), c.hasDisplay() ? vsSrc.getLanguage() : cs.getLanguage(), null, mergeDesignations(def, convertDesignations(c.getDesignation())), - expParams, isAbstract, inactive, def == null ? null : def.getDefinition(), imports, noInactive, false, exp.getProperty(), def != null ? def.getProperty() : null, null, def == null ? null : def.getExtension(), c.getExtension(), exp); + expParams, isAbstract, inactive, imports, noInactive, false, exp.getProperty(), def == null ? null : makeCSProps(def.getDefinition(), def.getProperty()), null, def == null ? null : def.getExtension(), c.getExtension(), exp); } } if (inc.getFilter().size() > 0) { @@ -911,7 +919,7 @@ public class ValueSetExpander extends ValueSetProcessBase { if (def.getDisplay().contains(fc.getValue()) && passesOtherFilters(filters, cs, def.getCode())) { for (String code : getCodesForConcept(def, expParams)) { ValueSetExpansionContainsComponent t = addCode(wc, inc.getSystem(), code, def.getDisplay(), cs.getLanguage(), null, def.getDesignation(), expParams, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def), - def.getDefinition(), imports, noInactive, false, exp.getProperty(), def.getProperty(), null, def.getExtension(), null, exp); + imports, noInactive, false, exp.getProperty(), makeCSProps(def.getDefinition(), def.getProperty()), null, def.getExtension(), null, exp); } } } From 14162902de868454ee47d3b56089e5bfbe7fa1b0 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sat, 12 Aug 2023 23:06:16 +1000 Subject: [PATCH 14/15] fix tests + fix FML parser problem --- .../terminology/tests/ExternalTerminologyServiceTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/ExternalTerminologyServiceTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/ExternalTerminologyServiceTests.java index 11eb70b1d..4b14810d2 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/ExternalTerminologyServiceTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/ExternalTerminologyServiceTests.java @@ -46,8 +46,8 @@ public class ExternalTerminologyServiceTests implements ITxTesterLoader { private JsonObject test; } -// private static final String SERVER = FhirSettings.getTxFhirDevelopment(); - private static final String SERVER = FhirSettings.getTxFhirLocal(); + private static final String SERVER = FhirSettings.getTxFhirDevelopment(); +// private static final String SERVER = FhirSettings.getTxFhirLocal(); // private static final String SERVER = "https://r4.ontoserver.csiro.au/fhir"; From 2474c198c774c0de4322e77f4fc0fc40c765f986 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sat, 12 Aug 2023 23:06:27 +1000 Subject: [PATCH 15/15] fix FML parser problem --- .../java/org/hl7/fhir/r5/elementmodel/FmlParser.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java index 0664857d8..972840a08 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java @@ -28,6 +28,7 @@ import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities; import org.hl7.fhir.utilities.SourceLocation; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; @@ -563,12 +564,14 @@ public class FmlParser extends ParserBase { } private void parseParameter(Element ref, FHIRLexer lexer) throws FHIRLexerException, FHIRFormatError { + boolean r5 = VersionUtilities.isR5Plus(context.getVersion()); + String name = r5 ? "parameter" : "variable"; if (!lexer.isConstant()) { - ref.addElement("parameter").markLocation(lexer.getCurrentLocation()).makeElement("valueId").setValue(lexer.take()); + ref.addElement(name).markLocation(lexer.getCurrentLocation()).makeElement(r5 ? "valueId" : "value").setValue(lexer.take()); } else if (lexer.isStringConstant()) - ref.addElement("parameter").markLocation(lexer.getCurrentLocation()).makeElement("valueString").setValue(lexer.readConstant("??")); + ref.addElement(name).markLocation(lexer.getCurrentLocation()).makeElement(r5 ? "valueString" : "value").setValue(lexer.readConstant("??")); else { - ref.addElement("parameter").markLocation(lexer.getCurrentLocation()).makeElement("valueString").setValue(readConstant(lexer.take(), lexer)); + ref.addElement(name).markLocation(lexer.getCurrentLocation()).makeElement(r5 ? "valueString" : "value").setValue(readConstant(lexer.take(), lexer)); } }