Cleaning up cli code

This commit is contained in:
markiantorno 2020-04-02 11:40:44 -04:00
parent aef4404405
commit b0a15a11a2
7 changed files with 517 additions and 389 deletions

View File

@ -51,6 +51,7 @@ POSSIBILITY OF SUCH DAMAGE.
import java.awt.Desktop; import java.awt.Desktop;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -63,6 +64,7 @@ import java.util.UUID;
import org.hl7.fhir.r5.conformance.CapabilityStatementUtilities; import org.hl7.fhir.r5.conformance.CapabilityStatementUtilities;
import org.hl7.fhir.r5.conformance.CapabilityStatementUtilities.CapabilityStatementComparisonOutput; import org.hl7.fhir.r5.conformance.CapabilityStatementUtilities.CapabilityStatementComparisonOutput;
import org.hl7.fhir.r5.conformance.ProfileComparer; import org.hl7.fhir.r5.conformance.ProfileComparer;
import org.hl7.fhir.r5.context.SimpleWorkerContext;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat; import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r5.formats.IParser; import org.hl7.fhir.r5.formats.IParser;
import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.formats.IParser.OutputStyle;
@ -71,7 +73,6 @@ import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r5.model.CapabilityStatement; 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.DomainResource;
import org.hl7.fhir.r5.model.FhirPublication; import org.hl7.fhir.r5.model.FhirPublication;
import org.hl7.fhir.r5.model.ImplementationGuide; import org.hl7.fhir.r5.model.ImplementationGuide;
@ -86,23 +87,24 @@ import org.hl7.fhir.validation.ValidationEngine.ScanOutputItem;
import org.hl7.fhir.utilities.TextFile; 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.utilities.cache.ToolsVersion;
import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.validation.cliutils.ComparisonUtils;
import org.hl7.fhir.validation.cliutils.DisplayUtils;
import org.hl7.fhir.validation.cliutils.ParamUtils;
import org.hl7.fhir.validation.cliutils.Utils;
/** /**
* 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,12 +125,12 @@ 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, "-proxy");
if (!Utilities.noString(proxy)) { if (!Utilities.noString(proxy)) {
String[] p = proxy.split("\\:"); String[] p = proxy.split("\\:");
@ -136,263 +138,26 @@ public class Validator {
System.setProperty("http.proxyPort", p[1]); System.setProperty("http.proxyPort", p[1]);
} }
if (hasParam(args, "-tests")) { if (ParamUtils.hasParam(args, "-tests")) {
try { Utils.runValidationEngineTests();
Class<?> clazz = Class.forName("org.hl7.fhir.validation.r5.tests.ValidationEngineTests"); } else if (args.length == 0 || ParamUtils.hasParam(args, "help") || ParamUtils.hasParam(args, "?") || ParamUtils.hasParam(args, "-?") || ParamUtils.hasParam(args, "/?")) {
clazz.getMethod("execute").invoke(clazz); DisplayUtils.displayHelpDetails();
} catch (Exception e) { } else if (ParamUtils.hasParam(args, "-compare")) {
e.printStackTrace(); DisplayUtils.printCliArgumentsAndInfo(args);
} String dest = ParamUtils.getParam(args, "-dest");
} 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 = Utils.getTerminologyServerLog(args);
if (hasParam(args, "-txLog")) { ValidationEngine validator = Utils.getValidationEngine(args, txLog);
txLog = getParam(args, "-txLog"); Utils.checkIGFileReferences(args);
new File(txLog).delete(); ComparisonUtils.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:"); DisplayUtils.printCliArgumentsAndInfo(args);
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 map = null; String map = null;
List<String> igs = new ArrayList<String>(); List<String> igs = new ArrayList<String>();
@ -407,7 +172,7 @@ public class Validator {
EngineMode mode = EngineMode.VALIDATION; EngineMode mode = EngineMode.VALIDATION;
String output = null; String output = null;
Boolean canDoNative = null; Boolean canDoNative = null;
List<String> sources= new ArrayList<String>(); List<String> sources = new ArrayList<String>();
Map<String, String> locations = new HashMap<String, String>(); Map<String, String> locations = new HashMap<String, String>();
String sv = "current"; String sv = "current";
String txLog = null; String txLog = null;
@ -425,7 +190,7 @@ public class Validator {
sv = args[++i]; sv = args[++i];
sv = VersionUtilities.getCurrentPackageVersion(sv); sv = VersionUtilities.getCurrentPackageVersion(sv);
} else if (args[i].equals("-output")) { } else if (args[i].equals("-output")) {
if (i+1 == args.length) if (i + 1 == args.length)
throw new Error("Specified -output without indicating output file"); throw new Error("Specified -output without indicating output file");
else else
output = args[++i]; output = args[++i];
@ -433,21 +198,21 @@ public class Validator {
i++; // ignore next parameter i++; // ignore next parameter
} else if (args[i].equals("-profile")) { } else if (args[i].equals("-profile")) {
String p = null; String p = null;
if (i+1 == args.length) if (i + 1 == args.length)
throw new Error("Specified -profile without indicating profile source"); throw new Error("Specified -profile without indicating profile source");
else { else {
p = args[++i]; p = args[++i];
profiles.add(p); profiles.add(p);
} }
if (p != null && i+1 < args.length && args[i+1].equals("@")) { if (p != null && i + 1 < args.length && args[i + 1].equals("@")) {
i++; i++;
if (i+1 == args.length) if (i + 1 == args.length)
throw new Error("Specified -profile with @ without indicating profile location"); throw new Error("Specified -profile with @ without indicating profile location");
else else
locations.put(p, args[++i]); locations.put(p, args[++i]);
} }
} else if (args[i].equals("-questionnaire")) { } else if (args[i].equals("-questionnaire")) {
if (i+1 == args.length) if (i + 1 == args.length)
throw new Error("Specified -questionnaire without indicating questionnaire file"); throw new Error("Specified -questionnaire without indicating questionnaire file");
else else
questionnaires.add(args[++i]); questionnaires.add(args[++i]);
@ -459,30 +224,11 @@ public class Validator {
doDebug = true; doDebug = true;
} else if (args[i].equals("-sct")) { } else if (args[i].equals("-sct")) {
String s = args[++i]; String s = args[++i];
if ("intl".equalsIgnoreCase(s)) snomedCT = resolveSnomedCTCode(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")) { } else if (args[i].equals("-recurse")) {
recursive = true; recursive = true;
} else if (args[i].equals("-locale")) { } else if (args[i].equals("-locale")) {
if (i+1 == args.length) { if (i + 1 == args.length) {
throw new Error("Specified -locale without indicating locale"); throw new Error("Specified -locale without indicating locale");
} else { } else {
locale = new Locale(args[++i]); locale = new Locale(args[++i]);
@ -508,28 +254,28 @@ public class Validator {
} else if (args[i].equals("-scan")) { } else if (args[i].equals("-scan")) {
mode = EngineMode.SCAN; mode = EngineMode.SCAN;
} else if (args[i].equals("-tx")) { } else if (args[i].equals("-tx")) {
if (i+1 == args.length) if (i + 1 == args.length)
throw new Error("Specified -tx without indicating terminology server"); throw new Error("Specified -tx without indicating terminology server");
else else
txServer = "n/a".equals(args[++i]) ? null : args[i]; txServer = "n/a".equals(args[++i]) ? null : args[i];
} else if (args[i].equals("-txLog")) { } else if (args[i].equals("-txLog")) {
if (i+1 == args.length) if (i + 1 == args.length)
throw new Error("Specified -txLog without indicating file"); throw new Error("Specified -txLog without indicating file");
else else
txLog = args[++i]; txLog = args[++i];
} else if (args[i].equals("-log")) { } else if (args[i].equals("-log")) {
if (i+1 == args.length) if (i + 1 == args.length)
throw new Error("Specified -log without indicating file"); throw new Error("Specified -log without indicating file");
else else
mapLog = args[++i]; mapLog = args[++i];
} else if (args[i].equals("-language")) { } else if (args[i].equals("-language")) {
if (i+1 == args.length) if (i + 1 == args.length)
throw new Error("Specified -language without indicating language"); throw new Error("Specified -language without indicating language");
else else
lang = args[++i]; lang = args[++i];
} else if (args[i].equals("-ig") || args[i].equals("-defn")) { } else if (args[i].equals("-ig") || args[i].equals("-defn")) {
if (i+1 == args.length) if (i + 1 == args.length)
throw new Error("Specified "+args[i]+" without indicating ig file"); throw new Error("Specified " + args[i] + " without indicating ig file");
else { else {
String s = args[++i]; String s = args[++i];
if (s.equals("hl7.fhir.core")) { if (s.equals("hl7.fhir.core")) {
@ -546,13 +292,12 @@ public class Validator {
sv = "4.0"; sv = "4.0";
} else if (s.startsWith("hl7.fhir.r5.core#") || s.equals("hl7.fhir.r5.core")) { } else if (s.startsWith("hl7.fhir.r5.core#") || s.equals("hl7.fhir.r5.core")) {
sv = "current"; sv = "current";
} } else
else
igs.add(s); igs.add(s);
} }
} else if (args[i].equals("-map")) { } else if (args[i].equals("-map")) {
if (map == null) { if (map == null) {
if (i+1 == args.length) if (i + 1 == args.length)
throw new Error("Specified -map without indicating map file"); throw new Error("Specified -map without indicating map file");
else else
map = args[++i]; map = args[++i];
@ -566,7 +311,7 @@ public class Validator {
} else if (args[i].equals("-fhirpath")) { } else if (args[i].equals("-fhirpath")) {
mode = EngineMode.FHIRPATH; mode = EngineMode.FHIRPATH;
if (fhirpath == null) if (fhirpath == null)
if (i+1 == args.length) if (i + 1 == args.length)
throw new Error("Specified -fhirpath without indicating a FHIRPath expression"); throw new Error("Specified -fhirpath without indicating a FHIRPath expression");
else else
fhirpath = args[++i]; fhirpath = args[++i];
@ -580,14 +325,14 @@ public class Validator {
throw new Exception("Must provide at least one source file"); 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(sv) + "#" + VersionUtilities.getCurrentVersion(sv);
System.out.println(" .. FHIR Version "+sv+", definitions from "+definitions); System.out.println(" .. FHIR Version " + sv + ", definitions from " + definitions);
System.out.println(" .. connect to tx server @ "+txServer); System.out.println(" .. connect to tx server @ " + txServer);
ValidationEngine validator = new ValidationEngine(definitions, txServer, txLog, FhirPublication.fromCode(sv), sv); ValidationEngine validator = new ValidationEngine(definitions, txServer, txLog, FhirPublication.fromCode(sv), sv);
validator.setDebug(doDebug); validator.setDebug(doDebug);
System.out.println(" (v"+validator.getContext().getVersion()+")"); System.out.println(" (v" + validator.getContext().getVersion() + ")");
for (String src : igs) { for (String src : igs) {
System.out.println("+ .. load IG from "+src); System.out.println("+ .. load IG from " + src);
validator.loadIg(src, recursive); validator.loadIg(src, recursive);
} }
validator.setQuestionnaires(questionnaires); validator.setQuestionnaires(questionnaires);
@ -608,7 +353,7 @@ public class Validator {
if (mode == EngineMode.VERSION) { if (mode == EngineMode.VERSION) {
if (sources.size() > 1) { if (sources.size() > 1) {
throw new Exception("Can only have one source when converting versions (found "+sources+")"); throw new Exception("Can only have one source when converting versions (found " + sources + ")");
} }
if (targetVer == null) { if (targetVer == null) {
throw new Exception("Must provide a map when converting versions"); throw new Exception("Must provide a map when converting versions");
@ -624,12 +369,12 @@ public class Validator {
System.out.println(" ...success"); System.out.println(" ...success");
TextFile.bytesToFile(r, output); TextFile.bytesToFile(r, output);
} catch (Exception e) { } catch (Exception e) {
System.out.println(" ...Failure: "+e.getMessage()); System.out.println(" ...Failure: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
} }
} else if (mode == EngineMode.TRANSFORM) { } else if (mode == EngineMode.TRANSFORM) {
if (sources.size() > 1) if (sources.size() > 1)
throw new Exception("Can only have one source when doing a transform (found "+sources+")"); throw new Exception("Can only have one source when doing a transform (found " + sources + ")");
if (txServer == null) if (txServer == null)
throw new Exception("Must provide a terminology server when doing a transform"); throw new Exception("Must provide a terminology server when doing a transform");
if (map == null) if (map == null)
@ -647,7 +392,7 @@ public class Validator {
s.close(); s.close();
} }
} catch (Exception e) { } catch (Exception e) {
System.out.println(" ...Failure: "+e.getMessage()); System.out.println(" ...Failure: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
} }
} else if (mode == EngineMode.NARRATIVE) { } else if (mode == EngineMode.NARRATIVE) {
@ -666,14 +411,14 @@ public class Validator {
validator.convert(sources.get(0), output); validator.convert(sources.get(0), output);
System.out.println(" ...convert"); System.out.println(" ...convert");
} else if (mode == EngineMode.FHIRPATH) { } else if (mode == EngineMode.FHIRPATH) {
System.out.println(" ...evaluating "+fhirpath); System.out.println(" ...evaluating " + fhirpath);
System.out.println(validator.evaluateFhirPath(sources.get(0), 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 : profiles) {
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(locations.getOrDefault(s, s));
} }
} }
@ -681,8 +426,8 @@ public class Validator {
if (Utilities.noString(output)) if (Utilities.noString(output))
throw new Exception("Output parameter required when scanning"); throw new Exception("Output parameter required when scanning");
if (!(new File(output).isDirectory())) if (!(new File(output).isDirectory()))
throw new Exception("Output '"+output+"' must be a directory when scanning"); throw new Exception("Output '" + output + "' must be a directory when scanning");
System.out.println(" .. scan "+sources+" against loaded IGs"); System.out.println(" .. scan " + sources + " against loaded IGs");
Set<String> urls = new HashSet<>(); Set<String> urls = new HashSet<>();
for (ImplementationGuide ig : validator.getContext().allImplementationGuides()) { for (ImplementationGuide ig : validator.getContext().allImplementationGuides()) {
if (ig.getUrl().contains("/ImplementationGuide") && !ig.getUrl().equals("http://hl7.org/fhir/ImplementationGuide/fhir")) if (ig.getUrl().contains("/ImplementationGuide") && !ig.getUrl().equals("http://hl7.org/fhir/ImplementationGuide/fhir"))
@ -690,21 +435,21 @@ public class Validator {
} }
List<ScanOutputItem> res = validator.validateScan(sources, urls); List<ScanOutputItem> res = validator.validateScan(sources, urls);
validator.genScanOutput(output, res); validator.genScanOutput(output, res);
System.out.println("Done. output in "+Utilities.path(output, "scan.html")); System.out.println("Done. output in " + Utilities.path(output, "scan.html"));
} else { } else {
if (profiles.size() > 0) if (profiles.size() > 0)
System.out.println(" .. validate "+sources+" against "+profiles.toString()); System.out.println(" .. validate " + sources + " against " + profiles.toString());
else else
System.out.println(" .. validate "+sources); System.out.println(" .. validate " + sources);
validator.prepare(); // generate any missing snapshots validator.prepare(); // generate any missing snapshots
Resource r = validator.validate(sources, profiles); Resource r = validator.validate(sources, profiles);
int ec = 0; int ec = 0;
if (output == null) { if (output == null) {
if (r instanceof Bundle) if (r instanceof Bundle)
for (BundleEntryComponent e : ((Bundle)r).getEntry()) for (BundleEntryComponent e : ((Bundle) r).getEntry())
ec = displayOO((OperationOutcome)e.getResource()) + ec; ec = displayOO((OperationOutcome) e.getResource()) + ec;
else else
ec = displayOO((OperationOutcome)r); ec = displayOO((OperationOutcome) r);
} else { } else {
FileOutputStream s = new FileOutputStream(output); FileOutputStream s = new FileOutputStream(output);
x.compose(s, r); x.compose(s, r);
@ -716,11 +461,29 @@ public class Validator {
} }
} }
private static String chooseName(String[] args, String name, CanonicalResource mr) { public static String resolveSnomedCTCode(String s) {
String s = getParam(args, "-"+name); String snomedCT;
if (Utilities.noString(s)) if ("intl".equalsIgnoreCase(s))
s = mr.present(); snomedCT = "900000000000207008";
return s; 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");
return snomedCT;
} }
private static String getGitBuild() { private static String getGitBuild() {
@ -734,15 +497,15 @@ public class Validator {
String file = ToolingExtensions.readStringExtension(oo, ToolingExtensions.EXT_OO_FILE); String file = ToolingExtensions.readStringExtension(oo, ToolingExtensions.EXT_OO_FILE);
for (OperationOutcomeIssueComponent issue : oo.getIssue()) { for (OperationOutcomeIssueComponent issue : oo.getIssue()) {
if (issue.getSeverity()==OperationOutcome.IssueSeverity.FATAL || issue.getSeverity()==OperationOutcome.IssueSeverity.ERROR) if (issue.getSeverity() == OperationOutcome.IssueSeverity.FATAL || issue.getSeverity() == OperationOutcome.IssueSeverity.ERROR)
error++; error++;
else if (issue.getSeverity()==OperationOutcome.IssueSeverity.WARNING) else if (issue.getSeverity() == OperationOutcome.IssueSeverity.WARNING)
warn++; warn++;
else else
info++; info++;
} }
System.out.println((error==0?"Success...":"*FAILURE* ")+ "validating "+file+": "+" error:"+Integer.toString(error)+" warn:"+Integer.toString(warn)+" info:"+Integer.toString(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()) { for (OperationOutcomeIssueComponent issue : oo.getIssue()) {
System.out.println(getIssueSummary(issue)); System.out.println(getIssueSummary(issue));
} }
@ -755,38 +518,15 @@ public class Validator {
if (issue.hasExpression()) { if (issue.hasExpression()) {
int line = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_LINE, -1); int line = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_LINE, -1);
int col = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_COL, -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)+")" : ""); loc = issue.getExpression().get(0).asStringValue() + (line >= 0 && col >= 0 ? " (line " + Integer.toString(line) + ", col" + Integer.toString(col) + ")" : "");
} else if (issue.hasLocation()) { } else if (issue.hasLocation()) {
loc = issue.getLocation().get(0).asStringValue(); loc = issue.getLocation().get(0).asStringValue();
} else { } else {
int line = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_LINE, -1); int line = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_LINE, -1);
int col = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_COL, -1); int col = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_COL, -1);
loc = (line >= 0 && col >= 0 ? "line "+Integer.toString(line)+", col"+Integer.toString(col) : "??"); loc = (line >= 0 && col >= 0 ? "line " + Integer.toString(line) + ", col" + Integer.toString(col) : "??");
} }
return " " + issue.getSeverity().getDisplay() + " @ " + loc + " : " + issue.getDetails().getText(); 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;
}
} }

View File

@ -0,0 +1,4 @@
package org.hl7.fhir.validation.cliutils;
public class CliContext {
}

View File

@ -0,0 +1,98 @@
package org.hl7.fhir.validation.cliutils;
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 ComparisonUtils {
public static void doLeftRightComparison(String[] args, String dest, ValidationEngine validator) throws IOException {
// ok now set up the comparison
String left = ParamUtils.getParam(args, "-left");
String right = ParamUtils.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) {
ComparisonUtils.compareStructureDefinitions(dest, validator, left, right, (StructureDefinition) resLeft, (StructureDefinition) resRight);
} else if (resLeft instanceof CapabilityStatement && resRight instanceof CapabilityStatement) {
ComparisonUtils.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 = ParamUtils.getParam(args, "-" + name);
if (Utilities.noString(s))
s = mr.present();
return s;
}
}

View File

@ -0,0 +1,143 @@
package org.hl7.fhir.validation.cliutils;
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;
public class DisplayUtils {
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");
}
}

View File

@ -0,0 +1,33 @@
package org.hl7.fhir.validation.cliutils;
public class ParamUtils {
/** TODO proper error checking, streams
* 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) {
for (String a : args)
if (a.equals(param))
return true;
return false;
}
/** TODO proper error checking, streams
* Fetches the vlaue 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;
}
}

View File

@ -0,0 +1,129 @@
package org.hl7.fhir.validation.cliutils;
import org.hl7.fhir.r5.context.SimpleWorkerContext;
import org.hl7.fhir.r5.model.Constants;
import org.hl7.fhir.r5.model.FhirPublication;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.validation.ValidationEngine;
import java.io.File;
public class Utils {
public static String getVersion(String[] args) {
String v = ParamUtils.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 = Utils.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.
*
* 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.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 String getTerminologyServerLog(String[] args) {
String txLog = null;
if (ParamUtils.hasParam(args, "-txLog")) {
txLog = ParamUtils.getParam(args, "-txLog");
new File(txLog).delete();
}
return txLog;
}
public static ValidationEngine getValidationEngine(String[] args, String txLog) throws Exception {
String v = Utils.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);
}
public static void checkIGFileReferences(String[] args) {
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);
}
}
}
}
}
private static Resource loadResource(String[] args, String param, SimpleWorkerContext context) {
String resString = ParamUtils.getParam(args, param);
Resource resource = context.fetchResource(Resource.class, resString);
if (resource == null) {
System.out.println("Unable to locate resource for " + param + ", " + resString);
}
return resource;
}
public static Resource getRightResource(String[] args, SimpleWorkerContext context) {
return loadResource(args, "-right", context);
}
public static Resource getLeftResource(String[] args, SimpleWorkerContext context) {
return loadResource(args, "-left", context);
}
}

View File

@ -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");
} }