001package org.hl7.fhir.validation.cli.services; 002 003import java.text.DateFormat; 004import java.util.Date; 005import java.util.List; 006 007import org.hl7.fhir.utilities.Utilities; 008import org.hl7.fhir.utilities.validation.ValidationMessage; 009import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 010import org.hl7.fhir.validation.ValidationRecord; 011import org.hl7.fhir.validation.cli.utils.VersionUtil; 012 013public class HTMLOutputGenerator { 014 015 private List<ValidationRecord> records; 016 017 public HTMLOutputGenerator(List<ValidationRecord> records) { 018 super(); 019 this.records = records; 020 } 021 022 public String generate(long time) { 023 024 StringBuilder b = new StringBuilder(); 025 b.append(genHeader(time)); 026 int i = 0; 027 for (ValidationRecord f : records) { 028 i++; 029 b.append(genSummaryRow(i, f)); 030 } 031 b.append("</table>\r\n"); 032 033 i = 0; 034 int id = 0; 035 for (ValidationRecord f : records) { 036 i++; 037 b.append(genStart(i, f)); 038 if (f.getMessages().size() > 0) { 039 b.append( 040 " <table class=\"grid\">\r\n"+ 041 " <tr>\r\n"+ 042 " <td><b>Path</b></td><td><b>Severity</b></td><td><b>Message</b></td>\r\n"+ 043 " </tr>\r\n"); 044 for (ValidationMessage vm : f.getMessages()) { 045 id++; 046 b.append(genDetails(vm, "m"+id)); 047 } 048 b.append("</table>\r\n"); 049 } else { 050 b.append("<p>No Issues detected</p>\r\n"); 051 } 052 } 053 return b.toString(); 054 } 055 056 private String genHeader(long time) { 057 int err = 0; 058 int warn = 0; 059 int info = 0; 060 for (ValidationRecord f : records) { 061 err = err + f.getErr(); 062 warn = warn + f.getWarn(); 063 info = info + f.getInfo(); 064 } 065 066 return 067 "<!DOCTYPE HTML>\r\n"+ 068 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\r\n"+ 069 "<head>\r\n"+ 070 " <title>Validation Results</title>\r\n"+ 071 " <link href=\"http://hl7.org/fhir/fhir.css\" rel=\"stylesheet\"/>\r\n"+ 072 " <style>\r\n"+ 073 " span.flip { background-color: #4CAF50; color: white; border: solid 1px #a6d8a8; padding: 2px }\r\n"+ 074 " </style>\r\n"+ 075 " <script>\r\n"+ 076 " function flip(id) {\r\n"+ 077 " var span = document.getElementById('s'+id);\r\n"+ 078 " var div = document.getElementById(id);\r\n"+ 079 " if (document.getElementById('s'+id).innerHTML == 'Show Reasoning') {\r\n"+ 080 " div.style.display = 'block';\r\n"+ 081 " span.innerHTML = 'Hide Reasoning';\r\n"+ 082 " } else {\r\n"+ 083 " div.style.display = 'none';\r\n"+ 084 " span.innerHTML = 'Show Reasoning';\r\n"+ 085 " }\r\n"+ 086 " }\r\n"+ 087 " </script>\r\n"+ 088 "</head>\r\n"+ 089 "<body style=\"margin: 20px; background-color: #ffffff\">\r\n"+ 090 " <h1>Validation Results</h1>\r\n"+ 091 " <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"+ 092 " <table class=\"grid\">\r\n"+ 093 " <tr>\r\n"+ 094 " <td><b>Filename</b></td><td><b>Errors</b></td><td><b>Warnings</b></td><td><b>Hints</b></td>\r\n"+ 095 " </tr>\r\n"; 096 } 097 098 private String now() { 099 return DateFormat.getDateTimeInstance().format(new Date()); 100 } 101 102 private String genSummaryRow(int i, ValidationRecord rec) { 103 String color = colorForLevel(IssueSeverity.ERROR, false); 104 if (rec.getErr() == 0) { 105 color = "#EFFFEF"; 106 } 107 return 108 " <tr style=\"background-color: "+color+"\">\r\n"+ 109 " <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"+ 110 " </tr>\r\n"; 111 } 112 113 private String genStart(int i, ValidationRecord f) { 114 String xlink = Utilities.isAbsoluteUrl(f.getLocation()) ? f.getLocation() : "file:"+f.getLocation(); 115 return 116 "<hr/>\r\n"+ 117 "<a name=\"l"+i+"\"> </a>\r\n"+ 118 "<h2><a href=\""+xlink+"\">"+Utilities.escapeXml(f.getLocation())+"</a></h2>\r\n"; 119 } 120 121 122 private String genDetails(ValidationMessage vm, String id) { 123 String path = vm.getLocation() == null ? "" : vm.getLocation()+ lineCol(vm); 124 String level = vm.isSlicingHint() ? "Slicing Information" : vm.isSignpost() ? "Process Info" : vm.getLevel().toCode(); 125 String color = colorForLevel(vm.getLevel(), vm.isSignpost()); 126 String mid = vm.getMessageId(); 127 String msg = vm.getHtml(); 128 String msgdetails = vm.isSlicingHint() ? vm.getSliceHtml() : vm.getHtml(); 129 if (vm.isSlicingHint()) { 130 return 131 " <tr style=\"background-color: "+color+"\">\r\n"+ 132 " <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"+ 133 " </tr>\r\n"; 134 135 } else { 136 return 137 " <tr style=\"background-color: "+color+"\">\r\n"+ 138 " <td><b>"+path+"</b></td><td><b>"+level+"</b></td><td title=\""+mid+"\"><b>"+msg+"</b></td>\r\n"+ 139 " </tr>\r\n"; 140 } 141 } 142 143 private String lineCol(ValidationMessage vm) { 144 return vm.getLine() > 0 ? " (l"+vm.getLine()+"/c"+vm.getCol()+")" : ""; 145 } 146 147 148 private String colorForLevel(IssueSeverity level, boolean signpost) { 149 if (signpost) { 150 return "#d6feff"; 151 } 152 switch (level) { 153 case ERROR: 154 return "#ffcccc"; 155 case FATAL: 156 return "#ff9999"; 157 case WARNING: 158 return "#ffebcc"; 159 default: // INFORMATION: 160 return "#ffffe6"; 161 } 162 } 163 164 private String halfColorForLevel(IssueSeverity level, boolean signpost) { 165 if (signpost) { 166 return "#e3feff"; 167 } 168 switch (level) { 169 case ERROR: 170 return "#ffeeee"; 171 case FATAL: 172 return "#ffcccc"; 173 case WARNING: 174 return "#fff4ee"; 175 default: // INFORMATION: 176 return "#fffff2"; 177 } 178 } 179 180 181}