View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.renderers;
5   
6   import java.io.IOException;
7   import java.io.Writer;
8   import java.util.Iterator;
9   import java.util.List;
10  
11  import net.sourceforge.pmd.PMD;
12  import net.sourceforge.pmd.Report;
13  import net.sourceforge.pmd.RuleViolation;
14  import net.sourceforge.pmd.lang.rule.properties.StringProperty;
15  import net.sourceforge.pmd.util.StringUtil;
16  
17  /**
18   * Renderer to basic HTML format.
19   * 
20   * FIXME: this class should just work with the XMLRenderer and then apply 
21   * 		an XSLT transformation + stylesheet. No need to hard-code HTML
22   * 		markup here.
23   */
24  public class HTMLRenderer extends AbstractIncrementingRenderer {
25  
26      public static final String NAME = "html";
27  
28      public static final StringProperty LINE_PREFIX = new StringProperty("linePrefix", "Prefix for line number anchor in the source file.", null, 1);
29      public static final StringProperty LINK_PREFIX = new StringProperty("linkPrefix", "Path to HTML source.", null, 0);
30  
31      private String linkPrefix;
32      private String linePrefix;
33  
34      private int violationCount = 1;
35      boolean colorize = true;
36  
37      public HTMLRenderer() {
38  		super(NAME, "HTML format");
39  	
40  		definePropertyDescriptor(LINK_PREFIX);
41  		definePropertyDescriptor(LINE_PREFIX);
42      }
43  
44      public String defaultFileExtension() { return "html"; }
45      
46      /**
47       * Write the body of the main body of the HTML content.
48       *
49       * @param writer
50       * @param report
51       * @throws IOException
52       */
53      public void renderBody(Writer writer, Report report) throws IOException {
54  		linkPrefix = getProperty(LINK_PREFIX);
55  		linePrefix = getProperty(LINE_PREFIX);
56  
57  		writer.write("<center><h3>PMD report</h3></center>");
58  		writer.write("<center><h3>Problems found</h3></center>");
59  		writer.write("<table align=\"center\" cellspacing=\"0\" cellpadding=\"3\"><tr>" + PMD.EOL
60  			+ "<th>#</th><th>File</th><th>Line</th><th>Problem</th></tr>" + PMD.EOL);
61  		setWriter(writer);
62  		renderFileReport(report);
63  		writer.write("</table>");
64  		glomProcessingErrors(writer, errors);
65  		if (showSuppressedViolations) {
66  		    glomSuppressions(writer, suppressed);
67  		}
68      }
69  
70      /**
71       * {@inheritDoc}
72       */
73      @Override
74      public void start() throws IOException {
75  		Writer writer = getWriter();
76  		writer.write("<html><head><title>PMD</title></head><body>" + PMD.EOL);
77  		writer.write("<center><h3>PMD report</h3></center>");
78  		writer.write("<center><h3>Problems found</h3></center>");
79  		writer.write("<table align=\"center\" cellspacing=\"0\" cellpadding=\"3\"><tr>" + PMD.EOL
80  			+ "<th>#</th><th>File</th><th>Line</th><th>Problem</th></tr>" + PMD.EOL);
81      }
82  
83      /**
84       * {@inheritDoc}
85       */
86      @Override
87      public void renderFileViolations(Iterator<RuleViolation> violations) throws IOException {
88  		Writer writer = getWriter();
89  		glomRuleViolations(writer, violations);
90      }
91  
92      /**
93       * {@inheritDoc}
94       */
95      @Override
96      public void end() throws IOException {
97  		Writer writer = getWriter();
98  		writer.write("</table>");
99  		glomProcessingErrors(writer, errors);
100 		if (showSuppressedViolations) {
101 		    glomSuppressions(writer, suppressed);
102 		}
103 		writer.write("</body></html>" + PMD.EOL);
104     }
105 
106     private void glomRuleViolations(Writer writer, Iterator<RuleViolation> violations) throws IOException {
107 	    
108     	StringBuilder buf = new StringBuilder(500);
109 	    
110 		while (violations.hasNext()) {
111 		    RuleViolation rv = violations.next();
112 		    buf.setLength(0);
113 		    buf.append("<tr");
114 		    if (colorize) {
115 		    	buf.append(" bgcolor=\"lightgrey\"");
116 		    }
117 		    colorize = !colorize;
118 		    buf.append("> " + PMD.EOL);
119 		    buf.append("<td align=\"center\">" + violationCount + "</td>" + PMD.EOL);
120 		    buf.append("<td width=\"*%\">"
121 			    + maybeWrap(rv.getFilename(), linePrefix == null ? "" : linePrefix
122 				    + Integer.toString(rv.getBeginLine())) + "</td>" + PMD.EOL);
123 		    buf.append("<td align=\"center\" width=\"5%\">" + Integer.toString(rv.getBeginLine()) + "</td>" + PMD.EOL);
124 	
125 		    String d = StringUtil.htmlEncode(rv.getDescription());
126 	
127 		    String infoUrl = rv.getRule().getExternalInfoUrl();
128 		    if (StringUtil.isNotEmpty(infoUrl)) {
129 				d = "<a href=\"" + infoUrl + "\">" + d + "</a>";
130 			    }
131 		    buf.append("<td width=\"*\">" + d + "</td>" + PMD.EOL);
132 		    buf.append("</tr>" + PMD.EOL);
133 		    writer.write(buf.toString());
134 		    violationCount++;
135 			}
136     }
137 
138     private void glomProcessingErrors(Writer writer, List<Report.ProcessingError> errors) throws IOException {
139 	
140     	if (errors.isEmpty()) {
141     	    return;
142     	}
143     	
144 	    writer.write("<hr/>");
145 	    writer.write("<center><h3>Processing errors</h3></center>");
146 	    writer.write("<table align=\"center\" cellspacing=\"0\" cellpadding=\"3\"><tr>" + PMD.EOL
147 		    + "<th>File</th><th>Problem</th></tr>" + PMD.EOL);
148 
149 	    StringBuffer buf = new StringBuffer(500);
150 	    boolean colorize = true;
151 	    for (Report.ProcessingError pe : errors) {
152 			buf.setLength(0);
153 			buf.append("<tr");
154 			if (colorize) {
155 			    buf.append(" bgcolor=\"lightgrey\"");
156 			}
157 			colorize = !colorize;
158 			buf.append("> " + PMD.EOL);
159 			buf.append("<td>" + pe.getFile() + "</td>" + PMD.EOL);
160 			buf.append("<td>" + pe.getMsg() + "</td>" + PMD.EOL);
161 			buf.append("</tr>" + PMD.EOL);
162 			writer.write(buf.toString());	
163 		    }
164 	    writer.write("</table>");
165     }
166 
167     private void glomSuppressions(Writer writer, List<Report.SuppressedViolation> suppressed) throws IOException {
168 		if (suppressed.isEmpty()) {
169 		    return;
170 		}
171 
172 		writer.write("<hr/>");
173 		writer.write("<center><h3>Suppressed warnings</h3></center>");
174 		writer.write("<table align=\"center\" cellspacing=\"0\" cellpadding=\"3\"><tr>" + PMD.EOL
175 			    + "<th>File</th><th>Line</th><th>Rule</th><th>NOPMD or Annotation</th><th>Reason</th></tr>"
176 			    + PMD.EOL);
177 	
178 		StringBuilder buf = new StringBuilder(500);
179 		boolean colorize = true;
180 		for (Report.SuppressedViolation sv : suppressed) {
181 			buf.setLength(0);
182 			buf.append("<tr");
183 			if (colorize) {
184 			    buf.append(" bgcolor=\"lightgrey\"");
185 			}
186 			colorize = !colorize;
187 			buf.append("> " + PMD.EOL);
188 			buf.append("<td align=\"left\">" + sv.getRuleViolation().getFilename() + "</td>" + PMD.EOL);
189 			buf.append("<td align=\"center\">" + sv.getRuleViolation().getBeginLine() + "</td>" + PMD.EOL);
190 			buf.append("<td align=\"center\">" + sv.getRuleViolation().getRule().getName() + "</td>" + PMD.EOL);
191 			buf.append("<td align=\"center\">" + (sv.suppressedByNOPMD() ? "NOPMD" : "Annotation") + "</td>"
192 					+ PMD.EOL);
193 			buf.append("<td align=\"center\">" + (sv.getUserMessage() == null ? "" : sv.getUserMessage()) + "</td>"
194 					+ PMD.EOL);
195 			buf.append("</tr>" + PMD.EOL);
196 			writer.write(buf.toString());
197 			}
198 		writer.write("</table>");
199     }
200 
201     private String maybeWrap(String filename, String line) {
202 		if (StringUtil.isEmpty(linkPrefix)) {
203 		    return filename;
204 		}
205 		String newFileName = filename;
206 		int index = filename.lastIndexOf('.');
207 		if (index >= 0) {
208 		    newFileName = filename.substring(0, index).replace('\\', '/');
209 		}
210 		return "<a href=\"" + linkPrefix + newFileName + ".html#" + line + "\">" + newFileName + "</a>";
211     }
212 }