diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java index 505f044c6..922f8fad7 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java @@ -211,6 +211,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP @Getter @Setter private boolean showMessagesFromReferences; @Getter @Setter private boolean doImplicitFHIRPathStringConversion; @Getter @Setter private HtmlInMarkdownCheck htmlInMarkdownCheck; + @Getter @Setter private boolean allowDoubleQuotesInFHIRPath; @Getter @Setter private Locale locale; @Getter @Setter private List igs = new ArrayList<>(); @Getter @Setter private List extensionDomains = new ArrayList<>(); @@ -262,6 +263,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP showMessagesFromReferences = other.showMessagesFromReferences; doImplicitFHIRPathStringConversion = other.doImplicitFHIRPathStringConversion; htmlInMarkdownCheck = other.htmlInMarkdownCheck; + allowDoubleQuotesInFHIRPath = other.allowDoubleQuotesInFHIRPath; locale = other.locale; igs.addAll(other.igs); extensionDomains.addAll(other.extensionDomains); @@ -471,6 +473,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP context.loadFromPackage(npmX, null); this.fhirPathEngine = new FHIRPathEngine(context); + this.fhirPathEngine.setAllowDoubleQuotes(false); } private String getVersionFromPack(Map source) { @@ -839,6 +842,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP validator.setQuestionnaireMode(questionnaireMode); validator.setLevel(level); validator.setHtmlInMarkdownCheck(htmlInMarkdownCheck); + validator.setAllowDoubleQuotesInFHIRPath(allowDoubleQuotesInFHIRPath); validator.setNoUnicodeBiDiControlChars(noUnicodeBiDiControlChars); validator.setDoImplicitFHIRPathStringConversion(doImplicitFHIRPathStringConversion); if (format == FhirFormat.SHC) { 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 f3d523dcc..ef4188e59 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 @@ -12,6 +12,7 @@ import org.hl7.fhir.r5.utils.validation.BundleValidationRule; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.settings.FhirSettings; +import org.hl7.fhir.validation.cli.services.ValidatorWatchMode; import org.hl7.fhir.validation.cli.utils.EngineMode; import org.hl7.fhir.validation.cli.utils.QuestionnaireMode; import org.hl7.fhir.validation.cli.utils.ValidationLevel; @@ -53,7 +54,8 @@ public class CliContext { private boolean doImplicitFHIRPathStringConversion = false; @JsonProperty("htmlInMarkdownCheck") private HtmlInMarkdownCheck htmlInMarkdownCheck = HtmlInMarkdownCheck.WARNING; - + @JsonProperty("allowDoubleQuotesInFHIRPath") + private boolean allowDoubleQuotesInFHIRPath = false; @JsonProperty("langTransform") private String langTransform = null; @JsonProperty("map") @@ -139,6 +141,8 @@ public class CliContext { @JsonProperty("fhirSettingsFile") private String fhirSettingsFile; + @JsonProperty("watchMode") + private ValidatorWatchMode watchMode = ValidatorWatchMode.NONE; @JsonProperty("map") public String getMap() { @@ -294,6 +298,16 @@ public class CliContext { this.htmlInMarkdownCheck = htmlInMarkdownCheck; } + @JsonProperty("allowDoubleQuotesInFHIRPath") + public boolean isAllowDoubleQuotesInFHIRPath() { + return allowDoubleQuotesInFHIRPath; + } + + @JsonProperty("allowDoubleQuotesInFHIRPath") + public void setAllowDoubleQuotesInFHIRPath(boolean allowDoubleQuotesInFHIRPath) { + this.allowDoubleQuotesInFHIRPath = allowDoubleQuotesInFHIRPath; + } + @JsonProperty("locale") public String getLanguageCode() { return locale; @@ -705,8 +719,10 @@ public class CliContext { noInvariants == that.noInvariants && displayWarnings == that.displayWarnings && wantInvariantsInMessages == that.wantInvariantsInMessages && + allowDoubleQuotesInFHIRPath == that.allowDoubleQuotesInFHIRPath && Objects.equals(extensions, that.extensions) && Objects.equals(map, that.map) && + Objects.equals(htmlInMarkdownCheck, that.htmlInMarkdownCheck) && Objects.equals(output, that.output) && Objects.equals(outputSuffix, that.outputSuffix) && Objects.equals(htmlOutput, that.htmlOutput) && @@ -727,21 +743,23 @@ public class CliContext { Objects.equals(profiles, that.profiles) && Objects.equals(sources, that.sources) && Objects.equals(crumbTrails, that.crumbTrails) && - Objects.equals(forPublication, that.forPublication) && + Objects.equals(forPublication, that.forPublication)&& Objects.equals(allowExampleUrls, that.allowExampleUrls) && Objects.equals(showTimes, that.showTimes) && mode == that.mode && Objects.equals(locale, that.locale) && Objects.equals(outputStyle, that.outputStyle) && Objects.equals(jurisdiction, that.jurisdiction) && - Objects.equals(locations, that.locations); + Objects.equals(locations, that.locations) && + Objects.equals(watchMode, that.watchMode) ; } @Override public int hashCode() { return Objects.hash(doNative, extensions, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching, noExtensibleBindingMessages, noInvariants, displayWarnings, wantInvariantsInMessages, map, output, outputSuffix, htmlOutput, txServer, sv, txLog, txCache, mapLog, lang, srcLang, tgtLang, fhirpath, snomedCT, - targetVer, igs, questionnaireMode, level, profiles, sources, inputs, mode, locale, locations, crumbTrails, forPublication, showTimes, allowExampleUrls, outputStyle, jurisdiction, noUnicodeBiDiControlChars); + targetVer, igs, questionnaireMode, level, profiles, sources, inputs, mode, locale, locations, crumbTrails, forPublication, showTimes, allowExampleUrls, outputStyle, jurisdiction, noUnicodeBiDiControlChars, watchMode, + htmlInMarkdownCheck, allowDoubleQuotesInFHIRPath); } @Override @@ -792,6 +810,9 @@ public class CliContext { ", locale='" + locale + '\'' + ", locations=" + locations + ", bundleValidationRules=" + bundleValidationRules + + ", htmlInMarkdownCheck=" + htmlInMarkdownCheck + + ", allowDoubleQuotesInFHIRPath=" + allowDoubleQuotesInFHIRPath + + ", watchMode=" + watchMode + '}'; } @@ -805,4 +826,17 @@ public class CliContext { public String getFhirSettingsFile() { return fhirSettingsFile; } + + @JsonProperty("watchMode") + public ValidatorWatchMode getWatchMode() { + return watchMode; + } + + @JsonProperty("watchMode") + public CliContext setWatchMode(ValidatorWatchMode watchMode) { + this.watchMode = watchMode; + return this; + } + + } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java index deba08fc5..96865dcc4 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java @@ -143,11 +143,10 @@ public class ValidationService { return versions; } - public void validateSources(CliContext cliContext, ValidationEngine validator) throws Exception { + public void validateSources(CliContext cliContext, ValidationEngine validator, ValidatorWatchMode watch) throws Exception { if (cliContext.getProfiles().size() > 0) { System.out.println(" Profiles: " + cliContext.getProfiles()); } - ValidatorWatchMode watch = ValidatorWatchMode.NONE; IgLoader igLoader = new IgLoader(validator.getPcm(), validator.getContext(), validator.getVersion()); List records = new ArrayList<>(); @@ -467,6 +466,7 @@ public class ValidationService { validationEngine.setShowMessagesFromReferences(cliContext.isShowMessagesFromReferences()); validationEngine.setDoImplicitFHIRPathStringConversion(cliContext.isDoImplicitFHIRPathStringConversion()); validationEngine.setHtmlInMarkdownCheck(cliContext.getHtmlInMarkdownCheck()); + validationEngine.setAllowDoubleQuotesInFHIRPath(cliContext.isAllowDoubleQuotesInFHIRPath()); validationEngine.setNoExtensibleBindingMessages(cliContext.isNoExtensibleBindingMessages()); validationEngine.setNoUnicodeBiDiControlChars(cliContext.isNoUnicodeBiDiControlChars()); validationEngine.setNoInvariantChecks(cliContext.isNoInvariants()); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/tasks/ValidateTask.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/tasks/ValidateTask.java index 0912757c5..4a486d2c6 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/tasks/ValidateTask.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/tasks/ValidateTask.java @@ -7,6 +7,7 @@ import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.validation.ValidationEngine; import org.hl7.fhir.validation.cli.model.CliContext; import org.hl7.fhir.validation.cli.services.ValidationService; +import org.hl7.fhir.validation.cli.services.ValidatorWatchMode; import org.hl7.fhir.validation.cli.utils.Display; import java.io.PrintStream; @@ -54,7 +55,7 @@ public class ValidateTask extends ValidationEngineTask { } System.out.println("Validating"); - validationService.validateSources(cliContext, validationEngine); + validationService.validateSources(cliContext, validationEngine, cliContext.getWatchMode()); } } 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 177af45c5..c1c0d919d 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 @@ -10,6 +10,7 @@ import org.hl7.fhir.r5.utils.validation.BundleValidationRule; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.validation.cli.model.CliContext; import org.hl7.fhir.validation.cli.model.HtmlInMarkdownCheck; +import org.hl7.fhir.validation.cli.services.ValidatorWatchMode; public class Params { @@ -83,6 +84,7 @@ public class Params { public static final String HTML_IN_MARKDOWN = "-html-in-markdown"; public static final String SRC_LANG = "-src-lang"; public static final String TGT_LANG = "-tgt-lang"; + public static final String ALLOW_DOUBLE_QUOTES = "-allow-double-quotes-in-fhirpath"; public static final String RUN_TESTS = "-run-tests"; @@ -96,6 +98,7 @@ public class Params { public static final String INPUT = "-input"; public static final String FILTER = "-filter"; private static final String FHIR_SETTINGS_PARAM = "-fhir-settings"; + private static final String WATCH_MODE_PARAM = "-watch-mode"; /** * Checks the list of passed in params to see if it contains the passed in param. @@ -240,6 +243,8 @@ public class Params { cliContext.setNoInternalCaching(true); } else if (args[i].equals(NO_EXTENSIBLE_BINDING_WARNINGS)) { cliContext.setNoExtensibleBindingMessages(true); + } else if (args[i].equals(ALLOW_DOUBLE_QUOTES)) { + cliContext.setAllowDoubleQuotesInFHIRPath(true); } else if (args[i].equals(NO_UNICODE_BIDI_CONTROL_CHARS)) { cliContext.setNoUnicodeBiDiControlChars(true); } else if (args[i].equals(NO_INVARIANTS)) { @@ -381,6 +386,12 @@ public class Params { } else { throw new Exception("Can only nominate a single -map parameter"); } + } else if (args[i].equals(WATCH_MODE_PARAM)) { + if (i + 1 == args.length) { + throw new Error("Specified -watch-mode without indicating mode value"); + } else { + cliContext.setWatchMode(readWatchMode(args[++i])); + } } else if (args[i].startsWith(X)) { i++; } else if (args[i].equals(CONVERT)) { @@ -402,6 +413,21 @@ public class Params { return cliContext; } + private static ValidatorWatchMode readWatchMode(String s) { + if (s == null) { + return ValidatorWatchMode.NONE; + } + switch (s.toLowerCase()) { + case "all" : return ValidatorWatchMode.ALL; + case "none" : return ValidatorWatchMode.NONE; + case "single" : return ValidatorWatchMode.SINGLE; + case "a" : return ValidatorWatchMode.ALL; + case "n" : return ValidatorWatchMode.NONE; + case "s" : return ValidatorWatchMode.SINGLE; + } + throw new Error("The watch mode ''"+s+"'' is not valid"); + } + private static String processJurisdiction(String s) { if (s.startsWith("urn:iso:std:iso:3166#") || s.startsWith("urn:iso:std:iso:3166:-2#") || s.startsWith("http://unstats.un.org/unsd/methods/m49/m49.htm#")) { return s; diff --git a/org.hl7.fhir.validation/src/main/resources/help/validate.txt b/org.hl7.fhir.validation/src/main/resources/help/validate.txt index fea863e23..f37632bb7 100644 --- a/org.hl7.fhir.validation/src/main/resources/help/validate.txt +++ b/org.hl7.fhir.validation/src/main/resources/help/validate.txt @@ -5,8 +5,8 @@ The validation tool compares a resource against the base definitions and any profiles declared in the resource (Resource.meta.profile) or specified on the command line -The FHIR validation tool validates a FHIR resource or bundle. Schema and -schematron checking is performed, then some additional checks are performed. +The FHIR validation tool validates a FHIR resource or bundle. Syntax and content is checked +against the specification and other profiles as specified. * XML & Json (FHIR versions {{XML_AND_JSON_FHIR_VERSIONS}}) * Turtle (FHIR versions {{TURTLE_FHIR_VERSIONS}}) @@ -72,6 +72,16 @@ The following parameters are supported: Default: results are sent to the std out. -outputSuffix [string]: used in -convert and -snapshot to deal with one or more result files (where -output can only have one) +-watch-mode [mode]: + Specify that the validator remain running and re-validate when any + of the validated files changes. The validator has to be terminated with + ctrl-c etc in this mode. + This parameter can have one of the following values: + * none: the default - don't wait, just stop when finished + * single: when any of the validated files changes, re-validate it + * all: when any of the validated files changes, re-validate all of them + All is useful when the content includes internal dependencies e.g. + a profile and it's value sets. -debug Produce additional information about the loading/validation process -recurse diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/ValidatorCliTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/ValidatorCliTests.java index b5747140d..04f3454dc 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/ValidatorCliTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/ValidatorCliTests.java @@ -3,6 +3,7 @@ package org.hl7.fhir.validation; import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.validation.cli.model.CliContext; import org.hl7.fhir.validation.cli.services.ValidationService; +import org.hl7.fhir.validation.cli.services.ValidatorWatchMode; import org.hl7.fhir.validation.cli.tasks.*; import org.hl7.fhir.validation.cli.utils.Params; @@ -245,9 +246,10 @@ public class ValidatorCliTests { final String[] args = new String[]{"dummyFile.json"}; CliContext cliContext = Params.loadCliContext(args); ValidatorCli cli = mockValidatorCliWithService(cliContext); + ValidatorWatchMode watchMode = ValidatorWatchMode.NONE; cli.readParamsAndExecuteTask(cliContext, args); Mockito.verify(validationService).determineVersion(same(cliContext)); - Mockito.verify(validationService).validateSources(same(cliContext), same(validationEngine)); + Mockito.verify(validationService).validateSources(same(cliContext), same(validationEngine), same(watchMode)); } @Test