CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-net-sourceforge-pmd--pmd-core

PMD Core - The foundational library module providing essential infrastructure for PMD static code analysis including AST handling, rule execution, configuration management, and reporting mechanisms.

Pending
Overview
Eval results
Files

reporting-system.mddocs/

Reporting System

The Reporting System provides comprehensive infrastructure for collecting, managing, and processing PMD analysis results. It includes violation tracking, error handling, event-driven processing, and statistical reporting capabilities.

Capabilities

Report Management

Central report class that collects and manages all PMD analysis results including violations, errors, and statistics.

/**
 * Collects all PMD analysis results including violations, errors, and metrics.
 * Provides filtering, merging, and statistical analysis capabilities.
 */
public final class Report {
    
    /**
     * Build report programmatically using builder pattern
     * @param lambda Function to configure report builder
     * @return Constructed Report with specified content
     */
    static Report buildReport(Consumer<? super FileAnalysisListener> lambda);
    
    /**
     * Get all rule violations found during analysis
     * @return Unmodifiable list of RuleViolation instances
     */
    List<RuleViolation> getViolations();
    
    /**
     * Get violations that were suppressed by comments or configuration
     * @return List of SuppressedViolation instances
     */
    List<SuppressedViolation> getSuppressedViolations();
    
    /**
     * Get processing errors that occurred during analysis
     * @return List of ProcessingError instances for files that failed to process
     */
    List<ProcessingError> getProcessingErrors();
    
    /**
     * Get configuration errors from rule loading or setup
     * @return List of ConfigurationError instances
     */
    List<ConfigurationError> getConfigurationErrors();
    
    /**
     * Create filtered report containing only violations matching predicate
     * @param filter Predicate to test each violation
     * @return New Report containing only matching violations
     */
    Report filterViolations(Predicate<RuleViolation> filter);
    
    /**
     * Merge this report with another report
     * @param other Report to merge with this one
     * @return New Report containing combined results
     */
    Report union(Report other);
    
    /**
     * Get statistical summary of report contents
     * @return ReportStats with counts and analysis metrics
     */
    ReportStats getStats();
    
    /**
     * Check if report contains any violations or errors
     * @return true if report has no violations, errors, or other issues
     */
    boolean isEmpty();
    
    /**
     * Configuration error during rule setup or loading
     */
    static final class ConfigurationError {
        Rule getRule();
        String getIssue();
        Throwable getCause();
    }
    
    /**
     * Processing error for files that failed analysis
     */
    static final class ProcessingError {
        Throwable getError();
        String getFile();
        String getMessage();
    }
    
    /**
     * Violation that was suppressed by user configuration
     */
    static final class SuppressedViolation {
        RuleViolation getRuleViolation();
        String getSuppressedByUser();
        String getSuppressedByRule();
    }
    
    /**
     * Builder interface for programmatic report construction
     */
    interface GlobalReportBuilderListener {
        void onRuleViolation(RuleViolation violation);
        void onSuppressedRuleViolation(SuppressedViolation violation);
        void onProcessingError(ProcessingError error);
        void onConfigurationError(ConfigurationError error);
    }
    
    /**
     * File-specific builder for incremental report construction
     */
    interface ReportBuilderListener {
        void onRuleViolation(RuleViolation violation);
        void onSuppressedRuleViolation(SuppressedViolation violation);
        void onError(ProcessingError error);
    }
}

Usage Examples:

import net.sourceforge.pmd.reporting.*;
import java.util.List;
import java.util.function.Predicate;

// Working with analysis reports
public class ReportAnalysisExamples {
    
    public void analyzeReport(Report report) {
        // Get basic statistics
        ReportStats stats = report.getStats();
        System.out.printf("Analysis Results:%n");
        System.out.printf("  Violations: %d%n", stats.getNumViolations());
        System.out.printf("  Errors: %d%n", stats.getNumErrors());
        System.out.printf("  Processing Errors: %d%n", stats.getNumProcessingErrors());
        System.out.printf("  Configuration Errors: %d%n", stats.getNumConfigErrors());
        
        // Check if analysis was successful
        if (report.isEmpty()) {
            System.out.println("No issues found!");
        } else {
            processViolations(report);
            processErrors(report);
        }
    }
    
