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()) return;
141     	
142 	    writer.write("<hr/>");
143 	    writer.write("<center><h3>Processing errors</h3></center>");
144 	    writer.write("<table align=\"center\" cellspacing=\"0\" cellpadding=\"3\"><tr>" + PMD.EOL
145 		    + "<th>File</th><th>Problem</th></tr>" + PMD.EOL);
146 
147 	    StringBuffer buf = new StringBuffer(500);
148 	    boolean colorize = true;
149 	    for (Report.ProcessingError pe : errors) {
150 			buf.setLength(0);
151 			buf.append("<tr");
152 			if (colorize) {
153 			    buf.append(" bgcolor=\"lightgrey\"");
154 			}
155 			colorize = !colorize;
156 			buf.append("> " + PMD.EOL);
157 			buf.append("<td>" + pe.getFile() + "</td>" + PMD.EOL);
158 			buf.append("<td>" + pe.getMsg() + "</td>" + PMD.EOL);
159 			buf.append("</tr>" + PMD.EOL);
160 			writer.write(buf.toString());	
161 		    }
162 	    writer.write("</table>");
163     }
164 
165     private void glomSuppressions(Writer writer, List<Report.SuppressedViolation> suppressed) throws IOException {
166 		if (suppressed.isEmpty())  return;
167 
168 		writer.write("<hr/>");
169 		writer.write("<center><h3>Suppressed warnings</h3></center>");
170 		writer.write("<table align=\"center\" cellspacing=\"0\" cellpadding=\"3\"><tr>" + PMD.EOL
171 			    + "<th>File</th><th>Line</th><th>Rule</th><th>NOPMD or Annotation</th><th>Reason</th></tr>"
172 			    + PMD.EOL);
173 	
174 		StringBuilder buf = new StringBuilder(500);
175 		boolean colorize = true;
176 		for (Report.SuppressedViolation sv : suppressed) {
177 			buf.setLength(0);
178 			buf.append("<tr");
179 			if (colorize) {
180 			    buf.append(" bgcolor=\"lightgrey\"");
181 			}
182 			colorize = !colorize;
183 			buf.append("> " + PMD.EOL);
184 			buf.append("<td align=\"left\">" + sv.getRuleViolation().getFilename() + "</td>" + PMD.EOL);
185 			buf.append("<td align=\"center\">" + sv.getRuleViolation().getBeginLine() + "</td>" + PMD.EOL);
186 			buf.append("<td align=\"center\">" + sv.getRuleViolation().getRule().getName() + "</td>" + PMD.EOL);
187 			buf.append("<td align=\"center\">" + (sv.suppressedByNOPMD() ? "NOPMD" : "Annotation") + "</td>"
188 					+ PMD.EOL);
189 			buf.append("<td align=\"center\">" + (sv.getUserMessage() == null ? "" : sv.getUserMessage()) + "</td>"
190 					+ PMD.EOL);
191 			buf.append("</tr>" + PMD.EOL);
192 			writer.write(buf.toString());
193 			}
194 		writer.write("</table>");
195     }
196 
197     private String maybeWrap(String filename, String line) {
198 		if (StringUtil.isEmpty(linkPrefix)) {
199 		    return filename;
200 		}
201 		String newFileName = filename;
202 		int index = filename.lastIndexOf('.');
203 		if (index >= 0) {
204 		    newFileName = filename.substring(0, index).replace('\\', '/');
205 		}
206 		return "<a href=\"" + linkPrefix + newFileName + ".html#" + line + "\">" + newFileName + "</a>";
207     }
208 }