add parameter -html-output for enhanced presentation of slicing information (issue #283)
This commit is contained in:
parent
bc1d67db96
commit
82afa47590
|
@ -5,6 +5,7 @@ Validator Changes:
|
|||
* fix bug checking patterns (missed in some circumstances)
|
||||
* fix bug checking type of resources in bundles
|
||||
* improve messages around cardinality errors in profiles
|
||||
* add parameter -html-output for enhanced presentation of slicing information
|
||||
|
||||
Other code changes:
|
||||
* Render binding description in profile tables if it doesn't contain paragraphs
|
||||
|
|
|
@ -46,6 +46,7 @@ public class OperationOutcomeUtilities {
|
|||
|
||||
public static OperationOutcomeIssueComponent convertToIssue(ValidationMessage message, OperationOutcome op) {
|
||||
OperationOutcomeIssueComponent issue = new OperationOutcome.OperationOutcomeIssueComponent();
|
||||
issue.setUserData("source.vm", message);
|
||||
issue.setCode(convert(message.getType()));
|
||||
|
||||
if (message.getLocation() != null) {
|
||||
|
|
|
@ -114,6 +114,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
|||
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
|
||||
import org.hl7.fhir.validation.BaseValidator.ValidationControl;
|
||||
import org.hl7.fhir.validation.ValidationEngine.ValidationRecord;
|
||||
import org.hl7.fhir.validation.cli.model.ScanOutputItem;
|
||||
import org.hl7.fhir.validation.cli.services.StandAloneValidatorFetcher.IPackageInstaller;
|
||||
import org.hl7.fhir.validation.cli.utils.*;
|
||||
|
@ -195,6 +196,50 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
*/
|
||||
public class ValidationEngine implements IValidatorResourceFetcher, IPackageInstaller {
|
||||
|
||||
public class ValidationRecord {
|
||||
|
||||
private String location;
|
||||
private List<ValidationMessage> messages;
|
||||
int err = 0;
|
||||
int warn = 0;
|
||||
int info = 0;
|
||||
|
||||
public ValidationRecord(String location, List<ValidationMessage> messages) {
|
||||
this.location = location;
|
||||
this.messages = messages;
|
||||
for (ValidationMessage vm : messages) {
|
||||
if (vm.getLevel().equals(ValidationMessage.IssueSeverity.FATAL)||vm.getLevel().equals(ValidationMessage.IssueSeverity.ERROR))
|
||||
err++;
|
||||
else if (vm.getLevel().equals(ValidationMessage.IssueSeverity.WARNING))
|
||||
warn++;
|
||||
else if (!vm.isSignpost()) {
|
||||
info++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public List<ValidationMessage> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
public int getErr() {
|
||||
return err;
|
||||
}
|
||||
|
||||
public int getWarn() {
|
||||
return warn;
|
||||
}
|
||||
|
||||
public int getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class TransformSupportServices implements ITransformerServices {
|
||||
|
||||
private List<Base> outputs;
|
||||
|
@ -1171,7 +1216,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
|||
public OperationOutcome validate(String source, List<String> profiles) throws FHIRException, IOException {
|
||||
List<String> l = new ArrayList<String>();
|
||||
l.add(source);
|
||||
return (OperationOutcome)validate(l, profiles);
|
||||
return (OperationOutcome)validate(l, profiles, null);
|
||||
}
|
||||
|
||||
public List<ScanOutputItem> validateScan(List<String> sources, Set<String> guides) throws FHIRException, IOException, EOperationOutcome {
|
||||
|
@ -1267,7 +1312,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
|||
}
|
||||
}
|
||||
|
||||
public Resource validate(List<String> sources, List<String> profiles) throws FHIRException, IOException {
|
||||
public Resource validate(List<String> sources, List<String> profiles, List<ValidationRecord> record) throws FHIRException, IOException {
|
||||
if (profiles.size() > 0) {
|
||||
System.out.println(" Profiles: "+profiles);
|
||||
}
|
||||
|
@ -1281,7 +1326,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
|||
System.out.print(" Validate " + ref);
|
||||
Content cnt = loadContent(ref, "validate", false);
|
||||
try {
|
||||
OperationOutcome outcome = validate(ref, cnt.focus, cnt.cntType, profiles);
|
||||
OperationOutcome outcome = validate(ref, cnt.focus, cnt.cntType, profiles, record);
|
||||
ToolingExtensions.addStringExtension(outcome, ToolingExtensions.EXT_OO_FILE, ref);
|
||||
System.out.println(" " + context.clock().milestone());
|
||||
results.addEntry().setResource(outcome);
|
||||
|
@ -1355,7 +1400,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
|||
}
|
||||
|
||||
|
||||
public OperationOutcome validate(String location, byte[] source, FhirFormat cntType, List<String> profiles) throws FHIRException, IOException, EOperationOutcome, SAXException {
|
||||
public OperationOutcome validate(String location, byte[] source, FhirFormat cntType, List<String> profiles, List<ValidationRecord> record) throws FHIRException, IOException, EOperationOutcome, SAXException {
|
||||
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
|
||||
if (doNative) {
|
||||
SchemaValidator.validateSchema(location, cntType, messages);
|
||||
|
@ -1365,6 +1410,9 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst
|
|||
if (showTimes) {
|
||||
System.out.println(location+": "+validator.reportTimes());
|
||||
}
|
||||
if (record != null) {
|
||||
record.add(new ValidationRecord(location, messages));
|
||||
}
|
||||
return messagesToOutcome(messages);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@ public class CliContext {
|
|||
private String map = null;
|
||||
@JsonProperty("output")
|
||||
private String output = null;
|
||||
@JsonProperty("htmlOutput")
|
||||
private String htmlOutput = null;
|
||||
@JsonProperty("txServer")
|
||||
private String txServer = "http://tx.fhir.org";
|
||||
@JsonProperty("sv")
|
||||
|
@ -252,6 +254,17 @@ public class CliContext {
|
|||
return this;
|
||||
}
|
||||
|
||||
@JsonProperty("output")
|
||||
public String getHtmlOutput() {
|
||||
return htmlOutput;
|
||||
}
|
||||
|
||||
@JsonProperty("output")
|
||||
public CliContext setHtmlOutput(String htmlOutput) {
|
||||
this.htmlOutput = htmlOutput;
|
||||
return this;
|
||||
}
|
||||
|
||||
@JsonProperty("canDoNative")
|
||||
public boolean getCanDoNative() {
|
||||
return canDoNative;
|
||||
|
@ -472,6 +485,7 @@ public class CliContext {
|
|||
noExtensibleBindingMessages == that.noExtensibleBindingMessages &&
|
||||
Objects.equals(map, that.map) &&
|
||||
Objects.equals(output, that.output) &&
|
||||
Objects.equals(htmlOutput, that.htmlOutput) &&
|
||||
Objects.equals(txServer, that.txServer) &&
|
||||
Objects.equals(sv, that.sv) &&
|
||||
Objects.equals(txLog, that.txLog) &&
|
||||
|
@ -493,7 +507,7 @@ public class CliContext {
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(doNative, anyExtensionsAllowed, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching, noExtensibleBindingMessages, map, output, txServer, sv, txLog, mapLog, lang, fhirpath, snomedCT, targetVer, igs, questionnaireMode, profiles, sources, mode, locale, locations, crumbTrails, showTimes);
|
||||
return Objects.hash(doNative, anyExtensionsAllowed, hintAboutNonMustSupport, recursive, doDebug, assumeValidRestReferences, canDoNative, noInternalCaching, noExtensibleBindingMessages, map, output, htmlOutput, txServer, sv, txLog, mapLog, lang, fhirpath, snomedCT, targetVer, igs, questionnaireMode, profiles, sources, mode, locale, locations, crumbTrails, showTimes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -510,6 +524,7 @@ public class CliContext {
|
|||
", noExtensibleBindingMessages=" + noExtensibleBindingMessages +
|
||||
", map='" + map + '\'' +
|
||||
", output='" + output + '\'' +
|
||||
", htmlOutput='" + htmlOutput + '\'' +
|
||||
", txServer='" + txServer + '\'' +
|
||||
", sv='" + sv + '\'' +
|
||||
", txLog='" + txLog + '\'' +
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
package org.hl7.fhir.validation.cli.services;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
||||
import org.hl7.fhir.validation.ValidationEngine.ValidationRecord;
|
||||
import org.hl7.fhir.validation.cli.utils.VersionUtil;
|
||||
|
||||
public class HTMLOutputGenerator {
|
||||
|
||||
private List<ValidationRecord> records;
|
||||
|
||||
public HTMLOutputGenerator(List<ValidationRecord> records) {
|
||||
super();
|
||||
this.records = records;
|
||||
}
|
||||
|
||||
public String generate(long time) {
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(genHeader(time));
|
||||
int i = 0;
|
||||
for (ValidationRecord f : records) {
|
||||
i++;
|
||||
b.append(genSummaryRow(i, f));
|
||||
}
|
||||
b.append("</table>\r\n");
|
||||
|
||||
i = 0;
|
||||
int id = 0;
|
||||
for (ValidationRecord f : records) {
|
||||
i++;
|
||||
b.append(genStart(i, f));
|
||||
if (f.getMessages().size() > 0) {
|
||||
b.append(
|
||||
" <table class=\"grid\">\r\n"+
|
||||
" <tr>\r\n"+
|
||||
" <td><b>Path</b></td><td><b>Severity</b></td><td><b>Message</b></td>\r\n"+
|
||||
" </tr>\r\n");
|
||||
for (ValidationMessage vm : f.getMessages()) {
|
||||
id++;
|
||||
b.append(genDetails(vm, "m"+id));
|
||||
}
|
||||
b.append("</table>\r\n");
|
||||
} else {
|
||||
b.append("<p>No Issues detected</p>\r\n");
|
||||
}
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private String genHeader(long time) {
|
||||
int err = 0;
|
||||
int warn = 0;
|
||||
int info = 0;
|
||||
for (ValidationRecord f : records) {
|
||||
err = err + f.getErr();
|
||||
warn = warn + f.getWarn();
|
||||
info = info + f.getInfo();
|
||||
}
|
||||
|
||||
return
|
||||
"<!DOCTYPE HTML>\r\n"+
|
||||
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\r\n"+
|
||||
"<head>\r\n"+
|
||||
" <title>Validation Results</title>\r\n"+
|
||||
" <link href=\"http://hl7.org/fhir/fhir.css\" rel=\"stylesheet\"/>\r\n"+
|
||||
" <style>\r\n"+
|
||||
" span.flip { background-color: #4CAF50; color: white; border: solid 1px #a6d8a8; padding: 2px }\r\n"+
|
||||
" </style>\r\n"+
|
||||
" <script>\r\n"+
|
||||
" function flip(id) {\r\n"+
|
||||
" var span = document.getElementById('s'+id);\r\n"+
|
||||
" var div = document.getElementById(id);\r\n"+
|
||||
" if (document.getElementById('s'+id).innerHTML == 'Show Reasoning') {\r\n"+
|
||||
" div.style.display = 'block';\r\n"+
|
||||
" span.innerHTML = 'Hide Reasoning';\r\n"+
|
||||
" } else {\r\n"+
|
||||
" div.style.display = 'none';\r\n"+
|
||||
" span.innerHTML = 'Show Reasoning';\r\n"+
|
||||
" }\r\n"+
|
||||
" }\r\n"+
|
||||
" </script>\r\n"+
|
||||
"</head>\r\n"+
|
||||
"<body style=\"margin: 20px; background-color: #ffffff\">\r\n"+
|
||||
" <h1>Validation Results</h1>\r\n"+
|
||||
" <p>"+err+" "+Utilities.pluralize("error", err)+", "+warn+" "+Utilities.pluralize("warning", warn)+", "+info+" "+Utilities.pluralize("hint", info)+". Generated "+now()+" by Validator "+VersionUtil.getVersionString()+" ("+time+"ms)</p>\r\n"+
|
||||
" <table class=\"grid\">\r\n"+
|
||||
" <tr>\r\n"+
|
||||
" <td><b>Filename</b></td><td><b>Errors</b></td><td><b>Warnings</b></td><td><b>Hints</b></td>\r\n"+
|
||||
" </tr>\r\n";
|
||||
}
|
||||
|
||||
private String now() {
|
||||
return DateFormat.getDateTimeInstance().format(new Date());
|
||||
}
|
||||
|
||||
private String genSummaryRow(int i, ValidationRecord rec) {
|
||||
String color = colorForLevel(IssueSeverity.ERROR, false);
|
||||
if (rec.getErr() == 0) {
|
||||
color = "#EFFFEF";
|
||||
}
|
||||
return
|
||||
" <tr style=\"background-color: "+color+"\">\r\n"+
|
||||
" <td><a href=\"#;"+i+"\"><b>"+Utilities.escapeXml(rec.getLocation())+"</b></a></td><td><b>"+rec.getErr()+"</b></td><td><b>"+rec.getWarn()+"</b></td><td><b>"+rec.getInfo()+"</b></td>\r\n"+
|
||||
" </tr>\r\n";
|
||||
}
|
||||
|
||||
private String genStart(int i, ValidationRecord f) {
|
||||
String xlink = Utilities.isAbsoluteUrl(f.getLocation()) ? f.getLocation() : "file:"+f.getLocation();
|
||||
return
|
||||
"<hr/>\r\n"+
|
||||
"<a name=\"l"+i+"\"> </a>\r\n"+
|
||||
"<h2><a href=\""+xlink+"\">"+Utilities.escapeXml(f.getLocation())+"</a></h2>\r\n";
|
||||
}
|
||||
|
||||
|
||||
private String genDetails(ValidationMessage vm, String id) {
|
||||
String path = vm.getLocation() == null ? "" : vm.getLocation()+ lineCol(vm);
|
||||
String level = vm.isSlicingHint() ? "Slicing Information" : vm.isSignpost() ? "Process Info" : vm.getLevel().toCode();
|
||||
String color = colorForLevel(vm.getLevel(), vm.isSignpost());
|
||||
String mid = vm.getMessageId();
|
||||
String msg = vm.getHtml();
|
||||
String msgdetails = vm.isSlicingHint() ? vm.getSliceHtml() : vm.getHtml();
|
||||
if (vm.isSlicingHint()) {
|
||||
return
|
||||
" <tr style=\"background-color: "+color+"\">\r\n"+
|
||||
" <td><b>"+path+"</b></td><td><b>"+level+"</b></td><td><b>"+msg+"</b> <span id=\"s"+id+"\" class=\"flip\" onclick=\"flip('"+id+"')\">Show Reasoning</span><div id=\""+id+"\" style=\"display: none\"><p> </p>"+msgdetails+"</div></td>\r\n"+
|
||||
" </tr>\r\n";
|
||||
|
||||
} else {
|
||||
return
|
||||
" <tr style=\"background-color: "+color+"\">\r\n"+
|
||||
" <td><b>"+path+"</b></td><td><b>"+level+"</b></td><td title=\""+mid+"\"><b>"+msg+"</b></td>\r\n"+
|
||||
" </tr>\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
private String lineCol(ValidationMessage vm) {
|
||||
return vm.getLine() > 0 ? " (l"+vm.getLine()+"/c"+vm.getCol()+")" : "";
|
||||
}
|
||||
|
||||
|
||||
private String colorForLevel(IssueSeverity level, boolean signpost) {
|
||||
if (signpost) {
|
||||
return "#d6feff";
|
||||
}
|
||||
switch (level) {
|
||||
case ERROR:
|
||||
return "#ffcccc";
|
||||
case FATAL:
|
||||
return "#ff9999";
|
||||
case WARNING:
|
||||
return "#ffebcc";
|
||||
default: // INFORMATION:
|
||||
return "#ffffe6";
|
||||
}
|
||||
}
|
||||
|
||||
private String halfColorForLevel(IssueSeverity level, boolean signpost) {
|
||||
if (signpost) {
|
||||
return "#e3feff";
|
||||
}
|
||||
switch (level) {
|
||||
case ERROR:
|
||||
return "#ffeeee";
|
||||
case FATAL:
|
||||
return "#ffcccc";
|
||||
case WARNING:
|
||||
return "#fff4ee";
|
||||
default: // INFORMATION:
|
||||
return "#fffff2";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -27,6 +27,7 @@ import org.hl7.fhir.utilities.Utilities;
|
|||
import org.hl7.fhir.utilities.VersionUtilities;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.validation.ValidationEngine;
|
||||
import org.hl7.fhir.validation.ValidationEngine.ValidationRecord;
|
||||
import org.hl7.fhir.validation.cli.model.*;
|
||||
import org.hl7.fhir.validation.cli.utils.EngineMode;
|
||||
import org.hl7.fhir.validation.cli.utils.VersionSourceInformation;
|
||||
|
@ -69,7 +70,9 @@ public class ValidationService {
|
|||
}
|
||||
|
||||
public static void validateSources(CliContext cliContext, ValidationEngine validator) throws Exception {
|
||||
Resource r = validator.validate(cliContext.getSources(), cliContext.getProfiles());
|
||||
long start = System.currentTimeMillis();
|
||||
List<ValidationRecord> records = new ArrayList<>();
|
||||
Resource r = validator.validate(cliContext.getSources(), cliContext.getProfiles(), records);
|
||||
int ec = 0;
|
||||
System.out.println("Done. "+validator.getContext().clock().report());
|
||||
System.out.println();
|
||||
|
@ -92,6 +95,11 @@ public class ValidationService {
|
|||
x.compose(s, r);
|
||||
s.close();
|
||||
}
|
||||
if (cliContext.getHtmlOutput() != null) {
|
||||
String html = new HTMLOutputGenerator(records).generate(System.currentTimeMillis()-start);
|
||||
TextFile.stringToFile(html, cliContext.getHtmlOutput());
|
||||
System.out.println("HTML Summary in "+cliContext.getHtmlOutput());
|
||||
}
|
||||
System.exit(ec > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
|
@ -288,10 +296,12 @@ public class ValidationService {
|
|||
System.out.println("Scanning for versions (no -version parameter):");
|
||||
VersionSourceInformation versions = ValidationService.scanForVersions(cliContext);
|
||||
for (String s : versions.getReport()) {
|
||||
System.out.println(" " + s);
|
||||
if (!s.equals("(nothing found)")) {
|
||||
System.out.println(" " + s);
|
||||
}
|
||||
}
|
||||
if (versions.isEmpty()) {
|
||||
System.out.println("-> Using Default version '" + VersionUtilities.CURRENT_VERSION + "'");
|
||||
System.out.println(" No Version Info found: Using Default version '" + VersionUtilities.CURRENT_VERSION + "'");
|
||||
return "current";
|
||||
}
|
||||
if (versions.size() == 1) {
|
||||
|
|
|
@ -12,6 +12,7 @@ public class Params {
|
|||
|
||||
public static final String VERSION = "-version";
|
||||
public static final String OUTPUT = "-output";
|
||||
public static final String HTML_OUTPUT = "-html-output";
|
||||
public static final String PROXY = "-proxy";
|
||||
public static final String PROFILE = "-profile";
|
||||
public static final String BUNDLE = "-bundle";
|
||||
|
@ -93,6 +94,11 @@ public class Params {
|
|||
throw new Error("Specified -output without indicating output file");
|
||||
else
|
||||
cliContext.setOutput(args[++i]);
|
||||
} else if (args[i].equals(HTML_OUTPUT)) {
|
||||
if (i + 1 == args.length)
|
||||
throw new Error("Specified -html-output without indicating output file");
|
||||
else
|
||||
cliContext.setHtmlOutput(args[++i]);
|
||||
} else if (args[i].equals(PROXY)) {
|
||||
i++; // ignore next parameter
|
||||
} else if (args[i].equals(PROFILE)) {
|
||||
|
|
Loading…
Reference in New Issue