    public void processViolations(Report report) {
        List<RuleViolation> violations = report.getViolations();
        
        // Group violations by file
        Map<String, List<RuleViolation>> violationsByFile = violations.stream()
            .collect(Collectors.groupingBy(RuleViolation::getFilename));
        
        violationsByFile.forEach((file, fileViolations) -> {
            System.out.printf("%n%s (%d violations):%n", file, fileViolations.size());
            fileViolations.forEach(violation -> {
                System.out.printf("  Line %d: %s [%s]%n",
                    violation.getBeginLine(),
                    violation.getDescription(),
                    violation.getRule().getName());
            });
        });
        
        // Show suppressed violations if any
        List<Report.SuppressedViolation> suppressed = report.getSuppressedViolations();
        if (!suppressed.isEmpty()) {
            System.out.printf("%nSuppressed violations: %d%n", suppressed.size());
            suppressed.forEach(sv -> {
                RuleViolation violation = sv.getRuleViolation();
                System.out.printf("  %s:%d - %s (suppressed by: %s)%n",
                    violation.getFilename(),
                    violation.getBeginLine(), 
                    violation.getRule().getName(),
                    sv.getSuppressedByUser() != null ? "user" : "rule");
            });
        }
    }
    
    public void processErrors(Report report) {
        // Handle processing errors
        List<Report.ProcessingError> processingErrors = report.getProcessingErrors();
        if (!processingErrors.isEmpty()) {
            System.out.printf("%nProcessing errors: %d%n", processingErrors.size());
            processingErrors.forEach(error -> {
                System.out.printf("  %s: %s%n", error.getFile(), error.getMessage());
                if (error.getError() != null) {
                    error.getError().printStackTrace();
                }
            });
        }
        
        // Handle configuration errors
        List<Report.ConfigurationError> configErrors = report.getConfigurationErrors();
        if (!configErrors.isEmpty()) {
            System.out.printf("%nConfiguration errors: %d%n", configErrors.size());
            configErrors.forEach(error -> {
                System.out.printf("  Rule %s: %s%n", 
                    error.getRule() != null ? error.getRule().getName() : "unknown",
                    error.getIssue());
            });
        }
    }
    
    public Report filterHighPriorityViolations(Report report) {
        // Filter to only high priority violations
        Predicate<RuleViolation> highPriorityFilter = violation -> 
            violation.getRule().getPriority() == RulePriority.HIGH ||
            violation.getRule().getPriority() == RulePriority.MEDIUM_HIGH;
        
        return report.filterViolations(highPriorityFilter);
    }
    
    public Report mergeReports(List<Report> reports) {
        // Merge multiple reports into one
        return reports.stream()
            .reduce(Report.empty(), Report::union);
    }
}

// Building custom reports programmatically
public Report buildCustomReport() {
    return Report.buildReport(builder -> {
        // Add custom violations
        builder.onRuleViolation(createViolation("CustomRule", "Custom message", 42));
        
        // Add processing error
        builder.onProcessingError(new Report.ProcessingError(
            new RuntimeException("Parse error"), 
            "BadFile.java", 
            "Syntax error in file"));
        
        // Add configuration error
        builder.onConfigurationError(new Report.ConfigurationError(
            someRule, 
            "Property 'threshold' must be positive", 
            null));
    });
}

Rule Violation Interface

Interface representing individual rule violation instances with location, context, and rule information.

/**
 * Represents a rule violation instance with location and context information.
 * Provides access to rule, location, and descriptive details.
 */
public interface RuleViolation {
    
    /**
     * Get the rule that was violated
     * @return Rule instance that detected this violation
     */
    Rule getRule();
    
    /**
     * Get violation description message
     * @return Human-readable description of the violation
     */
    String getDescription();
    
    /**
     * Check if violation is suppressed
     * @return true if violation was suppressed by comments or configuration
     */
    boolean isSuppressed();
    
    /**
     * Get source filename where violation occurred
     * @return File path or name containing the violation
     */
    String getFilename();
    
    /**
     * Get violation start line number
     * @return One-based line number where violation begins
     */
    int getBeginLine();
    
    /**
     * Get violation start column number
     * @return One-based column number where violation begins
     */
    int getBeginColumn();
    
    /**
     * Get violation end line number
     * @return One-based line number where violation ends
     */
    int getEndLine();
    
