Merge branch 'master' of https://github.com/hapifhir/org.hl7.fhir.core
This commit is contained in:
commit
a52a1127f8
|
@ -9,9 +9,9 @@ package org.hl7.fhir.validation;
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -48,61 +48,26 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.awt.Desktop;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import org.hl7.fhir.r5.conformance.CapabilityStatementUtilities;
|
|
||||||
import org.hl7.fhir.r5.conformance.CapabilityStatementUtilities.CapabilityStatementComparisonOutput;
|
|
||||||
import org.hl7.fhir.r5.conformance.ProfileComparer;
|
|
||||||
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
|
||||||
import org.hl7.fhir.r5.formats.IParser;
|
|
||||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
|
||||||
import org.hl7.fhir.r5.formats.XmlParser;
|
|
||||||
import org.hl7.fhir.r5.formats.JsonParser;
|
|
||||||
import org.hl7.fhir.r5.model.Bundle;
|
|
||||||
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
|
|
||||||
import org.hl7.fhir.r5.model.CapabilityStatement;
|
|
||||||
import org.hl7.fhir.r5.model.Constants;
|
|
||||||
import org.hl7.fhir.r5.model.DomainResource;
|
|
||||||
import org.hl7.fhir.r5.model.FhirPublication;
|
|
||||||
import org.hl7.fhir.r5.model.ImplementationGuide;
|
import org.hl7.fhir.r5.model.ImplementationGuide;
|
||||||
import org.hl7.fhir.r5.model.CanonicalResource;
|
|
||||||
import org.hl7.fhir.r5.model.OperationOutcome;
|
|
||||||
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
|
|
||||||
import org.hl7.fhir.r5.model.Resource;
|
|
||||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||||
import org.hl7.fhir.r5.utils.KeyGenerator;
|
|
||||||
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
|
||||||
import org.hl7.fhir.validation.ValidationEngine.ScanOutputItem;
|
|
||||||
import org.hl7.fhir.utilities.TextFile;
|
|
||||||
import org.hl7.fhir.utilities.Utilities;
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
import org.hl7.fhir.utilities.VersionUtilities;
|
import org.hl7.fhir.utilities.VersionUtilities;
|
||||||
import org.hl7.fhir.utilities.cache.PackageCacheManager;
|
import org.hl7.fhir.validation.cli.*;
|
||||||
import org.hl7.fhir.utilities.cache.ToolsVersion;
|
|
||||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A executable class that will validate one or more FHIR resources against
|
* A executable class that will validate one or more FHIR resources against
|
||||||
* the specification
|
* the specification
|
||||||
*
|
* <p>
|
||||||
* todo: schema validation (w3c xml, json schema, shex?)
|
* todo: schema validation (w3c xml, json schema, shex?)
|
||||||
*
|
* <p>
|
||||||
* if you want to host validation inside a process, skip this class, and look at
|
* if you want to host validation inside a process, skip this class, and look at
|
||||||
* ValidationEngine
|
* ValidationEngine
|
||||||
*
|
* <p>
|
||||||
* todo: find a gome for this:
|
* todo: find a gome for this:
|
||||||
|
|
||||||
* @author Grahame
|
|
||||||
*
|
*
|
||||||
|
* @author Grahame
|
||||||
*/
|
*/
|
||||||
public class Validator {
|
public class Validator {
|
||||||
|
|
||||||
|
@ -123,670 +88,73 @@ public class Validator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String toMB(long maxMemory) {
|
private static String toMB(long maxMemory) {
|
||||||
return Long.toString(maxMemory / (1024*1024));
|
return Long.toString(maxMemory / (1024 * 1024));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
System.out.println("FHIR Validation tool " + VersionUtil.getVersionString());
|
System.out.println("FHIR Validation tool " + VersionUtil.getVersionString());
|
||||||
System.out.println("Detected Java version: " + 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");
|
System.out.println("Detected Java version: " + 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, "-proxy");
|
String proxy = getNamedParam(args, Params.PROXY);
|
||||||
if (!Utilities.noString(proxy)) {
|
if (!Utilities.noString(proxy)) {
|
||||||
String[] p = proxy.split("\\:");
|
String[] p = proxy.split("\\:");
|
||||||
System.setProperty("http.proxyHost", p[0]);
|
System.setProperty("http.proxyHost", p[0]);
|
||||||
System.setProperty("http.proxyPort", p[1]);
|
System.setProperty("http.proxyPort", p[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasParam(args, "-tests")) {
|
if (Params.hasParam(args, Params.TEST)) {
|
||||||
try {
|
Common.runValidationEngineTests();
|
||||||
Class<?> clazz = Class.forName("org.hl7.fhir.validation.r5.tests.ValidationEngineTests");
|
} else if (args.length == 0 || Params.hasParam(args, Params.HELP) || Params.hasParam(args, "?") || Params.hasParam(args, "-?") || Params.hasParam(args, "/?")) {
|
||||||
clazz.getMethod("execute").invoke(clazz);
|
Display.displayHelpDetails();
|
||||||
} catch (Exception e) {
|
} else if (Params.hasParam(args, Params.COMPARE)) {
|
||||||
e.printStackTrace();
|
Display.printCliArgumentsAndInfo(args);
|
||||||
}
|
String dest = Params.getParam(args, Params.DESTINATION);
|
||||||
} else if (args.length == 0 || hasParam(args, "help") || hasParam(args, "?") || hasParam(args, "-?") || hasParam(args, "/?") ) {
|
|
||||||
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("");
|
|
||||||
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");
|
|
||||||
} else if (hasParam(args, "-compare")) {
|
|
||||||
System.out.print("Arguments:");
|
|
||||||
for (String s : args)
|
|
||||||
System.out.print(s.contains(" ") ? " \""+s+"\"" : " "+s);
|
|
||||||
System.out.println();
|
|
||||||
System.out.println("Directories: Current = "+System.getProperty("user.dir")+", Package Cache = "+new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION).getFolder());
|
|
||||||
|
|
||||||
String dest = getParam(args, "-dest");
|
|
||||||
if (dest == null)
|
if (dest == null)
|
||||||
System.out.println("no -dest parameter provided");
|
System.out.println("no -dest parameter provided");
|
||||||
else if (!new File(dest).isDirectory())
|
else if (!new File(dest).isDirectory())
|
||||||
System.out.println("Specified destination (-dest parameter) is not valid: \""+dest+"\")");
|
System.out.println("Specified destination (-dest parameter) is not valid: \"" + dest + "\")");
|
||||||
else {
|
else {
|
||||||
// first, prepare the context
|
// first, prepare the context
|
||||||
String txLog = null;
|
String txLog = Params.getTerminologyServerLog(args);
|
||||||
if (hasParam(args, "-txLog")) {
|
ValidationEngine validator = Common.getValidationEngine(args, txLog);
|
||||||
txLog = getParam(args, "-txLog");
|
Params.checkIGFileReferences(args);
|
||||||
new File(txLog).delete();
|
Comparison.doLeftRightComparison(args, dest, validator);
|
||||||
}
|
|
||||||
String v = getParam(args, "-version");
|
|
||||||
if (v == null) {
|
|
||||||
v = "current";
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
|
||||||
if ("-ig".equals(args[i])) {
|
|
||||||
if (i+1 == args.length)
|
|
||||||
throw new Error("Specified -ig without indicating ig file");
|
|
||||||
else {
|
|
||||||
String n = args[i+1];
|
|
||||||
if (n.startsWith("hl7.fhir.core#")) {
|
|
||||||
v = VersionUtilities.getCurrentPackageVersion(n.substring(14));
|
|
||||||
} else if (n.startsWith("hl7.fhir.r2.core#") || n.equals("hl7.fhir.r2.core")) {
|
|
||||||
v = "1.0";
|
|
||||||
} else if (n.startsWith("hl7.fhir.r2b.core#") || n.equals("hl7.fhir.r2b.core")) {
|
|
||||||
v = "1.4";
|
|
||||||
} else if (n.startsWith("hl7.fhir.r3.core#") || n.equals("hl7.fhir.r3.core")) {
|
|
||||||
v = "3.0";
|
|
||||||
} else if (n.startsWith("hl7.fhir.r4.core#") || n.equals("hl7.fhir.r4.core")) {
|
|
||||||
v = "4.0";
|
|
||||||
} else if (n.startsWith("hl7.fhir.r5.core#") || n.equals("hl7.fhir.r5.core")) {
|
|
||||||
v = "current";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ("1.0".equals(v)) {
|
|
||||||
v = "1.0";
|
|
||||||
} else if ("1.4".equals(v)) {
|
|
||||||
v = "1.4";
|
|
||||||
} else if ("3.0".equals(v)) {
|
|
||||||
v = "3.0";
|
|
||||||
} else if ("4.0".equals(v)) {
|
|
||||||
v = "4.0";
|
|
||||||
} else if (v.startsWith(Constants.VERSION)) {
|
|
||||||
v = "current";
|
|
||||||
}
|
|
||||||
String definitions = VersionUtilities.packageForVersion(v)+"#"+v;
|
|
||||||
System.out.println("Loading (v = "+v+", tx server http://tx.fhir.org)");
|
|
||||||
ValidationEngine validator = new ValidationEngine(definitions, "http://tx.fhir.org", txLog, FhirPublication.fromCode(v), v);
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
|
||||||
if ("-ig".equals(args[i])) {
|
|
||||||
if (i+1 == args.length)
|
|
||||||
throw new Error("Specified -ig without indicating ig file");
|
|
||||||
else {
|
|
||||||
String s = args[++i];
|
|
||||||
if (!s.startsWith("hl7.fhir.core-")) {
|
|
||||||
System.out.println("Load Package: "+s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ok now set up the comparison
|
|
||||||
String left = getParam(args, "-left");
|
|
||||||
String right = getParam(args, "-right");
|
|
||||||
Resource resLeft = validator.getContext().fetchResource(Resource.class, left);
|
|
||||||
Resource resRight = validator.getContext().fetchResource(Resource.class, right);
|
|
||||||
if (resLeft == null) {
|
|
||||||
System.out.println("Unable to locate left resource " +left);
|
|
||||||
}
|
|
||||||
if (resRight == null) {
|
|
||||||
System.out.println("Unable to locate right resource " +right);
|
|
||||||
}
|
|
||||||
if (resLeft != null && resRight != null) {
|
|
||||||
if (resLeft instanceof StructureDefinition && resRight instanceof StructureDefinition) {
|
|
||||||
System.out.println("Comparing StructureDefinitions "+left+" to "+right);
|
|
||||||
ProfileComparer pc = new ProfileComparer(validator.getContext(), dest);
|
|
||||||
StructureDefinition sdL = (StructureDefinition) resLeft;
|
|
||||||
StructureDefinition sdR = (StructureDefinition) resRight;
|
|
||||||
pc.compareProfiles(sdL, sdR);
|
|
||||||
System.out.println("Generating output to "+dest+"...");
|
|
||||||
File htmlFile = new File(pc.generate());
|
|
||||||
Desktop.getDesktop().browse(htmlFile.toURI());
|
|
||||||
System.out.println("Done");
|
|
||||||
} else if (resLeft instanceof CapabilityStatement && resRight instanceof CapabilityStatement) {
|
|
||||||
String nameLeft = chooseName(args, "leftName", (CanonicalResource) resLeft);
|
|
||||||
String nameRight = chooseName(args, "rightName", (CanonicalResource) resRight);
|
|
||||||
System.out.println("Comparing CapabilityStatements "+left+" to "+right);
|
|
||||||
CapabilityStatementUtilities pc = new CapabilityStatementUtilities(validator.getContext(), dest, new KeyGenerator("http://fhir.org/temp/"+UUID.randomUUID().toString().toLowerCase()));
|
|
||||||
CapabilityStatement capL = (CapabilityStatement) resLeft;
|
|
||||||
CapabilityStatement capR = (CapabilityStatement) resRight;
|
|
||||||
CapabilityStatementComparisonOutput output = pc.isCompatible(nameLeft, nameRight, capL, capR);
|
|
||||||
|
|
||||||
String destTxt = Utilities.path(dest, "output.txt");
|
|
||||||
System.out.println("Generating output to "+destTxt+"...");
|
|
||||||
StringBuilder b = new StringBuilder();
|
|
||||||
for (ValidationMessage msg : output.getMessages()) {
|
|
||||||
b.append(msg.summary());
|
|
||||||
b.append("\r\n");
|
|
||||||
}
|
|
||||||
TextFile.stringToFile(b.toString(), destTxt);
|
|
||||||
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-union.xml")), output.getSuperset());
|
|
||||||
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-intersection.xml")), output.getSubset());
|
|
||||||
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "OperationOutcome-issues.xml")), output.getOutcome());
|
|
||||||
new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-union.json")), output.getSuperset());
|
|
||||||
new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-intersection.json")), output.getSubset());
|
|
||||||
new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "OperationOutcome-issues.json")), output.getOutcome());
|
|
||||||
|
|
||||||
String destHtml = Utilities.path(dest, "index.html");
|
|
||||||
File htmlFile = new File(destHtml);
|
|
||||||
Desktop.getDesktop().browse(htmlFile.toURI());
|
|
||||||
System.out.println("Done");
|
|
||||||
} else
|
|
||||||
System.out.println("Unable to compare left resource " +left+" ("+resLeft.fhirType()+") with right resource "+right+" ("+resRight.fhirType()+")");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
System.out.print("Arguments:");
|
Display.printCliArgumentsAndInfo(args);
|
||||||
for (String s : args)
|
CliContext cliContext = Params.loadCliContext(args);
|
||||||
System.out.print(s.contains(" ") ? " \""+s+"\"" : " "+s);
|
|
||||||
System.out.println();
|
|
||||||
System.out.println("Directories: Current = "+System.getProperty("user.dir")+", Package Cache = "+new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION).getFolder());
|
|
||||||
|
|
||||||
String map = null;
|
|
||||||
List<String> igs = new ArrayList<String>();
|
|
||||||
List<String> questionnaires = new ArrayList<String>();
|
|
||||||
String txServer = "http://tx.fhir.org";
|
|
||||||
boolean doNative = false;
|
|
||||||
boolean anyExtensionsAllowed = true;
|
|
||||||
boolean hintAboutNonMustSupport = false;
|
|
||||||
boolean recursive = false;
|
|
||||||
Locale locale = null;
|
|
||||||
List<String> profiles = new ArrayList<String>();
|
|
||||||
EngineMode mode = EngineMode.VALIDATION;
|
|
||||||
String output = null;
|
|
||||||
Boolean canDoNative = null;
|
|
||||||
List<String> sources= new ArrayList<String>();
|
|
||||||
Map<String, String> locations = new HashMap<String, String>();
|
|
||||||
String sv = "current";
|
|
||||||
String txLog = null;
|
|
||||||
String mapLog = null;
|
|
||||||
String lang = null;
|
|
||||||
String fhirpath = null;
|
|
||||||
String snomedCT = "900000000000207008";
|
|
||||||
String targetVer = null;
|
|
||||||
boolean doDebug = false;
|
|
||||||
boolean assumeValidRestReferences = false;
|
|
||||||
|
|
||||||
// load the parameters - so order doesn't matter
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
|
||||||
if (args[i].equals("-version")) {
|
|
||||||
sv = args[++i];
|
|
||||||
sv = VersionUtilities.getCurrentPackageVersion(sv);
|
|
||||||
} else if (args[i].equals("-output")) {
|
|
||||||
if (i+1 == args.length)
|
|
||||||
throw new Error("Specified -output without indicating output file");
|
|
||||||
else
|
|
||||||
output = args[++i];
|
|
||||||
} else if (args[i].equals("-proxy")) {
|
|
||||||
i++; // ignore next parameter
|
|
||||||
} else if (args[i].equals("-profile")) {
|
|
||||||
String p = null;
|
|
||||||
if (i+1 == args.length)
|
|
||||||
throw new Error("Specified -profile without indicating profile source");
|
|
||||||
else {
|
|
||||||
p = args[++i];
|
|
||||||
profiles.add(p);
|
|
||||||
}
|
|
||||||
if (p != null && i+1 < args.length && args[i+1].equals("@")) {
|
|
||||||
i++;
|
|
||||||
if (i+1 == args.length)
|
|
||||||
throw new Error("Specified -profile with @ without indicating profile location");
|
|
||||||
else
|
|
||||||
locations.put(p, args[++i]);
|
|
||||||
}
|
|
||||||
} else if (args[i].equals("-questionnaire")) {
|
|
||||||
if (i+1 == args.length)
|
|
||||||
throw new Error("Specified -questionnaire without indicating questionnaire file");
|
|
||||||
else
|
|
||||||
questionnaires.add(args[++i]);
|
|
||||||
} else if (args[i].equals("-native")) {
|
|
||||||
doNative = true;
|
|
||||||
} else if (args[i].equals("-assumeValidRestReferences")) {
|
|
||||||
assumeValidRestReferences = true;
|
|
||||||
} else if (args[i].equals("-debug")) {
|
|
||||||
doDebug = true;
|
|
||||||
} else if (args[i].equals("-sct")) {
|
|
||||||
String s = args[++i];
|
|
||||||
if ("intl".equalsIgnoreCase(s))
|
|
||||||
snomedCT = "900000000000207008";
|
|
||||||
else if ("us".equalsIgnoreCase(s))
|
|
||||||
snomedCT = "731000124108";
|
|
||||||
else if ("uk".equalsIgnoreCase(s))
|
|
||||||
snomedCT = "999000041000000102";
|
|
||||||
else if ("au".equalsIgnoreCase(s))
|
|
||||||
snomedCT = "32506021000036107";
|
|
||||||
else if ("ca".equalsIgnoreCase(s))
|
|
||||||
snomedCT = "20611000087101";
|
|
||||||
else if ("nl".equalsIgnoreCase(s))
|
|
||||||
snomedCT = "11000146104";
|
|
||||||
else if ("se".equalsIgnoreCase(s))
|
|
||||||
snomedCT = "45991000052106";
|
|
||||||
else if ("es".equalsIgnoreCase(s))
|
|
||||||
snomedCT = "449081005";
|
|
||||||
else if ("dk".equalsIgnoreCase(s))
|
|
||||||
snomedCT = "554471000005108";
|
|
||||||
else
|
|
||||||
throw new Error("Snomed edition '"+s+"' not known");
|
|
||||||
} else if (args[i].equals("-recurse")) {
|
|
||||||
recursive = true;
|
|
||||||
} else if (args[i].equals("-locale")) {
|
|
||||||
if (i+1 == args.length) {
|
|
||||||
throw new Error("Specified -locale without indicating locale");
|
|
||||||
} else {
|
|
||||||
locale = new Locale(args[++i]);
|
|
||||||
}
|
|
||||||
} else if (args[i].equals("-strictExtensions")) {
|
|
||||||
anyExtensionsAllowed = false;
|
|
||||||
} else if (args[i].equals("-hintAboutNonMustSupport")) {
|
|
||||||
hintAboutNonMustSupport = true;
|
|
||||||
} else if (args[i].equals("-to-version")) {
|
|
||||||
targetVer = args[++i];
|
|
||||||
mode = EngineMode.VERSION;
|
|
||||||
} else if (args[i].equals("-do-native")) {
|
|
||||||
canDoNative = true;
|
|
||||||
} else if (args[i].equals("-no-native")) {
|
|
||||||
canDoNative = false;
|
|
||||||
} else if (args[i].equals("-transform")) {
|
|
||||||
map = args[++i];
|
|
||||||
mode = EngineMode.TRANSFORM;
|
|
||||||
} else if (args[i].equals("-narrative")) {
|
|
||||||
mode = EngineMode.NARRATIVE;
|
|
||||||
} else if (args[i].equals("-snapshot")) {
|
|
||||||
mode = EngineMode.SNAPSHOT;
|
|
||||||
} else if (args[i].equals("-scan")) {
|
|
||||||
mode = EngineMode.SCAN;
|
|
||||||
} else if (args[i].equals("-tx")) {
|
|
||||||
if (i+1 == args.length)
|
|
||||||
throw new Error("Specified -tx without indicating terminology server");
|
|
||||||
else
|
|
||||||
txServer = "n/a".equals(args[++i]) ? null : args[i];
|
|
||||||
} else if (args[i].equals("-txLog")) {
|
|
||||||
if (i+1 == args.length)
|
|
||||||
throw new Error("Specified -txLog without indicating file");
|
|
||||||
else
|
|
||||||
txLog = args[++i];
|
|
||||||
} else if (args[i].equals("-log")) {
|
|
||||||
if (i+1 == args.length)
|
|
||||||
throw new Error("Specified -log without indicating file");
|
|
||||||
else
|
|
||||||
mapLog = args[++i];
|
|
||||||
} else if (args[i].equals("-language")) {
|
|
||||||
if (i+1 == args.length)
|
|
||||||
throw new Error("Specified -language without indicating language");
|
|
||||||
else
|
|
||||||
lang = args[++i];
|
|
||||||
} else if (args[i].equals("-ig") || args[i].equals("-defn")) {
|
|
||||||
if (i+1 == args.length)
|
|
||||||
throw new Error("Specified "+args[i]+" without indicating ig file");
|
|
||||||
else {
|
|
||||||
String s = args[++i];
|
|
||||||
if (s.equals("hl7.fhir.core")) {
|
|
||||||
sv = "current";
|
|
||||||
} else if (s.startsWith("hl7.fhir.core#")) {
|
|
||||||
sv = VersionUtilities.getCurrentPackageVersion(s.substring(14));
|
|
||||||
} else if (s.startsWith("hl7.fhir.r2.core#") || s.equals("hl7.fhir.r2.core")) {
|
|
||||||
sv = "1.0";
|
|
||||||
} else if (s.startsWith("hl7.fhir.r2b.core#") || s.equals("hl7.fhir.r2b.core")) {
|
|
||||||
sv = "1.4";
|
|
||||||
} else if (s.startsWith("hl7.fhir.r3.core#") || s.equals("hl7.fhir.r3.core")) {
|
|
||||||
sv = "3.0";
|
|
||||||
} else if (s.startsWith("hl7.fhir.r4.core#") || s.equals("hl7.fhir.r4.core")) {
|
|
||||||
sv = "4.0";
|
|
||||||
} else if (s.startsWith("hl7.fhir.r5.core#") || s.equals("hl7.fhir.r5.core")) {
|
|
||||||
sv = "current";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
igs.add(s);
|
|
||||||
}
|
|
||||||
} else if (args[i].equals("-map")) {
|
|
||||||
if (map == null) {
|
|
||||||
if (i+1 == args.length)
|
|
||||||
throw new Error("Specified -map without indicating map file");
|
|
||||||
else
|
|
||||||
map = args[++i];
|
|
||||||
} else {
|
|
||||||
throw new Exception("Can only nominate a single -map parameter");
|
|
||||||
}
|
|
||||||
} else if (args[i].startsWith("-x")) {
|
|
||||||
i++;
|
|
||||||
} else if (args[i].equals("-convert")) {
|
|
||||||
mode = EngineMode.CONVERT;
|
|
||||||
} else if (args[i].equals("-fhirpath")) {
|
|
||||||
mode = EngineMode.FHIRPATH;
|
|
||||||
if (fhirpath == null)
|
|
||||||
if (i+1 == args.length)
|
|
||||||
throw new Error("Specified -fhirpath without indicating a FHIRPath expression");
|
|
||||||
else
|
|
||||||
fhirpath = args[++i];
|
|
||||||
else
|
|
||||||
throw new Exception("Can only nominate a single -fhirpath parameter");
|
|
||||||
} else {
|
|
||||||
sources.add(args[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sources.isEmpty())
|
|
||||||
throw new Exception("Must provide at least one source file");
|
|
||||||
|
|
||||||
// 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
|
// 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(sv)+"#"+VersionUtilities.getCurrentVersion(sv);
|
String definitions = VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv());
|
||||||
System.out.println(" .. FHIR Version "+sv+", definitions from "+definitions);
|
ValidationEngine validator = ValidationUtils.getValidator(cliContext, definitions);
|
||||||
System.out.println(" .. connect to tx server @ "+txServer);
|
|
||||||
ValidationEngine validator = new ValidationEngine(definitions, txServer, txLog, FhirPublication.fromCode(sv), sv);
|
|
||||||
validator.setDebug(doDebug);
|
|
||||||
System.out.println(" (v"+validator.getContext().getVersion()+")");
|
|
||||||
for (String src : igs) {
|
|
||||||
System.out.println("+ .. load IG from "+src);
|
|
||||||
validator.loadIg(src, recursive);
|
|
||||||
}
|
|
||||||
validator.setQuestionnaires(questionnaires);
|
|
||||||
validator.setNative(doNative);
|
|
||||||
validator.setHintAboutNonMustSupport(hintAboutNonMustSupport);
|
|
||||||
validator.setAnyExtensionsAllowed(anyExtensionsAllowed);
|
|
||||||
validator.setLanguage(lang);
|
|
||||||
validator.setLocale(locale);
|
|
||||||
validator.setSnomedExtension(snomedCT);
|
|
||||||
validator.setAssumeValidRestReferences(assumeValidRestReferences);
|
|
||||||
|
|
||||||
IParser x;
|
if (cliContext.getMode() == EngineMode.VERSION) {
|
||||||
if (output != null && output.endsWith(".json"))
|
ValidationUtils.transformVersion(cliContext, validator);
|
||||||
x = new JsonParser();
|
} else if (cliContext.getMode() == EngineMode.TRANSFORM) {
|
||||||
else
|
ValidationUtils.transform(cliContext, validator);
|
||||||
x = new XmlParser();
|
} else if (cliContext.getMode() == EngineMode.NARRATIVE) {
|
||||||
x.setOutputStyle(OutputStyle.PRETTY);
|
ValidationUtils.generateNarrative(cliContext, validator);
|
||||||
|
} else if (cliContext.getMode() == EngineMode.SNAPSHOT) {
|
||||||
if (mode == EngineMode.VERSION) {
|
ValidationUtils.generateSnapshot(cliContext, validator);
|
||||||
if (sources.size() > 1) {
|
} else if (cliContext.getMode() == EngineMode.CONVERT) {
|
||||||
throw new Exception("Can only have one source when converting versions (found "+sources+")");
|
ValidationUtils.convertSources(cliContext, validator);
|
||||||
}
|
} else if (cliContext.getMode() == EngineMode.FHIRPATH) {
|
||||||
if (targetVer == null) {
|
ValidationUtils.evaluateFhirpath(cliContext, validator);
|
||||||
throw new Exception("Must provide a map when converting versions");
|
|
||||||
}
|
|
||||||
if (output == null) {
|
|
||||||
throw new Exception("Must nominate an output when converting versions");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (mapLog != null) {
|
|
||||||
validator.setMapLog(mapLog);
|
|
||||||
}
|
|
||||||
byte[] r = validator.transformVersion(sources.get(0), targetVer, output.endsWith(".json") ? FhirFormat.JSON : FhirFormat.XML, canDoNative);
|
|
||||||
System.out.println(" ...success");
|
|
||||||
TextFile.bytesToFile(r, output);
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.out.println(" ...Failure: "+e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
} else if (mode == EngineMode.TRANSFORM) {
|
|
||||||
if (sources.size() > 1)
|
|
||||||
throw new Exception("Can only have one source when doing a transform (found "+sources+")");
|
|
||||||
if (txServer == null)
|
|
||||||
throw new Exception("Must provide a terminology server when doing a transform");
|
|
||||||
if (map == null)
|
|
||||||
throw new Exception("Must provide a map when doing a transform");
|
|
||||||
try {
|
|
||||||
validator.setMapLog(mapLog);
|
|
||||||
org.hl7.fhir.r5.elementmodel.Element r = validator.transform(sources.get(0), map);
|
|
||||||
System.out.println(" ...success");
|
|
||||||
if (output != null) {
|
|
||||||
FileOutputStream s = new FileOutputStream(output);
|
|
||||||
if (output != null && output.endsWith(".json"))
|
|
||||||
new org.hl7.fhir.r5.elementmodel.JsonParser(validator.getContext()).compose(r, s, OutputStyle.PRETTY, null);
|
|
||||||
else
|
|
||||||
new org.hl7.fhir.r5.elementmodel.XmlParser(validator.getContext()).compose(r, s, OutputStyle.PRETTY, null);
|
|
||||||
s.close();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.out.println(" ...Failure: "+e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
} else if (mode == EngineMode.NARRATIVE) {
|
|
||||||
DomainResource r = validator.generate(sources.get(0), sv);
|
|
||||||
System.out.println(" ...generated narrative successfully");
|
|
||||||
if (output != null) {
|
|
||||||
validator.handleOutput(r, output, sv);
|
|
||||||
}
|
|
||||||
} else if (mode == EngineMode.SNAPSHOT) {
|
|
||||||
StructureDefinition r = validator.snapshot(sources.get(0), sv);
|
|
||||||
System.out.println(" ...generated snapshot successfully");
|
|
||||||
if (output != null) {
|
|
||||||
validator.handleOutput(r, output, sv);
|
|
||||||
}
|
|
||||||
} else if (mode == EngineMode.CONVERT) {
|
|
||||||
validator.convert(sources.get(0), output);
|
|
||||||
System.out.println(" ...convert");
|
|
||||||
} else if (mode == EngineMode.FHIRPATH) {
|
|
||||||
System.out.println(" ...evaluating "+fhirpath);
|
|
||||||
System.out.println(validator.evaluateFhirPath(sources.get(0), fhirpath));
|
|
||||||
} else {
|
} else {
|
||||||
if (definitions == null)
|
if (definitions == null) {
|
||||||
throw new Exception("Must provide a defn when doing validation");
|
throw new Exception("Must provide a defn when doing validation");
|
||||||
for (String s : profiles) {
|
}
|
||||||
|
for (String s : cliContext.getProfiles()) {
|
||||||
if (!validator.getContext().hasResource(StructureDefinition.class, s) && !validator.getContext().hasResource(ImplementationGuide.class, s)) {
|
if (!validator.getContext().hasResource(StructureDefinition.class, s) && !validator.getContext().hasResource(ImplementationGuide.class, s)) {
|
||||||
System.out.println("Fetch Profile from "+s);
|
System.out.println("Fetch Profile from " + s);
|
||||||
validator.loadProfile(locations.getOrDefault(s, s));
|
validator.loadProfile(cliContext.getLocations().getOrDefault(s, s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mode == EngineMode.SCAN) {
|
if (cliContext.getMode() == EngineMode.SCAN) {
|
||||||
if (Utilities.noString(output))
|
ValidationUtils.validateScan(cliContext, validator);
|
||||||
throw new Exception("Output parameter required when scanning");
|
} else {
|
||||||
if (!(new File(output).isDirectory()))
|
ValidationUtils.validateSources(cliContext, validator);
|
||||||
throw new Exception("Output '"+output+"' must be a directory when scanning");
|
|
||||||
System.out.println(" .. scan "+sources+" against loaded IGs");
|
|
||||||
Set<String> urls = new HashSet<>();
|
|
||||||
for (ImplementationGuide ig : validator.getContext().allImplementationGuides()) {
|
|
||||||
if (ig.getUrl().contains("/ImplementationGuide") && !ig.getUrl().equals("http://hl7.org/fhir/ImplementationGuide/fhir"))
|
|
||||||
urls.add(ig.getUrl());
|
|
||||||
}
|
|
||||||
List<ScanOutputItem> res = validator.validateScan(sources, urls);
|
|
||||||
validator.genScanOutput(output, res);
|
|
||||||
System.out.println("Done. output in "+Utilities.path(output, "scan.html"));
|
|
||||||
} else {
|
|
||||||
if (profiles.size() > 0)
|
|
||||||
System.out.println(" .. validate "+sources+" against "+profiles.toString());
|
|
||||||
else
|
|
||||||
System.out.println(" .. validate "+sources);
|
|
||||||
validator.prepare(); // generate any missing snapshots
|
|
||||||
Resource r = validator.validate(sources, profiles);
|
|
||||||
int ec = 0;
|
|
||||||
if (output == null) {
|
|
||||||
if (r instanceof Bundle)
|
|
||||||
for (BundleEntryComponent e : ((Bundle)r).getEntry())
|
|
||||||
ec = displayOO((OperationOutcome)e.getResource()) + ec;
|
|
||||||
else
|
|
||||||
ec = displayOO((OperationOutcome)r);
|
|
||||||
} else {
|
|
||||||
FileOutputStream s = new FileOutputStream(output);
|
|
||||||
x.compose(s, r);
|
|
||||||
s.close();
|
|
||||||
}
|
|
||||||
System.exit(ec > 0 ? 1 : 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String chooseName(String[] args, String name, CanonicalResource mr) {
|
|
||||||
String s = getParam(args, "-"+name);
|
|
||||||
if (Utilities.noString(s))
|
|
||||||
s = mr.present();
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getGitBuild() {
|
|
||||||
return "??";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int displayOO(OperationOutcome oo) {
|
|
||||||
int error = 0;
|
|
||||||
int warn = 0;
|
|
||||||
int info = 0;
|
|
||||||
String file = ToolingExtensions.readStringExtension(oo, ToolingExtensions.EXT_OO_FILE);
|
|
||||||
|
|
||||||
for (OperationOutcomeIssueComponent issue : oo.getIssue()) {
|
|
||||||
if (issue.getSeverity()==OperationOutcome.IssueSeverity.FATAL || issue.getSeverity()==OperationOutcome.IssueSeverity.ERROR)
|
|
||||||
error++;
|
|
||||||
else if (issue.getSeverity()==OperationOutcome.IssueSeverity.WARNING)
|
|
||||||
warn++;
|
|
||||||
else
|
|
||||||
info++;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println((error==0?"Success...":"*FAILURE* ")+ "validating "+file+": "+" error:"+Integer.toString(error)+" warn:"+Integer.toString(warn)+" info:"+Integer.toString(info));
|
|
||||||
for (OperationOutcomeIssueComponent issue : oo.getIssue()) {
|
|
||||||
System.out.println(getIssueSummary(issue));
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getIssueSummary(OperationOutcomeIssueComponent issue) {
|
|
||||||
String loc = null;
|
|
||||||
if (issue.hasExpression()) {
|
|
||||||
int line = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_LINE, -1);
|
|
||||||
int col = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_COL, -1);
|
|
||||||
loc = issue.getExpression().get(0).asStringValue() + (line >= 0 && col >= 0 ? " (line "+Integer.toString(line)+", col"+Integer.toString(col)+")" : "");
|
|
||||||
} else if (issue.hasLocation()) {
|
|
||||||
loc = issue.getLocation().get(0).asStringValue();
|
|
||||||
} else {
|
|
||||||
int line = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_LINE, -1);
|
|
||||||
int col = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_COL, -1);
|
|
||||||
loc = (line >= 0 && col >= 0 ? "line "+Integer.toString(line)+", col"+Integer.toString(col) : "??");
|
|
||||||
}
|
|
||||||
return " " + issue.getSeverity().getDisplay() + " @ " + loc + " : " + issue.getDetails().getText();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static boolean hasParam(String[] args, String param) {
|
|
||||||
for (String a : args)
|
|
||||||
if (a.equals(param))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private 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];
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static boolean hasTransformParam(String[] args) {
|
|
||||||
for (String s : args) {
|
|
||||||
if (s.equals("-transform"))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,289 @@
|
||||||
|
package org.hl7.fhir.validation.cli;
|
||||||
|
|
||||||
|
import org.hl7.fhir.validation.Validator;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A POJO for storing the flags/values for the CLI validator.
|
||||||
|
*/
|
||||||
|
public class CliContext {
|
||||||
|
|
||||||
|
private String map = null;
|
||||||
|
private List<String> igs = new ArrayList<String>();
|
||||||
|
private List<String> questionnaires = new ArrayList<String>();
|
||||||
|
private String txServer = "http://tx.fhir.org";
|
||||||
|
private boolean doNative = false;
|
||||||
|
private boolean anyExtensionsAllowed = true;
|
||||||
|
private boolean hintAboutNonMustSupport = false;
|
||||||
|
private boolean recursive = false;
|
||||||
|
private Locale locale = null;
|
||||||
|
private List<String> profiles = new ArrayList<String>();
|
||||||
|
private Validator.EngineMode mode = Validator.EngineMode.VALIDATION;
|
||||||
|
private String output = null;
|
||||||
|
private Boolean canDoNative = null;
|
||||||
|
private List<String> sources = new ArrayList<String>();
|
||||||
|
private Map<String, String> locations = new HashMap<String, String>();
|
||||||
|
private String sv = "current";
|
||||||
|
private String txLog = null;
|
||||||
|
private String mapLog = null;
|
||||||
|
private String lang = null;
|
||||||
|
private String fhirpath = null;
|
||||||
|
private String snomedCT = SnomedVersion.INTL.getCode();
|
||||||
|
private String targetVer = null;
|
||||||
|
private boolean doDebug = false;
|
||||||
|
private boolean assumeValidRestReferences = false;
|
||||||
|
|
||||||
|
public String getMap() {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setMap(String map) {
|
||||||
|
this.map = map;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getIgs() {
|
||||||
|
return igs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setIgs(List<String> igs) {
|
||||||
|
this.igs = igs;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext addIg(String ig) {
|
||||||
|
if (this.igs == null) {
|
||||||
|
this.igs = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.igs.add(ig);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getQuestionnaires() {
|
||||||
|
return questionnaires;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setQuestionnaires(List<String> questionnaires) {
|
||||||
|
this.questionnaires = questionnaires;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext addQuestionnaire(String questionnaire) {
|
||||||
|
if (this.questionnaires == null) {
|
||||||
|
this.questionnaires = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.questionnaires.add(questionnaire);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTxServer() {
|
||||||
|
return txServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setTxServer(String txServer) {
|
||||||
|
this.txServer = txServer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDoNative() {
|
||||||
|
return doNative;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setDoNative(boolean doNative) {
|
||||||
|
this.doNative = doNative;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAnyExtensionsAllowed() {
|
||||||
|
return anyExtensionsAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setAnyExtensionsAllowed(boolean anyExtensionsAllowed) {
|
||||||
|
this.anyExtensionsAllowed = anyExtensionsAllowed;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHintAboutNonMustSupport() {
|
||||||
|
return hintAboutNonMustSupport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setHintAboutNonMustSupport(boolean hintAboutNonMustSupport) {
|
||||||
|
this.hintAboutNonMustSupport = hintAboutNonMustSupport;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRecursive() {
|
||||||
|
return recursive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setRecursive(boolean recursive) {
|
||||||
|
this.recursive = recursive;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Locale getLocale() {
|
||||||
|
return locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setLocale(Locale locale) {
|
||||||
|
this.locale = locale;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getProfiles() {
|
||||||
|
return profiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setProfiles(List<String> profiles) {
|
||||||
|
this.profiles = profiles;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext addProfile(String profile) {
|
||||||
|
if (this.profiles == null) {
|
||||||
|
this.profiles = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.profiles.add(profile);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Validator.EngineMode getMode() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setMode(Validator.EngineMode mode) {
|
||||||
|
this.mode = mode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOutput() {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setOutput(String output) {
|
||||||
|
this.output = output;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getCanDoNative() {
|
||||||
|
return canDoNative;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setCanDoNative(Boolean canDoNative) {
|
||||||
|
this.canDoNative = canDoNative;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getSources() {
|
||||||
|
return sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setSources(List<String> sources) {
|
||||||
|
this.sources = sources;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext addSource(String source) {
|
||||||
|
if (this.sources == null) {
|
||||||
|
this.sources = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.sources.add(source);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getLocations() {
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setLocations(Map<String, String> locations) {
|
||||||
|
this.locations = locations;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext addLocation(String profile, String location) {
|
||||||
|
this.locations.put(profile, location);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSv() {
|
||||||
|
return sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setSv(String sv) {
|
||||||
|
this.sv = sv;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTxLog() {
|
||||||
|
return txLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setTxLog(String txLog) {
|
||||||
|
this.txLog = txLog;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMapLog() {
|
||||||
|
return mapLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setMapLog(String mapLog) {
|
||||||
|
this.mapLog = mapLog;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLang() {
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setLang(String lang) {
|
||||||
|
this.lang = lang;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFhirpath() {
|
||||||
|
return fhirpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setFhirpath(String fhirpath) {
|
||||||
|
this.fhirpath = fhirpath;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSnomedCT() {
|
||||||
|
return snomedCT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setSnomedCT(String snomedCT) {
|
||||||
|
this.snomedCT = SnomedVersion.resolveSnomedCTCode(snomedCT);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTargetVer() {
|
||||||
|
return targetVer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setTargetVer(String targetVer) {
|
||||||
|
this.targetVer = targetVer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDoDebug() {
|
||||||
|
return doDebug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setDoDebug(boolean doDebug) {
|
||||||
|
this.doDebug = doDebug;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAssumeValidRestReferences() {
|
||||||
|
return assumeValidRestReferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CliContext setAssumeValidRestReferences(boolean assumeValidRestReferences) {
|
||||||
|
this.assumeValidRestReferences = assumeValidRestReferences;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
package org.hl7.fhir.validation.cli;
|
||||||
|
|
||||||
|
import org.hl7.fhir.r5.model.Constants;
|
||||||
|
import org.hl7.fhir.r5.model.FhirPublication;
|
||||||
|
import org.hl7.fhir.utilities.VersionUtilities;
|
||||||
|
import org.hl7.fhir.validation.ValidationEngine;
|
||||||
|
|
||||||
|
public class Common {
|
||||||
|
|
||||||
|
public static String getVersion(String[] args) {
|
||||||
|
String v = Params.getParam(args, "-version");
|
||||||
|
if (v == null) {
|
||||||
|
v = "current";
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
if ("-ig".equals(args[i])) {
|
||||||
|
if (i + 1 == args.length)
|
||||||
|
throw new Error("Specified -ig without indicating ig file");
|
||||||
|
else {
|
||||||
|
String n = args[i + 1];
|
||||||
|
v = Common.getVersionFromIGName(v, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ("1.0".equals(v)) {
|
||||||
|
v = "1.0";
|
||||||
|
} else if ("1.4".equals(v)) {
|
||||||
|
v = "1.4";
|
||||||
|
} else if ("3.0".equals(v)) {
|
||||||
|
v = "3.0";
|
||||||
|
} else if ("4.0".equals(v)) {
|
||||||
|
v = "4.0";
|
||||||
|
} else if (v.startsWith(Constants.VERSION)) {
|
||||||
|
v = "current";
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates the current implementation guide file name and sets the current version accordingly.
|
||||||
|
* <p>
|
||||||
|
* If igFileName is not one of the known patterns, will return whatever value is passed in as default.
|
||||||
|
*
|
||||||
|
* @param defaultValue Version to return if no associated version can be determined from passed in igFileName
|
||||||
|
* @param igFileName Name of the implementation guide
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getVersionFromIGName(String defaultValue, String igFileName) {
|
||||||
|
if (igFileName.equals("hl7.fhir.core")) {
|
||||||
|
defaultValue = "current";
|
||||||
|
} else if (igFileName.startsWith("hl7.fhir.core#")) {
|
||||||
|
defaultValue = VersionUtilities.getCurrentPackageVersion(igFileName.substring(14));
|
||||||
|
} else if (igFileName.startsWith("hl7.fhir.r2.core#") || igFileName.equals("hl7.fhir.r2.core")) {
|
||||||
|
defaultValue = "1.0";
|
||||||
|
} else if (igFileName.startsWith("hl7.fhir.r2b.core#") || igFileName.equals("hl7.fhir.r2b.core")) {
|
||||||
|
defaultValue = "1.4";
|
||||||
|
} else if (igFileName.startsWith("hl7.fhir.r3.core#") || igFileName.equals("hl7.fhir.r3.core")) {
|
||||||
|
defaultValue = "3.0";
|
||||||
|
} else if (igFileName.startsWith("hl7.fhir.r4.core#") || igFileName.equals("hl7.fhir.r4.core")) {
|
||||||
|
defaultValue = "4.0";
|
||||||
|
} else if (igFileName.startsWith("hl7.fhir.r5.core#") || igFileName.equals("hl7.fhir.r5.core")) {
|
||||||
|
defaultValue = "current";
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers the validation engine tests to run.
|
||||||
|
*/
|
||||||
|
public static void runValidationEngineTests() {
|
||||||
|
try {
|
||||||
|
Class<?> clazz = Class.forName("org.hl7.fhir.validation.r5.tests.ValidationEngineTests");
|
||||||
|
clazz.getMethod("execute").invoke(clazz);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ValidationEngine getValidationEngine(String[] args, String txLog) throws Exception {
|
||||||
|
String v = Common.getVersion(args);
|
||||||
|
String definitions = VersionUtilities.packageForVersion(v) + "#" + v;
|
||||||
|
System.out.println("Loading (v = " + v + ", tx server http://tx.fhir.org)");
|
||||||
|
return new ValidationEngine(definitions, "http://tx.fhir.org", txLog, FhirPublication.fromCode(v), v);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package org.hl7.fhir.validation.cli;
|
||||||
|
|
||||||
|
import org.hl7.fhir.r5.conformance.CapabilityStatementUtilities;
|
||||||
|
import org.hl7.fhir.r5.conformance.ProfileComparer;
|
||||||
|
import org.hl7.fhir.r5.formats.IParser;
|
||||||
|
import org.hl7.fhir.r5.formats.JsonParser;
|
||||||
|
import org.hl7.fhir.r5.formats.XmlParser;
|
||||||
|
import org.hl7.fhir.r5.model.CanonicalResource;
|
||||||
|
import org.hl7.fhir.r5.model.CapabilityStatement;
|
||||||
|
import org.hl7.fhir.r5.model.Resource;
|
||||||
|
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||||
|
import org.hl7.fhir.r5.utils.KeyGenerator;
|
||||||
|
import org.hl7.fhir.utilities.TextFile;
|
||||||
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||||
|
import org.hl7.fhir.validation.ValidationEngine;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class Comparison {
|
||||||
|
|
||||||
|
public static void doLeftRightComparison(String[] args, String dest, ValidationEngine validator) throws IOException {
|
||||||
|
// ok now set up the comparison
|
||||||
|
String left = Params.getParam(args, Params.LEFT);
|
||||||
|
String right = Params.getParam(args, Params.RIGHT);
|
||||||
|
|
||||||
|
Resource resLeft = validator.getContext().fetchResource(Resource.class, left);
|
||||||
|
Resource resRight = validator.getContext().fetchResource(Resource.class, right);
|
||||||
|
if (resLeft == null) {
|
||||||
|
System.out.println("Unable to locate left resource " + left);
|
||||||
|
}
|
||||||
|
if (resRight == null) {
|
||||||
|
System.out.println("Unable to locate right resource " + right);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resLeft != null && resRight != null) {
|
||||||
|
if (resLeft instanceof StructureDefinition && resRight instanceof StructureDefinition) {
|
||||||
|
Comparison.compareStructureDefinitions(dest, validator, left, right, (StructureDefinition) resLeft, (StructureDefinition) resRight);
|
||||||
|
} else if (resLeft instanceof CapabilityStatement && resRight instanceof CapabilityStatement) {
|
||||||
|
Comparison.compareCapabilityStatements(args, dest, validator, left, right, (CanonicalResource) resLeft, (CanonicalResource) resRight);
|
||||||
|
} else
|
||||||
|
System.out.println("Unable to compare left resource " + left + " (" + resLeft.fhirType() + ") with right resource " + right + " (" + resRight.fhirType() + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compareCapabilityStatements(String[] args, String dest, ValidationEngine validator, String left, String right, CanonicalResource resLeft, CanonicalResource resRight) throws IOException {
|
||||||
|
String nameLeft = chooseName(args, "leftName", resLeft);
|
||||||
|
String nameRight = chooseName(args, "rightName", resRight);
|
||||||
|
System.out.println("Comparing CapabilityStatements " + left + " to " + right);
|
||||||
|
CapabilityStatementUtilities pc = new CapabilityStatementUtilities(validator.getContext(), dest, new KeyGenerator("http://fhir.org/temp/" + UUID.randomUUID().toString().toLowerCase()));
|
||||||
|
CapabilityStatement capL = (CapabilityStatement) resLeft;
|
||||||
|
CapabilityStatement capR = (CapabilityStatement) resRight;
|
||||||
|
CapabilityStatementUtilities.CapabilityStatementComparisonOutput output = pc.isCompatible(nameLeft, nameRight, capL, capR);
|
||||||
|
|
||||||
|
String destTxt = Utilities.path(dest, "output.txt");
|
||||||
|
System.out.println("Generating output to " + destTxt + "...");
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
for (ValidationMessage msg : output.getMessages()) {
|
||||||
|
b.append(msg.summary());
|
||||||
|
b.append("\r\n");
|
||||||
|
}
|
||||||
|
TextFile.stringToFile(b.toString(), destTxt);
|
||||||
|
new XmlParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-union.xml")), output.getSuperset());
|
||||||
|
new XmlParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-intersection.xml")), output.getSubset());
|
||||||
|
new XmlParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "OperationOutcome-issues.xml")), output.getOutcome());
|
||||||
|
new JsonParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-union.json")), output.getSuperset());
|
||||||
|
new JsonParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-intersection.json")), output.getSubset());
|
||||||
|
new JsonParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "OperationOutcome-issues.json")), output.getOutcome());
|
||||||
|
|
||||||
|
String destHtml = Utilities.path(dest, "index.html");
|
||||||
|
File htmlFile = new File(destHtml);
|
||||||
|
Desktop.getDesktop().browse(htmlFile.toURI());
|
||||||
|
System.out.println("Done");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compareStructureDefinitions(String dest, ValidationEngine validator, String left, String right, StructureDefinition resLeft, StructureDefinition resRight) throws IOException {
|
||||||
|
System.out.println("Comparing StructureDefinitions " + left + " to " + right);
|
||||||
|
ProfileComparer pc = new ProfileComparer(validator.getContext(), dest);
|
||||||
|
StructureDefinition sdL = resLeft;
|
||||||
|
StructureDefinition sdR = resRight;
|
||||||
|
pc.compareProfiles(sdL, sdR);
|
||||||
|
System.out.println("Generating output to " + dest + "...");
|
||||||
|
File htmlFile = new File(pc.generate());
|
||||||
|
Desktop.getDesktop().browse(htmlFile.toURI());
|
||||||
|
System.out.println("Done");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String chooseName(String[] args, String name, CanonicalResource mr) {
|
||||||
|
String s = Params.getParam(args, "-" + name);
|
||||||
|
if (Utilities.noString(s))
|
||||||
|
s = mr.present();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
package org.hl7.fhir.validation.cli;
|
||||||
|
|
||||||
|
import org.hl7.fhir.r5.model.Constants;
|
||||||
|
import org.hl7.fhir.utilities.VersionUtilities;
|
||||||
|
import org.hl7.fhir.utilities.cache.PackageCacheManager;
|
||||||
|
import org.hl7.fhir.utilities.cache.ToolsVersion;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for displaying output to the cli user.
|
||||||
|
*
|
||||||
|
* TODO - Clean this up for localization
|
||||||
|
*/
|
||||||
|
public class Display {
|
||||||
|
|
||||||
|
public static void printCliArgumentsAndInfo(String[] args) throws IOException {
|
||||||
|
System.out.print("Arguments:");
|
||||||
|
for (String s : args) {
|
||||||
|
System.out.print(s.contains(" ") ? " \"" + s + "\"" : " " + s);
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("Directories: Current = " + System.getProperty("user.dir") + ", Package Cache = " + new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION).getFolder());
|
||||||
|
}
|
||||||
|
|
||||||
|
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("");
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,231 @@
|
||||||
|
package org.hl7.fhir.validation.cli;
|
||||||
|
|
||||||
|
import org.hl7.fhir.utilities.VersionUtilities;
|
||||||
|
import org.hl7.fhir.validation.Validator;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class Params {
|
||||||
|
|
||||||
|
public static final String VERSION = "-version";
|
||||||
|
public static final String OUTPUT = "-output";
|
||||||
|
public static final String PROXY = "-proxy";
|
||||||
|
public static final String PROFILE = "-profile";
|
||||||
|
public static final String QUESTIONNAIRE = "-questionnaire";
|
||||||
|
public static final String NATIVE = "-native";
|
||||||
|
public static final String ASSUME_VALID_REST_REF = "-assumeValidRestReferences";
|
||||||
|
public static final String DEBUG = "-debug";
|
||||||
|
public static final String SCT = "-sct";
|
||||||
|
public static final String RECURSE = "-recurse";
|
||||||
|
public static final String LOCALE = "-locale";
|
||||||
|
public static final String STRICT_EXTENSIONS = "-strictExtensions";
|
||||||
|
public static final String HINT_ABOUT_NON_MUST_SUPPORT = "-hintAboutNonMustSupport";
|
||||||
|
public static final String TO_VERSION = "-to-version";
|
||||||
|
public static final String DO_NATIVE = "-do-native";
|
||||||
|
public static final String NO_NATIVE = "-no-native";
|
||||||
|
public static final String TRANSFORM = "-transform";
|
||||||
|
public static final String NARRATIVE = "-narrative";
|
||||||
|
public static final String SNAPSHOT = "-snapshot";
|
||||||
|
public static final String SCAN = "-scan";
|
||||||
|
public static final String TERMINOLOGY = "-tx";
|
||||||
|
public static final String TERMINOLOGY_LOG = "-txLog";
|
||||||
|
public static final String LOG = "-log";
|
||||||
|
public static final String LANGUAGE = "-language";
|
||||||
|
public static final String IMPLEMENTATION_GUIDE = "-ig";
|
||||||
|
public static final String DEFINITION = "defn";
|
||||||
|
public static final String MAP = "-map";
|
||||||
|
public static final String X = "-x";
|
||||||
|
public static final String CONVERT = "-convert";
|
||||||
|
public static final String FHIRPATH = "-fhirpath";
|
||||||
|
public static final String TEST = "-tests";
|
||||||
|
public static final String HELP = "help";
|
||||||
|
public static final String COMPARE = "-compare";
|
||||||
|
public static final String DESTINATION = "-dest";
|
||||||
|
public static final String LEFT = "-left";
|
||||||
|
public static final String RIGHT = "-right";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the list of passed in params to see if it contains the passed in param.
|
||||||
|
*
|
||||||
|
* @param args Array of params to search.
|
||||||
|
* @param param {@link String} param to search for.
|
||||||
|
* @return {@link Boolean#TRUE} if the list contains the given param.
|
||||||
|
*/
|
||||||
|
public static boolean hasParam(String[] args, String param) {
|
||||||
|
return Arrays.asList(args).contains(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the value for the passed in param from the provided list of params.
|
||||||
|
*
|
||||||
|
* @param args Array of params to search.
|
||||||
|
* @param param {@link String} param keyword to search for.
|
||||||
|
* @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];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO Don't do this all in one for loop. Use the above methods.
|
||||||
|
*/
|
||||||
|
public static CliContext loadCliContext(String[] args) throws Exception {
|
||||||
|
CliContext cliContext = new CliContext();
|
||||||
|
|
||||||
|
// load the parameters - so order doesn't matter
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
if (args[i].equals(VERSION)) {
|
||||||
|
cliContext.setSv(VersionUtilities.getCurrentPackageVersion(args[++i]));
|
||||||
|
} else if (args[i].equals(OUTPUT)) {
|
||||||
|
if (i + 1 == args.length)
|
||||||
|
throw new Error("Specified -output without indicating output file");
|
||||||
|
else
|
||||||
|
cliContext.setOutput(args[++i]);
|
||||||
|
} else if (args[i].equals(PROXY)) {
|
||||||
|
i++; // ignore next parameter
|
||||||
|
} else if (args[i].equals(PROFILE)) {
|
||||||
|
String p = null;
|
||||||
|
if (i + 1 == args.length)
|
||||||
|
throw new Error("Specified -profile without indicating profile source");
|
||||||
|
else {
|
||||||
|
p = args[++i];
|
||||||
|
cliContext.addProfile(args[++i]);
|
||||||
|
}
|
||||||
|
if (p != null && i + 1 < args.length && args[i + 1].equals("@")) {
|
||||||
|
i++;
|
||||||
|
if (i + 1 == args.length)
|
||||||
|
throw new Error("Specified -profile with @ without indicating profile location");
|
||||||
|
else
|
||||||
|
cliContext.addLocation(p, args[++i]);
|
||||||
|
}
|
||||||
|
} else if (args[i].equals(QUESTIONNAIRE)) {
|
||||||
|
if (i + 1 == args.length)
|
||||||
|
throw new Error("Specified -questionnaire without indicating questionnaire file");
|
||||||
|
else
|
||||||
|
cliContext.addQuestionnaire(args[++i]);
|
||||||
|
} else if (args[i].equals(NATIVE)) {
|
||||||
|
cliContext.setDoNative(true);
|
||||||
|
} else if (args[i].equals(ASSUME_VALID_REST_REF)) {
|
||||||
|
cliContext.setAssumeValidRestReferences(true);
|
||||||
|
} else if (args[i].equals(DEBUG)) {
|
||||||
|
cliContext.setDoDebug(true);
|
||||||
|
} else if (args[i].equals(SCT)) {
|
||||||
|
cliContext.setSnomedCT(args[++i]);
|
||||||
|
} else if (args[i].equals(RECURSE)) {
|
||||||
|
cliContext.setRecursive(true);
|
||||||
|
} else if (args[i].equals(LOCALE)) {
|
||||||
|
if (i + 1 == args.length) {
|
||||||
|
throw new Error("Specified -locale without indicating locale");
|
||||||
|
} else {
|
||||||
|
cliContext.setLocale(new Locale(args[++i]));
|
||||||
|
}
|
||||||
|
} else if (args[i].equals(STRICT_EXTENSIONS)) {
|
||||||
|
cliContext.setAnyExtensionsAllowed(false);
|
||||||
|
} 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);
|
||||||
|
} 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);
|
||||||
|
} else if (args[i].equals(NARRATIVE)) {
|
||||||
|
cliContext.setMode(Validator.EngineMode.NARRATIVE);
|
||||||
|
} else if (args[i].equals(SNAPSHOT)) {
|
||||||
|
cliContext.setMode(Validator.EngineMode.SNAPSHOT);
|
||||||
|
} else if (args[i].equals(SCAN)) {
|
||||||
|
cliContext.setMode(Validator.EngineMode.SCAN);
|
||||||
|
} else if (args[i].equals(TERMINOLOGY)) {
|
||||||
|
if (i + 1 == args.length)
|
||||||
|
throw new Error("Specified -tx without indicating terminology server");
|
||||||
|
else
|
||||||
|
cliContext.setTxServer("n/a".equals(args[++i]) ? null : args[i]);
|
||||||
|
} else if (args[i].equals(TERMINOLOGY_LOG)) {
|
||||||
|
if (i + 1 == args.length)
|
||||||
|
throw new Error("Specified -txLog without indicating file");
|
||||||
|
else
|
||||||
|
cliContext.setTxLog(args[++i]);
|
||||||
|
} else if (args[i].equals(LOG)) {
|
||||||
|
if (i + 1 == args.length)
|
||||||
|
throw new Error("Specified -log without indicating file");
|
||||||
|
else
|
||||||
|
cliContext.setMapLog(args[++i]);
|
||||||
|
} else if (args[i].equals(LANGUAGE)) {
|
||||||
|
if (i + 1 == args.length)
|
||||||
|
throw new Error("Specified -language without indicating language");
|
||||||
|
else
|
||||||
|
cliContext.setLang(args[++i]);
|
||||||
|
} else if (args[i].equals(IMPLEMENTATION_GUIDE) || args[i].equals(DEFINITION)) {
|
||||||
|
if (i + 1 == args.length)
|
||||||
|
throw new Error("Specified " + args[i] + " without indicating ig file");
|
||||||
|
else {
|
||||||
|
String s = args[++i];
|
||||||
|
cliContext.setSv(Common.getVersionFromIGName(null, s));
|
||||||
|
if (cliContext.getSv() == null) {
|
||||||
|
cliContext.addIg(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (args[i].equals(MAP)) {
|
||||||
|
if (cliContext.getMap() == null) {
|
||||||
|
if (i + 1 == args.length)
|
||||||
|
throw new Error("Specified -map without indicating map file");
|
||||||
|
else
|
||||||
|
cliContext.setMap(args[++i]);
|
||||||
|
} else {
|
||||||
|
throw new Exception("Can only nominate a single -map parameter");
|
||||||
|
}
|
||||||
|
} else if (args[i].startsWith(X)) {
|
||||||
|
i++;
|
||||||
|
} else if (args[i].equals(CONVERT)) {
|
||||||
|
cliContext.setMode(Validator.EngineMode.CONVERT);
|
||||||
|
} else if (args[i].equals(FHIRPATH)) {
|
||||||
|
cliContext.setMode(Validator.EngineMode.FHIRPATH);
|
||||||
|
if (cliContext.getFhirpath() == null)
|
||||||
|
if (i + 1 == args.length)
|
||||||
|
throw new Error("Specified -fhirpath without indicating a FHIRPath expression");
|
||||||
|
else
|
||||||
|
cliContext.setFhirpath(args[++i]);
|
||||||
|
else
|
||||||
|
throw new Exception("Can only nominate a single -fhirpath parameter");
|
||||||
|
} else {
|
||||||
|
cliContext.addSource(args[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cliContext.getSources().isEmpty())
|
||||||
|
throw new Exception("Must provide at least one source file");
|
||||||
|
return cliContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getTerminologyServerLog(String[] args) {
|
||||||
|
String txLog = null;
|
||||||
|
if (hasParam(args, "-txLog")) {
|
||||||
|
txLog = getParam(args, "-txLog");
|
||||||
|
new File(txLog).delete();
|
||||||
|
}
|
||||||
|
return txLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkIGFileReferences(String[] args) {
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
if (IMPLEMENTATION_GUIDE.equals(args[i])) {
|
||||||
|
if (i + 1 == args.length)
|
||||||
|
throw new Error("Specified -ig without indicating ig file");
|
||||||
|
else {
|
||||||
|
String s = args[++i];
|
||||||
|
if (!s.startsWith("hl7.fhir.core-")) {
|
||||||
|
System.out.println("Load Package: " + s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package org.hl7.fhir.validation.cli;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public enum SnomedVersion {
|
||||||
|
INTL("intl", "900000000000207008"),
|
||||||
|
US("us", "731000124108"),
|
||||||
|
UK("uk", "999000041000000102"),
|
||||||
|
AU("au", "32506021000036107"),
|
||||||
|
CA("ca", "20611000087101"),
|
||||||
|
NL("nl", "11000146104"),
|
||||||
|
SE("se", "45991000052106"),
|
||||||
|
ES("es", "449081005"),
|
||||||
|
DK("dk", "554471000005108");
|
||||||
|
|
||||||
|
private static final String DEFAULT_CODE = "900000000000207008";
|
||||||
|
|
||||||
|
private final String lang;
|
||||||
|
private final String code;
|
||||||
|
|
||||||
|
SnomedVersion(String lang, String code) {
|
||||||
|
this.lang = lang;
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLang() {
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String resolveSnomedCTCode(String s) {
|
||||||
|
String foundCode;
|
||||||
|
Optional<SnomedVersion> opt = Arrays.stream(values())
|
||||||
|
.filter(v -> v.lang.equals(s))
|
||||||
|
.findFirst();
|
||||||
|
if (opt.isPresent()) {
|
||||||
|
return opt.get().code;
|
||||||
|
} else {
|
||||||
|
throw new Error("Snomed edition '" + s + "' not known");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
package org.hl7.fhir.validation.cli;
|
||||||
|
|
||||||
|
import org.hl7.fhir.r5.elementmodel.Manager;
|
||||||
|
import org.hl7.fhir.r5.formats.IParser;
|
||||||
|
import org.hl7.fhir.r5.formats.JsonParser;
|
||||||
|
import org.hl7.fhir.r5.formats.XmlParser;
|
||||||
|
import org.hl7.fhir.r5.model.*;
|
||||||
|
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||||
|
import org.hl7.fhir.utilities.TextFile;
|
||||||
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
import org.hl7.fhir.validation.ValidationEngine;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class ValidationUtils {
|
||||||
|
|
||||||
|
public static void validateSources(CliContext cliContext, ValidationEngine validator) throws Exception {
|
||||||
|
if (cliContext.getProfiles().size() > 0)
|
||||||
|
System.out.println(" .. validate " + cliContext.getSources() + " against " + cliContext.getProfiles().toString());
|
||||||
|
else
|
||||||
|
System.out.println(" .. validate " + cliContext.getSources());
|
||||||
|
validator.prepare(); // generate any missing snapshots
|
||||||
|
Resource r = validator.validate(cliContext.getSources(), cliContext.getProfiles());
|
||||||
|
int ec = 0;
|
||||||
|
if (cliContext.getOutput() == null) {
|
||||||
|
if (r instanceof Bundle)
|
||||||
|
for (Bundle.BundleEntryComponent e : ((Bundle) r).getEntry())
|
||||||
|
ec = displayOperationOutcome((OperationOutcome) e.getResource()) + ec;
|
||||||
|
else
|
||||||
|
ec = displayOperationOutcome((OperationOutcome) r);
|
||||||
|
} else {
|
||||||
|
IParser x;
|
||||||
|
if (cliContext.getOutput() != null && cliContext.getOutput().endsWith(".json")) {
|
||||||
|
x = new JsonParser();
|
||||||
|
} else {
|
||||||
|
x = new XmlParser();
|
||||||
|
}
|
||||||
|
x.setOutputStyle(IParser.OutputStyle.PRETTY);
|
||||||
|
FileOutputStream s = new FileOutputStream(cliContext.getOutput());
|
||||||
|
x.compose(s, r);
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
System.exit(ec > 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void validateScan(CliContext cliContext, ValidationEngine validator) throws Exception {
|
||||||
|
if (Utilities.noString(cliContext.getOutput()))
|
||||||
|
throw new Exception("Output parameter required when scanning");
|
||||||
|
if (!(new File(cliContext.getOutput()).isDirectory()))
|
||||||
|
throw new Exception("Output '" + cliContext.getOutput() + "' must be a directory when scanning");
|
||||||
|
System.out.println(" .. scan " + cliContext.getSources() + " against loaded IGs");
|
||||||
|
Set<String> urls = new HashSet<>();
|
||||||
|
for (ImplementationGuide ig : validator.getContext().allImplementationGuides()) {
|
||||||
|
if (ig.getUrl().contains("/ImplementationGuide") && !ig.getUrl().equals("http://hl7.org/fhir/ImplementationGuide/fhir"))
|
||||||
|
urls.add(ig.getUrl());
|
||||||
|
}
|
||||||
|
List<ValidationEngine.ScanOutputItem> res = validator.validateScan(cliContext.getSources(), urls);
|
||||||
|
validator.genScanOutput(cliContext.getOutput(), res);
|
||||||
|
System.out.println("Done. output in " + Utilities.path(cliContext.getOutput(), "scan.html"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void convertSources(CliContext cliContext, ValidationEngine validator) throws Exception {
|
||||||
|
validator.convert(cliContext.getSources().get(0), cliContext.getOutput());
|
||||||
|
System.out.println(" ...convert");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void evaluateFhirpath(CliContext cliContext, ValidationEngine validator) throws Exception {
|
||||||
|
System.out.println(" ...evaluating " + cliContext.getFhirpath());
|
||||||
|
System.out.println(validator.evaluateFhirPath(cliContext.getSources().get(0), cliContext.getFhirpath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void generateSnapshot(CliContext cliContext, ValidationEngine validator) throws Exception {
|
||||||
|
StructureDefinition r = validator.snapshot(cliContext.getSources().get(0), cliContext.getSv());
|
||||||
|
System.out.println(" ...generated snapshot successfully");
|
||||||
|
if (cliContext.getOutput() != null) {
|
||||||
|
validator.handleOutput(r, cliContext.getOutput(), cliContext.getSv());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void generateNarrative(CliContext cliContext, ValidationEngine validator) throws Exception {
|
||||||
|
DomainResource r = validator.generate(cliContext.getSources().get(0), cliContext.getSv());
|
||||||
|
System.out.println(" ...generated narrative successfully");
|
||||||
|
if (cliContext.getOutput() != null) {
|
||||||
|
validator.handleOutput(r, cliContext.getOutput(), cliContext.getSv());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void transform(CliContext cliContext, ValidationEngine validator) throws Exception {
|
||||||
|
if (cliContext.getSources().size() > 1)
|
||||||
|
throw new Exception("Can only have one source when doing a transform (found " + cliContext.getSources() + ")");
|
||||||
|
if (cliContext.getTxServer() == null)
|
||||||
|
throw new Exception("Must provide a terminology server when doing a transform");
|
||||||
|
if (cliContext.getMap() == null)
|
||||||
|
throw new Exception("Must provide a map when doing a transform");
|
||||||
|
try {
|
||||||
|
validator.setMapLog(cliContext.getMapLog());
|
||||||
|
org.hl7.fhir.r5.elementmodel.Element r = validator.transform(cliContext.getSources().get(0), cliContext.getMap());
|
||||||
|
System.out.println(" ...success");
|
||||||
|
if (cliContext.getOutput() != null) {
|
||||||
|
FileOutputStream s = new FileOutputStream(cliContext.getOutput());
|
||||||
|
if (cliContext.getOutput() != null && cliContext.getOutput().endsWith(".json"))
|
||||||
|
new org.hl7.fhir.r5.elementmodel.JsonParser(validator.getContext()).compose(r, s, IParser.OutputStyle.PRETTY, null);
|
||||||
|
else
|
||||||
|
new org.hl7.fhir.r5.elementmodel.XmlParser(validator.getContext()).compose(r, s, IParser.OutputStyle.PRETTY, null);
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(" ...Failure: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void transformVersion(CliContext cliContext, ValidationEngine validator) throws Exception {
|
||||||
|
if (cliContext.getSources().size() > 1) {
|
||||||
|
throw new Exception("Can only have one source when converting versions (found " + cliContext.getSources() + ")");
|
||||||
|
}
|
||||||
|
if (cliContext.getTargetVer() == null) {
|
||||||
|
throw new Exception("Must provide a map when converting versions");
|
||||||
|
}
|
||||||
|
if (cliContext.getOutput() == null) {
|
||||||
|
throw new Exception("Must nominate an output when converting versions");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (cliContext.getMapLog() != null) {
|
||||||
|
validator.setMapLog(cliContext.getMapLog());
|
||||||
|
}
|
||||||
|
byte[] r = validator.transformVersion(cliContext.getSources().get(0), cliContext.getTargetVer(), cliContext.getOutput().endsWith(".json") ? Manager.FhirFormat.JSON : Manager.FhirFormat.XML, cliContext.getCanDoNative());
|
||||||
|
System.out.println(" ...success");
|
||||||
|
TextFile.bytesToFile(r, cliContext.getOutput());
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(" ...Failure: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ValidationEngine getValidator(CliContext cliContext, String definitions) throws Exception {
|
||||||
|
System.out.println(" .. FHIR Version " + cliContext.getSv() + ", definitions from " + definitions);
|
||||||
|
System.out.println(" .. connect to tx server @ " + cliContext.getTxServer());
|
||||||
|
ValidationEngine validator = new ValidationEngine(definitions, cliContext.getTxServer(), cliContext.getTxLog(), FhirPublication.fromCode(cliContext.getSv()), cliContext.getSv());
|
||||||
|
validator.setDebug(cliContext.isDoDebug());
|
||||||
|
System.out.println(" (v" + validator.getContext().getVersion() + ")");
|
||||||
|
for (String src : cliContext.getIgs()) {
|
||||||
|
System.out.println("+ .. load IG from " + src);
|
||||||
|
validator.loadIg(src, cliContext.isRecursive());
|
||||||
|
}
|
||||||
|
validator.setQuestionnaires(cliContext.getQuestionnaires());
|
||||||
|
validator.setNative(cliContext.isDoNative());
|
||||||
|
validator.setHintAboutNonMustSupport(cliContext.isHintAboutNonMustSupport());
|
||||||
|
validator.setAnyExtensionsAllowed(cliContext.isAnyExtensionsAllowed());
|
||||||
|
validator.setLanguage(cliContext.getLang());
|
||||||
|
validator.setLocale(cliContext.getLocale());
|
||||||
|
validator.setSnomedExtension(cliContext.getSnomedCT());
|
||||||
|
validator.setAssumeValidRestReferences(cliContext.isAssumeValidRestReferences());
|
||||||
|
return validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int displayOperationOutcome(OperationOutcome oo) {
|
||||||
|
int error = 0;
|
||||||
|
int warn = 0;
|
||||||
|
int info = 0;
|
||||||
|
String file = ToolingExtensions.readStringExtension(oo, ToolingExtensions.EXT_OO_FILE);
|
||||||
|
|
||||||
|
for (OperationOutcome.OperationOutcomeIssueComponent issue : oo.getIssue()) {
|
||||||
|
if (issue.getSeverity() == OperationOutcome.IssueSeverity.FATAL || issue.getSeverity() == OperationOutcome.IssueSeverity.ERROR)
|
||||||
|
error++;
|
||||||
|
else if (issue.getSeverity() == OperationOutcome.IssueSeverity.WARNING)
|
||||||
|
warn++;
|
||||||
|
else
|
||||||
|
info++;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println((error == 0 ? "Success..." : "*FAILURE* ") + "validating " + file + ": " + " error:" + Integer.toString(error) + " warn:" + Integer.toString(warn) + " info:" + Integer.toString(info));
|
||||||
|
for (OperationOutcome.OperationOutcomeIssueComponent issue : oo.getIssue()) {
|
||||||
|
System.out.println(getIssueSummary(issue));
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getIssueSummary(OperationOutcome.OperationOutcomeIssueComponent issue) {
|
||||||
|
String loc = null;
|
||||||
|
if (issue.hasExpression()) {
|
||||||
|
int line = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_LINE, -1);
|
||||||
|
int col = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_COL, -1);
|
||||||
|
loc = issue.getExpression().get(0).asStringValue() + (line >= 0 && col >= 0 ? " (line " + Integer.toString(line) + ", col" + Integer.toString(col) + ")" : "");
|
||||||
|
} else if (issue.hasLocation()) {
|
||||||
|
loc = issue.getLocation().get(0).asStringValue();
|
||||||
|
} else {
|
||||||
|
int line = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_LINE, -1);
|
||||||
|
int col = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_COL, -1);
|
||||||
|
loc = (line >= 0 && col >= 0 ? "line " + Integer.toString(line) + ", col" + Integer.toString(col) : "??");
|
||||||
|
}
|
||||||
|
return " " + issue.getSeverity().getDisplay() + " @ " + loc + " : " + issue.getDetails().getText();
|
||||||
|
}
|
||||||
|
}
|
|
@ -176,24 +176,6 @@ public class ValidationEngineTests {
|
||||||
System.out.println(" .. done: "+Integer.toString(e)+" errors, "+Integer.toString(w)+" warnings, "+Integer.toString(h)+" information messages");
|
System.out.println(" .. done: "+Integer.toString(e)+" errors, "+Integer.toString(w)+" warnings, "+Integer.toString(h)+" information messages");
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Test
|
|
||||||
// public void testTransform() throws Exception {
|
|
||||||
// if (!TestUtilities.silent)
|
|
||||||
// System.out.println("Transform CCDA");
|
|
||||||
// if (!TestUtilities.silent)
|
|
||||||
// System.out.println(" .. load FHIR from " +Utilities.path(TestUtilities.home(), "publish"));
|
|
||||||
// ValidationEngine ve = new ValidationEngine(Utilities.path(TestUtilities.home(), "publish"), DEF_TX, null, FhirVersion.R4);
|
|
||||||
// if (!TestUtilities.silent)
|
|
||||||
// System.out.println(" .. load CCDA from " +Utilities.path(TestUtilities.home(), "guides\\ccda2\\mapping\\logical"));
|
|
||||||
// ve.loadIg(Utilities.path(TestUtilities.home(), "guides\\ccda2\\mapping\\logical"));
|
|
||||||
// if (!TestUtilities.silent)
|
|
||||||
// System.out.println(" .. load Maps from " +Utilities.path(TestUtilities.home(), "guides\\ccda2\\mapping\\map"));
|
|
||||||
// ve.loadIg(Utilities.path(TestUtilities.home(), "guides\\ccda2\\mapping\\map"));
|
|
||||||
// Resource r = ve.transform(Utilities.path(TestUtilities.home(), "guides\\ccda2\\mapping\\example\\ccd.xml"), "http://hl7.org/fhir/StructureMap/cda");
|
|
||||||
// if (!TestUtilities.silent)
|
|
||||||
// System.out.println(" .. done");
|
|
||||||
// }
|
|
||||||
|
|
||||||
private int errors(OperationOutcome op) {
|
private int errors(OperationOutcome op) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (OperationOutcomeIssueComponent vm : op.getIssue()) {
|
for (OperationOutcomeIssueComponent vm : op.getIssue()) {
|
||||||
|
@ -228,7 +210,6 @@ public class ValidationEngineTests {
|
||||||
self.test102();
|
self.test102();
|
||||||
self.test140();
|
self.test140();
|
||||||
self.test301USCore();
|
self.test301USCore();
|
||||||
// self.testTransform();
|
|
||||||
System.out.println("Finished");
|
System.out.println("Finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue