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 Rule System provides comprehensive infrastructure for defining, configuring, and executing static analysis rules. It includes rule interfaces, collection management, priority handling, loading mechanisms, and property-based configuration.
Core interface for PMD rules with configuration, execution, and lifecycle management capabilities.
/**
* Core interface for PMD rules with configuration and execution capabilities.
* Extends PropertySource to support configurable properties.
*/
public interface Rule extends PropertySource {
/**
* Suppress violations by regex pattern property descriptor
*/
PropertyDescriptor<Optional<Pattern>> VIOLATION_SUPPRESS_REGEX_DESCRIPTOR;
/**
* Suppress violations by XPath expression property descriptor
*/
PropertyDescriptor<Optional<String>> VIOLATION_SUPPRESS_XPATH_DESCRIPTOR;
/**
* Get rule's target language
* @return Language this rule analyzes
*/
Language getLanguage();
/**
* Set rule's target language
* @param language Language for this rule to analyze
*/
void setLanguage(Language language);
/**
* Get minimum language version required by rule
* @return Minimum LanguageVersion, or null if no minimum
*/
LanguageVersion getMinimumLanguageVersion();
/**
* Set minimum language version required
* @param version Minimum LanguageVersion required
*/
void setMinimumLanguageVersion(LanguageVersion version);
/**
* Get maximum language version supported by rule
* @return Maximum LanguageVersion, or null if no maximum
*/
LanguageVersion getMaximumLanguageVersion();
/**
* Set maximum language version supported
* @param version Maximum LanguageVersion supported
*/
void setMaximumLanguageVersion(LanguageVersion version);
/**
* Check if rule is deprecated
* @return true if rule is marked as deprecated
*/
boolean isDeprecated();
/**
* Set deprecation status
* @param deprecated true to mark rule as deprecated
*/
void setDeprecated(boolean deprecated);
/**
* Get rule name (unique identifier within ruleset)
* @return Name of the rule
*/
String getName();
/**
* Set rule name
* @param name Unique name for the rule
*/
void setName(String name);
/**
* Get PMD version when rule was added
* @return Version string when rule was introduced
*/
String getSince();
/**
* Set PMD version when rule was added
* @param since Version string when rule was introduced
*/
void setSince(String since);
/**
* Get rule implementation class name
* @return Fully qualified class name of rule implementation
*/
String getRuleClass();
/**
* Set rule implementation class name
* @param ruleClass Fully qualified class name
*/
void setRuleClass(String ruleClass);
/**
* Get violation message template
* @return Message template for violations (may contain {0} placeholders)
*/
String getMessage();
/**
* Set violation message template
* @param message Template for violation messages
*/
void setMessage(String message);
/**
* Get rule description
* @return Detailed description of what rule checks
*/
String getDescription();
/**
* Set rule description
* @param description Detailed description of rule purpose
*/
void setDescription(String description);
/**
* Get external documentation URL
* @return URL to external documentation, or null
*/
String getExternalInfoUrl();
/**
* Set external documentation URL
* @param externalInfoUrl URL to external documentation
*/
void setExternalInfoUrl(String externalInfoUrl);
/**
* Get rule priority level
* @return RulePriority indicating severity/importance
*/
RulePriority getPriority();
/**
* Set rule priority level
* @param priority RulePriority for violation severity
*/
void setPriority(RulePriority priority);
/**
* Check if rule uses Data Flow Analysis (DFA)
* @return true if rule requires DFA processing
*/
boolean isDfa();
/**
* Enable/disable Data Flow Analysis
* @param dfa true to enable DFA processing for this rule
*/
void setDfa(boolean dfa);
/**
* Check if rule uses type resolution
* @return true if rule requires type information
*/
boolean isTypeResolution();
/**
* Enable/disable type resolution
* @param typeResolution true to enable type resolution for this rule
*/
void setTypeResolution(boolean typeResolution);
/**
* Check if rule processes multiple files
* @return true if rule analyzes across multiple files
*/
boolean usesMultifile();
/**
* Set multifile processing capability
* @param multifile true if rule should process multiple files
*/
void setMultifile(boolean multifile);
/**
* Create deep copy of rule with all properties
* @return Independent copy of this rule
*/
Rule deepCopy();
/**
* Apply rule to AST node within rule context
* @param target AST node to analyze
* @param ctx RuleContext for violation reporting
*/
void apply(Node target, RuleContext ctx);
}Usage Examples:
import net.sourceforge.pmd.lang.rule.*;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.properties.*;
// Example custom rule implementation
public class MyCustomRule extends AbstractRule {
private static final PropertyDescriptor<Integer> THRESHOLD_DESCRIPTOR =
PropertyFactory.intProperty("threshold")
.desc("Maximum allowed complexity")
.defaultValue(10)
.build();
public MyCustomRule() {
definePropertyDescriptor(THRESHOLD_DESCRIPTOR);
setName("MyCustomRule");
setMessage("Complexity {0} exceeds threshold {1}");
setDescription("Detects overly complex code structures");
setPriority(RulePriority.MEDIUM);
setLanguage(LanguageRegistry.getLanguage("java"));
}
@Override
public void apply(Node target, RuleContext ctx) {
int threshold = getProperty(THRESHOLD_DESCRIPTOR);
int complexity = calculateComplexity(target);
if (complexity > threshold) {
ctx.addViolation(target, complexity, threshold);
}
}
private int calculateComplexity(Node node) {
// Custom complexity calculation logic
return node.getNumChildren() + 1;
}
}
// Configuring rule properties programmatically
Rule rule = new MyCustomRule();
rule.setProperty(MyCustomRule.THRESHOLD_DESCRIPTOR, 15);
rule.setPriority(RulePriority.HIGH);
rule.setMessage("Custom message: complexity {0} is too high");
// Creating rule copy with modifications
Rule ruleCopy = rule.deepCopy();
ruleCopy.setName("MyCustomRuleVariant");
ruleCopy.setPriority(RulePriority.LOW);Collection of rules with optional file filtering patterns and metadata management.
/**
* Collection of rules with optional file filtering patterns.
* Implements ChecksumAware for caching support.
*/
public class RuleSet implements ChecksumAware {
/**
* Copy constructor
* @param rs RuleSet to copy
*/
RuleSet(RuleSet rs);
/**
* Create ruleset containing single rule
* @param rule Single rule for the ruleset
* @return RuleSet containing only the specified rule
*/
static RuleSet forSingleRule(Rule rule);
/**
* Create new ruleset builder for programmatic construction
* @return RuleSetBuilder for fluent ruleset creation
*/
static RuleSetBuilder create();
/**
* Get source filename where ruleset was defined
* @return Filename of ruleset definition, or null if programmatic
*/
String getFileName();
/**
* Get ruleset name
* @return Name of the ruleset
*/
String getName();
/**
* Get ruleset description
* @return Description of ruleset purpose and contents
*/
String getDescription();
/**
* Get all rules in set
* @return Unmodifiable list of rules
*/
List<Rule> getRules();
/**
* Get rule by name
* @param ruleName Name of rule to find
* @return Rule with matching name, or null if not found
*/
Rule getRuleByName(String ruleName);
/**
* Check if ruleset applies to specific file
* @param file FileId to check against include/exclude patterns
* @return true if ruleset should be applied to file
*/
boolean applies(FileId file);
/**
* Check if ruleset applies to language version
* @param languageVersion LanguageVersion to check compatibility
* @return true if ruleset contains rules for language version
*/
boolean applies(LanguageVersion languageVersion);
/**
* Get checksum for caching and change detection
* @return Checksum representing current ruleset state
*/
long getChecksum();
/**
* Get number of rules in set
* @return Count of rules
*/
int size();
/**
* Iterate over rules in set
* @return Iterator for rules
*/
Iterator<Rule> iterator();
}Usage Examples:
import net.sourceforge.pmd.lang.rule.*;
import java.util.Arrays;
// Creating ruleset from single rule
Rule customRule = new MyCustomRule();
RuleSet singleRuleSet = RuleSet.forSingleRule(customRule);
// Building ruleset programmatically
RuleSet programmaticRuleSet = RuleSet.create()
.withName("Custom Analysis Rules")
.withDescription("Rules for custom static analysis")
.addRule(new MyCustomRule())
.addRule(new AnotherCustomRule())
.build();
// Working with loaded ruleset
RuleSetLoader loader = analysis.newRuleSetLoader();
RuleSet javaRules = loader.loadFromResource("rulesets/java/quickstart.xml");
System.out.printf("Loaded %d rules from %s%n",
javaRules.size(),
javaRules.getName());
// Check rule applicability
FileId javaFile = FileId.fromPathLikeString("src/main/java/Example.java");
if (javaRules.applies(javaFile)) {
System.out.println("Ruleset applies to Java files");
}
// Find specific rule
Rule specificRule = javaRules.getRuleByName("UnusedPrivateField");
if (specificRule != null) {
System.out.printf("Found rule: %s - %s%n",
specificRule.getName(),
specificRule.getDescription());
}
// Iterate through rules
for (Rule rule : javaRules) {
System.out.printf("Rule: %s (Priority: %s)%n",
rule.getName(),
rule.getPriority().getName());
}Comprehensive ruleset loading from various sources including files, resources, URLs, and programmatic construction.
/**
* Loads rulesets from various sources with filtering and configuration options.
*/
public final class RuleSetLoader {
/**
* Create RuleSetLoader from PMD configuration
* @param configuration PMDConfiguration with loading settings
* @return Configured RuleSetLoader instance
*/
static RuleSetLoader fromPmdConfig(PMDConfiguration configuration);
/**
* Load ruleset from resource path, file, or URL
* @param ruleSetReferenceId Resource path, file path, or URL to ruleset
* @return Loaded RuleSet with all rules and configurations
* @throws RuleSetLoadException if loading fails
*/
RuleSet loadFromResource(String ruleSetReferenceId);
/**
* Load multiple rulesets from list of references
* @param ruleSetReferenceIds List of resource paths, file paths, or URLs
* @return List of loaded RuleSet instances
* @throws RuleSetLoadException if any loading fails
*/
List<RuleSet> loadFromResources(List<String> ruleSetReferenceIds);
/**
* Create new ruleset builder for programmatic construction
* @return RuleSetBuilder for creating custom rulesets
*/
RuleSetBuilder newRuleSetBuilder();
/**
* Enable compatibility mode for legacy rulesets
* @param enable true to enable compatibility processing
* @return Previous compatibility mode setting
*/
boolean enableCompatibility(boolean enable);
/**
* Create filtered loader that only loads rules above priority threshold
* @param minimumPriority Minimum RulePriority to include
* @return New RuleSetLoader with priority filtering
*/
RuleSetLoader filterAbovePriority(RulePriority minimumPriority);
/**
* Get exceptions that occurred during loading
* @return List of RuleSetLoadException for failed loads
*/
List<RuleSetLoadException> getLoadExceptions();
}Usage Examples:
import net.sourceforge.pmd.lang.rule.*;
import java.util.Arrays;
import java.util.List;
// Create loader from configuration
PMDConfiguration config = new PMDConfiguration();
RuleSetLoader loader = RuleSetLoader.fromPmdConfig(config);
// Load single ruleset
try {
RuleSet javaQuickstart = loader.loadFromResource("rulesets/java/quickstart.xml");
System.out.printf("Loaded %d rules%n", javaQuickstart.size());
} catch (RuleSetLoadException e) {
System.err.println("Failed to load ruleset: " + e.getMessage());
}
// Load multiple rulesets
List<String> rulesetPaths = Arrays.asList(
"rulesets/java/quickstart.xml",
"rulesets/java/design.xml",
"rulesets/java/errorprone.xml",
"custom-rules.xml",
"https://example.com/remote-rules.xml"
);
try {
List<RuleSet> ruleSets = loader.loadFromResources(rulesetPaths);
int totalRules = ruleSets.stream().mapToInt(RuleSet::size).sum();
System.out.printf("Loaded %d rulesets with %d total rules%n",
ruleSets.size(), totalRules);
} catch (RuleSetLoadException e) {
System.err.println("Failed to load some rulesets: " + e.getMessage());
}
// Filter rules by priority
RuleSetLoader highPriorityLoader = loader.filterAbovePriority(RulePriority.MEDIUM);
RuleSet filteredRules = highPriorityLoader.loadFromResource("rulesets/java/quickstart.xml");
// Check for loading exceptions
List<RuleSetLoadException> exceptions = loader.getLoadExceptions();
if (!exceptions.isEmpty()) {
System.err.println("Loading issues occurred:");
for (RuleSetLoadException exception : exceptions) {
System.err.printf(" %s: %s%n", exception.getLocation(), exception.getMessage());
}
}
// Build custom ruleset
RuleSetBuilder builder = loader.newRuleSetBuilder();
RuleSet customRuleSet = builder
.withName("Custom Project Rules")
.withDescription("Rules specific to our project")
.addRule(new MyCustomRule())
.addRuleByReference("rulesets/java/design.xml/CyclomaticComplexity")
.build();Enumeration defining rule priority levels from HIGH to LOW with utility methods for comparison and conversion.
/**
* Rule priority levels from HIGH (most important) to LOW (least important).
* Used for filtering rules and determining violation severity.
*/
public enum RulePriority {
/** Highest priority - critical issues */
HIGH(1),
/** Medium-high priority - important issues */
MEDIUM_HIGH(2),
/** Medium priority - standard issues */
MEDIUM(3),
/** Medium-low priority - minor issues */
MEDIUM_LOW(4),
/** Lowest priority - informational issues */
LOW(5);
/**
* Get numeric priority value (lower numbers = higher priority)
* @return Numeric priority (1-5)
*/
int getPriority();
/**
* Get priority name as string
* @return String representation of priority level
*/
String getName();
/**
* Get RulePriority by numeric value
* @param priority Numeric priority (1-5)
* @return RulePriority corresponding to numeric value
* @throws IllegalArgumentException if priority not in valid range
*/
static RulePriority valueOf(int priority);
/**
* Get RulePriority by name (case-insensitive)
* @param name Priority name (HIGH, MEDIUM, LOW, etc.)
* @return RulePriority corresponding to name
* @throws IllegalArgumentException if name not recognized
*/
static RulePriority valueOfName(String name);
}Usage Examples:
import net.sourceforge.pmd.lang.rule.RulePriority;
// Working with rule priorities
RulePriority high = RulePriority.HIGH;
System.out.printf("Priority %s has numeric value %d%n",
high.getName(), high.getPriority());
// Creating priorities from values
RulePriority medium = RulePriority.valueOf(3);
RulePriority low = RulePriority.valueOfName("LOW");
// Comparing priorities
if (RulePriority.HIGH.getPriority() < RulePriority.LOW.getPriority()) {
System.out.println("HIGH priority has lower numeric value than LOW");
}
// Using priorities in rule configuration
Rule rule = new MyCustomRule();
rule.setPriority(RulePriority.MEDIUM_HIGH);
// Filtering by priority in configuration
PMDConfiguration config = new PMDConfiguration();
config.setMinimumPriority(RulePriority.MEDIUM); // Only MEDIUM+ priority rules
// Priority-based rule filtering
RuleSetLoader loader = RuleSetLoader.fromPmdConfig(config);
RuleSetLoader highPriorityLoader = loader.filterAbovePriority(RulePriority.MEDIUM_HIGH);
// Setting priorities programmatically for different environments
if (isProductionBuild()) {
config.setMinimumPriority(RulePriority.HIGH);
} else if (isDevelopmentBuild()) {
config.setMinimumPriority(RulePriority.LOW);
} else {
config.setMinimumPriority(RulePriority.MEDIUM);
}/**
* Builder for constructing RuleSet instances programmatically
*/
interface RuleSetBuilder {
RuleSetBuilder withName(String name);
RuleSetBuilder withDescription(String description);
RuleSetBuilder addRule(Rule rule);
RuleSetBuilder addRuleByReference(String ruleReference);
RuleSetBuilder filterRules(Predicate<Rule> filter);
RuleSet build();
}
/**
* Context for rule execution providing violation reporting capabilities
*/
interface RuleContext {
void addViolation(Node node);
void addViolation(Node node, Object... args);
void addViolationWithMessage(Node node, String message);
LanguageVersion getLanguageVersion();
String getFilename();
TextDocument getTextDocument();
boolean isIgnored(Rule rule);
}
/**
* Exception thrown when ruleset loading fails
*/
class RuleSetLoadException extends Exception {
String getLocation();
String getCause();
List<String> getErrors();
}
/**
* Interface for objects that can compute checksums for caching
*/
interface ChecksumAware {
long getChecksum();
}
/**
* File identifier for tracking files across analysis
*/
interface FileId {
String getAbsolutePath();
String getDisplayName();
URI getUri();
static FileId fromPathLikeString(String pathLike);
static FileId fromPath(Path path);
}
/**
* Language version representing specific version of programming language
*/
interface LanguageVersion extends Comparable<LanguageVersion> {
Language getLanguage();
String getVersion();
String getName();
String getShortName();
String getTerseName();
}
/**
* Abstract base class for rule implementations
*/
abstract class AbstractRule implements Rule {
protected void definePropertyDescriptor(PropertyDescriptor<?> propertyDescriptor);
protected void addRuleChainVisit(String astNodeName);
protected void addRuleChainVisit(Class<? extends Node> nodeType);
}Install with Tessl CLI
npx tessl i tessl/maven-net-sourceforge-pmd--pmd-core