    /**
     * Get violation end column number
     * @return One-based column number where violation ends
     */
    int getEndColumn();
    
    /**
     * Get package name containing the violation
     * @return Package name, or empty string if not applicable
     */
    String getPackageName();
    
    /**
     * Get class name containing the violation
     * @return Class name, or empty string if not applicable
     */
    String getClassName();
    
    /**
     * Get method name containing the violation
     * @return Method name, or empty string if not applicable
     */
    String getMethodName();
    
    /**
     * Get variable name associated with violation
     * @return Variable name, or empty string if not applicable
     */
    String getVariableName();
}

Usage Examples:

import net.sourceforge.pmd.reporting.RuleViolation;

// Processing rule violations
public class ViolationProcessor {
    
    public void processViolation(RuleViolation violation) {
        // Basic violation information
        System.out.printf("Violation: %s%n", violation.getDescription());
        System.out.printf("Rule: %s (Priority: %s)%n", 
            violation.getRule().getName(),
            violation.getRule().getPriority().getName());
        
        // Location information
        System.out.printf("File: %s%n", violation.getFilename());
        System.out.printf("Location: %d:%d-%d:%d%n",
            violation.getBeginLine(), violation.getBeginColumn(),
            violation.getEndLine(), violation.getEndColumn());
        
        // Context information (if available)
        if (!violation.getPackageName().isEmpty()) {
            System.out.printf("Package: %s%n", violation.getPackageName());
        }
        if (!violation.getClassName().isEmpty()) {
            System.out.printf("Class: %s%n", violation.getClassName());
        }
        if (!violation.getMethodName().isEmpty()) {
            System.out.printf("Method: %s%n", violation.getMethodName());
        }
        if (!violation.getVariableName().isEmpty()) {
            System.out.printf("Variable: %s%n", violation.getVariableName());
        }
        
        // Check suppression status
        if (violation.isSuppressed()) {
            System.out.println("Note: This violation is suppressed");
        }
    }
    
    public void generateViolationReport(List<RuleViolation> violations) {
        // Sort violations by file and line number
        violations.sort(Comparator
            .comparing(RuleViolation::getFilename)
            .thenComparing(RuleViolation::getBeginLine));
        
        String currentFile = "";
        for (RuleViolation violation : violations) {
            if (!violation.getFilename().equals(currentFile)) {
                currentFile = violation.getFilename();
                System.out.printf("%n=== %s ===%n", currentFile);
            }
            
            System.out.printf("Line %d: %s [%s]%n",
                violation.getBeginLine(),
                violation.getDescription(),
                violation.getRule().getName());
        }
    }
    
    public Map<String, List<RuleViolation>> groupByRule(List<RuleViolation> violations) {
        return violations.stream()
            .collect(Collectors.groupingBy(v -> v.getRule().getName()));
    }
    
    public Map<String, Long> countByPriority(List<RuleViolation> violations) {
        return violations.stream()
            .collect(Collectors.groupingBy(
                v -> v.getRule().getPriority().getName(),
                Collectors.counting()));
    }
}

Analysis Event Listeners

Event-driven interfaces for receiving real-time analysis events and custom result processing.

/**
 * Receives events during PMD analysis for custom processing.
 * Implements AutoCloseable for proper resource management.
 */
public interface GlobalAnalysisListener extends AutoCloseable {
    
    /**
     * Get listener initializer for setup phase
     * @return ListenerInitializer for configuration
     */
    ListenerInitializer initializer();
    
    /**
     * Start analysis of specific file
     * @param file TextFile being analyzed
     * @return FileAnalysisListener for file-specific events
     */
    FileAnalysisListener startFileAnalysis(TextFile file);
    
    /**
     * Handle configuration error
     * @param error ConfigurationError that occurred during setup
     */
    void onConfigError(Report.ConfigurationError error);
    
    /**
     * Create no-operation listener that ignores all events
     * @return GlobalAnalysisListener that performs no actions
     */
    static GlobalAnalysisListener noop();
    
    /**
     * Combine multiple listeners into single listener (tee pattern)
     * @param listeners List of listeners to combine
     * @return GlobalAnalysisListener that forwards events to all listeners
     */
    static GlobalAnalysisListener tee(List<? extends GlobalAnalysisListener> listeners);
    
