From 36fa3a97af51d7b3d779eada9e9224173b4ba230 Mon Sep 17 00:00:00 2001 From: Mark Iantorno Date: Fri, 16 Oct 2020 10:44:01 -0400 Subject: [PATCH] Validator cleanup (#365) * cleaning up validator class * wip * I left my debug code in --- .../org/hl7/fhir/utilities/TimeTracker.java | 1 - .../org/hl7/fhir/validation/TimeTracker.java | 6 +- .../hl7/fhir/validation/ValidationEngine.java | 533 ++++++------------ .../org/hl7/fhir/validation/Validator.java | 256 ++++----- .../fhir/validation/cli/model/CliContext.java | 51 +- .../validation/cli/model/ScanOutputItem.java | 53 ++ .../cli/services/ValidationService.java | 10 +- .../validation/cli/utils/AsteriskFilter.java | 41 ++ .../hl7/fhir/validation/cli/utils/Common.java | 8 + .../fhir/validation/cli/utils/Display.java | 157 ++---- .../fhir/validation/cli/utils/EngineMode.java | 4 + .../hl7/fhir/validation/cli/utils/Params.java | 30 +- .../cli/utils/QuestionnaireMode.java | 3 + .../cli/utils/VersionSourceInformation.java | 41 ++ .../instance/InstanceValidator.java | 2 +- .../instance/type/QuestionnaireValidator.java | 3 +- .../src/main/resources/help.txt | 121 ++++ .../hl7/fhir/validation/cli/BaseRestTest.java | 37 -- .../fhir/validation/cli/ValidatorGuiTest.java | 35 -- .../cli/controller/HttpGetContextTest.java | 48 -- .../cli/controller/HttpPutContextTest.java | 26 - 21 files changed, 667 insertions(+), 799 deletions(-) create mode 100644 org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/ScanOutputItem.java create mode 100644 org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/AsteriskFilter.java create mode 100644 org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/EngineMode.java create mode 100644 org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/QuestionnaireMode.java create mode 100644 org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/VersionSourceInformation.java create mode 100644 org.hl7.fhir.validation/src/main/resources/help.txt delete mode 100644 org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/BaseRestTest.java delete mode 100644 org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/ValidatorGuiTest.java delete mode 100644 org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/controller/HttpGetContextTest.java delete mode 100644 org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/controller/HttpPutContextTest.java diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/TimeTracker.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/TimeTracker.java index 0ea7c03b6..def73143d 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/TimeTracker.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/TimeTracker.java @@ -67,7 +67,6 @@ public class TimeTracker { c.count++; c.length = c.length + System.nanoTime() - session.start; } - public String report() { CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/TimeTracker.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/TimeTracker.java index e9e7c3c92..cb33273e4 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/TimeTracker.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/TimeTracker.java @@ -6,9 +6,7 @@ public class TimeTracker { private long sdTime = 0; private long loadTime = 0; private long fpeTime = 0; - - - + public long getOverall() { return overall; } @@ -51,7 +49,5 @@ public class TimeTracker { sdTime = 0; loadTime = 0; fpeTime = 0; - } - } \ No newline at end of file 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 21ac84707..3d0dea8c8 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 @@ -7,7 +7,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -108,39 +107,15 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.Source; import org.hl7.fhir.utilities.xhtml.XhtmlComposer; import org.hl7.fhir.validation.BaseValidator.ValidationControl; -import org.hl7.fhir.validation.Validator.QuestionnaireMode; +import org.hl7.fhir.validation.cli.model.ScanOutputItem; import org.hl7.fhir.validation.cli.services.StandAloneValidatorFetcher.IPackageInstaller; +import org.hl7.fhir.validation.cli.utils.AsteriskFilter; +import org.hl7.fhir.validation.cli.utils.Common; +import org.hl7.fhir.validation.cli.utils.QuestionnaireMode; +import org.hl7.fhir.validation.cli.utils.VersionSourceInformation; import org.hl7.fhir.validation.instance.InstanceValidator; import org.xml.sax.SAXException; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ /* Copyright (c) 2011+, HL7, Inc @@ -172,14 +147,14 @@ POSSIBILITY OF SUCH DAMAGE. */ /** - * This is just a wrapper around the InstanceValidator class for convenient use - * + * This is just a wrapper around the InstanceValidator class for convenient use + * * The following resource formats are supported: XML, JSON, Turtle * The following versions are supported: 1.0.2, 1.4.0, 3.0.2, 4.0.1, and current - * + * * Note: the validation engine is intended to be threadsafe * To Use: - * + * * 1/ Initialize * ValidationEngine validator = new ValidationEngine(src); * - this must be the packageId of the relevant core specification @@ -187,109 +162,34 @@ POSSIBILITY OF SUCH DAMAGE. * * validator.connectToTSServer(txServer); * - this is optional; in the absence of a terminology service, snomed, loinc etc will not be validated - * + * * validator.loadIg(src); - * - call this any number of times for the Implementation Guide(s) of interest. + * - call this any number of times for the Implementation Guide(s) of interest. * - See https://confluence.hl7.org/display/FHIR/Using+the+FHIR+Validator for documentation about the src parameter (-ig parameter) - * + * * validator.loadQuestionnaire(src) * - url or filename of a questionnaire to load. Any loaded questionnaires will be used while validating - * + * * validator.setNative(doNative); * - whether to do xml/json/rdf schema validation as well * * You only need to do this initialization once. You can validate as many times as you like - * + * * 2. validate * validator.validate(src, profiles); - * - source (as stream, byte[]), or url or filename of a resource to validate. + * - source (as stream, byte[]), or url or filename of a resource to validate. * Also validate against any profiles (as canonical URLS, equivalent to listing them in Resource.meta.profile) - * - * if the source is provided as byte[] or stream, you need to provide a format too, though you can + * + * if the source is provided as byte[] or stream, you need to provide a format too, though you can * leave that as null, and the validator will guess - * + * * 3. Or, instead of validating, transform (see documentation and use in Validator.java) - * + * * @author Grahame Grieve * */ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInstaller { - public static class VersionSourceInformation { - - private List report = new ArrayList<>(); - private List versions = new ArrayList<>(); - - public void see(String version, String src) { - version = VersionUtilities.getMajMin(version); - report.add(src+": "+version); - if (!versions.contains(version)) { - versions.add(version); - Collections.sort(versions); - } - } - - public boolean isEmpty() { - return versions.isEmpty(); - } - - public int size() { - return versions.size(); - } - - public String version() { - return versions.get(0); - } - - public List getReport() { - if (report.isEmpty()) { - report.add("(nothing found)"); - } - return report; - } - - } - - public class ScanOutputItem { - private String ref; - private ImplementationGuide ig; - private StructureDefinition profile; - private OperationOutcome outcome; - private String id; - public ScanOutputItem(String ref, ImplementationGuide ig, StructureDefinition profile, OperationOutcome outcome) { - super(); - this.ref = ref; - this.ig = ig; - this.profile = profile; - this.outcome = outcome; - } - public String getRef() { - return ref; - } - public ImplementationGuide getIg() { - return ig; - } - public StructureDefinition getProfile() { - return profile; - } - public OperationOutcome getOutcome() { - return outcome; - } - public String getId() { - return id; - } - public void setId(String id) { - this.id = id; - } - public String getTitle() { - if (profile != null) - return "Validate " +ref+" against "+profile.present()+" ("+profile.getUrl()+")"; - if (ig != null) - return "Validate " +ref+" against global profile specified in "+ig.present()+" ("+ig.getUrl()+")"; - return "Validate " +ref+" against FHIR Spec"; - } - } - public class TransformSupportServices implements ITransformerServices { private List outputs; @@ -308,7 +208,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst @Override public Base createType(Object appInfo, String name) throws FHIRException { StructureDefinition sd = context.fetchResource(StructureDefinition.class, name); - return Manager.build(context, sd); + return Manager.build(context, sd); } @Override @@ -333,9 +233,8 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst public List performSearch(Object appContext, String url) throws FHIRException { throw new FHIRException("performSearch is not supported yet"); } - } - + private SimpleWorkerContext context; // private FHIRPathEngine fpe; private Map binaries = new HashMap(); @@ -360,53 +259,16 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst private Map validationControl = new HashMap<>(); private QuestionnaireMode questionnaireMode; - private class AsteriskFilter implements FilenameFilter { - String dir; - String regex; - - public AsteriskFilter(String filter) throws IOException { - if (!filter.matches("(.*(\\\\|\\/))*(.*)\\*(.*)")) - throw new IOException("Filter names must have the following syntax: [directorypath][prefix]?*[suffix]? I.e. The asterisk must be in the filename, not the directory path"); - dir = filter.replaceAll("(.*(\\\\|\\/))*(.*)\\*(.*)", "$1"); - String expression = filter.replaceAll("(.*(\\\\|\\/))*(.*)", "$3"); - regex = ""; - for (int i = 0; i < expression.length(); i++) { - if (Character.isAlphabetic(expression.codePointAt(i)) || Character.isDigit(expression.codePointAt(i))) - regex = regex + expression.charAt(i); - else if (expression.charAt(i)=='*') - regex = regex + ".*"; - else - regex = regex + "\\" + expression.charAt(i); - } - File f = new File(dir); - if (!f.exists()) { - throw new IOException("Directory " + dir + " does not exist"); - } - if (!f.isDirectory()) { - throw new IOException("Directory " + dir + " is not a directory"); - } - } - - public boolean accept(File dir, String s) { - boolean match = s.matches(regex); - return match; - } - - public String getDir() { - return dir; - } - } - public ValidationEngine() throws IOException { pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); context = SimpleWorkerContext.fromNothing(); initContext(null); } - + public String setTerminologyServer(String src, String log, FhirPublication version) throws FHIRException, URISyntaxException { - return connectToTSServer(src, log, version); + return connectToTSServer(src, log, version); } - + public boolean isHintAboutNonMustSupport() { return hintAboutNonMustSupport; } @@ -423,7 +285,6 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst this.anyExtensionsAllowed = anyExtensionsAllowed; } - public boolean isShowTimes() { return showTimes; } @@ -436,29 +297,29 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); loadCoreDefinitions(src, false, null); context.setCanRunWithoutTerminology(canRunWithoutTerminologyServer); - setTerminologyServer(txsrvr, txLog, version); + setTerminologyServer(txsrvr, txLog, version); this.version = vString; } - + public ValidationEngine(String src, String txsrvr, String txLog, FhirPublication version, String vString) throws FHIRException, IOException, URISyntaxException { pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); loadCoreDefinitions(src, false, null); setTerminologyServer(txsrvr, txLog, version); this.version = vString; } - + public ValidationEngine(String src, FhirPublication version, String vString, TimeTracker tt) throws FHIRException, IOException, URISyntaxException { pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); loadCoreDefinitions(src, false, tt); this.version = vString; } - - + + public ValidationEngine(String src) throws FHIRException, IOException { loadCoreDefinitions(src, false, null); pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); } - + public String getLanguage() { return language; } @@ -476,7 +337,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst version = npm.fhirVersion(); context = SimpleWorkerContext.fromPackage(npm, loaderForVersion()); } else { - Map source = loadIgSource(src, recursive, true); + Map source = loadIgSource(src, recursive, true); if (version == null) { version = getVersionFromPack(source); } @@ -501,7 +362,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst private IContextResourceLoader loaderForVersion() { return loaderForVersion(version); } - + private IContextResourceLoader loaderForVersion(String version) { if (Utilities.noString(version)) return null; @@ -510,11 +371,11 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst if (version.startsWith("1.4")) return new R2016MayToR5Loader(new String[] { "Conformance", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire","ConceptMap","StructureMap", "NamingSystem"}, new NullLoaderKnowledgeProvider()); // special case if (version.startsWith("3.0")) - return new R3ToR5Loader(new String[] { "CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire","ConceptMap","StructureMap", "NamingSystem"}, new NullLoaderKnowledgeProvider()); + return new R3ToR5Loader(new String[] { "CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire","ConceptMap","StructureMap", "NamingSystem"}, new NullLoaderKnowledgeProvider()); if (version.startsWith("4.0")) - return new R4ToR5Loader(new String[] { "CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire","ConceptMap","StructureMap", "NamingSystem"}, new NullLoaderKnowledgeProvider()); + return new R4ToR5Loader(new String[] { "CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire","ConceptMap","StructureMap", "NamingSystem"}, new NullLoaderKnowledgeProvider()); if (version.startsWith("5.0")) - return new R5ToR5Loader(new String[] { "CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire","ConceptMap","StructureMap", "NamingSystem"}, new NullLoaderKnowledgeProvider()); + return new R5ToR5Loader(new String[] { "CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire","ConceptMap","StructureMap", "NamingSystem"}, new NullLoaderKnowledgeProvider()); return null; } @@ -544,10 +405,10 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst private byte[] loadProfileSource(String src) throws FHIRException, FileNotFoundException, IOException { if (Utilities.noString(src)) { throw new FHIRException("Profile Source '" + src + "' could not be processed"); - } else if (src.startsWith("https:") || src.startsWith("http:")) { + } else if (Common.isNetworkPath(src)) { return loadProfileFromUrl(src); } else if (new File(src).exists()) { - return loadProfileFromFile(src); + return loadProfileFromFile(src); } else { throw new FHIRException("Definitions Source '"+src+"' could not be processed"); } @@ -565,21 +426,21 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst private byte[] loadProfileFromFile(String src) throws FileNotFoundException, IOException { File f = new File(src); - if (f.isDirectory()) + if (f.isDirectory()) throw new IOException("You must provide a file name, not a directory name"); return TextFile.fileToBytes(src); } - /** explore should be true if we're trying to load an -ig parameter, and false if we're loading source + /** explore should be true if we're trying to load an -ig parameter, and false if we're loading source * @throws IOException **/ - + private Map loadIgSource(String src, boolean recursive, boolean explore) throws FHIRException, IOException { // src can be one of the following: // - a canonical url for an ig - this will be converted to a package id and loaded into the cache // - a package id for an ig - this will be loaded into the cache // - a direct reference to a package ("package.tgz") - this will be extracted by the cache manager, but not put in the cache // - a folder containing resources - these will be loaded directly - if (src.startsWith("https:") || src.startsWith("http:")) { + if (Common.isNetworkPath(src)) { String v = null; if (src.contains("|")) { v = src.substring(src.indexOf("|")+1); @@ -591,7 +452,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst else return fetchFromUrl(src+(v == null ? "" : "|"+v), explore); } - + File f = new File(Utilities.path(src)); if (f.exists()) { if (f.isDirectory() && new File(Utilities.path(src, "package.tgz")).exists()) @@ -621,7 +482,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } private Map loadIgSourceForVersion(String src, boolean recursive, boolean explore, VersionSourceInformation versions) throws FHIRException, IOException { - if (src.startsWith("https:") || src.startsWith("http:")) { + if (Common.isNetworkPath(src)) { String v = null; if (src.contains("|")) { v = src.substring(src.indexOf("|")+1); @@ -635,7 +496,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst return fetchVersionFromUrl(src+(v == null ? "" : "|"+v), explore, versions); } } - + File f = new File(Utilities.path(src)); if (f.exists()) { if (f.isDirectory() && new File(Utilities.path(src, "package.tgz")).exists()) { @@ -669,7 +530,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst throw new FHIRException("Unable to find/resolve/read -ig "+src); } - + private Map fetchFromUrl(String src, boolean explore) throws FHIRException, IOException { if (src.endsWith(".tgz")) return loadPackage(fetchFromUrlSpecific(src, false), src); @@ -693,7 +554,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst stream = fetchFromUrlSpecific(Utilities.pathURL(src, "validator.pack"), true); //// ----- } - + // ok, having tried all that... now we'll just try to access it directly byte[] cnt; List errors = new ArrayList<>(); @@ -707,14 +568,14 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } if (cnt == null) { throw new FHIRException("Unable to fetch content from "+src+" ("+errors.toString()+")"); - + } FhirFormat fmt = checkIsResource(cnt, src); if (fmt != null) { Map res = new HashMap(); res.put(Utilities.changeFileExt(src, "."+fmt.getExtension()), cnt); return res; - } + } throw new FHIRException("Unable to read content from "+src+": cannot determine format"); } @@ -745,20 +606,20 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst stream = fetchFromUrlSpecific(Utilities.pathURL(src, "validator.pack"), true); //// ----- } - + // ok, having tried all that... now we'll just try to access it directly byte[] cnt; if (stream == null) cnt = fetchFromUrlSpecific(src, "application/json", true, null); else cnt = TextFile.streamToBytes(stream); - + FhirFormat fmt = checkIsResource(cnt, src); if (fmt != null) { Map res = new HashMap(); res.put(Utilities.changeFileExt(src, "."+fmt.getExtension()), cnt); return res; - } + } String fn = Utilities.path("[tmp]", "fetch-resource-error-content.bin"); TextFile.bytesToFile(cnt, fn); System.out.println("Error Fetching "+src); @@ -800,7 +661,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst URL url = new URL(source); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestProperty("Accept", contentType); - return TextFile.streamToBytes(conn.getInputStream()); + return TextFile.streamToBytes(conn.getInputStream()); } } catch (IOException e) { if (errors != null) { @@ -867,7 +728,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } } } - + for (String s : pi.listResources("CodeSystem", "ConceptMap", "ImplementationGuide", "CapabilityStatement", "SearchParameter", "Conformance", "StructureMap", "ValueSet", "StructureDefinition")) { res.put(s, TextFile.streamToBytes(pi.load("package", s))); } @@ -888,11 +749,11 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst byte[] buf = new byte[1024]; while ((n = in.read(buf, 0, 1024)) > -1) { b.write(buf, 0, n); - } + } res.put(name, b.toByteArray()); zip.closeEntry(); } - zip.close(); + zip.close(); return res; } @@ -970,8 +831,8 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst public SimpleWorkerContext getContext() { return context; } - - + + public boolean isNoInvariantChecks() { return noInvariantChecks; } @@ -1004,20 +865,20 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } if (debug) System.out.println(" .. not a resource: "+filename); - return null; + return null; } private FhirFormat checkIsResource(String path) throws IOException { String ext = Utilities.getFileExtension(path); - if (Utilities.existsInList(ext, "xml")) + if (Utilities.existsInList(ext, "xml")) return FhirFormat.XML; - if (Utilities.existsInList(ext, "json")) + if (Utilities.existsInList(ext, "json")) return FhirFormat.JSON; - if (Utilities.existsInList(ext, "ttl")) + if (Utilities.existsInList(ext, "ttl")) return FhirFormat.TURTLE; - if (Utilities.existsInList(ext, "map")) + if (Utilities.existsInList(ext, "map")) return FhirFormat.TEXT; - if (Utilities.existsInList(ext, "txt")) + if (Utilities.existsInList(ext, "txt")) return FhirFormat.TEXT; return checkIsResource(TextFile.fileToBytes(path), path); @@ -1034,7 +895,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } catch (Exception e) { if (context.isCanRunWithoutTerminology()) { return "n/a: Running without Terminology Server (error: "+e.getMessage()+")"; - } else + } else throw e; } } @@ -1045,13 +906,13 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst return; if (context.hasResource(ImplementationGuide.class, src)) return; - + byte[] source = loadProfileSource(src); FhirFormat fmt = FormatUtilities.determineFormat(source); Resource r = FormatUtilities.makeParser(fmt).parse(source); context.cacheResource(r); } - + public void scanForIgVersion(String src, boolean recursive, VersionSourceInformation versions) throws IOException, FHIRException, Exception { Map source = loadIgSourceForVersion(src, recursive, true, versions); if (source != null && source.containsKey("version.info")) @@ -1071,10 +932,10 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst System.out.print(" Load " + src); if (!src.contains("#")) { System.out.print("#"+npm.version()); - } + } int count = context.loadFromPackage(npm, loaderForVersion(npm.fhirVersion())); - System.out.println(" - "+count+" resources ("+context.clock().milestone()+")"); - } else { + System.out.println(" - "+count+" resources ("+context.clock().milestone()+")"); + } else { System.out.print(" Load " + src); String canonical = null; int count = 0; @@ -1108,7 +969,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst if (canonical != null) { grabNatives(source, canonical); } - System.out.println(" - "+count+" resources ("+context.clock().milestone()+")"); + System.out.println(" - "+count+" resources ("+context.clock().milestone()+")"); } } @@ -1116,7 +977,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst if (debug) System.out.print("* load file: "+fn); Resource r = null; - try { + try { r = loadResourceByVersion(version, t.getValue(), fn); if (debug) System.out.println(" .. success"); @@ -1209,7 +1070,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } } - public void setQuestionnaireMode(Validator.QuestionnaireMode questionnaireMode) { + public void setQuestionnaireMode(QuestionnaireMode questionnaireMode) { this.questionnaireMode = questionnaireMode; } @@ -1221,7 +1082,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst byte[] focus = null; FhirFormat cntType = null; } - + public Content loadContent(String source, String opName, boolean asIg) throws FHIRException, IOException { Map s = loadIgSource(source, false, asIg); Content res = new Content(); @@ -1230,13 +1091,13 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst for (Entry t: s.entrySet()) { res.focus = t.getValue(); if (t.getKey().endsWith(".json")) - res.cntType = FhirFormat.JSON; + res.cntType = FhirFormat.JSON; else if (t.getKey().endsWith(".xml")) - res.cntType = FhirFormat.XML; + res.cntType = FhirFormat.XML; else if (t.getKey().endsWith(".ttl")) - res.cntType = FhirFormat.TURTLE; + res.cntType = FhirFormat.TURTLE; else if (t.getKey().endsWith(".txt") || t.getKey().endsWith(".map")) - res.cntType = FhirFormat.TEXT; + res.cntType = FhirFormat.TEXT; else throw new FHIRException("Todo: Determining resource type is not yet done"); } @@ -1264,20 +1125,20 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } return list; } - + public OperationOutcome validate(String source, List profiles) throws FHIRException, IOException { List l = new ArrayList(); l.add(source); return (OperationOutcome)validate(l, profiles); } - + public List validateScan(List sources, Set guides) throws FHIRException, IOException, EOperationOutcome { List refs = new ArrayList(); - handleSources(sources, refs); - + parseSources(sources, refs); + List res = new ArrayList(); InstanceValidator validator = getValidator(); - + for (String ref : refs) { Content cnt = loadContent(ref, "validate", false); List messages = new ArrayList(); @@ -1290,10 +1151,10 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } catch (Exception ex) { res.add(new ScanOutputItem(ref, null, null, exceptionToOutcome(ex))); } - if (e != null) { + if (e != null) { String rt = e.fhirType(); for (String u : guides) { - ImplementationGuide ig = context.fetchResource(ImplementationGuide.class, u); + ImplementationGuide ig = context.fetchResource(ImplementationGuide.class, u); System.out.println("Check Guide "+ig.getUrl()); String canonical = ig.getUrl().contains("/Impl") ? ig.getUrl().substring(0, ig.getUrl().indexOf("/Impl")) : ig.getUrl(); String url = getGlobal(ig, rt); @@ -1307,7 +1168,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst res.add(new ScanOutputItem(ref, ig, null, exceptionToOutcome(ex))); } } - Set done = new HashSet<>(); + Set done = new HashSet<>(); for (StructureDefinition sd : context.allStructures()) { if (!done.contains(sd.getUrl())) { done.add(sd.getUrl()); @@ -1328,7 +1189,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } return res; } - + private List asSdList(StructureDefinition sd) { List res = new ArrayList(); res.add(sd); @@ -1349,7 +1210,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst public void scanForVersions(List sources, VersionSourceInformation versions) throws FHIRException, IOException { List refs = new ArrayList(); - handleSources(sources, refs); + parseSources(sources, refs); for (String ref : refs) { Content cnt = loadContent(ref, "validate", false); String s = TextFile.bytesToString(cnt.focus); @@ -1365,20 +1226,20 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst if (s.contains("http://hl7.org/fhir/1.4")) { versions.see("1.4", "Profile in "+ref); } - } + } } - + public Resource validate(List sources, List profiles) throws FHIRException, IOException { if (profiles.size() > 0) { System.out.println(" Profiles: "+profiles); } List refs = new ArrayList(); - boolean asBundle = handleSources(sources, refs); + boolean asBundle = parseSources(sources, refs); Bundle results = new Bundle(); results.setType(Bundle.BundleType.COLLECTION); for (String ref : refs) { TimeTracker.Session tts = context.clock().start("validation"); - context.clock().milestone(); + context.clock().milestone(); System.out.print(" Validate " + ref); Content cnt = loadContent(ref, "validate", false); try { @@ -1399,34 +1260,30 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst return results.getEntryFirstRep().getResource(); } - - public OperationOutcome validateString(String location, String source, FhirFormat format, List profiles) throws FHIRException, IOException, EOperationOutcome, SAXException { - return validate(location, source.getBytes(), format, profiles); - } - - // Public to allow reporting of results in alternate ways - public boolean handleSources(List sources, List refs) throws IOException { - boolean asBundle = sources.size() > 1; + /** + * Iterates through the list of passed in sources, extracting all references and populated them in the passed in list. + * @return {@link Boolean#TRUE} if more than one reference is found. + */ + public boolean parseSources(List sources, List refs) throws IOException { + boolean multipleRefsFound = sources.size() > 1; for (String source : sources) { - if (handleSource(source, refs)) { - asBundle = true; // Code needs to be written this way to ensure handleSource gets called - } + multipleRefsFound |= extractReferences(source, refs); } - - return asBundle; + return multipleRefsFound; } - - private boolean handleSource(String name, List refs) throws IOException { - boolean isBundle = false; - if (name.startsWith("https:") || name.startsWith("http:")) { - refs.add(name); - } else if (name.contains("*")) { - isBundle = true; + /** + * Parses passed in resource path, adding any found references to the passed in list. + * @return {@link Boolean#TRUE} if more than one reference is found. + */ + private boolean extractReferences(String name, List refs) throws IOException { + if (Common.isNetworkPath(name)) { + refs.add(name); + } else if (Common.isWildcardPath(name)) { AsteriskFilter filter = new AsteriskFilter(name); File[] files = new File(filter.getDir()).listFiles(filter); - for (int i=0; i < files.length; i++) { - refs.add(files[i].getPath()); + for (File file : files) { + refs.add(file.getPath()); } } else { File file = new File(name); @@ -1442,7 +1299,6 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst if (file.isFile()) { refs.add(name); } else { - isBundle = true; for (int i=0; i < file.listFiles().length; i++) { File[] fileList = file.listFiles(); if (fileList[i].isFile()) @@ -1450,12 +1306,12 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } } } - - return isBundle; + return refs.size() > 1 ; } public OperationOutcome validate(byte[] source, FhirFormat cntType, List profiles, List messages) throws FHIRException, IOException, EOperationOutcome { InstanceValidator validator = getValidator(); + validator.validate(null, messages, new ByteArrayInputStream(source), cntType, asSdList(profiles)); return messagesToOutcome(messages); } @@ -1491,12 +1347,12 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst InstanceValidator validator = getValidator(); validator.setResourceIdRule(resourceIdRule); validator.setBestPracticeWarningLevel(bpWarnings); - validator.setCheckDisplay(displayOption); + validator.setCheckDisplay(displayOption); validator.validate(null, messages, new ByteArrayInputStream(source), cntType, asSdList(profiles)); return messagesToOutcome(messages); } - - + + private void validateSHEX(String location, List messages) { messages.add(new ValidationMessage(Source.InstanceValidator, IssueType.INFORMATIONAL, location, "SHEX Validation is not done yet", IssueSeverity.INFORMATION)); } @@ -1505,28 +1361,8 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst messages.add(new ValidationMessage(Source.InstanceValidator, IssueType.INFORMATIONAL, location, "XML Schema Validation is not done yet", IssueSeverity.INFORMATION)); } - private Map loadSchemas() throws IOException { - Map res = new HashMap(); - for (Entry e : readZip(new ByteArrayInputStream(binaries.get("http://hl7.org/fhir#fhir-all-xsd.zip"))).entrySet()) { - if (e.getKey().equals("fhir-single.xsd")) - res.put(e.getKey(), e.getValue()); - if (e.getKey().equals("fhir-invariants.sch")) - res.put(e.getKey(), e.getValue()); - } - return res; - } - - private Map loadTransforms() throws IOException { - Map res = new HashMap(); - for (Entry e : readZip(new ByteArrayInputStream(binaries.get("http://hl7.org/fhir#fhir-all-xsd.zip"))).entrySet()) { - if (e.getKey().endsWith(".xsl")) - res.put(e.getKey(), e.getValue()); - } - return res; - } - private void validateJsonSchema(String location, List messages) { - messages.add(new ValidationMessage(Source.InstanceValidator, IssueType.INFORMATIONAL, location, "JSON Schema Validation is not done yet", IssueSeverity.INFORMATION)); + messages.add(new ValidationMessage(Source.InstanceValidator, IssueType.INFORMATIONAL, location, "JSON Schema Validation is not done yet", IssueSeverity.INFORMATION)); } private List filterMessages(List messages) { @@ -1538,7 +1374,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst filteredValidation.sort(null); return filteredValidation; } - + private OperationOutcome exceptionToOutcome(Exception ex) throws IOException, FHIRException, EOperationOutcome { OperationOutcome op = new OperationOutcome(); op.addIssue().setCode(org.hl7.fhir.r5.model.OperationOutcome.IssueType.EXCEPTION).setSeverity(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.FATAL).getDetails().setText(ex.getMessage()); @@ -1546,7 +1382,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst RendererFactory.factory(op, rc).render(op); return op; } - + private OperationOutcome messagesToOutcome(List messages) throws IOException, FHIRException, EOperationOutcome { OperationOutcome op = new OperationOutcome(); for (ValidationMessage vm : filterMessages(messages)) { @@ -1565,26 +1401,21 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst RendererFactory.factory(op, rc).render(op); return op; } - - public static String issueSummary (OperationOutcomeIssueComponent issue) { - String source = ToolingExtensions.readStringExtension(issue, ToolingExtensions.EXT_ISSUE_SOURCE); - return issue.getSeverity().toString()+" @ "+issue.getLocation() + " " +issue.getDetails().getText() +(source != null ? " (src = "+source+")" : ""); - } public org.hl7.fhir.r5.elementmodel.Element transform(String source, String map) throws FHIRException, IOException { Content cnt = loadContent(source, "validate", false); return transform(cnt.focus, cnt.cntType, map); } - + public org.hl7.fhir.r5.elementmodel.Element transform(byte[] source, FhirFormat cntType, String mapUri) throws FHIRException, IOException { List outputs = new ArrayList(); - + StructureMapUtilities scu = new StructureMapUtilities(context, new TransformSupportServices(outputs)); - org.hl7.fhir.r5.elementmodel.Element src = Manager.parse(context, new ByteArrayInputStream(source), cntType); + org.hl7.fhir.r5.elementmodel.Element src = Manager.parse(context, new ByteArrayInputStream(source), cntType); StructureMap map = context.getTransform(mapUri); if (map == null) throw new Error("Unable to find map "+mapUri+" (Known Maps = "+context.listMapUrls()+")"); - + org.hl7.fhir.r5.elementmodel.Element resource = getTargetResourceFromStructureMap(map); scu.transform(null, src, map, resource); return resource; @@ -1612,7 +1443,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst if(structureDefinition == null) throw new FHIRException("Unable to find StructureDefinition for target type ('"+targetTypeUrl+"')"); - + return Manager.build(getContext(), structureDefinition); } @@ -1623,7 +1454,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst RendererFactory.factory(res, rc).render((DomainResource) res); return (DomainResource) res; } - + public void convert(String source, String output) throws FHIRException, IOException { Content cnt = loadContent(source, "validate", false); Element e = Manager.parse(context, new ByteArrayInputStream(cnt.focus), cnt.cntType); @@ -1640,12 +1471,12 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst public StructureDefinition snapshot(String source, String version) throws FHIRException, IOException { Content cnt = loadContent(source, "validate", false); Resource res = loadResourceByVersion(version, cnt.focus, Utilities.getFileNameForName(source)); - + if (!(res instanceof StructureDefinition)) throw new FHIRException("Require a StructureDefinition for generating a snapshot"); StructureDefinition sd = (StructureDefinition) res; StructureDefinition base = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); - + new ProfileUtilities(context, null, null).setAutoFixSliceNames(true).generateSnapshot(base, sd, sd.getUrl(), null, sd.getName()); return sd; } @@ -1656,7 +1487,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst public void dropResource(String type, String id) { context.dropResource(type, id); - + } public String getVersion() { @@ -1713,7 +1544,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst makeSnapshot(sdb); new ProfileUtilities(context, null, null).setAutoFixSliceNames(true).generateSnapshot(sdb, sd, sd.getUrl(), null, sd.getName()); } - + } public boolean isDebug() { @@ -1746,7 +1577,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst b.append(" text-align: center;\r\n"); b.append("}\r\n"); b.append("\r\n"); - b.append("th span\r\n"); + b.append("th span\r\n"); b.append("{\r\n"); b.append(" -ms-writing-mode: tb-rl;\r\n"); b.append(" -webkit-writing-mode: vertical-rl;\r\n"); @@ -1761,17 +1592,17 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst // organise Set refs = new HashSet<>(); - Set igs = new HashSet<>(); - Map> profiles = new HashMap<>(); + Set igs = new HashSet<>(); + Map> profiles = new HashMap<>(); for (ScanOutputItem item : items) { - refs.add(item.ref); - if (item.ig != null) { - igs.add(item.ig.getUrl()); - if (!profiles.containsKey(item.ig.getUrl())) { - profiles.put(item.ig.getUrl(), new HashSet<>()); + refs.add(item.getRef()); + if (item.getIg() != null) { + igs.add(item.getIg().getUrl()); + if (!profiles.containsKey(item.getIg().getUrl())) { + profiles.put(item.getIg().getUrl(), new HashSet<>()); } - if (item.profile != null) - profiles.get(item.ig.getUrl()).add(item.profile.getUrl()); + if (item.getProfile() != null) + profiles.get(item.getIg().getUrl()).add(item.getProfile().getUrl()); } } @@ -1780,16 +1611,16 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst b.append(""); for (String s : sorted(igs)) { ImplementationGuide ig = context.fetchResource(ImplementationGuide.class, s); - b.append(""+ig.present()+""); + b.append(""+ig.present()+""); } b.append("\r\n"); b.append("SourceCore Spec"); for (String s : sorted(igs)) { ImplementationGuide ig = context.fetchResource(ImplementationGuide.class, s); - b.append("Global"); + b.append("Global"); for (String sp : sorted(profiles.get(s))) { StructureDefinition sd = context.fetchResource(StructureDefinition.class, sp); - b.append(""+sd.present()+""); + b.append(""+sd.present()+""); } } b.append("\r\n"); @@ -1802,7 +1633,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst ImplementationGuide ig = context.fetchResource(ImplementationGuide.class, si); b.append(genOutcome(items, s, si, null)); for (String sp : sorted(profiles.get(ig.getUrl()))) { - b.append(genOutcome(items, s, si, sp)); + b.append(genOutcome(items, s, si, sp)); } } b.append("\r\n"); @@ -1813,7 +1644,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst b.append(""); b.append(""); for (String s : sorted(refs)) { - b.append(""); + b.append(""); } b.append("\r\n"); b.append(""); @@ -1824,8 +1655,8 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst for (String si : sorted(igs)) { b.append(""); ImplementationGuide ig = context.fetchResource(ImplementationGuide.class, si); - b.append(""); - b.append(""); + b.append(""); + b.append(""); for (String s : sorted(refs)) { b.append(genOutcome(items, s, si, null)); } @@ -1834,7 +1665,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst for (String sp : sorted(profiles.get(ig.getUrl()))) { b.append(""); StructureDefinition sd = context.fetchResource(StructureDefinition.class, sp); - b.append(""); + b.append(""); for (String s : sorted(refs)) { b.append(genOutcome(items, s, si, sp)); } @@ -1842,39 +1673,37 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } } b.append("
"+s+""+s+"
Core Spec
"+ig.present()+"Global"+ig.present()+"Global
"+sd.present()+""+sd.present()+"
\r\n"); - + b.append(""); b.append(""); TextFile.stringToFile(b.toString(), Utilities.path(folder, "scan.html")); - - } private String genOutcome(List items, String src, String ig, String profile) { ScanOutputItem item = null; for (ScanOutputItem t : items) { boolean match = true; - if (!t.ref.equals(src)) + if (!t.getRef().equals(src)) match = false; - if (!((ig == null && t.ig == null) || (ig != null && t.ig != null && ig.equals(t.ig.getUrl())))) + if (!((ig == null && t.getIg() == null) || (ig != null && t.getIg() != null && ig.equals(t.getIg().getUrl())))) match = false; - if (!((profile == null && t.profile == null) || (profile != null && t.profile != null && profile.equals(t.profile.getUrl())))) + if (!((profile == null && t.getProfile() == null) || (profile != null && t.getProfile() != null && profile.equals(t.getProfile().getUrl())))) match = false; if (match) { item = t; break; } } - + if (item == null) return ""; boolean ok = true; - for (OperationOutcomeIssueComponent iss : item.outcome.getIssue()) { + for (OperationOutcomeIssueComponent iss : item.getOutcome().getIssue()) { if (iss.getSeverity() == org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR || iss.getSeverity() == org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.FATAL) { ok = false; } } - if (ok) + if (ok) return "\u2714"; else return "\u2716"; @@ -1922,7 +1751,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst InputStream s = c.getInputStream(); FileOutputStream f = new FileOutputStream(filename); transfer(s, f, 1024); - f.close(); + f.close(); } public static void transfer(InputStream in, OutputStream out, int buffer) throws IOException { @@ -1934,11 +1763,11 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst private void genScanOutputItem(ScanOutputItem item, String filename) throws IOException, FHIRException, EOperationOutcome { RenderingContext rc = new RenderingContext(context, null, null, "http://hl7.org/fhir", "", null, ResourceRendererMode.RESOURCE); rc.setNoSlowLookup(true); - RendererFactory.factory(item.outcome, rc).render(item.outcome); - String s = new XhtmlComposer(XhtmlComposer.HTML).compose(item.outcome.getText().getDiv()); - + RendererFactory.factory(item.getOutcome(), rc).render(item.getOutcome()); + String s = new XhtmlComposer(XhtmlComposer.HTML).compose(item.getOutcome().getText().getDiv()); + String title = item.getTitle(); - + StringBuilder b = new StringBuilder(); b.append(""); b.append(""); @@ -1947,14 +1776,12 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst b.append(""); b.append(""); b.append("

"+title+"

"); - b.append(s); + b.append(s); b.append(""); b.append(""); TextFile.stringToFile(b.toString(), filename); - } - private List sorted(Set keys) { List names = new ArrayList(); if (keys != null) @@ -1971,7 +1798,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } if (fetcher != null) { return fetcher.fetch(appContext, url); - } + } return null; } @@ -1986,7 +1813,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } else if (fetcher != null) { return fetcher.validationPolicy(appContext, path, url); } else { - return ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE; + return ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE; } } @@ -1997,8 +1824,8 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } if (context.fetchResource(Resource.class, url) != null) return true; - if (Utilities.existsInList(url, "http://hl7.org/fhir/sid/us-ssn", "http://hl7.org/fhir/sid/cvx", "http://hl7.org/fhir/sid/ndc", "http://hl7.org/fhir/sid/us-npi", "http://hl7.org/fhir/sid/icd-10", - "http://hl7.org/fhir/sid/icd-10-vn", "http://hl7.org/fhir/sid/icd-10-cm", "http://hl7.org/fhir/sid/icd-9-cm", "http://hl7.org/fhir/w5", "http://hl7.org/fhir/fivews", + if (Utilities.existsInList(url, "http://hl7.org/fhir/sid/us-ssn", "http://hl7.org/fhir/sid/cvx", "http://hl7.org/fhir/sid/ndc", "http://hl7.org/fhir/sid/us-npi", "http://hl7.org/fhir/sid/icd-10", + "http://hl7.org/fhir/sid/icd-10-vn", "http://hl7.org/fhir/sid/icd-10-cm", "http://hl7.org/fhir/sid/icd-9-cm", "http://hl7.org/fhir/w5", "http://hl7.org/fhir/fivews", "http://hl7.org/fhir/workflow", "http://hl7.org/fhir/ConsentPolicy/opt-out", "http://hl7.org/fhir/ConsentPolicy/opt-in")) { return true; } @@ -2123,8 +1950,8 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst public void setNoExtensibleBindingMessages(boolean noExtensibleBindingMessages) { this.noExtensibleBindingMessages = noExtensibleBindingMessages; } - - + + public boolean isSecurityChecks() { return securityChecks; @@ -2134,7 +1961,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst this.securityChecks = securityChecks; } - + public boolean isCrumbTrails() { return crumbTrails; } @@ -2146,7 +1973,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst public byte[] transformVersion(String source, String targetVer, FhirFormat format, Boolean canDoNative) throws FHIRException, IOException, Exception { Content cnt = loadContent(source, "validate", false); org.hl7.fhir.r5.elementmodel.Element src = Manager.parse(context, new ByteArrayInputStream(cnt.focus), cnt.cntType); - + // if the src has a url, we try to use the java code if ((canDoNative == null && src.hasChild("url")) || (canDoNative != null && canDoNative)) { try { @@ -2181,7 +2008,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst Manager.compose(context, resource, bs, format, OutputStyle.PRETTY, null); return bs.toByteArray(); } - + private String getMapId(String type, String targetVer) { if (VersionUtilities.isR2Ver(version)) { if (VersionUtilities.isR3Ver(targetVer)) { @@ -2269,7 +2096,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst throw new FHIRException("Target Version not supported yet: "+targetVer); } } - + public byte[] convertVersionNativeR2b(String targetVer, Content cnt, FhirFormat format) throws IOException, Exception { org.hl7.fhir.dstu2016may.model.Resource r2b; switch (cnt.cntType) { @@ -2407,7 +2234,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst throw new FHIRException("Target Version not supported yet: "+targetVer); } } - + public byte[] convertVersionNativeR4(String targetVer, Content cnt, FhirFormat format) throws IOException, Exception { org.hl7.fhir.r4.model.Resource r4; @@ -2487,7 +2314,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst public FilesystemPackageCacheManager getPcm() { return pcm; } - + public List getBundleValidationRules() { return bundleValidationRules; } @@ -2495,28 +2322,28 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst public boolean packageExists(String id, String ver) throws IOException, FHIRException { return pcm.packageExists(id, ver); } - + public void loadPackage(String id, String ver) throws IOException, FHIRException { loadIg(id+(ver == null ? "" : "#"+ver), true); } /** * Systems that host the ValidationEngine can use this to control what validation the validator performs. - * - * Using this, you can turn particular kinds of validation on and off. In addition, you can override + * + * Using this, you can turn particular kinds of validation on and off. In addition, you can override * the error | warning | hint level and make it a different level. - * + * * Each entry has - * * 'allowed': a boolean flag. if this is false, the Validator will not report the error. - * * 'level' : set to error, warning, information - * + * * 'allowed': a boolean flag. if this is false, the Validator will not report the error. + * * 'level' : set to error, warning, information + * * Entries are registered by ID, using the IDs in /org.hl7.fhir.utilities/src/main/resources/Messages.properties - * - * This feature is not supported by the validator CLI - and won't be. It's for systems hosting + * + * This feature is not supported by the validator CLI - and won't be. It's for systems hosting * the validation framework in their own implementation context */ public Map getValidationControl() { return validationControl; } - + } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java index add394963..521cb7cf1 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/Validator.java @@ -1,33 +1,33 @@ package org.hl7.fhir.validation; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ /* Copyright (c) 2011+, HL7, Inc @@ -58,22 +58,16 @@ POSSIBILITY OF SUCH DAMAGE. */ -import java.io.File; -import java.net.URI; - import org.hl7.fhir.r5.model.ImplementationGuide; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.utilities.TimeTracker; -import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; -import org.hl7.fhir.validation.ValidationEngine.VersionSourceInformation; -import org.hl7.fhir.validation.cli.ValidatorGui; import org.hl7.fhir.validation.cli.model.CliContext; import org.hl7.fhir.validation.cli.services.ComparisonService; import org.hl7.fhir.validation.cli.services.ValidationService; -import org.hl7.fhir.validation.cli.utils.Common; -import org.hl7.fhir.validation.cli.utils.Display; -import org.hl7.fhir.validation.cli.utils.Params; +import org.hl7.fhir.validation.cli.utils.*; + +import java.io.File; /** * A executable class that will validate one or more FHIR resources against @@ -84,125 +78,108 @@ import org.hl7.fhir.validation.cli.utils.Params; * if you want to host validation inside a process, skip this class, and look at * ValidationEngine *

- * todo: find a gome for this: + * todo: find a home for this: * * @author Grahame */ public class Validator { - public enum EngineMode { - VALIDATION, TRANSFORM, NARRATIVE, SNAPSHOT, SCAN, CONVERT, FHIRPATH, VERSION - } - - public enum QuestionnaireMode { NONE, CHECK, REQUIRED } - - private static CliContext cliContext; - - private static String getNamedParam(String[] args, String param) { - boolean found = false; - for (String a : args) { - if (found) - return a; - if (a.equals(param)) { - found = true; - } - } - return null; - } - - private static String toMB(long maxMemory) { - return Long.toString(maxMemory / (1024 * 1024)); - } - - private static CliContext getCliContext() { - if (cliContext == null) { - cliContext = new CliContext(); - } - return cliContext; - } - - private static void goToWebPage(String url) { - try { - - URI uri= new URI(url); - - java.awt.Desktop.getDesktop().browse(uri); - System.out.println("Web page opened in browser"); - - } catch (Exception e) { - - e.printStackTrace(); - } - } + public static final String HTTP_PROXY_HOST = "http.proxyHost"; + public static final String HTTP_PROXY_PORT = "http.proxyPort"; public static void main(String[] args) throws Exception { TimeTracker tt = new TimeTracker(); - TimeTracker.Session tts = tt.start("Loading"); + TimeTracker.Session tts = tt.start("Loading"); - System.out.println("FHIR Validation tool " + VersionUtil.getVersionString()); - System.out.println(" Java: " + System.getProperty("java.version") + " from " + System.getProperty("java.home") + " on " + System.getProperty("os.arch") + " (" + System.getProperty("sun.arch.data.model") + "bit). " + toMB(Runtime.getRuntime().maxMemory()) + "MB available"); - String proxy = getNamedParam(args, Params.PROXY); - if (!Utilities.noString(proxy)) { - String[] p = proxy.split("\\:"); - System.setProperty("http.proxyHost", p[0]); - System.setProperty("http.proxyPort", p[1]); + Display.displayVersion(); + Display.displaySystemInfo(); + + if (Params.hasParam(args, Params.PROXY)) { + String[] p = Params.getParam(args, Params.PROXY).split("\\:"); + System.setProperty(HTTP_PROXY_HOST, p[0]); + System.setProperty(HTTP_PROXY_PORT, p[1]); } - if (Params.hasParam(args, Params.GUI)) { - cliContext = Params.loadCliContext(args); - String v = Common.getVersion(args); - String definitions = VersionUtilities.packageForVersion(v) + "#" + v; - ValidationEngine validationEngine = Common.getValidationEngine(v, definitions, cliContext.getTxLog(), null); - ValidatorGui.start(cliContext, validationEngine, true); - } else if (Params.hasParam(args, Params.TEST)) { + CliContext cliContext = Params.loadCliContext(args); + + if (Params.hasParam(args, Params.TEST)) { Common.runValidationEngineTests(); - } else if (args.length == 0 || Params.hasParam(args, Params.HELP) || Params.hasParam(args, "?") || Params.hasParam(args, "-?") || Params.hasParam(args, "/?")) { + } else if (shouldDisplayHelpToUser(args)) { Display.displayHelpDetails(); } else if (Params.hasParam(args, Params.COMPARE)) { - Display.printCliArgumentsAndInfo(args); - String dest = Params.getParam(args, Params.DESTINATION); - if (dest == null) - System.out.println("no -dest parameter provided"); - else if (!new File(dest).isDirectory()) - System.out.println("Specified destination (-dest parameter) is not valid: \"" + dest + "\")"); - else { - // first, prepare the context - cliContext = Params.loadCliContext(args); - if (cliContext.getSv() == null) { - cliContext.setSv(determineVersion(cliContext)); - } - String v = VersionUtilities.getCurrentVersion(cliContext.getSv()); - String definitions = VersionUtilities.packageForVersion(v) + "#" + v; - ValidationEngine validator = ValidationService.getValidator(cliContext, definitions, tt); - ComparisonService.doLeftRightComparison(args, dest, validator); + if (destinationDirectoryValid(Params.getParam(args, Params.DESTINATION))) { + doLeftRightComparison(args, cliContext, tt); } } else { Display.printCliArgumentsAndInfo(args); - cliContext = Params.loadCliContext(args); + doValidation(tt, tts, cliContext); + } + } - if (cliContext.getSv() == null) { - cliContext.setSv(determineVersion(cliContext)); - } + private static boolean destinationDirectoryValid(String dest) { + if (dest == null) { + System.out.println("no -dest parameter provided"); + return false; + } else if (!new File(dest).isDirectory()) { + System.out.println("Specified destination (-dest parameter) is not valid: \"" + dest + "\")"); + return false; + } else { + System.out.println("Valid destination directory provided: \"" + dest + "\")"); + return true; + } + } - System.out.println("Loading"); - // Comment this out because definitions filename doesn't necessarily contain version (and many not even be 14 characters long). Version gets spit out a couple of lines later after we've loaded the context - String definitions = VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv()); - ValidationEngine validator = ValidationService.getValidator(cliContext, definitions, tt); - tts.end(); - if (cliContext.getMode() == EngineMode.VERSION) { + private static boolean shouldDisplayHelpToUser(String[] args) { + return (args.length == 0 + || Params.hasParam(args, Params.HELP) + || Params.hasParam(args, "?") + || Params.hasParam(args, "-?") + || Params.hasParam(args, "/?")); + } - ValidationService.transformVersion(cliContext, validator); - } else if (cliContext.getMode() == EngineMode.TRANSFORM) { + private static void doLeftRightComparison(String[] args, CliContext cliContext, TimeTracker tt) throws Exception { + Display.printCliArgumentsAndInfo(args); + if (cliContext.getSv() == null) { + cliContext.setSv(determineVersion(cliContext)); + } + String v = VersionUtilities.getCurrentVersion(cliContext.getSv()); + String definitions = VersionUtilities.packageForVersion(v) + "#" + v; + ValidationEngine validator = ValidationService.getValidator(cliContext, definitions, tt); + ComparisonService.doLeftRightComparison(args, Params.getParam(args, Params.DESTINATION), validator); + } + + private static void doValidation(TimeTracker tt, TimeTracker.Session tts, CliContext cliContext) throws Exception { + if (cliContext.getSv() == null) { + cliContext.setSv(determineVersion(cliContext)); + } + System.out.println("Loading"); + // Comment this out because definitions filename doesn't necessarily contain version (and many not even be 14 characters long). + // Version gets spit out a couple of lines later after we've loaded the context + String definitions = VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv()); + ValidationEngine validator = ValidationService.getValidator(cliContext, definitions, tt); + tts.end(); + switch (cliContext.getMode()) { + case TRANSFORM: ValidationService.transform(cliContext, validator); - } else if (cliContext.getMode() == EngineMode.NARRATIVE) { + break; + case NARRATIVE: ValidationService.generateNarrative(cliContext, validator); - } else if (cliContext.getMode() == EngineMode.SNAPSHOT) { + break; + case SNAPSHOT: ValidationService.generateSnapshot(cliContext, validator); - } else if (cliContext.getMode() == EngineMode.CONVERT) { + break; + case CONVERT: ValidationService.convertSources(cliContext, validator); - } else if (cliContext.getMode() == EngineMode.FHIRPATH) { + break; + case FHIRPATH: ValidationService.evaluateFhirpath(cliContext, validator); - } else { + break; + case VERSION: + ValidationService.transformVersion(cliContext, validator); + break; + case VALIDATION: + case SCAN: + default: for (String s : cliContext.getProfiles()) { if (!validator.getContext().hasResource(StructureDefinition.class, s) && !validator.getContext().hasResource(ImplementationGuide.class, s)) { System.out.println(" Fetch Profile from " + s); @@ -215,10 +192,9 @@ public class Validator { } else { ValidationService.validateSources(cliContext, validator); } - } - System.out.println("Done. "+tt.report()); + break; } - + System.out.println("Done. " + tt.report()); } public static String determineVersion(CliContext cliContext) throws Exception { @@ -228,17 +204,17 @@ public class Validator { System.out.println("Scanning for versions (no -version parameter):"); VersionSourceInformation versions = ValidationService.scanForVersions(cliContext); for (String s : versions.getReport()) { - System.out.println(" "+s); + System.out.println(" " + s); } if (versions.isEmpty()) { - System.out.println("-> Using Default version '"+VersionUtilities.CURRENT_VERSION+"'"); + System.out.println("-> Using Default version '" + VersionUtilities.CURRENT_VERSION + "'"); return "current"; } if (versions.size() == 1) { - System.out.println("-> use version "+versions.version()); - return versions.version(); + System.out.println("-> use version " + versions.version()); + return versions.version(); } - throw new Exception("-> Multiple versions found. Specify a particular version using the -version parameter"); + throw new Exception("-> Multiple versions found. Specify a particular version using the -version parameter"); } - + } \ No newline at end of file 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 5870b5f98..551277f28 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 @@ -8,7 +8,8 @@ import java.util.Map; import java.util.Objects; import org.hl7.fhir.r5.utils.IResourceValidator.BundleValidationRule; -import org.hl7.fhir.validation.Validator; +import org.hl7.fhir.validation.cli.utils.QuestionnaireMode; +import org.hl7.fhir.validation.cli.utils.EngineMode; import com.fasterxml.jackson.annotation.JsonProperty; @@ -60,7 +61,7 @@ public class CliContext { @JsonProperty("igs") private List igs = new ArrayList(); @JsonProperty("questionnaire") - private Validator.QuestionnaireMode questionnaireMode = Validator.QuestionnaireMode.CHECK; + private QuestionnaireMode questionnaireMode = QuestionnaireMode.CHECK; @JsonProperty("profiles") private List profiles = new ArrayList(); @@ -68,7 +69,7 @@ public class CliContext { private List sources = new ArrayList(); @JsonProperty("mode") - private Validator.EngineMode mode = Validator.EngineMode.VALIDATION; + private EngineMode mode = EngineMode.VALIDATION; @JsonProperty("securityChecks") private boolean securityChecks = false; @@ -125,12 +126,12 @@ public class CliContext { } @JsonProperty("questionnaire") - public Validator.QuestionnaireMode getQuestionnaireMode() { + public QuestionnaireMode getQuestionnaireMode() { return questionnaireMode; } @JsonProperty("questionnaire") - public CliContext setQuestionnaireMode(Validator.QuestionnaireMode questionnaireMode) { + public CliContext setQuestionnaireMode(QuestionnaireMode questionnaireMode) { this.questionnaireMode = questionnaireMode; return this; } @@ -230,12 +231,12 @@ public class CliContext { } @JsonProperty("mode") - public Validator.EngineMode getMode() { + public EngineMode getMode() { return mode; } @JsonProperty("mode") - public CliContext setMode(Validator.EngineMode mode) { + public CliContext setMode(EngineMode mode) { this.mode = mode; return this; } @@ -494,4 +495,40 @@ public class CliContext { public int hashCode() { return Objects.hash(doNative, anyExtensionsAllowed, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching, noExtensibleBindingMessages, map, output, txServer, sv, txLog, mapLog, lang, fhirpath, snomedCT, targetVer, igs, questionnaireMode, profiles, sources, mode, locale, locations, crumbTrails, showTimes); } + + @Override + public String toString() { + return "CliContext{" + + "doNative=" + doNative + + ", anyExtensionsAllowed=" + anyExtensionsAllowed + + ", hintAboutNonMustSupport=" + hintAboutNonMustSupport + + ", recursive=" + recursive + + ", doDebug=" + doDebug + + ", assumeValidRestReferences=" + assumeValidRestReferences + + ", canDoNative=" + canDoNative + + ", noInternalCaching=" + noInternalCaching + + ", noExtensibleBindingMessages=" + noExtensibleBindingMessages + + ", map='" + map + '\'' + + ", output='" + output + '\'' + + ", txServer='" + txServer + '\'' + + ", sv='" + sv + '\'' + + ", txLog='" + txLog + '\'' + + ", mapLog='" + mapLog + '\'' + + ", lang='" + lang + '\'' + + ", fhirpath='" + fhirpath + '\'' + + ", snomedCT='" + snomedCT + '\'' + + ", targetVer='" + targetVer + '\'' + + ", igs=" + igs + + ", questionnaireMode=" + questionnaireMode + + ", profiles=" + profiles + + ", sources=" + sources + + ", mode=" + mode + + ", securityChecks=" + securityChecks + + ", crumbTrails=" + crumbTrails + + ", showTimes=" + showTimes + + ", locale='" + locale + '\'' + + ", locations=" + locations + + ", bundleValidationRules=" + bundleValidationRules + + '}'; + } } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/ScanOutputItem.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/ScanOutputItem.java new file mode 100644 index 000000000..0f09a40b3 --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/ScanOutputItem.java @@ -0,0 +1,53 @@ +package org.hl7.fhir.validation.cli.model; + +import org.hl7.fhir.r5.model.ImplementationGuide; +import org.hl7.fhir.r5.model.OperationOutcome; +import org.hl7.fhir.r5.model.StructureDefinition; + +public class ScanOutputItem { + private String ref; + private ImplementationGuide ig; + private StructureDefinition profile; + private OperationOutcome outcome; + private String id; + + public ScanOutputItem(String ref, ImplementationGuide ig, StructureDefinition profile, OperationOutcome outcome) { + super(); + this.ref = ref; + this.ig = ig; + this.profile = profile; + this.outcome = outcome; + } + + public String getRef() { + return ref; + } + + public ImplementationGuide getIg() { + return ig; + } + + public StructureDefinition getProfile() { + return profile; + } + + public OperationOutcome getOutcome() { + return outcome; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTitle() { + if (profile != null) + return "Validate " + ref + " against " + profile.present() + " (" + profile.getUrl() + ")"; + if (ig != null) + return "Validate " + ref + " against global profile specified in " + ig.present() + " (" + ig.getUrl() + ")"; + return "Validate " + ref + " against FHIR Spec"; + } +} \ 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 17aca317e..0004cd102 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 @@ -26,12 +26,8 @@ import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.validation.ValidationEngine; -import org.hl7.fhir.validation.ValidationEngine.VersionSourceInformation; -import org.hl7.fhir.validation.cli.model.CliContext; -import org.hl7.fhir.validation.cli.model.FileInfo; -import org.hl7.fhir.validation.cli.model.ValidationOutcome; -import org.hl7.fhir.validation.cli.model.ValidationRequest; -import org.hl7.fhir.validation.cli.model.ValidationResponse; +import org.hl7.fhir.validation.cli.model.*; +import org.hl7.fhir.validation.cli.utils.VersionSourceInformation; public class ValidationService { @@ -110,7 +106,7 @@ public class ValidationService { if (ig.getUrl().contains("/ImplementationGuide") && !ig.getUrl().equals("http://hl7.org/fhir/ImplementationGuide/fhir")) urls.add(ig.getUrl()); } - List res = validator.validateScan(cliContext.getSources(), urls); + List res = validator.validateScan(cliContext.getSources(), urls); validator.genScanOutput(cliContext.getOutput(), res); System.out.println("Done. output in " + Utilities.path(cliContext.getOutput(), "scan.html")); } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/AsteriskFilter.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/AsteriskFilter.java new file mode 100644 index 000000000..c2786e98d --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/AsteriskFilter.java @@ -0,0 +1,41 @@ +package org.hl7.fhir.validation.cli.utils; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; + +public class AsteriskFilter implements FilenameFilter { + String dir; + String regex; + + public AsteriskFilter(String filter) throws IOException { + if (!filter.matches("(.*(\\\\|\\/))*(.*)\\*(.*)")) + throw new IOException("Filter names must have the following syntax: [directorypath][prefix]?*[suffix]? I.e. The asterisk must be in the filename, not the directory path"); + dir = filter.replaceAll("(.*(\\\\|\\/))*(.*)\\*(.*)", "$1"); + String expression = filter.replaceAll("(.*(\\\\|\\/))*(.*)", "$3"); + regex = ""; + for (int i = 0; i < expression.length(); i++) { + if (Character.isAlphabetic(expression.codePointAt(i)) || Character.isDigit(expression.codePointAt(i))) + regex = regex + expression.charAt(i); + else if (expression.charAt(i)=='*') + regex = regex + ".*"; + else + regex = regex + "\\" + expression.charAt(i); + } + File f = new File(dir); + if (!f.exists()) { + throw new IOException("Directory " + dir + " does not exist"); + } + if (!f.isDirectory()) { + throw new IOException("Directory " + dir + " is not a directory"); + } + } + + public boolean accept(File dir, String s) { + return s.matches(regex); + } + + public String getDir() { + return dir; + } +} \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Common.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Common.java index babbb6a03..1fa047690 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Common.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Common.java @@ -92,4 +92,12 @@ public class Common { return ve; } + public static boolean isNetworkPath(String path) { + return path.startsWith("https:") || path.startsWith("http:"); + } + + public static boolean isWildcardPath(String name) { + return name.contains("*"); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Display.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Display.java index a224cd23d..bf16fd4da 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Display.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Display.java @@ -1,11 +1,15 @@ package org.hl7.fhir.validation.cli.utils; +import java.io.File; import java.io.IOException; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; import org.hl7.fhir.r5.model.Constants; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; import org.hl7.fhir.utilities.npm.ToolsVersion; +import org.hl7.fhir.validation.VersionUtil; /** * Class for displaying output to the cli user. @@ -14,6 +18,10 @@ import org.hl7.fhir.utilities.npm.ToolsVersion; */ public class Display { + private static String toMB(long maxMemory) { + return Long.toString(maxMemory / (1024 * 1024)); + } + public static void printCliArgumentsAndInfo(String[] args) throws IOException { System.out.println(" Paths: Current = " + System.getProperty("user.dir") + ", Package Cache = " + new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION).getFolder()); System.out.print(" Params:"); @@ -23,128 +31,35 @@ public class Display { System.out.println(); } + /** + * Loads the help details from resources/help.txt, and displays them on the command line to the user. + */ public static void displayHelpDetails() { - System.out.println(""); - System.out.println("The FHIR validation tool validates a FHIR resource or bundle."); - System.out.println("The validation tool compares a resource against the base definitions and any"); - System.out.println("profiles declared in the resource (Resource.meta.profile) or specified on the "); - System.out.println("command line"); - System.out.println(""); - System.out.println("The FHIR validation tool validates a FHIR resource or bundle."); - System.out.println("Schema and schematron checking is performed, then some additional checks are performed. "); - System.out.println("* XML & Json (FHIR versions 1.0, 1.4, 3.0, 4.0, " + Constants.VERSION_MM + ")"); - System.out.println("* Turtle (FHIR versions 3.0, 4.0, " + Constants.VERSION_MM + ")"); - System.out.println(""); - System.out.println("If requested, instances will also be verified against the appropriate schema"); - System.out.println("W3C XML Schema, JSON schema or ShEx, as appropriate"); - System.out.println(""); - System.out.println("Usage: java -jar [validator].jar (parameters)"); - System.out.println(""); - System.out.println("The following parameters are supported:"); - System.out.println("[source]: a file, url, directory or pattern for resources to validate. At"); - System.out.println(" least one source must be declared. If there is more than one source or if"); - System.out.println(" the source is other than a single file or url and the output parameter is"); - System.out.println(" used, results will be provided as a Bundle."); - System.out.println(" Patterns are limited to a directory followed by a filename with an embedded"); - System.out.println(" asterisk. E.g. foo*-examples.xml or someresource.*, etc."); - System.out.println("-version [ver]: The FHIR version to use. This can only appear once. "); - System.out.println(" valid values 1.0 | 1.4 | 3.0 | " + VersionUtilities.CURRENT_VERSION + " or 1.0.2 | 1.4.0 | 3.0.2 | 4.0.1 | " + VersionUtilities.CURRENT_FULL_VERSION); - System.out.println(" Default value is " + VersionUtilities.CURRENT_VERSION); - System.out.println("-ig [package|file|folder|url]: an IG or profile definition to load. Can be "); - System.out.println(" the URL of an implementation guide or a package ([id]-[ver]) for"); - System.out.println(" a built implementation guide or a local folder that contains a"); - System.out.println(" set of conformance resources."); - System.out.println(" No default value. This parameter can appear any number of times"); - System.out.println("-tx [url]: the [base] url of a FHIR terminology service"); - System.out.println(" Default value is http://tx.fhir.org. This parameter can appear once"); - System.out.println(" To run without terminology value, specific n/a as the URL"); - System.out.println("-txLog [file]: Produce a log of the terminology server operations in [file]"); - System.out.println(" Default value is not to produce a log"); - System.out.println("-profile [url]: the canonical URL to validate against (same as if it was "); - System.out.println(" specified in Resource.meta.profile). If no profile is specified, the "); - System.out.println(" resource is validated against the base specification. This parameter "); - System.out.println(" can appear any number of times."); - System.out.println(" Note: the profile (and it's dependencies) have to be made available "); - System.out.println(" through one of the -ig parameters. Note that package dependencies will "); - System.out.println(" automatically be resolved"); - System.out.println("-questionnaire [file|url}: the location of a questionnaire. If provided, then the validator will validate"); - System.out.println(" any QuestionnaireResponse that claims to match the Questionnaire against it"); - System.out.println(" no default value. This parameter can appear any number of times"); - System.out.println("-output [file]: a filename for the results (OperationOutcome)"); - System.out.println(" Default: results are sent to the std out."); - System.out.println("-debug"); - System.out.println(" Produce additional information about the loading/validation process"); - System.out.println("-recurse"); - System.out.println(" Look in subfolders when -ig refers to a folder"); - System.out.println("-locale"); - System.out.println(" Specifies the locale/language of the validation result messages (eg.: de-DE"); - System.out.println("-sct"); - System.out.println(" Specify the edition of SNOMED CT to use. Valid Choices:"); - System.out.println(" intl | us | uk | au | nl | ca | se | dk | es"); - System.out.println(" tx.fhir.org only supports a subset. To add to this list or tx.fhir.org"); - System.out.println(" ask on https://chat.fhir.org/#narrow/stream/179202-terminology"); - System.out.println("-native: use schema for validation as well"); - System.out.println(" * XML: w3c schema+schematron"); - System.out.println(" * JSON: json.schema"); - System.out.println(" * RDF: SHEX"); - System.out.println(" Default: false"); - System.out.println("-language: [lang]"); - System.out.println(" The language to use when validating coding displays - same value as for xml:lang"); - System.out.println(" Not used if the resource specifies language"); - System.out.println(" Default: no specified language"); - System.out.println("-strictExtensions: If present, treat extensions not defined within the specified FHIR version and any"); - System.out.println(" referenced implementation guides or profiles as errors. (Default is to only raise information messages.)"); - System.out.println("-hintAboutNonMustSupport: If present, raise hints if the instance contains data elements that are not"); - System.out.println(" marked as mustSupport=true. Useful to identify elements included that may be ignored by recipients"); - System.out.println("-assumeValidRestReferences: If present, assume that URLs that reference resources follow the RESTful URI pattern"); - System.out.println(" and it is safe to infer the type from the URL"); - System.out.println("-security-checks: If present, check that string content doesn't include any html-like tags that might create"); - System.out.println(" problems downstream (though all external input must always be santized by escaping for either html or sql)"); - System.out.println(""); - System.out.println("The validator also supports the param -proxy=[address]:[port] for if you use a proxy"); - System.out.println(""); - System.out.println("Parameters can appear in any order"); - System.out.println(""); - System.out.println("Alternatively, you can use the validator to execute a transformation as described by a structure map."); - System.out.println("To do this, you must provide some additional parameters:"); - System.out.println(""); - System.out.println(" -transform [map]"); - System.out.println(""); - System.out.println("* [map] the URI of the map that the transform starts with"); - System.out.println(""); - System.out.println("Any other dependency maps have to be loaded through an -ig reference "); - System.out.println(""); - System.out.println("-transform uses the parameters -defn, -txserver, -ig (at least one with the map files), and -output"); - System.out.println(""); - System.out.println("Alternatively, you can use the validator to generate narrative for a resource."); - System.out.println("To do this, you must provide a specific parameter:"); - System.out.println(""); - System.out.println(" -narrative"); - System.out.println(""); - System.out.println("-narrative requires the parameters -defn, -txserver, -source, and -output. ig and profile may be used"); - System.out.println(""); - System.out.println("Alternatively, you can use the validator to convert a resource or logical model."); - System.out.println("To do this, you must provide a specific parameter:"); - System.out.println(""); - System.out.println(" -convert"); - System.out.println(""); - System.out.println("-convert requires the parameters -source and -output. ig may be used to provide a logical model"); - System.out.println(""); - System.out.println("Alternatively, you can use the validator to evaluate a FHIRPath expression on a resource or logical model."); - System.out.println("To do this, you must provide a specific parameter:"); - System.out.println(""); - System.out.println(" -fhirpath [FHIRPath]"); - System.out.println(""); - System.out.println("* [FHIRPath] the FHIRPath expression to evaluate"); - System.out.println(""); - System.out.println("-fhirpath requires the parameters -source. ig may be used to provide a logical model"); - System.out.println(""); - System.out.println("Finally, you can use the validator to generate a snapshot for a profile."); - System.out.println("To do this, you must provide a specific parameter:"); - System.out.println(""); - System.out.println(" -snapshot"); - System.out.println(""); - System.out.println("-snapshot requires the parameters -defn, -txserver, -source, and -output. ig may be used to provide necessary base profiles"); + ClassLoader classLoader = Display.class.getClassLoader(); + File file = new File(classLoader.getResource("help.txt").getFile()); + try { + String data = FileUtils.readFileToString(file, "UTF-8"); + System.out.println(data); + } catch (IOException e) { + e.printStackTrace(); + } } + /** + * Prints out system info to the command line. + */ + public static void displaySystemInfo() { + System.out.println(" Java: " + System.getProperty("java.version") + + " from " + System.getProperty("java.home") + + " on " + System.getProperty("os.arch") + + " (" + System.getProperty("sun.arch.data.model") + "bit). " + + toMB(Runtime.getRuntime().maxMemory()) + "MB available"); + } + + /** + * Prints current version of the validator. + */ + public static void displayVersion() { + System.out.println("FHIR Validation tool " + VersionUtil.getVersionString()); + } } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/EngineMode.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/EngineMode.java new file mode 100644 index 000000000..7c5835f9c --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/EngineMode.java @@ -0,0 +1,4 @@ +package org.hl7.fhir.validation.cli.utils; +public enum EngineMode { + VALIDATION, TRANSFORM, NARRATIVE, SNAPSHOT, SCAN, CONVERT, FHIRPATH, VERSION +} 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 81c4218dd..6417254e7 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 @@ -6,12 +6,10 @@ import java.util.Locale; import org.hl7.fhir.r5.utils.IResourceValidator.BundleValidationRule; import org.hl7.fhir.utilities.VersionUtilities; -import org.hl7.fhir.validation.Validator; import org.hl7.fhir.validation.cli.model.CliContext; public class Params { - public static final String GUI = "-gui"; public static final String VERSION = "-version"; public static final String OUTPUT = "-output"; public static final String PROXY = "-proxy"; @@ -74,9 +72,9 @@ public class Params { * @return {@link String} value for the provided param. */ public static String getParam(String[] args, String param) { - for (int i = 0; i < args.length - 1; i++) - if (args[i].equals(param)) - return args[i + 1]; + for (int i = 0; i < args.length - 1; i++) { + if (args[i].equals(param)) return args[i + 1]; + } return null; } @@ -104,7 +102,7 @@ public class Params { } else { p = args[++i]; cliContext.addProfile(p); - } + } } else if (args[i].equals(BUNDLE)) { String p = null; String r = null; @@ -124,7 +122,7 @@ public class Params { throw new Error("Specified -questionnaire without indicating questionnaire file"); else { String q = args[++i]; - cliContext.setQuestionnaireMode(Validator.QuestionnaireMode.valueOf(q)); + cliContext.setQuestionnaireMode(QuestionnaireMode.valueOf(q)); } } else if (args[i].equals(NATIVE)) { cliContext.setDoNative(true); @@ -145,25 +143,25 @@ public class Params { } else if (args[i].equals(STRICT_EXTENSIONS)) { cliContext.setAnyExtensionsAllowed(false); } else if (args[i].equals(NO_INTERNAL_CACHING)) { - cliContext.setNoInternalCaching(true); + cliContext.setNoInternalCaching(true); } else if (args[i].equals(NO_EXTENSIBLE_BINDING_WARNINGS)) { - cliContext.setNoExtensibleBindingMessages(true); + cliContext.setNoExtensibleBindingMessages(true); } else if (args[i].equals(HINT_ABOUT_NON_MUST_SUPPORT)) { cliContext.setHintAboutNonMustSupport(true); } else if (args[i].equals(TO_VERSION)) { cliContext.setTargetVer(args[++i]); - cliContext.setMode(Validator.EngineMode.VERSION); + cliContext.setMode(EngineMode.VERSION); } else if (args[i].equals(DO_NATIVE)) { cliContext.setCanDoNative(true); } else if (args[i].equals(NO_NATIVE)) { cliContext.setCanDoNative(false); } else if (args[i].equals(TRANSFORM)) { cliContext.setMap(args[++i]); - cliContext.setMode(Validator.EngineMode.TRANSFORM); + cliContext.setMode(EngineMode.TRANSFORM); } else if (args[i].equals(NARRATIVE)) { - cliContext.setMode(Validator.EngineMode.NARRATIVE); + cliContext.setMode(EngineMode.NARRATIVE); } else if (args[i].equals(SNAPSHOT)) { - cliContext.setMode(Validator.EngineMode.SNAPSHOT); + cliContext.setMode(EngineMode.SNAPSHOT); } else if (args[i].equals(SECURITY_CHECKS)) { cliContext.setSecurityChecks(true); } else if (args[i].equals(CRUMB_TRAIL)) { @@ -171,7 +169,7 @@ public class Params { } else if (args[i].equals(SHOW_TIMES)) { cliContext.setShowTimes(true); } else if (args[i].equals(SCAN)) { - cliContext.setMode(Validator.EngineMode.SCAN); + cliContext.setMode(EngineMode.SCAN); } else if (args[i].equals(TERMINOLOGY)) { if (i + 1 == args.length) throw new Error("Specified -tx without indicating terminology server"); @@ -216,9 +214,9 @@ public class Params { } else if (args[i].startsWith(X)) { i++; } else if (args[i].equals(CONVERT)) { - cliContext.setMode(Validator.EngineMode.CONVERT); + cliContext.setMode(EngineMode.CONVERT); } else if (args[i].equals(FHIRPATH)) { - cliContext.setMode(Validator.EngineMode.FHIRPATH); + cliContext.setMode(EngineMode.FHIRPATH); if (cliContext.getFhirpath() == null) if (i + 1 == args.length) throw new Error("Specified -fhirpath without indicating a FHIRPath expression"); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/QuestionnaireMode.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/QuestionnaireMode.java new file mode 100644 index 000000000..ef13bfbc0 --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/QuestionnaireMode.java @@ -0,0 +1,3 @@ +package org.hl7.fhir.validation.cli.utils; + +public enum QuestionnaireMode { NONE, CHECK, REQUIRED } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/VersionSourceInformation.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/VersionSourceInformation.java new file mode 100644 index 000000000..c7baf0163 --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/VersionSourceInformation.java @@ -0,0 +1,41 @@ +package org.hl7.fhir.validation.cli.utils; + +import org.hl7.fhir.utilities.VersionUtilities; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class VersionSourceInformation { + + private List report = new ArrayList<>(); + private List versions = new ArrayList<>(); + + public void see(String version, String src) { + version = VersionUtilities.getMajMin(version); + report.add(src+": "+version); + if (!versions.contains(version)) { + versions.add(version); + Collections.sort(versions); + } + } + + public boolean isEmpty() { + return versions.isEmpty(); + } + + public int size() { + return versions.size(); + } + + public String version() { + return versions.get(0); + } + + public List getReport() { + if (report.isEmpty()) { + report.add("(nothing found)"); + } + return report; + } +} \ 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 770dc1f26..cc147f5c3 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 @@ -147,7 +147,7 @@ import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.utilities.xhtml.NodeType; import org.hl7.fhir.utilities.xhtml.XhtmlNode; import org.hl7.fhir.validation.BaseValidator; -import org.hl7.fhir.validation.Validator.QuestionnaireMode; +import org.hl7.fhir.validation.cli.utils.QuestionnaireMode; import org.hl7.fhir.validation.instance.type.BundleValidator; import org.hl7.fhir.validation.instance.type.CodeSystemValidator; import org.hl7.fhir.validation.instance.type.MeasureValidator; 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 94b7cf1c9..88f0521bf 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 @@ -43,8 +43,8 @@ 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.cli.utils.QuestionnaireMode; import org.hl7.fhir.validation.TimeTracker; -import org.hl7.fhir.validation.Validator.QuestionnaireMode; import org.hl7.fhir.validation.instance.EnableWhenEvaluator; import org.hl7.fhir.validation.instance.EnableWhenEvaluator.QStack; import org.hl7.fhir.validation.instance.utils.NodeStack; @@ -86,7 +86,6 @@ public class QuestionnaireValidator extends BaseValidator { } } - private void validateQuestionnaireElement(List errors, NodeStack ns, Element questionnaire, Element item, List parents) { // R4+ if ((FHIRVersion.isR4Plus(context.getVersion())) && (item.hasChildren("enableWhen"))) { diff --git a/org.hl7.fhir.validation/src/main/resources/help.txt b/org.hl7.fhir.validation/src/main/resources/help.txt new file mode 100644 index 000000000..b39419d5f --- /dev/null +++ b/org.hl7.fhir.validation/src/main/resources/help.txt @@ -0,0 +1,121 @@ + +The FHIR validation tool validates a FHIR resource or bundle. +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. +* XML & Json (FHIR versions 1.0, 1.4, 3.0, 4.0, " + Constants.VERSION_MM + ") +* Turtle (FHIR versions 3.0, 4.0, " + Constants.VERSION_MM + ") + +If requested, instances will also be verified against the appropriate schema +W3C XML Schema, JSON schema or ShEx, as appropriate + +Usage: java -jar [validator].jar (parameters) + +The following parameters are supported: +[source]: a file, url, directory or pattern for resources to validate. At + least one source must be declared. If there is more than one source or if + the source is other than a single file or url and the output parameter is + used, results will be provided as a Bundle. + Patterns are limited to a directory followed by a filename with an embedded + asterisk. E.g. foo*-examples.xml or someresource.*, etc. +-version [ver]: The FHIR version to use. This can only appear once. + valid values 1.0 | 1.4 | 3.0 | " + VersionUtilities.CURRENT_VERSION + " or 1.0.2 | 1.4.0 | 3.0.2 | 4.0.1 | " + VersionUtilities.CURRENT_FULL_VERSION); + Default value is " + VersionUtilities.CURRENT_VERSION); +-ig [package|file|folder|url]: an IG or profile definition to load. Can be + the URL of an implementation guide or a package ([id]-[ver]) for + a built implementation guide or a local folder that contains a + set of conformance resources. + No default value. This parameter can appear any number of times +-tx [url]: the [base] url of a FHIR terminology service + Default value is http://tx.fhir.org. This parameter can appear once + To run without terminology value, specific n/a as the URL +-txLog [file]: Produce a log of the terminology server operations in [file] + Default value is not to produce a log +-profile [url]: the canonical URL to validate against (same as if it was + specified in Resource.meta.profile). If no profile is specified, the + resource is validated against the base specification. This parameter + can appear any number of times. + Note: the profile (and it's dependencies) have to be made available + through one of the -ig parameters. Note that package dependencies will + automatically be resolved +-questionnaire [file|url}: the location of a questionnaire. If provided, then the validator will validate + any QuestionnaireResponse that claims to match the Questionnaire against it + no default value. This parameter can appear any number of times +-output [file]: a filename for the results (OperationOutcome) + Default: results are sent to the std out. +-debug + Produce additional information about the loading/validation process +-recurse + Look in subfolders when -ig refers to a folder +-locale + Specifies the locale/language of the validation result messages (eg.: de-DE +-sct + Specify the edition of SNOMED CT to use. Valid Choices: + intl | us | uk | au | nl | ca | se | dk | es + tx.fhir.org only supports a subset. To add to this list or tx.fhir.org + ask on https://chat.fhir.org/#narrow/stream/179202-terminology +-native: use schema for validation as well + * XML: w3c schema+schematron + * JSON: json.schema + * RDF: SHEX + Default: false +-language: [lang] + The language to use when validating coding displays - same value as for xml:lang + Not used if the resource specifies language + Default: no specified language +-strictExtensions: If present, treat extensions not defined within the specified FHIR version and any + referenced implementation guides or profiles as errors. (Default is to only raise information messages.) +-hintAboutNonMustSupport: If present, raise hints if the instance contains data elements that are not + marked as mustSupport=true. Useful to identify elements included that may be ignored by recipients +-assumeValidRestReferences: If present, assume that URLs that reference resources follow the RESTful URI pattern + and it is safe to infer the type from the URL +-security-checks: If present, check that string content doesn't include any html-like tags that might create + problems downstream (though all external input must always be santized by escaping for either html or sql) + +The validator also supports the param -proxy=[address]:[port] for if you use a proxy + +Parameters can appear in any order + +Alternatively, you can use the validator to execute a transformation as described by a structure map. +To do this, you must provide some additional parameters: + + -transform [map] + +* [map] the URI of the map that the transform starts with + +Any other dependency maps have to be loaded through an -ig reference + +-transform uses the parameters -defn, -txserver, -ig (at least one with the map files), and -output + +Alternatively, you can use the validator to generate narrative for a resource. +To do this, you must provide a specific parameter: + + -narrative + +-narrative requires the parameters -defn, -txserver, -source, and -output. ig and profile may be used + +Alternatively, you can use the validator to convert a resource or logical model. +To do this, you must provide a specific parameter: + + -convert + +-convert requires the parameters -source and -output. ig may be used to provide a logical model + +Alternatively, you can use the validator to evaluate a FHIRPath expression on a resource or logical model. +To do this, you must provide a specific parameter: + + -fhirpath [FHIRPath] + +* [FHIRPath] the FHIRPath expression to evaluate + +-fhirpath requires the parameters -source. ig may be used to provide a logical model + +Finally, you can use the validator to generate a snapshot for a profile. +To do this, you must provide a specific parameter: + + -snapshot + +-snapshot requires the parameters -defn, -txserver, -source, and -output. ig may be used to provide necessary base profiles \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/BaseRestTest.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/BaseRestTest.java deleted file mode 100644 index 3c30068b4..000000000 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/BaseRestTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.hl7.fhir.validation.cli; - -import java.io.IOException; - -import org.apache.http.HttpResponse; -import org.apache.http.util.EntityUtils; -import org.hl7.fhir.validation.cli.model.CliContext; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; - -public abstract class BaseRestTest { - - protected final String JSON_MIME_TYPE = "application/json"; - - @BeforeAll - public static void startServer() { - ValidatorGui.start(new CliContext(), null, false); - } - - @AfterAll - public static void stopServer() { - ValidatorGui.stop(); - } - - public static T retrieveResourceFromResponse(HttpResponse response, Class clazz) - throws IOException { - - String jsonFromResponse = EntityUtils.toString(response.getEntity()); - ObjectMapper mapper = new ObjectMapper() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - return mapper.readValue(jsonFromResponse, clazz); - } - -} \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/ValidatorGuiTest.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/ValidatorGuiTest.java deleted file mode 100644 index 40e7fd46c..000000000 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/ValidatorGuiTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.hl7.fhir.validation.cli; - -import java.io.IOException; - -import org.hl7.fhir.validation.cli.model.CliContext; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.chrome.ChromeDriver; -import org.openqa.selenium.chrome.ChromeOptions; - -import io.github.bonigarcia.wdm.WebDriverManager; - -class ValidatorGuiTest { - - private static final String DEF_TX = "http://tx.fhir.org"; - private final String HTML_TITLE_TAG = "FHIR HL7 Resrouce Validator GUI"; - - @Test - @DisplayName("Page boots correctly, and displays index.html") - public void UI_contains_correct_heading() throws IOException { - ValidatorGui.start(new CliContext(), null, false); - WebDriverManager.chromedriver().setup(); - ChromeOptions options = new ChromeOptions(); - options.addArguments("--headless"); - options.addArguments("--disable-gpu"); - WebDriver driver = new ChromeDriver(options); - driver.get("http://localhost:" + ValidatorGui.getPort() + "/home"); - - Assertions.assertTrue(driver.getPageSource().contains(HTML_TITLE_TAG)); - driver.quit(); - ValidatorGui.stop(); - } -} \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/controller/HttpGetContextTest.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/controller/HttpGetContextTest.java deleted file mode 100644 index fcc13ad4c..000000000 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/controller/HttpGetContextTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.hl7.fhir.validation.cli.controller; - -import java.io.IOException; - -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.entity.ContentType; -import org.apache.http.impl.client.HttpClientBuilder; -import org.hl7.fhir.validation.cli.BaseRestTest; -import org.hl7.fhir.validation.cli.ValidatorGui; -import org.hl7.fhir.validation.cli.model.CliContext; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -class HttpGetContextTest extends BaseRestTest { - - private final String GET_CONTEXT_URL = "http://localhost:" + ValidatorGui.getPort() + "/context"; - - @Test - @DisplayName("Testing status code on get context endpoint.") - public void testStatus() throws IOException { - HttpUriRequest request = new HttpGet(GET_CONTEXT_URL); - HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request); - Assertions.assertEquals(httpResponse.getStatusLine().getStatusCode(), HttpStatus.SC_OK); - } - - @Test - @DisplayName("Testing media type on get context endpoint.") - public void testMediaType() throws IOException { - HttpUriRequest request = new HttpGet(GET_CONTEXT_URL); - HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request); - String mimeType = ContentType.getOrDefault(httpResponse.getEntity()).getMimeType(); - Assertions.assertEquals(JSON_MIME_TYPE, mimeType ); - } - - @Test - @DisplayName("Testing status code on get context endpoint.") - public void testJSONPayload() throws IOException { - HttpUriRequest request = new HttpGet(GET_CONTEXT_URL); - HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request); - CliContext resource = retrieveResourceFromResponse(httpResponse, CliContext.class); - Assertions.assertEquals(new CliContext(), resource); - } - -} \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/controller/HttpPutContextTest.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/controller/HttpPutContextTest.java deleted file mode 100644 index d166f1fa0..000000000 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/cli/controller/HttpPutContextTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.hl7.fhir.validation.cli.controller; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import io.javalin.http.Context; - -class HttpPutContextTest { - - public CliContextController myCliContextController; - - public HttpPutContextTest() { - this.myCliContextController = new CliContextController(null); - } - - @Disabled - @Test - void handleSetCurrentCliContext() { - Context context = mock(Context.class); - this.myCliContextController.handleSetCurrentCliContext(context); - verify(context).status(200); - } -} \ No newline at end of file