Rework Renderer to support standardized option passing, as well as a more rigorous definition of those options. This approach allows for a RendererFactory to create Renderer instances, as well as a means to programmatically output details on all available Renderers. Both the PMD command line and Ant tasks now take advantage of the RendererFactory, and have been enhanced to support generic report option passing (ie. -parameter and <param> respectively). Removed obsoleted Renderer specific options from command line and Ant task (e.g. linkPrefix, etc). Finally, some Renderers were given more appropriate names. We should consider dropping some of these goofy Renderers out of the tree (there's about 4-5 Renderers for HTML!).

Added missing -showsuppresse option for PMD command line.  The default is the same as the Ant task (false), which is a change from prior PMD behavior, but I think this is justified for consistency's sake and the fact that most people likely use the Ant task anyway.

While working on CommandLineOptions, the argument processed code was cleaned up a bit so that missing parameters for options will now give better error messages.  This isn't to say there's not work that could still be done in this area, as the code is still rather too loosey-goosey for my tastes.


git-svn-id: https://pmd.svn.sourceforge.net/svnroot/pmd/trunk@6561 51baf565-9d33-0410-a72c-fc3788e3496d
This commit is contained in:
Ryan Gustafson committed 2008-10-06 03:06:55 +00:00
1 parent 11bbd666ae
commit d1cb839b37
37 files changed
+1791 -1367

No files matched your search

+38 -6
View File
@@ -302,12 +302,12 @@ The following is relatively complete list of the major changes (this may not be
Moved - PMD.createClasspathClassLoader(String) to Configuration.createClasspathClassLoader(String)
API Change - Changes to Node interface
Renamed - findChildrenOfType() as findDescendantsOfType()
Renamed - getFirstChildOfType() as getFirstDescendantOfType()
Renamed - containsChildOfType() as hasDescendantOfType()
Renamed - getAsXml() as getAsDocument()
Added - findChildrenOfType(), non recursive version
Added - getFirstChildOfType(), non recursive version
Renamed - Node.findChildrenOfType(Class) as Node.findDescendantsOfType(Class)
Renamed - Node.getFirstChildOfType(Class) as Node.getFirstDescendantOfType(Class)
Renamed - Node.containsChildOfType(Class) as Node.hasDescendantOfType(Class)
Renamed - Node.getAsXml() as Node.getAsDocument()
Added - Node.findChildrenOfType(Class), non recursive version
Added - Node.getFirstChildOfType(Class), non recursive version
API Change - Remove deprecated APIs
Removed - AccessNode.setXXX() methods, use AccessNode.setXXX(boolean) instead.
@@ -315,6 +315,38 @@ The following is relatively complete list of the major changes (this may not be
Removed - RuleSetFactory.createRuleSet(String,ClassLoaer), use RuleSetFactory.createRuleSets(String,ClassLoader) instead.
Removed - net.sourceforge.pmd.cpd.FileFinder use net.sourceforge.pmd.util.FileFinder instead.
API Change - Changes to Renderer class, and Renderer implementations
Added - Renderer.getName()
Added - Renderer.setName(String)
Added - Renderer.getDescription()
Added - Renderer.setDescription(String)
Added - Renderer.getPropertyDefinitions()
Added - Renderer.isShowSuppressedViolations()
Renamed - Renderer.showSuppressedViolations(boolean) to Renderer.setShowSuppressedViolations(boolean)
Renamed - PapariTextRenderer to TextColorRenderer
PMD command line changes:
Removed -lineprefix use -property linePrefix {value} instead
Removed -linkprefix use -property linkPrefix {value} instead
Removed -xslt use -property xsltFilename {value} instead
Removed -nojsp now obsolete
Removed -targetjdk use -lang {name} {version} instead
Added -lang {name} {version} to set language version to use
Added -property {name} {value} as generic way to pass properties to Renderers
Added -showsuppressed as a means to show suppressed rule violations (consistent with Ant task behavior)
Renamed 'nicehtml' report to 'xslt'
Renamed 'papari' report to 'textcolor'
Renamed -excludemarker option to -suppressmarker
Ant changes:
Removed - <formatter> 'linkPrefix' attribute, use <param name="linkPrefix"> instead
Removed - <formatter> 'linePrefix' attribute, use <param name="linePrefix"> instead
Added - <param name="name" value="value"/> as generic way to pass properties to Renderers on <formatter>
Renamed - <pmd> 'excludeMarker' attribute to 'suppressMarker'
New features:
New Language 'xml' added, for writing XPathRules against XML documents
@@ -4,27 +4,34 @@
package test.net.sourceforge.pmd;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.InputStreamReader;
import junit.framework.JUnit4TestAdapter;
import net.sourceforge.pmd.CommandLineOptions;
import net.sourceforge.pmd.renderers.CSVRenderer;
import net.sourceforge.pmd.renderers.EmacsRenderer;
import net.sourceforge.pmd.renderers.HTMLRenderer;
import net.sourceforge.pmd.renderers.IDEAJRenderer;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.renderers.SummaryHTMLRenderer;
import net.sourceforge.pmd.renderers.TextColorRenderer;
import net.sourceforge.pmd.renderers.TextPadRenderer;
import net.sourceforge.pmd.renderers.TextRenderer;
import net.sourceforge.pmd.renderers.VBHTMLRenderer;
import net.sourceforge.pmd.renderers.XMLRenderer;
import net.sourceforge.pmd.renderers.XSLTRenderer;
import net.sourceforge.pmd.renderers.YAHTMLRenderer;
import org.junit.Test;
import java.io.InputStreamReader;
import junit.framework.JUnit4TestAdapter;
public class CommandLineOptionsTest {
@Test
public void testTargetJDKVersion() {
public void testLang() {
// Testing command line default behavior (no -lang option, means Java 1.5)
CommandLineOptions opt = new CommandLineOptions(new String[]{"file", "format", "basic"});
assertEquals("LanguageVersion[Java 1.5]", opt.getVersion().toString());
@@ -129,19 +136,44 @@ public class CommandLineOptionsTest {
@Test
public void testRenderer() {
CommandLineOptions opt = new CommandLineOptions(new String[]{"file", "xml", "basic"});
assertTrue(opt.createRenderer() instanceof XMLRenderer);
Renderer renderer = opt.createRenderer();
assertTrue(renderer instanceof XMLRenderer);
opt = new CommandLineOptions(new String[]{"file", "html", "basic"});
assertTrue(opt.createRenderer() instanceof HTMLRenderer);
renderer = opt.createRenderer();
assertTrue(renderer instanceof HTMLRenderer);
opt = new CommandLineOptions(new String[]{"file", "text", "basic"});
assertTrue(opt.createRenderer() instanceof TextRenderer);
renderer = opt.createRenderer();
assertTrue(renderer instanceof TextRenderer);
opt = new CommandLineOptions(new String[]{"file", "emacs", "basic"});
assertTrue(opt.createRenderer() instanceof EmacsRenderer);
renderer = opt.createRenderer();
assertTrue(renderer instanceof EmacsRenderer);
opt = new CommandLineOptions(new String[]{"file", "csv", "basic"});
assertTrue(opt.createRenderer() instanceof CSVRenderer);
renderer = opt.createRenderer();
assertTrue(renderer instanceof CSVRenderer);
opt = new CommandLineOptions(new String[]{"file", "vbhtml", "basic"});
assertTrue(opt.createRenderer() instanceof VBHTMLRenderer);
renderer = opt.createRenderer();
assertTrue(renderer instanceof VBHTMLRenderer);
opt = new CommandLineOptions(new String[]{"file", "yahtml", "basic"});
renderer = opt.createRenderer();
assertTrue(renderer instanceof YAHTMLRenderer);
opt = new CommandLineOptions(new String[]{"file", "ideaj", "basic"});
assertTrue(opt.createRenderer() instanceof IDEAJRenderer);
renderer = opt.createRenderer();
assertTrue(renderer instanceof IDEAJRenderer);
opt = new CommandLineOptions(new String[]{"file", "summaryhtml", "basic"});
renderer = opt.createRenderer();
assertTrue(renderer instanceof SummaryHTMLRenderer);
opt = new CommandLineOptions(new String[]{"file", "textcolor", "basic"});
renderer = opt.createRenderer();
assertTrue(renderer instanceof TextColorRenderer);
opt = new CommandLineOptions(new String[]{"file", "textpad", "basic"});
renderer = opt.createRenderer();
assertTrue(renderer instanceof TextPadRenderer);
opt = new CommandLineOptions(new String[]{"file", "xml", "basic"});
renderer = opt.createRenderer();
assertTrue(renderer instanceof XMLRenderer);
opt = new CommandLineOptions(new String[]{"file", "xslt", "basic"});
renderer = opt.createRenderer();
assertTrue(renderer instanceof XSLTRenderer);
}
@Test(expected = IllegalArgumentException.class)
@@ -177,6 +209,14 @@ public class CommandLineOptionsTest {
new CommandLineOptions(new String[] { "file", "format", "basic", "-auxclasspath" });
}
@Test
public void testShowSuppressed() {
CommandLineOptions opt = new CommandLineOptions(new String[]{"file", "format", "basic"});
assertFalse(opt.isShowSuppressedViolations());
opt = new CommandLineOptions(new String[]{"-showsuppressed", "file", "format", "basic"});
assertTrue(opt.isShowSuppressedViolations());
}
public static junit.framework.Test suite() {
return new JUnit4TestAdapter(CommandLineOptionsTest.class);
}
@@ -8,6 +8,7 @@ import static org.junit.Assert.assertTrue;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import junit.framework.JUnit4TestAdapter;
import net.sourceforge.pmd.PMD;
@@ -24,7 +25,6 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.lang.java.rule.JavaRuleViolation;
import net.sourceforge.pmd.lang.java.symboltable.SourceFileScope;
import net.sourceforge.pmd.lang.rule.MockRule;
import net.sourceforge.pmd.lang.rule.properties.StringProperty;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.renderers.XMLRenderer;
import net.sourceforge.pmd.stat.Metric;
@@ -161,7 +161,7 @@ public class ReportTest extends RuleTst implements ReportListener {
ctx.setSourceCodeFilename("bar");
JavaNode s1 = getNode(10, 5, ctx.getSourceCodeFilename());
r.addRuleViolation(new JavaRuleViolation(new MockRule("name", "desc", "msg", "rulesetname"), ctx, s1));
Renderer rend = new XMLRenderer();
Renderer rend = new XMLRenderer(new Properties());
String result = rend.render(r);
assertTrue("sort order wrong", result.indexOf("bar") < result.indexOf("foo"));
}
@@ -176,7 +176,7 @@ public class ReportTest extends RuleTst implements ReportListener {
ctx.setSourceCodeFilename("foo2");
JavaNode s1 = getNode(20, 5, ctx.getSourceCodeFilename());
r.addRuleViolation(new JavaRuleViolation(new MockRule("rule1", "rule1", "msg", "rulesetname"), ctx, s1));
Renderer rend = new XMLRenderer();
Renderer rend = new XMLRenderer(new Properties());
String result = rend.render(r);
assertTrue("sort order wrong", result.indexOf("rule2") < result.indexOf("rule1"));
}
@@ -43,6 +43,15 @@ public class PMDTaskTest extends BuildFileTest {
assertOutputContaining("Too many fields");
}
@Test
public void testFormatterWithProperties() {
executeTarget("testFormatterWithProperties");
assertOutputContaining("Position literals first in String comparisons");
assertOutputContaining("Too many fields");
assertOutputContaining("link_prefix");
assertOutputContaining("line_prefix");
}
@Test
public void testAbstractNames() {
executeTarget("testAbstractNames");
@@ -40,6 +40,20 @@
</fileset>
</pmd>
</target>
<target name="testFormatterWithProperties">
<pmd>
<ruleset>${pmd.home}/rulesets/codesize.xml</ruleset>
<ruleset>${pmd.home}/rulesets/design.xml</ruleset>
<formatter type="summaryhtml" toConsole="true">
<param name="linkPrefix" value="link_prefix"/>
<param name="linePrefix" value="line_prefix"/>
</formatter>
<fileset dir="${pmd.home}/src/net/sourceforge/pmd/ant">
<include name="*.java"/>
</fileset>
</pmd>
</target>
<target name="testAbstractNames">
<pmd rulesetfiles="codesize,design">
@@ -9,7 +9,7 @@ import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.renderers.AbstractRenderer;
import net.sourceforge.pmd.renderers.Renderer;
import org.junit.Test;
@@ -39,7 +39,7 @@ public abstract class AbstractRendererTst extends RuleTst {
}
}
public abstract AbstractRenderer getRenderer();
public abstract Renderer getRenderer();
public abstract String getExpected();
@@ -3,15 +3,17 @@
*/
package test.net.sourceforge.pmd.renderers;
import java.util.Properties;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.renderers.AbstractRenderer;
import net.sourceforge.pmd.renderers.CSVRenderer;
import net.sourceforge.pmd.renderers.Renderer;
public class CSVRendererTest extends AbstractRendererTst {
public AbstractRenderer getRenderer() {
return new CSVRenderer();
public Renderer getRenderer() {
return new CSVRenderer(new Properties());
}
public String getExpected() {
@@ -1,13 +1,15 @@
package test.net.sourceforge.pmd.renderers;
import java.util.Properties;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.renderers.AbstractRenderer;
import net.sourceforge.pmd.renderers.EmacsRenderer;
import net.sourceforge.pmd.renderers.Renderer;
public class EmacsRendererTest extends AbstractRendererTst {
public AbstractRenderer getRenderer() {
return new EmacsRenderer();
public Renderer getRenderer() {
return new EmacsRenderer(new Properties());
}
public String getExpected() {
@@ -1,14 +1,16 @@
package test.net.sourceforge.pmd.renderers;
import java.util.Properties;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.renderers.AbstractRenderer;
import net.sourceforge.pmd.renderers.HTMLRenderer;
import net.sourceforge.pmd.renderers.Renderer;
public class HTMLRendererTest extends AbstractRendererTst {
public AbstractRenderer getRenderer() {
return new HTMLRenderer();
public Renderer getRenderer() {
return new HTMLRenderer(new Properties());
}
public String getExpected() {
@@ -1,13 +1,19 @@
package test.net.sourceforge.pmd.renderers;
import java.util.Properties;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.renderers.AbstractRenderer;
import net.sourceforge.pmd.renderers.IDEAJRenderer;
import net.sourceforge.pmd.renderers.Renderer;
public class IDEAJRendererTest extends AbstractRendererTst {
public AbstractRenderer getRenderer() {
return new IDEAJRenderer(new String[]{"","","","","Foo <init>","Foo.java"});
public Renderer getRenderer() {
Properties properties = new Properties();
properties.put(IDEAJRenderer.SOURCE_PATH, "");
properties.put(IDEAJRenderer.CLASS_AND_METHOD_NAME, "Foo <init>");
properties.put(IDEAJRenderer.FILE_NAME, "Foo.java");
return new IDEAJRenderer(properties);
}
public String getExpected() {
@@ -1,14 +1,15 @@
package test.net.sourceforge.pmd.renderers;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.renderers.AbstractRenderer;
import net.sourceforge.pmd.renderers.PapariTextRenderer;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.Reader;
import java.io.StringReader;
import java.util.Properties;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.renderers.TextColorRenderer;
public class PapariTextRendererTest extends AbstractRendererTst {
@@ -17,8 +18,8 @@ public class PapariTextRendererTest extends AbstractRendererTst {
naString = naString.substring(naString.lastIndexOf(File.separator) + 1);
}
public AbstractRenderer getRenderer() {
return new PapariTextRenderer(){
public Renderer getRenderer() {
return new TextColorRenderer(new Properties()){
protected Reader getReader(String sourceFile) throws FileNotFoundException {
return new StringReader("public class Foo {}");
}
@@ -1,14 +1,19 @@
package test.net.sourceforge.pmd.renderers;
import java.util.Properties;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.renderers.AbstractRenderer;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.renderers.SummaryHTMLRenderer;
public class SummaryHTMLRendererTest extends AbstractRendererTst {
public AbstractRenderer getRenderer() {
return new SummaryHTMLRenderer();
public Renderer getRenderer() {
Properties properties = new Properties();
properties.put(SummaryHTMLRenderer.LINK_PREFIX, "link_prefix");
properties.put(SummaryHTMLRenderer.LINE_PREFIX, "line_prefix");
return new SummaryHTMLRenderer(properties);
}
public String getExpected() {
@@ -18,7 +23,7 @@ public class SummaryHTMLRendererTest extends AbstractRendererTst {
"<th>#</th><th>File</th><th>Line</th><th>Problem</th></tr>" + PMD.EOL +
"<tr bgcolor=\"lightgrey\"> " + PMD.EOL +
"<td align=\"center\">1</td>" + PMD.EOL +
"<td width=\"*%\">n/a</td>" + PMD.EOL +
"<td width=\"*%\"><a href=\"link_prefixn/a.html#line_prefix1\">n/a</a></td>" + PMD.EOL +
"<td align=\"center\" width=\"5%\">1</td>" + PMD.EOL +
"<td width=\"*\">msg</td>" + PMD.EOL +
"</tr>" + PMD.EOL +
@@ -41,13 +46,13 @@ public class SummaryHTMLRendererTest extends AbstractRendererTst {
"<th>#</th><th>File</th><th>Line</th><th>Problem</th></tr>" + PMD.EOL +
"<tr bgcolor=\"lightgrey\"> " + PMD.EOL +
"<td align=\"center\">1</td>" + PMD.EOL +
"<td width=\"*%\">n/a</td>" + PMD.EOL +
"<td width=\"*%\"><a href=\"link_prefixn/a.html#line_prefix1\">n/a</a></td>" + PMD.EOL +
"<td align=\"center\" width=\"5%\">1</td>" + PMD.EOL +
"<td width=\"*\">msg</td>" + PMD.EOL +
"</tr>" + PMD.EOL +
"<tr> " + PMD.EOL +
"<td align=\"center\">2</td>" + PMD.EOL +
"<td width=\"*%\">n/a</td>" + PMD.EOL +
"<td width=\"*%\"><a href=\"link_prefixn/a.html#line_prefix1\">n/a</a></td>" + PMD.EOL +
"<td align=\"center\" width=\"5%\">1</td>" + PMD.EOL +
"<td width=\"*\">msg</td>" + PMD.EOL +
"</tr>" + PMD.EOL +
@@ -3,15 +3,17 @@
*/
package test.net.sourceforge.pmd.renderers;
import java.util.Properties;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.renderers.AbstractRenderer;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.renderers.TextPadRenderer;
public class TextPadRendererTest extends AbstractRendererTst{
public AbstractRenderer getRenderer() {
return new TextPadRenderer();
public Renderer getRenderer() {
return new TextPadRenderer(new Properties());
}
public String getExpected() {
@@ -1,14 +1,16 @@
package test.net.sourceforge.pmd.renderers;
import java.util.Properties;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.renderers.AbstractRenderer;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.renderers.TextRenderer;
public class TextRendererTest extends AbstractRendererTst {
public AbstractRenderer getRenderer() {
return new TextRenderer();
public Renderer getRenderer() {
return new TextRenderer(new Properties());
}
public String getExpected() {
@@ -1,14 +1,16 @@
package test.net.sourceforge.pmd.renderers;
import java.util.Properties;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.renderers.AbstractRenderer;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.renderers.VBHTMLRenderer;
public class VBHTMLRendererTest extends AbstractRendererTst {
public AbstractRenderer getRenderer() {
return new VBHTMLRenderer();
public Renderer getRenderer() {
return new VBHTMLRenderer(new Properties());
}
public String getExpected() {
@@ -8,6 +8,7 @@ import static org.junit.Assert.assertNull;
import java.io.IOException;
import java.io.StringReader;
import java.util.Properties;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@@ -118,7 +119,7 @@ public class XMLRendererTest extends RuleTst {
}
private Element parseRootElement(Report rpt) throws SAXException, IOException, ParserConfigurationException {
return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(new XMLRenderer().render(rpt)))).getDocumentElement();
return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(new XMLRenderer(new Properties()).render(rpt)))).getDocumentElement();
}
public static junit.framework.Test suite() {
@@ -2,9 +2,10 @@ package test.net.sourceforge.pmd.renderers;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import net.sourceforge.pmd.Report.ProcessingError;
import net.sourceforge.pmd.renderers.AbstractRenderer;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.renderers.YAHTMLRenderer;
import org.junit.After;
@@ -47,8 +48,10 @@ public class YAHTMLRendererTest extends AbstractRendererTst {
dir.delete();
}
public AbstractRenderer getRenderer() {
return new YAHTMLRenderer(outputDir);
public Renderer getRenderer() {
Properties properties = new Properties();
properties.put(YAHTMLRenderer.OUTPUT_DIR, outputDir);
return new YAHTMLRenderer(properties);
}
public String getExpected() {
File diff suppressed because it is too large. Load diff
+30 -80
View File
@@ -3,78 +3,31 @@
*/
package net.sourceforge.pmd.ant;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.renderers.EmacsRenderer;
import net.sourceforge.pmd.renderers.HTMLRenderer;
import net.sourceforge.pmd.renderers.PapariTextRenderer;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.renderers.SummaryHTMLRenderer;
import net.sourceforge.pmd.renderers.TextRenderer;
import net.sourceforge.pmd.renderers.VBHTMLRenderer;
import net.sourceforge.pmd.renderers.XMLRenderer;
import net.sourceforge.pmd.renderers.XSLTRenderer;
import net.sourceforge.pmd.renderers.YAHTMLRenderer;
import net.sourceforge.pmd.renderers.CSVRenderer;
import org.apache.tools.ant.BuildException;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.renderers.RendererFactory;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.types.Parameter;
public class Formatter {
private interface RendererBuilder {
Renderer build(Object[] optionalArg);
} // factory template
private File toFile;
private String linkPrefix;
private String linePrefix;
private String type;
private boolean toConsole;
private boolean showSuppressed;
private static final Map<String, RendererBuilder> RENDERERS_BY_CODE = new HashMap<String, RendererBuilder>(8);
static {
RENDERERS_BY_CODE.put("xml", new RendererBuilder() {
public Renderer build(Object[] arg) { return new XMLRenderer(); }
});
RENDERERS_BY_CODE.put("betterhtml", new RendererBuilder() {
public Renderer build(Object[] arg) { return new XSLTRenderer(); }
});
RENDERERS_BY_CODE.put("html", new RendererBuilder() {
public Renderer build(Object[] arg) { return new HTMLRenderer((String) arg[0], (String) arg[1]); }
});
RENDERERS_BY_CODE.put("summaryhtml", new RendererBuilder() {
public Renderer build(Object[] arg) { return new SummaryHTMLRenderer((String) arg[0], (String) arg[1]); }
});
RENDERERS_BY_CODE.put("papari", new RendererBuilder() {
public Renderer build(Object[] arg) { return new PapariTextRenderer(); }
});
RENDERERS_BY_CODE.put("csv", new RendererBuilder() {
public Renderer build(Object[] arg) { return new CSVRenderer(); }
});
RENDERERS_BY_CODE.put("emacs", new RendererBuilder() {
public Renderer build(Object[] arg) { return new EmacsRenderer(); }
});
RENDERERS_BY_CODE.put("vbhtml", new RendererBuilder() {
public Renderer build(Object[] arg) { return new VBHTMLRenderer(); }
});
RENDERERS_BY_CODE.put("yahtml", new RendererBuilder() {
public Renderer build(Object[] arg) { return new YAHTMLRenderer(); }
});
RENDERERS_BY_CODE.put("text", new RendererBuilder() {
public Renderer build(Object[] arg) { return new TextRenderer(); }
});
// add additional codes & factories here
}
private List<Parameter> parameters = new ArrayList<Parameter>();
public void setShowSuppressed(boolean value) {
this.showSuppressed = value;
@@ -84,10 +37,6 @@ public class Formatter {
this.type = type;
}
public void setLinkPrefix(String linkPrefix) {
this.linkPrefix = linkPrefix;
}
public void setToFile(File toFile) {
this.toFile = toFile;
}
@@ -95,9 +44,9 @@ public class Formatter {
public void setToConsole(boolean toConsole) {
this.toConsole = toConsole;
}
public void setLinePrefix(String linePrefix) {
this.linePrefix = linePrefix;
public void addConfiguredParam(Parameter parameter) {
this.parameters.add(parameter);
}
private Writer writer;
@@ -116,7 +65,7 @@ public class Formatter {
if (toFile != null) {
writer = getToFileWriter(baseDir);
}
renderer = getRenderer(toConsole);
renderer = createRenderer();
renderer.setWriter(writer);
renderer.start();
} catch (IOException ioe) {
@@ -148,7 +97,7 @@ public class Formatter {
}
private static String[] validRendererCodes() {
return RENDERERS_BY_CODE.keySet().toArray(new String[RENDERERS_BY_CODE.size()]);
return RendererFactory.REPORT_FORMAT_TO_RENDERER.keySet().toArray(new String[RendererFactory.REPORT_FORMAT_TO_RENDERER.size()]);
}
private static String unknownRendererMessage(String userSpecifiedType) {
@@ -164,24 +113,25 @@ public class Formatter {
return sb.toString();
}
private Renderer fromClassname(String rendererClassname) {
try {
return (Renderer) Class.forName(rendererClassname).newInstance();
} catch (Exception e) {
throw new BuildException(unknownRendererMessage(rendererClassname));
}
}
// FIXME - hm, what about this consoleRenderer thing... need a test for this
private Renderer getRenderer(boolean consoleRenderer) {
private Renderer createRenderer() {
if ("".equals(type)) {
throw new BuildException(unknownRendererMessage("<unspecified>"));
}
RendererBuilder builder = RENDERERS_BY_CODE.get(type);
Renderer renderer = builder == null ? fromClassname(type) : builder.build(new String[]{linkPrefix, linePrefix});
renderer.showSuppressedViolations(showSuppressed);
Properties properties = createProperties();
Renderer renderer = RendererFactory.createRenderer(type, properties);
renderer.setShowSuppressedViolations(showSuppressed);
return renderer;
}
private Properties createProperties() {
Properties properties = new Properties();
for (Parameter parameter : parameters) {
properties.put(parameter.getName(), parameter.getValue());
}
return properties;
}
private Writer getToFileWriter(String baseDir) throws IOException {
if (!toFile.isAbsolute()) {
+1 -1
View File
@@ -254,7 +254,7 @@ public class PMDTask extends Task {
final String inputPath = ds.getBasedir().getPath();
Renderer logRenderer = new AbstractRenderer() {
Renderer logRenderer = new AbstractRenderer("log", "Logging renderer", null) {
@Override
public void start() {}
@@ -1,102 +1,152 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.renderers;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.util.datasource.DataSource;
/**
* Abstract base class for {@link Renderer} implementations.
*/
public abstract class AbstractRenderer implements Renderer {
protected String name;
protected String description;
protected Map<String, String> propertyDefinitions = new LinkedHashMap<String, String>();
protected Properties properties;
protected boolean showSuppressedViolations = true;
private Writer writer;
private Report mainReport;
/**
* Method showSuppressedViolations.
* @param show boolean
* @see net.sourceforge.pmd.renderers.Renderer#showSuppressedViolations(boolean)
*/
public void showSuppressedViolations(boolean show) {
this.showSuppressedViolations = show;
public AbstractRenderer(String name, String description, java.util.Properties properties) {
this.name = name;
this.description = description;
this.properties = properties;
}
/**
* Method render.
* @param report Report
* @return String
* @see net.sourceforge.pmd.renderers.Renderer#render(Report)
* {@inheritDoc}
*/
public String getName() {
return name;
}
/**
* {@inheritDoc}
*/
public void setName(String name) {
this.name = name;
}
/**
* {@inheritDoc}
*/
public String getDescription() {
return description;
}
/**
* {@inheritDoc}
*/
public void setDescription(String description) {
this.description = description;
}
/**
* {@inheritDoc}
*/
public Map<String, String> getPropertyDefinitions() {
return propertyDefinitions;
}
/**
* Define a property.
* @param name The property name.
* @param description The description of the property.
*/
protected void defineProperty(String name, String description) {
propertyDefinitions.put(name, description);
}
/**
* {@inheritDoc}
*/
public boolean isShowSuppressedViolations() {
return showSuppressedViolations;
}
/**
* {@inheritDoc}
*/
public void setShowSuppressedViolations(boolean showSuppressedViolations) {
this.showSuppressedViolations = showSuppressedViolations;
}
/**
* {@inheritDoc}
*/
public String render(Report report) {
StringWriter w = new StringWriter();
try {
render(w, report);
} catch (IOException e) {
throw new Error("StringWriter doesn't throw IOException", e);
}
return w.toString();
StringWriter w = new StringWriter();
try {
render(w, report);
} catch (IOException e) {
throw new Error("StringWriter doesn't throw IOException", e);
}
return w.toString();
}
/**
* Method setWriter.
* @param writer Writer
* @see net.sourceforge.pmd.renderers.Renderer#setWriter(Writer)
* {@inheritDoc}
*/
public void setWriter(Writer writer) {
this.writer = writer;
this.writer = writer;
}
/**
* Method getWriter.
* @return Writer
* @see net.sourceforge.pmd.renderers.Renderer#getWriter()
* {@inheritDoc}
*/
public Writer getWriter() {
return writer;
return writer;
}
/**
* Method start.
* @throws IOException
* @see net.sourceforge.pmd.renderers.Renderer#start()
* {@inheritDoc}
*/
public void start() throws IOException {
// default (and backward compatible) behavior is to build a full report.
// Optimized rendering is done in OnTheFlyRenderer and descendants
mainReport = new Report();
// default (and backward compatible) behavior is to build a full report.
// Optimized rendering is done in OnTheFlyRenderer and descendants
mainReport = new Report();
}
/**
* Method startFileAnalysis.
* @param dataSource DataSource
* @see net.sourceforge.pmd.renderers.Renderer#startFileAnalysis(DataSource)
* {@inheritDoc}
*/
public void startFileAnalysis(DataSource dataSource) {}
public void startFileAnalysis(DataSource dataSource) {
}
/**
* Method renderFileReport.
* @param report Report
* @throws IOException
* @see net.sourceforge.pmd.renderers.Renderer#renderFileReport(Report)
* {@inheritDoc}
*/
public void renderFileReport(Report report) throws IOException {
// default (and backward compatible) behavior is to build a full report.
// Optimized rendering is done in OnTheFlyRenderer and descendants
mainReport.merge(report);
// default (and backward compatible) behavior is to build a full report.
// Optimized rendering is done in OnTheFlyRenderer and descendants
mainReport.merge(report);
}
/**
* Method end.
* @throws IOException
* @see net.sourceforge.pmd.renderers.Renderer#end()
* {@inheritDoc}
*/
public void end() throws IOException {
// default (and backward compatible) behavior is to build a full report.
// Optimized rendering is done in OnTheFlyRenderer and descendants
render(writer, mainReport);
// default (and backward compatible) behavior is to build a full report.
// Optimized rendering is done in OnTheFlyRenderer and descendants
render(writer, mainReport);
}
}
@@ -3,90 +3,78 @@
*/
package net.sourceforge.pmd.renderers;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.util.StringUtil;
import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import java.util.Properties;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.util.StringUtil;
/**
* Renderer to comma separated format.
*/
public class CSVRenderer extends OnTheFlyRenderer {
public static final String NAME = "csv";
private int violationCount = 1;
public CSVRenderer(Properties properties) {
super(NAME, "Comma-separated values tabular format.", properties);
}
/**
* Method start.
* @throws IOException
* @see net.sourceforge.pmd.renderers.Renderer#start()
* {@inheritDoc}
*/
@Override
public void start() throws IOException {
StringBuffer buf = new StringBuffer(300);
quoteAndCommify(buf, "Problem");
quoteAndCommify(buf, "Package");
quoteAndCommify(buf, "File");
quoteAndCommify(buf, "Priority");
quoteAndCommify(buf, "Line");
quoteAndCommify(buf, "Description");
quoteAndCommify(buf, "Rule set");
quote(buf, "Rule");
buf.append(PMD.EOL);
getWriter().write(buf.toString());
StringBuffer buf = new StringBuffer(300);
quoteAndCommify(buf, "Problem");
quoteAndCommify(buf, "Package");
quoteAndCommify(buf, "File");
quoteAndCommify(buf, "Priority");
quoteAndCommify(buf, "Line");
quoteAndCommify(buf, "Description");
quoteAndCommify(buf, "Rule set");
quote(buf, "Rule");
buf.append(PMD.EOL);
getWriter().write(buf.toString());
}
/**
* Method renderFileViolations.
* @param violations Iterator<RuleViolation>
* @throws IOException
* {@inheritDoc}
*/
@Override
public void renderFileViolations(Iterator<RuleViolation> violations) throws IOException {
StringBuffer buf = new StringBuffer(300);
Writer writer = getWriter();
StringBuffer buf = new StringBuffer(300);
Writer writer = getWriter();
RuleViolation rv;
while (violations.hasNext()) {
buf.setLength(0);
rv = violations.next();
quoteAndCommify(buf, Integer.toString(violationCount));
quoteAndCommify(buf, rv.getPackageName());
quoteAndCommify(buf, rv.getFilename());
quoteAndCommify(buf, Integer.toString(rv.getRule().getPriority().getPriority()));
quoteAndCommify(buf, Integer.toString(rv.getBeginLine()));
quoteAndCommify(buf, StringUtil.replaceString(rv.getDescription(), '\"', "'"));
quoteAndCommify(buf, rv.getRule().getRuleSetName());
quote(buf, rv.getRule().getName());
buf.append(PMD.EOL);
writer.write(buf.toString());
violationCount++;
}
RuleViolation rv;
while (violations.hasNext()) {
buf.setLength(0);
rv = violations.next();
quoteAndCommify(buf, Integer.toString(violationCount));
quoteAndCommify(buf, rv.getPackageName());
quoteAndCommify(buf, rv.getFilename());
quoteAndCommify(buf, Integer.toString(rv.getRule().getPriority().getPriority()));
quoteAndCommify(buf, Integer.toString(rv.getBeginLine()));
quoteAndCommify(buf, StringUtil.replaceString(rv.getDescription(), '\"', "'"));
quoteAndCommify(buf, rv.getRule().getRuleSetName());
quote(buf, rv.getRule().getName());
buf.append(PMD.EOL);
writer.write(buf.toString());
violationCount++;
}
/**
* Method end.
* @throws IOException
* @see net.sourceforge.pmd.renderers.Renderer#end()
*/
public void end() throws IOException {
}
/**
* Method quote.
* @param sb StringBuffer
* @param d String
*/
private void quote(StringBuffer sb, String d) {
sb.append('"').append(d).append('"');
private void quote(StringBuffer buffer, String s) {
buffer.append('"').append(s).append('"');
}
/**
* Method quoteAndCommify.
* @param sb StringBuffer
* @param d String
*/
private void quoteAndCommify(StringBuffer sb, String d) {
quote(sb, d);
sb.append(',');
private void quoteAndCommify(StringBuffer buffer, String s) {
quote(buffer, s);
buffer.append(',');
}
}
@@ -3,30 +3,40 @@
*/
package net.sourceforge.pmd.renderers;
import net.sourceforge.pmd.RuleViolation;
import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import java.util.Properties;
import net.sourceforge.pmd.RuleViolation;
/**
* Renderer to GNU Emacs parsable format.
*/
public class EmacsRenderer extends OnTheFlyRenderer {
public static final String NAME = "emacs";
protected static final String EOL = System.getProperty("line.separator", "\n");
public void start() throws IOException {}
public void renderFileViolations(Iterator<RuleViolation> violations) throws IOException {
Writer writer = getWriter();
StringBuffer buf = new StringBuffer();
while (violations.hasNext()) {
RuleViolation rv = violations.next();
buf.setLength(0);
buf.append(EOL).append(rv.getFilename());
buf.append(':').append(Integer.toString(rv.getBeginLine()));
buf.append(": ").append(rv.getDescription());
writer.write(buf.toString());
}
public EmacsRenderer(Properties properties) {
super(NAME, "GNU Emacs integration.", properties);
}
public void end() throws IOException {}
/**
* {@inheritDoc}
*/
@Override
public void renderFileViolations(Iterator<RuleViolation> violations) throws IOException {
Writer writer = getWriter();
StringBuffer buf = new StringBuffer();
while (violations.hasNext()) {
RuleViolation rv = violations.next();
buf.setLength(0);
buf.append(EOL).append(rv.getFilename());
buf.append(':').append(Integer.toString(rv.getBeginLine()));
buf.append(": ").append(rv.getDescription());
writer.write(buf.toString());
}
}
}
File diff suppressed because it is too large. Load diff
@@ -3,154 +3,115 @@
*/
package net.sourceforge.pmd.renderers;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.PMD;
import java.io.IOException;
import java.io.Writer;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.RuleViolation;
/**
* Renderer for IntelliJ IDEA integration.
*/
public class IDEAJRenderer extends OnTheFlyRenderer {
private static final String FILE_SEPARATOR = System.getProperty("file.separator");
private static final String PATH_SEPARATOR = System.getProperty("path.separator");
public static final String NAME = "ideaj";
public static final String SOURCE_PATH = "sourcePath";
public static final String CLASS_AND_METHOD_NAME = "classAndMethodName";
public static final String FILE_NAME = "fileName";
private static final String FILE_SEPARATOR = System.getProperty("file.separator");
private static final String PATH_SEPARATOR = System.getProperty("path.separator");
private final String sourcePath;
private final String classAndMethodName;
private final String fileName;
public IDEAJRenderer(Properties properties) {
super(NAME, "IntelliJ IDEA integration.", properties);
super.defineProperty(SOURCE_PATH, "Source path.");
super.defineProperty(CLASS_AND_METHOD_NAME,
"Class and Method name, pass '.method' when processing a directory.");
super.defineProperty(FILE_NAME, "File name.");
this.sourcePath = properties.getProperty(SOURCE_PATH);
this.classAndMethodName = properties.getProperty(CLASS_AND_METHOD_NAME);
this.fileName = properties.getProperty(FILE_NAME);
}
/**
* {@inheritDoc}
*/
@Override
public void renderFileViolations(Iterator<RuleViolation> violations) throws IOException {
Writer writer = getWriter();
if (classAndMethodName.equals(".method")) {
// working on a directory tree
renderDirectoy(writer, violations);
} else {
// working on one file
renderFile(writer, violations);
}
}
private void renderDirectoy(Writer writer, Iterator<RuleViolation> violations) throws IOException {
SourcePath sourcePath = new SourcePath(this.sourcePath);
StringBuffer buf = new StringBuffer();
while (violations.hasNext()) {
buf.setLength(0);
RuleViolation rv = violations.next();
buf.append(rv.getDescription() + PMD.EOL);
buf.append(" at ").append(getFullyQualifiedClassName(rv.getFilename(), sourcePath)).append(".method(");
buf.append(getSimpleFileName(rv.getFilename())).append(':').append(rv.getBeginLine()).append(')').append(
PMD.EOL);
writer.write(buf.toString());
}
}
private void renderFile(Writer writer, Iterator<RuleViolation> violations) throws IOException {
StringBuffer buf = new StringBuffer();
while (violations.hasNext()) {
buf.setLength(0);
RuleViolation rv = violations.next();
buf.append(rv.getDescription()).append(PMD.EOL);
buf.append(" at ").append(this.classAndMethodName).append('(').append(this.fileName).append(':').append(
rv.getBeginLine()).append(')').append(PMD.EOL);
writer.write(buf.toString());
}
}
private String getFullyQualifiedClassName(String fileName, SourcePath sourcePath) {
String classNameWithSlashes = sourcePath.clipPath(fileName);
String className = classNameWithSlashes.replace(FILE_SEPARATOR.charAt(0), '.');
return className.substring(0, className.length() - 5);
}
private String getSimpleFileName(String fileName) {
return fileName.substring(fileName.lastIndexOf(FILE_SEPARATOR) + 1);
}
private static class SourcePath {
private Set<String> paths = new HashSet<String>();
private Set<String> paths = new HashSet<String>();
/**
* Constructor for SourcePath.
* @param sourcePathString String
*/
public SourcePath(String sourcePathString) {
for (StringTokenizer st = new StringTokenizer(sourcePathString, PATH_SEPARATOR); st.hasMoreTokens();) {
paths.add(st.nextToken());
}
}
public SourcePath(String sourcePathString) {
for (StringTokenizer st = new StringTokenizer(sourcePathString, PATH_SEPARATOR); st.hasMoreTokens();) {
paths.add(st.nextToken());
}
}
/**
* Method clipPath.
* @param fullFilename String
* @return String
*/
public String clipPath(String fullFilename) {
for (String path: paths) {
if (fullFilename.startsWith(path)) {
return fullFilename.substring(path.length() + 1);
}
}
throw new RuntimeException("Couldn't find src path for " + fullFilename);
}
}
private String[] args;
/**
* Constructor for IDEAJRenderer.
* @param args String[]
*/
public IDEAJRenderer(String[] args) {
this.args = args;
}
/**
* Method start.
* @throws IOException
* @see net.sourceforge.pmd.renderers.Renderer#start()
*/
public void start() throws IOException {}
/**
* Method renderFileViolations.
* @param violations Iterator<RuleViolation>
* @throws IOException
*/
public void renderFileViolations(Iterator<RuleViolation> violations) throws IOException {
Writer writer = getWriter();
if (args[4].equals(".method")) {
// working on a directory tree
String sourcePath = args[3];
render(writer, violations, sourcePath);
return;
}
// working on one file
String classAndMethodName = args[4];
String singleFileName = args[5];
render(writer, violations, classAndMethodName, singleFileName);
}
/**
* Method end.
* @throws IOException
* @see net.sourceforge.pmd.renderers.Renderer#end()
*/
public void end() throws IOException {}
/**
* Method render.
* @param writer Writer
* @param violations Iterator<RuleViolation>
* @param sourcePathString String
* @throws IOException
*/
private void render(Writer writer, Iterator<RuleViolation> violations, String sourcePathString) throws IOException {
SourcePath sourcePath = new SourcePath(sourcePathString);
StringBuffer buf = new StringBuffer();
while (violations.hasNext()) {
buf.setLength(0);
RuleViolation rv = violations.next();
buf.append(rv.getDescription() + PMD.EOL);
buf.append(" at ").append(getFullyQualifiedClassName(rv.getFilename(), sourcePath)).append(".method(");
buf.append(getSimpleFileName(rv.getFilename())).append(':').append(rv.getBeginLine()).append(')').append(PMD.EOL);
writer.write(buf.toString());
}
}
/**
* Method render.
* @param writer Writer
* @param violations Iterator<RuleViolation>
* @param classAndMethod String
* @param file String
* @throws IOException
*/
private void render(Writer writer, Iterator<RuleViolation> violations, String classAndMethod, String file) throws IOException {
StringBuffer buf = new StringBuffer();
while (violations.hasNext()) {
buf.setLength(0);
RuleViolation rv = violations.next();
buf.append(rv.getDescription()).append(PMD.EOL);
buf.append(" at ").append(classAndMethod).append('(').append(file).append(':').append(rv.getBeginLine()).append(')').append(PMD.EOL);
writer.write(buf.toString());
}
}
/**
* Method getFullyQualifiedClassName.
* @param fileName String
* @param sourcePath SourcePath
* @return String
*/
private String getFullyQualifiedClassName(String fileName, SourcePath sourcePath) {
String classNameWithSlashes = sourcePath.clipPath(fileName);
String className = classNameWithSlashes.replace(FILE_SEPARATOR.charAt(0), '.');
return className.substring(0, className.length() - 5);
}
/**
* Method getSimpleFileName.
* @param fileName String
* @return String
*/
private String getSimpleFileName(String fileName) {
return fileName.substring(fileName.lastIndexOf(FILE_SEPARATOR) + 1);
public String clipPath(String fullFilename) {
for (String path : paths) {
if (fullFilename.startsWith(path)) {
return fullFilename.substring(path.length() + 1);
}
}
throw new RuntimeException("Couldn't find src path for " + fullFilename);
}
}
}
@@ -1,3 +1,6 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.renderers;
import java.io.IOException;
@@ -5,77 +8,87 @@ import java.io.Writer;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.RuleViolation;
/**
* Abstract base class for {@link Renderer} implementations which can produce
* output incrementally for {@link RuleViolation}s as source files are
* processed. Such {@link Renderer}s are able to produce large reports with
* significantly less working memory at any given time. Variations in the
* delivery of source file reports are reflected in the output of the
* {@link Renderer}, so report output can be different between runs.
*
* Only processing errors and suppressed violations are accumulated across all
* files. These are intended to be processed in the {@link #end()} method.
*/
// TODO Rename to AbstractIncrementalRenderer
public abstract class OnTheFlyRenderer extends AbstractRenderer {
/**
* Accumulated processing errors.
*/
protected List<Report.ProcessingError> errors = new LinkedList<Report.ProcessingError>();
/**
* Accumulated suppressed violations.
*/
protected List<Report.SuppressedViolation> suppressed = new LinkedList<Report.SuppressedViolation>();
public OnTheFlyRenderer(String name, String description, Properties properties) {
super(name, description, properties);
}
/**
* Method render.
* @param writer Writer
* @param report Report
* @throws IOException
* @see net.sourceforge.pmd.renderers.Renderer#render(Writer, Report)
* {@inheritDoc}
*/
public void render(Writer writer, Report report) throws IOException {
setWriter(writer);
start();
renderFileReport(report);
end();
setWriter(writer);
start();
renderFileReport(report);
end();
}
/**
* Method renderFileReport.
* @param report Report
* @throws IOException
* @see net.sourceforge.pmd.renderers.Renderer#renderFileReport(Report)
* {@inheritDoc}
*/
@Override
public void renderFileReport(Report report) throws IOException {
Iterator<RuleViolation> violations = report.iterator();
if (violations.hasNext()) {
renderFileViolations(violations);
getWriter().flush();
}
Iterator<RuleViolation> violations = report.iterator();
if (violations.hasNext()) {
renderFileViolations(violations);
getWriter().flush();
}
/*
* errors and suppressed violations are inserted in lists and will be
* processed when all files have been analyzed, i.e. in the end() method.
*/
for (Iterator<Report.ProcessingError> i = report.errors(); i.hasNext();) {
errors.add(i.next());
}
for (Iterator<Report.ProcessingError> i = report.errors(); i.hasNext();) {
errors.add(i.next());
}
if (showSuppressedViolations) {
suppressed.addAll(report.getSuppressedRuleViolations());
}
if (showSuppressedViolations) {
suppressed.addAll(report.getSuppressedRuleViolations());
}
}
/**
* Method start.
* @throws IOException
* @see net.sourceforge.pmd.renderers.Renderer#start()
* {@inheritDoc}
*/
public abstract void start() throws IOException;
@Override
public void start() throws IOException {
}
/**
* Method renderFileViolations.
* @param violations Iterator<RuleViolation>
* Render a series of {@link RuleViolation}s.
* @param violations The iterator of violations to render.
* @throws IOException
*/
public abstract void renderFileViolations(Iterator<RuleViolation> violations) throws IOException;
/**
* Method end.
* @throws IOException
* @see net.sourceforge.pmd.renderers.Renderer#end()
* {@inheritDoc}
*/
public abstract void end() throws IOException;
@Override
public void end() throws IOException {
}
}
@@ -1,205 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.renderers;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Iterator;
import java.util.Map;
/**
* <p>A console renderer with optional color support under *nix systems.</p>
* <p/>
* <pre>
* * file: ./src/gilot/Test.java
* src: Test.java:12
* rule: AtLeastOneConstructor
* msg: Each class should declare at least one constructor
* code: public class Test
* <p/>
* * file: ./src/gilot/log/format/LogInterpreter.java
* src: LogInterpreter.java:317
* rule: AvoidDuplicateLiterals
* msg: The same String literal appears 4 times in this file; the first occurrence is on line 317
* code: logger.error( "missing attribute 'app_arg' in rule '" + ((Element)element.getParent()).getAttributeValue( "name" ) + "'" );
* <p/>
* src: LogInterpreter.java:317
* rule: AvoidDuplicateLiterals
* msg: The same String literal appears 5 times in this file; the first occurrence is on line 317
* code: logger.error( "missing attribute 'app_arg' in rule '" + ((Element)element.getParent()).getAttributeValue( "name" ) + "'" );
* <p/>
* * warnings: 3
* <p/>
* </pre>
* <p/>
* <p>Colorization is turned on by supplying -D<b>pmd.color</b> - any value other than
* '0' or 'false', enables color - including an empty value (''). <b>Nota Bene:</b>
* colorization is atm only supported under *nix terminals accepting ansi escape
* sequences, such as xterm, rxvt et cetera.</p>
*/
public class PapariTextRenderer extends AbstractRenderer {
/**
* Directory from where java was invoked.
*/
private String pwd;
private String yellowBold = "";
private String whiteBold = "";
private String redBold = "";
private String cyan = "";
private String green = "";
private String colorReset = "";
/**
* Enables colors on *nix systems - not windows. Color support depends
* on the pmd.color property, which should be set with the -D option
* during execution - a set value other than 'false' or '0' enables color.
* <p/>
* btw, is it possible to do this on windows (ie; console colors)?
*/
private void initializeColorsIfSupported() {
if (System.getProperty("pmd.color") != null &&
!(System.getProperty("pmd.color").equals("0") || System.getProperty("pmd.color").equals("false"))) {
this.yellowBold = "\u001B[1;33m";
this.whiteBold = "\u001B[1;37m";
this.redBold = "\u001B[1;31m";
this.green = "\u001B[0;32m";
this.cyan = "\u001B[0;36m";
this.colorReset = "\u001B[0m";
}
}
public void render(Writer writer, Report report) throws IOException {
StringBuffer buf = new StringBuffer(500);
buf.append(PMD.EOL);
initializeColorsIfSupported();
String lastFile = null;
int numberOfErrors = 0;
int numberOfWarnings = 0;
for (Iterator<RuleViolation> i = report.iterator(); i.hasNext();) {
buf.setLength(0);
numberOfWarnings++;
RuleViolation rv = i.next();
if (!rv.getFilename().equals(lastFile)) {
lastFile = rv.getFilename();
buf.append(this.yellowBold + "*" + this.colorReset + " file: " + this.whiteBold + this.getRelativePath(lastFile) + this.colorReset + PMD.EOL);
}
buf.append(this.green + " src: " + this.cyan + lastFile.substring(lastFile.lastIndexOf(File.separator) + 1) + this.colorReset + ":" + this.cyan + rv.getBeginLine() + (rv.getEndLine() == -1 ? "" : ":" + rv.getEndLine()) + this.colorReset + PMD.EOL);
buf.append(this.green + " rule: " + this.colorReset + rv.getRule().getName() + PMD.EOL);
buf.append(this.green + " msg: " + this.colorReset + rv.getDescription() + PMD.EOL);
buf.append(this.green + " code: " + this.colorReset + this.getLine(lastFile, rv.getBeginLine()) + PMD.EOL + PMD.EOL);
writer.write(buf.toString());
}
writer.write(PMD.EOL + PMD.EOL);
writer.write("Summary:" + PMD.EOL + PMD.EOL);
Map<String, Integer> summary = report.getCountSummary();
for (Map.Entry<String, Integer> entry : summary.entrySet()) {
buf.setLength(0);
String key = entry.getKey();
buf.append(key).append(" : ").append(entry.getValue()).append(PMD.EOL);
writer.write(buf.toString());
}
for (Iterator<Report.ProcessingError> i = report.errors(); i.hasNext();) {
buf.setLength(0);
numberOfErrors++;
Report.ProcessingError error = i.next();
if (error.getFile().equals(lastFile)) {
lastFile = error.getFile();
buf.append(this.redBold + "*" + this.colorReset + " file: " + this.whiteBold + this.getRelativePath(lastFile) + this.colorReset + PMD.EOL);
}
buf.append(this.green + " err: " + this.cyan + error.getMsg() + this.colorReset + PMD.EOL + PMD.EOL);
writer.write(buf.toString());
}
// adding error message count, if any
if (numberOfErrors > 0) {
writer.write(this.redBold + "*" + this.colorReset + " errors: " + this.whiteBold + numberOfWarnings + this.colorReset + PMD.EOL);
}
writer.write(this.yellowBold + "*" + this.colorReset + " warnings: " + this.whiteBold + numberOfWarnings + this.colorReset + PMD.EOL);
}
/**
* Retrieves the requested line from the specified file.
*
* @param sourceFile the java or cpp source file
* @param line line number to extract
* @return a trimmed line of source code
*/
private String getLine(String sourceFile, int line) {
String code = null;
BufferedReader br = null;
try {
br = new BufferedReader(getReader(sourceFile));
for (int i = 0; line > i; i++) {
code = br.readLine().trim();
}
} catch (IOException ioErr) {
ioErr.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException ioErr) {
ioErr.printStackTrace();
}
}
}
return code;
}
protected Reader getReader(String sourceFile) throws FileNotFoundException {
return new FileReader(new File(sourceFile));
}
/**
* Attempts to determine the relative path to the file. If relative path cannot be found,
* the original path is returnedi, ie - the current path for the supplied file.
*
* @param fileName well, the file with its original path.
* @return the relative path to the file
*/
private String getRelativePath(String fileName) {
String relativePath;
// check if working directory need to be assigned
if (pwd == null) {
try {
this.pwd = new File(".").getCanonicalPath();
} catch (IOException ioErr) {
// to avoid further error
this.pwd = "";
}
}
// make sure that strings match before doing any substring-ing
if (fileName.indexOf(this.pwd) == 0) {
relativePath = "." + fileName.substring(this.pwd.length());
// remove current dir occuring twice - occurs if . was supplied as path
if (relativePath.startsWith("." + File.separator + "." + File.separator)) {
relativePath = relativePath.substring(2);
}
} else {
// this happens when pmd's supplied argument deviates from the pwd 'branch' (god knows this terminolgy - i hope i make some sense).
// for instance, if supplied=/usr/lots/of/src and pwd=/usr/lots/of/shared/source
// TODO: a fix to get relative path?
relativePath = fileName;
}
return relativePath;
}
}
@@ -5,19 +5,76 @@ package net.sourceforge.pmd.renderers;
import java.io.IOException;
import java.io.Writer;
import java.util.Map;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.util.datasource.DataSource;
/**
* This is an interface for rendering a Report. When a Renderer is being
* invoked, the sequence of method calls is something like the following:
* <ol>
* <li>Renderer construction/initialization</li>
* <li>{@link Renderer#setShowSuppressedViolations(boolean)}</li>
* <li>{@link Renderer#setWriter(Writer)}</li>
* <li>{@link Renderer#start()}</li>
* <li>{@link Renderer#startFileAnalysis(DataSource)} for each source file processed</li>
* <li>{@link Renderer#renderFileReport(Report)} for each Report instance</li>
* <li>{@link Renderer#end()}</li>
* </ol>
* <p>
* An implementation of the Renderer interface is expected to have a constructor
* which takes a single {@link java.util.Properties} argument. This is to allow
* easy factory creation and initialization of the Renderer, as well as to
* provide flexibility with regards to initialization in subclasses of the
* Renderer.
*/
// TODO Are implementations expected to be thread-safe?
public interface Renderer {
/**
* Method showSuppressedViolations.
* @param show boolean
* Get the name of the Renderer.
* @return The name of the Renderer.
*/
void showSuppressedViolations(boolean show);
String getName();
/**
* Set the name of the Renderer.
* @param name The name of the Renderer.
*/
void setName(String name);
/**
* Get the description of the Renderer.
* @return The description of the Renderer.
*/
String getDescription();
/**
* Set the description of the Renderer.
* @param description The description of the Renderer.
*/
void setDescription(String description);
/**
* Get the configuration property definitions for Renderer.
* The keys in the map are the configuration property names, with the
* corresponding value being a description.
* @return The configuration property definition map.
*/
Map<String, String> getPropertyDefinitions();
/**
* Get the indicator for whether to show suppressed violations.
* @return <code>true</code> if suppressed violations should show, <code>false</code> otherwise.
*/
boolean isShowSuppressedViolations();
/**
* Set the indicator for whether to show suppressed violations.
* @param showSuppressedViolations Whether to show suppressed violations.
*/
void setShowSuppressedViolations(boolean showSuppressedViolations);
/**
*
@@ -27,6 +84,7 @@ public interface Renderer {
* @param report Report
* @return String
*/
// TODO Remove this
String render(Report report);
/**
@@ -38,43 +96,59 @@ public interface Renderer {
* @param report Report
* @throws IOException
*/
// TODO Remove this
void render(Writer writer, Report report) throws IOException;
/**
* Method setWriter.
* @param writer Writer
*/
void setWriter(Writer writer);
/**
* Method getWriter.
* @return Writer
* Get the Writer for the Renderer.
* @return The Writer.
*/
Writer getWriter();
/**
* Method start.
* Set the Writer for the Renderer.
* @param writer The Writer.
*/
void setWriter(Writer writer);
/**
* This method is called before any source files are processed.
* The Renderer will have been fully initialized by the time this method
* is called, so the Writer and other state will be available.
* @throws IOException
*/
void start() throws IOException;
/**
* Method startFileAnalysis.
* @param dataSource DataSource
* This method is called each time a source file is processed. It is called
* after {@link Renderer#start()}, but before
* {@link Renderer#renderFileReport(Report)} and {@link Renderer#end()}.
*
* This method may be invoked by different threads which are processing
* files independently. Therefore, any non-trivial implementation of this
* method needs to be thread-safe.
*
* @param dataSource The source file.
*/
void startFileAnalysis(DataSource dataSource);
/**
* Method renderFileReport.
* @param report Report
* Render the given file Report. There may be multiple Report instances
* which need to be rendered if produced by different threads.
* It is called after {@link Renderer#start()} and
* {@link Renderer#startFileAnalysis(DataSource)}, but before {@link Renderer#end()}.
*
* @param report A file Report.
* @throws IOException
*
* @see Report
*/
void renderFileReport(Report report) throws IOException;
/**
* Method end.
* This method is at the very end of the Rendering process, after
* {@link Renderer#renderFileReport(Report)}.
* @throws IOException
*/
void end() throws IOException;
}
@@ -0,0 +1,129 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.renderers;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.logging.Logger;
/**
* This class handles the creation of Renderers.
* @see Renderer
*/
public class RendererFactory {
private static final Logger LOG = Logger.getLogger(RendererFactory.class.getName());
public static final Map<String, Class<? extends Renderer>> REPORT_FORMAT_TO_RENDERER;
static {
Map<String, Class<? extends Renderer>> map = new TreeMap<String, Class<? extends Renderer>>();
map.put(XMLRenderer.NAME, XMLRenderer.class);
map.put(IDEAJRenderer.NAME, IDEAJRenderer.class);
map.put(TextColorRenderer.NAME, TextColorRenderer.class);
map.put("papari", TextColorRenderer.class); // TODO Remove when we drop backward compatibility.
map.put(TextRenderer.NAME, TextRenderer.class);
map.put(TextPadRenderer.NAME, TextPadRenderer.class);
map.put(EmacsRenderer.NAME, EmacsRenderer.class);
map.put(CSVRenderer.NAME, CSVRenderer.class);
map.put(HTMLRenderer.NAME, HTMLRenderer.class);
map.put("nicehtml", XSLTRenderer.class); // TODO Remove when we drop backward compatibility.
map.put(XSLTRenderer.NAME, XSLTRenderer.class);
map.put(YAHTMLRenderer.NAME, YAHTMLRenderer.class);
map.put(SummaryHTMLRenderer.NAME, SummaryHTMLRenderer.class);
map.put(VBHTMLRenderer.NAME, VBHTMLRenderer.class);
REPORT_FORMAT_TO_RENDERER = Collections.unmodifiableMap(map);
}
/**
* Construct an instance of a Renderer based on report format name.
* @param reportFormat The report format name.
* @param properties Initialization properties for the corresponding Renderer.
* @return A Renderer instance.
*/
public static Renderer createRenderer(String reportFormat, Properties properties) {
Class<? extends Renderer> rendererClass = getRendererClass(reportFormat);
Constructor<? extends Renderer> constructor = getRendererConstructor(rendererClass);
Renderer renderer;
try {
if (constructor.getParameterTypes().length > 0) {
renderer = constructor.newInstance(properties);
} else {
renderer = constructor.newInstance();
}
} catch (InstantiationException e) {
throw new IllegalArgumentException("Unable to construct report renderer class: " + e.getLocalizedMessage());
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Unable to construct report renderer class: " + e.getLocalizedMessage());
} catch (InvocationTargetException e) {
throw new IllegalArgumentException("Unable to construct report renderer class: "
+ e.getTargetException().getLocalizedMessage());
}
// Warn about legacy report format usages
if (REPORT_FORMAT_TO_RENDERER.containsKey(reportFormat) && !reportFormat.equals(renderer.getName())) {
LOG.warning("Report format '" + reportFormat + "' is deprecated, and has been replaced with '"
+ renderer.getName()
+ "'. Future versions of PMD will remove support for this deprecated Report format usage.");
}
return renderer;
}
@SuppressWarnings("unchecked")
private static Class<? extends Renderer> getRendererClass(String reportFormat) {
Class<? extends Renderer> rendererClass = REPORT_FORMAT_TO_RENDERER.get(reportFormat);
// Look up a custom renderer class
if (rendererClass == null && !"".equals(reportFormat)) {
try {
Class<?> clazz = Class.forName(reportFormat);
if (!Renderer.class.isAssignableFrom(clazz)) {
throw new IllegalArgumentException("Custom report renderer class does not implement the "
+ Renderer.class.getName() + " interface.");
} else {
rendererClass = (Class<? extends Renderer>) clazz;
}
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Can't find the custom format " + reportFormat + ": "
+ e.getClass().getName());
}
}
return rendererClass;
}
private static Constructor<? extends Renderer> getRendererConstructor(Class<? extends Renderer> rendererClass) {
Constructor<? extends Renderer> constructor = null;
// 1) Properties constructor?
try {
constructor = rendererClass.getConstructor(Properties.class);
if (!Modifier.isPublic(constructor.getModifiers())) {
constructor = null;
}
} catch (NoSuchMethodException e) {
// Ok
}
// 2) No-arg constructor?
try {
constructor = rendererClass.getConstructor();
if (!Modifier.isPublic(constructor.getModifiers())) {
constructor = null;
}
} catch (NoSuchMethodException e2) {
// Ok
}
if (constructor == null) {
throw new IllegalArgumentException(
"Unable to find either a public java.util.Properties or no-arg constructors for Renderer class: "
+ rendererClass.getName());
}
return constructor;
}
}
@@ -1,69 +1,70 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.renderers;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report;
import java.io.IOException;
import java.io.Writer;
import java.util.Map;
import java.util.Properties;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report;
/**
* Renderer to a summarized HTML format.
*/
public class SummaryHTMLRenderer extends AbstractRenderer {
private String linePrefix,linkPrefix;
/**
* Constructor for SummaryHTMLRenderer.
* @param linkPrefix String
* @param linePrefix String
*/
public SummaryHTMLRenderer(String linkPrefix, String linePrefix) {
this.linePrefix = linePrefix;
this.linkPrefix = linkPrefix;
}
public SummaryHTMLRenderer(){
this(null,null);
public static final String NAME = "summaryhtml";
public static final String LINK_PREFIX = HTMLRenderer.LINK_PREFIX;
public static final String LINE_PREFIX = HTMLRenderer.LINE_PREFIX;
public SummaryHTMLRenderer(Properties properties) {
super(NAME, "Summary HTML format.", properties);
// These properties are defined here, but used by the HTMLRenderer
super.defineProperty(HTMLRenderer.LINK_PREFIX, "Path to HTML source.");
super.defineProperty(HTMLRenderer.LINE_PREFIX, "Prefix for line number anchor in the source file.");
}
/**
* Method render.
* @param writer Writer
* @param report Report
* @throws IOException
* @see net.sourceforge.pmd.renderers.Renderer#render(Writer, Report)
* {@inheritDoc}
*/
public void render(Writer writer, Report report) throws IOException {
writer.write("<html><head><title>PMD</title></head><body>" + PMD.EOL);
renderSummary(writer, report);
writer.write("<h2><center>Detail</h2></center>");
writer.write("<table align=\"center\" cellspacing=\"0\" cellpadding=\"3\"><tr>" + PMD.EOL);
new HTMLRenderer(linkPrefix,linePrefix).renderBody(writer, report);
writer.write("</table></body></html>");
writer.write("<html><head><title>PMD</title></head><body>" + PMD.EOL);
renderSummary(writer, report);
writer.write("<h2><center>Detail</h2></center>");
writer.write("<table align=\"center\" cellspacing=\"0\" cellpadding=\"3\"><tr>" + PMD.EOL);
new HTMLRenderer(properties).renderBody(writer, report);
writer.write("</table></body></html>");
}
/**
* Method renderSummary.
* @param writer Writer
* @param report Report
* Write a Summary HTML table.
*
* @param writer Writer to write to.
* @param report Report to write.
* @throws IOException
*/
public void renderSummary(Writer writer, Report report) throws IOException {
StringBuffer buf = new StringBuffer(500);
buf.append("<h2><center>Summary</h2></center>");
buf.append("<table align=\"center\" cellspacing=\"0\" cellpadding=\"3\">");
buf.append("<th>Rule name</th>");
buf.append("<th>Number of violations</th>");
writer.write(buf.toString());
Map<String, Integer> summary = report.getSummary();
for (Map.Entry<String, Integer> entry: summary.entrySet()) {
String ruleName = entry.getKey();
buf.setLength(0);
buf.append("<tr>");
buf.append("<td>" + ruleName + "</td>");
buf.append("<td align=center>" + entry.getValue().intValue() + "</td>");
buf.append("</tr>");
writer.write(buf.toString());
}
writer.write("</table>");
StringBuffer buf = new StringBuffer(500);
buf.append("<h2><center>Summary</h2></center>");
buf.append("<table align=\"center\" cellspacing=\"0\" cellpadding=\"3\">");
buf.append("<th>Rule name</th>");
buf.append("<th>Number of violations</th>");
writer.write(buf.toString());
Map<String, Integer> summary = report.getSummary();
for (Map.Entry<String, Integer> entry : summary.entrySet()) {
String ruleName = entry.getKey();
buf.setLength(0);
buf.append("<tr>");
buf.append("<td>" + ruleName + "</td>");
buf.append("<td align=center>" + entry.getValue().intValue() + "</td>");
buf.append("</tr>");
writer.write(buf.toString());
}
writer.write("</table>");
}
}
Loaded 30 of 37 files, more files were not shown because too many files have changed in this diff. Show more