    /**
     * Close listener and cleanup resources
     */
    void close();
}

/**
 * Receives file-specific analysis events.
 * Created by GlobalAnalysisListener for each file being analyzed.
 */
public interface FileAnalysisListener {
    
    /**
     * Handle rule violation found in current file
     * @param violation RuleViolation detected during analysis
     */
    void onRuleViolation(RuleViolation violation);
    
    /**
     * Handle suppressed rule violation
     * @param violation SuppressedViolation that was suppressed
     */
    void onSuppressedRuleViolation(Report.SuppressedViolation violation);
    
    /**
     * Handle processing error in current file
     * @param error ProcessingError that occurred during file analysis
     */
    void onError(Report.ProcessingError error);
}

/**
 * Initializer for setting up analysis listeners.
 */
interface ListenerInitializer {
    
    /**
     * Initialize listener with analysis context
     * @param ctx AnalysisContext with configuration and metadata
     */
    void initialize(AnalysisContext ctx);
}

Usage Examples:

import net.sourceforge.pmd.reporting.*;
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicInteger;

// Custom listener for real-time violation processing
public class CustomAnalysisListener implements GlobalAnalysisListener {
    private final PrintWriter output;
    private final AtomicInteger totalViolations = new AtomicInteger(0);
    private final AtomicInteger filesProcessed = new AtomicInteger(0);
    
    public CustomAnalysisListener(PrintWriter output) {
        this.output = output;
    }
    
    @Override
    public ListenerInitializer initializer() {
        return ctx -> {
            output.println("Starting PMD analysis...");
            output.printf("Analyzing %d files%n", ctx.getFileCount());
        };
    }
    
    @Override
    public FileAnalysisListener startFileAnalysis(TextFile file) {
        filesProcessed.incrementAndGet();
        output.printf("Analyzing: %s%n", file.getDisplayName());
        
        return new FileAnalysisListener() {
            private int fileViolations = 0;
            
            @Override
            public void onRuleViolation(RuleViolation violation) {
                fileViolations++;
                totalViolations.incrementAndGet();
                
                output.printf("  Line %d: %s [%s]%n",
                    violation.getBeginLine(),
                    violation.getDescription(),
                    violation.getRule().getName());
            }
            
            @Override
            public void onSuppressedRuleViolation(Report.SuppressedViolation violation) {
                output.printf("  Suppressed: %s%n", 
                    violation.getRuleViolation().getRule().getName());
            }
            
            @Override
            public void onError(Report.ProcessingError error) {
                output.printf("  ERROR: %s%n", error.getMessage());
            }
        };
    }
    
    @Override
    public void onConfigError(Report.ConfigurationError error) {
        output.printf("Configuration error: %s%n", error.getIssue());
    }
    
    @Override
    public void close() {
        output.printf("Analysis complete: %d violations in %d files%n",
            totalViolations.get(), filesProcessed.get());
        output.flush();
    }
}

// Statistics collecting listener
public class StatisticsListener implements GlobalAnalysisListener {
    private final Map<String, AtomicInteger> ruleViolationCounts = new ConcurrentHashMap<>();
    private final Map<String, AtomicInteger> fileViolationCounts = new ConcurrentHashMap<>();
    private final AtomicInteger totalErrors = new AtomicInteger(0);
    
    @Override
    public ListenerInitializer initializer() {
        return ctx -> {
            // Initialize statistics collection
            ruleViolationCounts.clear();
            fileViolationCounts.clear();
            totalErrors.set(0);
        };
    }
    
    @Override
    public FileAnalysisListener startFileAnalysis(TextFile file) {
        String fileName = file.getDisplayName();
        fileViolationCounts.put(fileName, new AtomicInteger(0));
        
        return new FileAnalysisListener() {
            @Override
            public void onRuleViolation(RuleViolation violation) {
                String ruleName = violation.getRule().getName();
                ruleViolationCounts.computeIfAbsent(ruleName, k -> new AtomicInteger(0))
                    .incrementAndGet();
                fileViolationCounts.get(fileName).incrementAndGet();
            }
            
            @Override
            public void onSuppressedRuleViolation(Report.SuppressedViolation violation) {
                // Track suppressed violations separately if needed
            }
            
            @Override
            public void onError(Report.ProcessingError error) {
                totalErrors.incrementAndGet();
            }
        };
    }
    
