PMD Core - The foundational library module providing essential infrastructure for PMD static code analysis including AST handling, rule execution, configuration management, and reporting mechanisms.
—
The Rendering System provides extensible infrastructure for formatting PMD analysis reports in various output formats. It includes a base renderer interface, built-in format implementations, and customizable properties for different output requirements.
Core interface for formatting PMD reports with support for multiple output formats and configurable properties.
/**
* Interface for rendering PMD reports in various formats.
* Extends PropertySource to support configurable rendering options.
*/
public interface Renderer extends PropertySource {
/**
* Get renderer name/identifier
* @return Unique name for the renderer
*/
String getName();
/**
* Set renderer name
* @param name Unique identifier for renderer
*/
void setName(String name);
/**
* Get renderer description
* @return Human-readable description of renderer purpose
*/
String getDescription();
/**
* Set renderer description
* @param description Description of renderer functionality
*/
void setDescription(String description);
/**
* Get default file extension for output
* @return File extension without dot (e.g., "xml", "html")
*/
String defaultFileExtension();
/**
* Check if suppressed violations are shown
* @return true if renderer includes suppressed violations
*/
boolean isShowSuppressedViolations();
/**
* Set whether to show suppressed violations
* @param show true to include suppressed violations in output
*/
void setShowSuppressedViolations(boolean show);
/**
* Set output writer for rendering
* @param writer Writer for formatted output
*/
void setWriter(Writer writer);
/**
* Set report file path (alternative to setWriter)
* @param reportFile Path to output file
*/
void setReportFile(String reportFile);
/**
* Create analysis listener for receiving events
* @return GlobalAnalysisListener that forwards to this renderer
*/
GlobalAnalysisListener newListener();
/**
* Start rendering process (write headers, opening tags, etc.)
*/
void start();
/**
* End rendering process (write footers, closing tags, etc.)
*/
void end();
/**
* Flush any buffered output
*/
void flush();
}Usage Examples:
import net.sourceforge.pmd.renderers.*;
import net.sourceforge.pmd.*;
import java.io.*;
// Basic renderer usage
public class RendererExample {
public void useXMLRenderer() throws IOException {
// Create XML renderer
XMLRenderer xmlRenderer = new XMLRenderer();
xmlRenderer.setName("xml-report");
xmlRenderer.setDescription("XML format report");
// Configure output
FileWriter writer = new FileWriter("pmd-report.xml");
xmlRenderer.setWriter(writer);
xmlRenderer.setShowSuppressedViolations(true);
// Use with PMD analysis
PMDConfiguration config = new PMDConfiguration();
config.addInputPath(Paths.get("src/main/java"));
config.addRuleSet("rulesets/java/quickstart.xml");
try (PmdAnalysis analysis = PmdAnalysis.create(config)) {
analysis.addRenderer(xmlRenderer);
analysis.files().addDirectory(Paths.get("src/main/java"));
RuleSetLoader loader = analysis.newRuleSetLoader();
analysis.addRuleSet(loader.loadFromResource("rulesets/java/quickstart.xml"));
// Execute analysis - results go to XML renderer
analysis.performAnalysis();
} finally {
writer.close();
}
}
public void useMultipleRenderers() throws IOException {
PMDConfiguration config = new PMDConfiguration();
config.addInputPath(Paths.get("src"));
try (PmdAnalysis analysis = PmdAnalysis.create(config)) {
// Add multiple renderers for different formats
// XML renderer
XMLRenderer xmlRenderer = new XMLRenderer();
xmlRenderer.setWriter(new FileWriter("report.xml"));
analysis.addRenderer(xmlRenderer);
// HTML renderer
HTMLRenderer htmlRenderer = new HTMLRenderer();
htmlRenderer.setWriter(new FileWriter("report.html"));
analysis.addRenderer(htmlRenderer);
// Text renderer for console output
TextRenderer textRenderer = new TextRenderer();
textRenderer.setWriter(new OutputStreamWriter(System.out));
analysis.addRenderer(textRenderer);
// JSON renderer
JsonRenderer jsonRenderer = new JsonRenderer();
jsonRenderer.setWriter(new FileWriter("report.json"));
analysis.addRenderer(jsonRenderer);
// Configure analysis and run
analysis.files().addDirectory(Paths.get("src"));
RuleSetLoader loader = analysis.newRuleSetLoader();
analysis.addRuleSet(loader.loadFromResource("rulesets/java/quickstart.xml"));
analysis.performAnalysis();
}
}
public void configureRendererProperties() {
// Configure HTML renderer with custom properties
HTMLRenderer htmlRenderer = new HTMLRenderer();
// Set renderer properties (if supported)
Properties props = new Properties();
props.setProperty("linkPrefix", "https://github.com/myorg/myrepo/blob/main/");
props.setProperty("linePrefix", "#L");
props.setProperty("title", "PMD Analysis Report");
// Apply properties to renderer (method depends on specific renderer)
htmlRenderer.getPropertyDescriptors().forEach(desc -> {
String value = props.getProperty(desc.name());
if (value != null && desc.type() == String.class) {
@SuppressWarnings("unchecked")
PropertyDescriptor<String> stringDesc = (PropertyDescriptor<String>) desc;
htmlRenderer.setProperty(stringDesc, value);
}
});
System.out.printf("Configured %s with %d properties%n",
htmlRenderer.getName(),
htmlRenderer.getPropertyDescriptors().size());
}
}PMD provides numerous built-in renderers for different output formats and integration scenarios.
/**
* Built-in renderer implementations for various output formats
*/
/**
* XML renderer for structured XML output
*/
class XMLRenderer implements Renderer {
XMLRenderer();
XMLRenderer(String name);
}
/**
* HTML renderer with styling and hyperlinks
*/
class HTMLRenderer implements Renderer {
HTMLRenderer();
HTMLRenderer(String name);
}
/**
* Simple text renderer for plain text output
*/
class TextRenderer implements Renderer {
TextRenderer();
TextRenderer(String name);
}
/**
* Colored text renderer with ANSI color codes
*/
class TextColorRenderer implements Renderer {
TextColorRenderer();
TextColorRenderer(Properties colorSettings);
}
/**
* CSV renderer for comma-separated values
*/
class CSVRenderer implements Renderer {
CSVRenderer();
}
/**
* JSON renderer for structured JSON output
*/
class JsonRenderer implements Renderer {
JsonRenderer();
}
/**
* SARIF renderer for Static Analysis Results Interchange Format
*/
class SarifRenderer implements Renderer {
SarifRenderer();
}
/**
* GNU Emacs integration renderer
*/
class EmacsRenderer implements Renderer {
EmacsRenderer();
}
/**
* IntelliJ IDEA integration renderer
*/
class IDEAJRenderer implements Renderer {
IDEAJRenderer();
}
/**
* Empty renderer that produces no output (silent mode)
*/
class EmptyRenderer implements Renderer {
EmptyRenderer();
}
/**
* Code Climate renderer for CI/CD integration
*/
class CodeClimateRenderer implements Renderer {
CodeClimateRenderer();
}Usage Examples:
import net.sourceforge.pmd.renderers.*;
import java.io.*;
import java.util.Properties;
// Using different built-in renderers
public class BuiltInRenderersExample {
public void demonstrateTextRenderers() throws IOException {
PMDConfiguration config = new PMDConfiguration();
try (PmdAnalysis analysis = PmdAnalysis.create(config)) {
// Simple text renderer
TextRenderer textRenderer = new TextRenderer();
textRenderer.setWriter(new FileWriter("report.txt"));
analysis.addRenderer(textRenderer);
// Colored text renderer for console
Properties colorProps = new Properties();
colorProps.setProperty("color.high", "red");
colorProps.setProperty("color.medium", "yellow");
colorProps.setProperty("color.low", "blue");
TextColorRenderer colorRenderer = new TextColorRenderer(colorProps);
colorRenderer.setWriter(new OutputStreamWriter(System.out));
analysis.addRenderer(colorRenderer);
// Configure and run analysis...
setupAndRunAnalysis(analysis);
}
}
public void demonstrateStructuredRenderers() throws IOException {
PMDConfiguration config = new PMDConfiguration();
try (PmdAnalysis analysis = PmdAnalysis.create(config)) {
// XML renderer
XMLRenderer xmlRenderer = new XMLRenderer("detailed-xml");
xmlRenderer.setWriter(new FileWriter("detailed-report.xml"));
xmlRenderer.setShowSuppressedViolations(true);
analysis.addRenderer(xmlRenderer);
// JSON renderer
JsonRenderer jsonRenderer = new JsonRenderer();
jsonRenderer.setWriter(new FileWriter("report.json"));
analysis.addRenderer(jsonRenderer);
// SARIF renderer for security tools
SarifRenderer sarifRenderer = new SarifRenderer();
sarifRenderer.setWriter(new FileWriter("report.sarif"));
analysis.addRenderer(sarifRenderer);
// CSV renderer for spreadsheet analysis
CSVRenderer csvRenderer = new CSVRenderer();
csvRenderer.setWriter(new FileWriter("report.csv"));
analysis.addRenderer(csvRenderer);
setupAndRunAnalysis(analysis);
}
}
public void demonstrateIDERenderers() throws IOException {
PMDConfiguration config = new PMDConfiguration();
try (PmdAnalysis analysis = PmdAnalysis.create(config)) {
// Emacs integration
EmacsRenderer emacsRenderer = new EmacsRenderer();
emacsRenderer.setWriter(new FileWriter("emacs-report.txt"));
analysis.addRenderer(emacsRenderer);
// IntelliJ IDEA integration
IDEAJRenderer ideaRenderer = new IDEAJRenderer();
ideaRenderer.setWriter(new FileWriter("idea-report.txt"));
analysis.addRenderer(ideaRenderer);
setupAndRunAnalysis(analysis);
}
}
public void demonstrateSpecialRenderers() throws IOException {
PMDConfiguration config = new PMDConfiguration();
try (PmdAnalysis analysis = PmdAnalysis.create(config)) {
// Empty renderer for silent mode (no output)
EmptyRenderer silentRenderer = new EmptyRenderer();
analysis.addRenderer(silentRenderer);
// Code Climate renderer for CI/CD
CodeClimateRenderer ccRenderer = new CodeClimateRenderer();
ccRenderer.setWriter(new FileWriter("codeclimate.json"));
analysis.addRenderer(ccRenderer);
setupAndRunAnalysis(analysis);
}
}
private void setupAndRunAnalysis(PmdAnalysis analysis) throws IOException {
analysis.files().addDirectory(Paths.get("src/main/java"));
RuleSetLoader loader = analysis.newRuleSetLoader();
analysis.addRuleSet(loader.loadFromResource("rulesets/java/quickstart.xml"));
analysis.performAnalysis();
}
}Framework for implementing custom renderers with specific formatting requirements.
/**
* Abstract base class for implementing custom renderers.
* Provides common functionality and lifecycle management.
*/
abstract class AbstractRenderer implements Renderer {
/**
* Constructor with renderer name
* @param name Unique name for the renderer
*/
protected AbstractRenderer(String name);
/**
* Constructor with name and description
* @param name Unique name for the renderer
* @param description Human-readable description
*/
protected AbstractRenderer(String name, String description);
/**
* Get output writer
* @return Writer for formatted output
*/
protected Writer getWriter();
/**
* Write string to output
* @param str String to write
*/
protected void write(String str) throws IOException;
/**
* Write formatted string to output
* @param format Format string
* @param args Format arguments
*/
protected void writef(String format, Object... args) throws IOException;
/**
* Write line to output
* @param line Line to write (newline added automatically)
*/
protected void writeln(String line) throws IOException;
/**
* Render single rule violation (template method)
* @param violation RuleViolation to render
*/
protected abstract void renderViolation(RuleViolation violation) throws IOException;
/**
* Render processing error (template method)
* @param error ProcessingError to render
*/
protected void renderError(Report.ProcessingError error) throws IOException;
/**
* Render configuration error (template method)
* @param error ConfigurationError to render
*/
protected void renderConfigError(Report.ConfigurationError error) throws IOException;
}Usage Examples:
import net.sourceforge.pmd.renderers.AbstractRenderer;
import net.sourceforge.pmd.reporting.*;
import java.io.IOException;
// Custom renderer implementation
public class CustomJSONRenderer extends AbstractRenderer {
private boolean firstViolation = true;
private int violationCount = 0;
public CustomJSONRenderer() {
super("custom-json", "Custom JSON renderer with extra metadata");
}
@Override
public String defaultFileExtension() {
return "json";
}
@Override
public void start() throws IOException {
writeln("{");
writeln(" \"tool\": \"PMD\",");
writef(" \"version\": \"%s\",%n", PMDVersion.VERSION);
writef(" \"timestamp\": \"%s\",%n", new Date());
writeln(" \"violations\": [");
firstViolation = true;
violationCount = 0;
}
@Override
protected void renderViolation(RuleViolation violation) throws IOException {
if (!firstViolation) {
writeln(",");
}
firstViolation = false;
violationCount++;
writeln(" {");
writef(" \"file\": \"%s\",%n", escapeJson(violation.getFilename()));
writef(" \"line\": %d,%n", violation.getBeginLine());
writef(" \"column\": %d,%n", violation.getBeginColumn());
writef(" \"endLine\": %d,%n", violation.getEndLine());
writef(" \"endColumn\": %d,%n", violation.getEndColumn());
writef(" \"rule\": \"%s\",%n", escapeJson(violation.getRule().getName()));
writef(" \"priority\": \"%s\",%n", violation.getRule().getPriority().getName());
writef(" \"message\": \"%s\",%n", escapeJson(violation.getDescription()));
// Add extra metadata
if (!violation.getPackageName().isEmpty()) {
writef(" \"package\": \"%s\",%n", escapeJson(violation.getPackageName()));
}
if (!violation.getClassName().isEmpty()) {
writef(" \"class\": \"%s\",%n", escapeJson(violation.getClassName()));
}
if (!violation.getMethodName().isEmpty()) {
writef(" \"method\": \"%s\",%n", escapeJson(violation.getMethodName()));
}
writef(" \"suppressed\": %b%n", violation.isSuppressed());
write(" }");
}
@Override
protected void renderError(Report.ProcessingError error) throws IOException {
// Add errors to separate section if needed
writef(" // Processing error in %s: %s%n",
error.getFile(), error.getMessage());
}
@Override
public void end() throws IOException {
writeln();
writeln(" ],");
writef(" \"summary\": {%n");
writef(" \"violationCount\": %d%n", violationCount);
writeln(" }");
writeln("}");
flush();
}
private String escapeJson(String str) {
if (str == null) return "null";
return str.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t");
}
}
// Custom renderer with properties
public class CustomHTMLRenderer extends AbstractRenderer {
private static final PropertyDescriptor<String> TITLE_PROPERTY =
PropertyFactory.stringProperty("title")
.desc("HTML report title")
.defaultValue("PMD Analysis Report")
.build();
private static final PropertyDescriptor<String> CSS_URL_PROPERTY =
PropertyFactory.stringProperty("cssUrl")
.desc("URL to external CSS file")
.defaultValue("")
.build();
public CustomHTMLRenderer() {
super("custom-html", "Custom HTML renderer with styling options");
definePropertyDescriptor(TITLE_PROPERTY);
definePropertyDescriptor(CSS_URL_PROPERTY);
}
@Override
public String defaultFileExtension() {
return "html";
}
@Override
public void start() throws IOException {
String title = getProperty(TITLE_PROPERTY);
String cssUrl = getProperty(CSS_URL_PROPERTY);
writeln("<!DOCTYPE html>");
writeln("<html>");
writeln("<head>");
writef(" <title>%s</title>%n", escapeHtml(title));
writeln(" <meta charset=\"UTF-8\">");
if (!cssUrl.isEmpty()) {
writef(" <link rel=\"stylesheet\" href=\"%s\">%n", escapeHtml(cssUrl));
} else {
writeDefaultCSS();
}
writeln("</head>");
writeln("<body>");
writef(" <h1>%s</h1>%n", escapeHtml(title));
writeln(" <table class=\"violations\">");
writeln(" <thead>");
writeln(" <tr>");
writeln(" <th>File</th>");
writeln(" <th>Line</th>");
writeln(" <th>Rule</th>");
writeln(" <th>Priority</th>");
writeln(" <th>Message</th>");
writeln(" </tr>");
writeln(" </thead>");
writeln(" <tbody>");
}
@Override
protected void renderViolation(RuleViolation violation) throws IOException {
writeln(" <tr class=\"violation\">");
writef(" <td>%s</td>%n", escapeHtml(violation.getFilename()));
writef(" <td>%d</td>%n", violation.getBeginLine());
writef(" <td>%s</td>%n", escapeHtml(violation.getRule().getName()));
writef(" <td class=\"priority-%s\">%s</td>%n",
violation.getRule().getPriority().name().toLowerCase(),
violation.getRule().getPriority().getName());
writef(" <td>%s</td>%n", escapeHtml(violation.getDescription()));
writeln(" </tr>");
}
@Override
public void end() throws IOException {
writeln(" </tbody>");
writeln(" </table>");
writeln("</body>");
writeln("</html>");
flush();
}
private void writeDefaultCSS() throws IOException {
writeln(" <style>");
writeln(" body { font-family: Arial, sans-serif; margin: 20px; }");
writeln(" table { border-collapse: collapse; width: 100%; }");
writeln(" th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }");
writeln(" th { background-color: #f2f2f2; }");
writeln(" .priority-high { color: #d32f2f; font-weight: bold; }");
writeln(" .priority-medium { color: #f57c00; }");
writeln(" .priority-low { color: #388e3c; }");
writeln(" </style>");
}
private String escapeHtml(String str) {
if (str == null) return "";
return str.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace("\"", """)
.replace("'", "'");
}
}
// Using custom renderers
public class CustomRendererUsage {
public void useCustomRenderers() throws IOException {
PMDConfiguration config = new PMDConfiguration();
try (PmdAnalysis analysis = PmdAnalysis.create(config)) {
// Use custom JSON renderer
CustomJSONRenderer jsonRenderer = new CustomJSONRenderer();
jsonRenderer.setWriter(new FileWriter("custom-report.json"));
analysis.addRenderer(jsonRenderer);
// Use custom HTML renderer with properties
CustomHTMLRenderer htmlRenderer = new CustomHTMLRenderer();
htmlRenderer.setProperty(htmlRenderer.TITLE_PROPERTY, "My Project Analysis");
htmlRenderer.setProperty(htmlRenderer.CSS_URL_PROPERTY, "styles/pmd-report.css");
htmlRenderer.setWriter(new FileWriter("custom-report.html"));
analysis.addRenderer(htmlRenderer);
// Configure analysis and run
analysis.files().addDirectory(Paths.get("src"));
RuleSetLoader loader = analysis.newRuleSetLoader();
analysis.addRuleSet(loader.loadFromResource("rulesets/java/quickstart.xml"));
analysis.performAnalysis();
}
}
}/**
* Factory for creating built-in renderers by name
*/
final class RendererFactory {
/**
* Create renderer by name
* @param name Renderer name (xml, html, text, json, etc.)
* @param properties Optional properties for configuration
* @return Renderer instance
*/
static Renderer createRenderer(String name, Properties properties);
/**
* Get all available renderer names
* @return Set of supported renderer names
*/
static Set<String> getAvailableRenderers();
/**
* Check if renderer name is supported
* @param name Renderer name to check
* @return true if renderer is available
*/
static boolean isRendererAvailable(String name);
}
/**
* Configuration for file name rendering in reports
*/
interface ConfigurableFileNameRenderer {
/**
* Get display name for file
* @param fileId File identifier
* @return Formatted display name
*/
String getDisplayName(FileId fileId);
/**
* Set root paths for relativizing file names
* @param roots List of root paths for making relative paths
*/
void setRelativizeRoots(List<Path> roots);
/**
* Add root path for relativizing
* @param root Root path to add
*/
void addRelativizeRoot(Path root);
}
/**
* Renderer properties for customization
*/
interface RendererProperties {
/**
* Set string property
* @param name Property name
* @param value Property value
*/
void setProperty(String name, String value);
/**
* Get string property with default
* @param name Property name
* @param defaultValue Default if not set
* @return Property value or default
*/
String getProperty(String name, String defaultValue);
/**
* Set boolean property
* @param name Property name
* @param value Property value
*/
void setBooleanProperty(String name, boolean value);
/**
* Get boolean property with default
* @param name Property name
* @param defaultValue Default if not set
* @return Property value or default
*/
boolean getBooleanProperty(String name, boolean defaultValue);
}Now I'll complete the todo list:
<function_calls> <invoke name="TodoWrite"> <parameter name="todos">[{"content": "Create tile.json manifest for PMD Core Knowledge Tile", "status": "completed", "activeForm": "Creating tile.json manifest"}, {"content": "Create entry-point documentation at docs/index.md with overview and capabilities", "status": "completed", "activeForm": "Creating entry-point documentation"}, {"content": "Create Core PMD Analysis sub-doc for PmdAnalysis and PMDConfiguration", "status": "completed", "activeForm": "Creating Core PMD Analysis sub-doc"}, {"content": "Create Rule System sub-doc for Rule, RuleSet, RulePriority APIs", "status": "completed", "activeForm": "Creating Rule System sub-doc"}, {"content": "Create Language Framework sub-doc for Language, LanguageRegistry APIs", "status": "completed", "activeForm": "Creating Language Framework sub-doc"}, {"content": "Create AST Processing sub-doc for Node and AST navigation APIs", "status": "completed", "activeForm": "Creating AST Processing sub-doc"}, {"content": "Create Reporting System sub-doc for Report, RuleViolation, listeners", "status": "completed", "activeForm": "Creating Reporting System sub-doc"}, {"content": "Create Properties System sub-doc for PropertySource, PropertyDescriptor APIs", "status": "completed", "activeForm": "Creating Properties System sub-doc"}, {"content": "Create Copy-Paste Detection sub-doc for CPD functionality", "status": "completed", "activeForm": "Creating Copy-Paste Detection sub-doc"}, {"content": "Create Utilities and Support Classes sub-doc for utility APIs", "status": "completed", "activeForm": "Creating Utilities sub-doc"}]
Install with Tessl CLI
npx tessl i tessl/maven-net-sourceforge-pmd--pmd-core