    @Override
    public void onConfigError(Report.ConfigurationError error) {
        totalErrors.incrementAndGet();
    }
    
    public void printStatistics() {
        System.out.println("Violation Statistics:");
        ruleViolationCounts.entrySet().stream()
            .sorted(Map.Entry.<String, AtomicInteger>comparingByValue(
                (a, b) -> b.get() - a.get()))
            .forEach(entry -> System.out.printf("  %s: %d%n", 
                entry.getKey(), entry.getValue().get()));
        
        System.out.printf("Total errors: %d%n", totalErrors.get());
    }
    
    @Override
    public void close() {
        printStatistics();
    }
}

// Usage with PmdAnalysis
try (PmdAnalysis analysis = PmdAnalysis.create(config)) {
    // Add custom listeners
    analysis.addListener(new CustomAnalysisListener(
        new PrintWriter(System.out, true)));
    analysis.addListener(new StatisticsListener());
    
    // Add files and rules
    analysis.files().addDirectory(Paths.get("src"));
    analysis.addRuleSet(ruleSet);
    
    // Run analysis - listeners receive events in real-time
    analysis.performAnalysis();
}

Types

/**
 * Statistical summary of report contents
 */
interface ReportStats {
    
    /**
     * Get total number of violations
     * @return Count of all rule violations
     */
    int getNumViolations();
    
    /**
     * Get total number of errors
     * @return Count of all errors (processing + configuration)
     */
    int getNumErrors();
    
    /**
     * Get number of processing errors
     * @return Count of file processing errors
     */
    int getNumProcessingErrors();
    
    /**
     * Get number of configuration errors  
     * @return Count of rule configuration errors
     */
    int getNumConfigErrors();
    
    /**
     * Get violation counts by rule name
     * @return Map of rule names to violation counts
     */
    Map<String, Integer> getViolationsByRule();
    
    /**
     * Get violation counts by file
     * @return Map of file names to violation counts
     */
    Map<String, Integer> getViolationsByFile();
}

/**
 * Analysis context provided to listeners
 */
interface AnalysisContext {
    
    /**
     * Get number of files to be analyzed
     * @return Total file count for analysis
     */
    int getFileCount();
    
    /**
     * Get PMD configuration
     * @return PMDConfiguration used for analysis
     */
    PMDConfiguration getConfiguration();
    
    /**
     * Get language registry
     * @return LanguageRegistry with supported languages
     */
    LanguageRegistry getLanguageRegistry();
    
    /**
     * Get active rulesets
     * @return List of RuleSet instances being applied
     */
    List<RuleSet> getRuleSets();
}

/**
 * Factory for creating rule violations
 */
interface RuleViolationFactory {
    
    /**
     * Create violation for AST node
     * @param rule Rule that detected violation
     * @param node AST node where violation occurred
     * @param message Violation message
     * @return RuleViolation instance
     */
    RuleViolation createViolation(Rule rule, Node node, String message);
    
    /**
     * Create violation with arguments for message formatting
     * @param rule Rule that detected violation
     * @param node AST node where violation occurred
     * @param messageArgs Arguments for message template
     * @return RuleViolation instance with formatted message
     */
    RuleViolation createViolation(Rule rule, Node node, Object... messageArgs);
}

/**
 * Thread-safe report builder for concurrent analysis
 */
interface ConcurrentReportBuilder {
    
    /**
     * Add violation to report (thread-safe)
     * @param violation RuleViolation to add
     */
    void addViolation(RuleViolation violation);
    
    /**
     * Add suppressed violation to report (thread-safe)
     * @param violation SuppressedViolation to add
     */
    void addSuppressedViolation(Report.SuppressedViolation violation);
    
    /**
     * Add processing error to report (thread-safe)
     * @param error ProcessingError to add
     */
    void addError(Report.ProcessingError error);
    
    /**
     * Build final report from collected data
     * @return Report containing all collected violations and errors
     */
    Report build();
}

Install with Tessl CLI

npx tessl i tessl/maven-net-sourceforge-pmd--pmd-core

docs

ast-processing.md

copy-paste-detection.md

core-analysis.md

index.md

language-framework.md

properties-system.md

rendering-system.md

reporting-system.md

rule-system.md

utilities.md

tile.json