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 Properties System provides a type-safe, configurable framework for managing rule and component properties. It includes property descriptors, validation, serialization, factory methods, and source management for flexible configuration.
Base interface for objects that support configurable properties with type-safe access and management.
/**
* Base interface for objects that can have configurable properties.
* Provides type-safe property access and management capabilities.
*/
public interface PropertySource {
/**
* Get property value by descriptor
* @param propertyDescriptor PropertyDescriptor defining the property
* @return Current value of the property
*/
<T> T getProperty(PropertyDescriptor<T> propertyDescriptor);
/**
* Set property value by descriptor
* @param propertyDescriptor PropertyDescriptor defining the property
* @param value New value for the property
* @throws IllegalArgumentException if value fails validation
*/
<T> void setProperty(PropertyDescriptor<T> propertyDescriptor, T value);
/**
* Check if property descriptor is supported
* @param descriptor PropertyDescriptor to check
* @return true if this source supports the property
*/
boolean hasDescriptor(PropertyDescriptor<?> descriptor);
/**
* Get all supported property descriptors
* @return Unmodifiable list of all PropertyDescriptor instances
*/
List<PropertyDescriptor<?>> getPropertyDescriptors();
/**
* Get property source for chained access
* @return This PropertySource instance for method chaining
*/
PropertySource getPropertySource();
/**
* Get all properties as map
* @return Map of PropertyDescriptor to current value for all properties
*/
Map<PropertyDescriptor<?>, Object> getPropertiesByPropertyDescriptor();
}Usage Examples:
import net.sourceforge.pmd.properties.*;
import net.sourceforge.pmd.lang.rule.Rule;
// Working with rule properties
public class PropertyExamples {
public void configureRule(Rule rule) {
// Get available properties
List<PropertyDescriptor<?>> properties = rule.getPropertyDescriptors();
System.out.printf("Rule %s has %d properties:%n",
rule.getName(), properties.size());
for (PropertyDescriptor<?> prop : properties) {
System.out.printf(" %s (%s): %s%n",
prop.name(),
prop.type().getSimpleName(),
prop.description());
}
// Check if rule supports specific property
PropertyDescriptor<Integer> thresholdProp = PropertyFactory
.intProperty("threshold").build();
if (rule.hasDescriptor(thresholdProp)) {
// Get current value
Integer currentThreshold = rule.getProperty(thresholdProp);
System.out.printf("Current threshold: %d%n", currentThreshold);
// Set new value
rule.setProperty(thresholdProp, 15);
}
// Get all property values
Map<PropertyDescriptor<?>, Object> allProps =
rule.getPropertiesByPropertyDescriptor();
allProps.forEach((desc, value) ->
System.out.printf("%s = %s%n", desc.name(), value));
}
public void bulkPropertyConfiguration(Rule rule) {
// Configure multiple properties at once
Map<PropertyDescriptor<?>, Object> newValues = new HashMap<>();
// Find and configure threshold property
rule.getPropertyDescriptors().stream()
.filter(prop -> prop.name().equals("threshold"))
.findFirst()
.ifPresent(prop -> {
if (prop.type() == Integer.class) {
@SuppressWarnings("unchecked")
PropertyDescriptor<Integer> intProp =
(PropertyDescriptor<Integer>) prop;
rule.setProperty(intProp, 20);
}
});
}
}Descriptor interface defining property metadata, validation, and serialization capabilities.
/**
* Describes a configurable property with type, constraints, and validation.
* Provides metadata and serialization capabilities for type-safe properties.
*/
public interface PropertyDescriptor<T> {
/**
* Get property name (unique identifier)
* @return Name of the property
*/
String name();
/**
* Get property description for documentation
* @return Human-readable description of property purpose
*/
String description();
/**
* Get property value type
* @return Class representing the property type
*/
Class<T> type();
/**
* Get default value for property
* @return Default value when property is not explicitly set
*/
T defaultValue();
/**
* Check if property is required (must be explicitly set)
* @return true if property must have a non-default value
*/
boolean isRequired();
/**
* Serialize property value to string representation
* @param value Property value to serialize
* @return String representation of the value
*/
String asDelimitedString(T value);
/**
* Parse property value from string representation
* @param propertyString String representation to parse
* @return Parsed property value
* @throws IllegalArgumentException if string cannot be parsed
*/
T valueFrom(String propertyString);
/**
* Validate property value and return error message
* @param value Value to validate
* @return Error message if validation fails, null if valid
*/
String errorFor(T value);
}Usage Examples:
import net.sourceforge.pmd.properties.*;
// Working with property descriptors
public class PropertyDescriptorExamples {
// Define custom property descriptors
private static final PropertyDescriptor<Integer> MAX_COMPLEXITY =
PropertyFactory.intProperty("maxComplexity")
.desc("Maximum allowed cyclomatic complexity")
.defaultValue(10)
.range(1, 100)
.build();
private static final PropertyDescriptor<String> NAMING_PATTERN =
PropertyFactory.stringProperty("namingPattern")
.desc("Regular expression for valid names")
.defaultValue("[a-zA-Z][a-zA-Z0-9]*")
.build();
private static final PropertyDescriptor<List<String>> IGNORED_ANNOTATIONS =
PropertyFactory.stringListProperty("ignoredAnnotations")
.desc("Annotations to ignore during analysis")
.defaultValue(Arrays.asList("SuppressWarnings", "Generated"))
.build();
public void demonstratePropertyUsage() {
// Working with property metadata
System.out.printf("Property: %s%n", MAX_COMPLEXITY.name());
System.out.printf("Type: %s%n", MAX_COMPLEXITY.type().getSimpleName());
System.out.printf("Description: %s%n", MAX_COMPLEXITY.description());
System.out.printf("Default: %s%n", MAX_COMPLEXITY.defaultValue());
System.out.printf("Required: %s%n", MAX_COMPLEXITY.isRequired());
// Serialization and deserialization
Integer value = 15;
String serialized = MAX_COMPLEXITY.asDelimitedString(value);
System.out.printf("Serialized: %s%n", serialized);
Integer deserialized = MAX_COMPLEXITY.valueFrom(serialized);
System.out.printf("Deserialized: %s%n", deserialized);
// Validation
String error = MAX_COMPLEXITY.errorFor(150); // Out of range
if (error != null) {
System.out.printf("Validation error: %s%n", error);
}
// Working with complex types
List<String> annotations = Arrays.asList("Override", "Deprecated");
String listSerialized = IGNORED_ANNOTATIONS.asDelimitedString(annotations);
System.out.printf("List serialized: %s%n", listSerialized);
List<String> listDeserialized = IGNORED_ANNOTATIONS.valueFrom(listSerialized);
System.out.printf("List deserialized: %s%n", listDeserialized);
}
public void validatePropertyValues() {
// Test various validation scenarios
testValidation(MAX_COMPLEXITY, 5, true); // Valid
testValidation(MAX_COMPLEXITY, 0, false); // Too low
testValidation(MAX_COMPLEXITY, 150, false); // Too high
testValidation(MAX_COMPLEXITY, null, false); // Null
testValidation(NAMING_PATTERN, "validName", true); // Valid
testValidation(NAMING_PATTERN, "123invalid", false); // Invalid pattern
testValidation(NAMING_PATTERN, "", false); // Empty
}
private <T> void testValidation(PropertyDescriptor<T> prop, T value, boolean expectValid) {
String error = prop.errorFor(value);
boolean isValid = (error == null);
System.out.printf("Property %s, value %s: %s%n",
prop.name(),
value,
isValid ? "VALID" : "INVALID (" + error + ")");
assert isValid == expectValid :
"Validation result mismatch for " + prop.name() + " = " + value;
}
}Factory class providing builder methods for creating typed property descriptors with validation and constraints.
/**
* Factory for creating typed property descriptors with builders.
* Provides convenient methods for common property types with validation.
*/
public final class PropertyFactory {
/**
* Create string property builder
* @param name Property name
* @return StringPropertyBuilder for configuring string properties
*/
static StringPropertyBuilder stringProperty(String name);
/**
* Create boolean property builder
* @param name Property name
* @return BooleanPropertyBuilder for configuring boolean properties
*/
static BooleanPropertyBuilder booleanProperty(String name);
/**
* Create integer property builder
* @param name Property name
* @return IntegerPropertyBuilder for configuring integer properties
*/
static IntegerPropertyBuilder intProperty(String name);
/**
* Create long property builder
* @param name Property name
* @return LongPropertyBuilder for configuring long properties
*/
static LongPropertyBuilder longProperty(String name);
/**
* Create double property builder
* @param name Property name
* @return DoublePropertyBuilder for configuring double properties
*/
static DoublePropertyBuilder doubleProperty(String name);
/**
* Create character property builder
* @param name Property name
* @return CharacterPropertyBuilder for configuring character properties
*/
static CharacterPropertyBuilder charProperty(String name);
/**
* Create regex pattern property builder
* @param name Property name
* @return RegexPropertyBuilder for configuring Pattern properties
*/
static RegexPropertyBuilder regexProperty(String name);
/**
* Create enum property builder
* @param name Property name
* @param enumType Enum class for valid values
* @return EnumPropertyBuilder for configuring enum properties
*/
static <E extends Enum<E>> EnumPropertyBuilder<E> enumProperty(String name, Class<E> enumType);
/**
* Create file property builder
* @param name Property name
* @return FilePropertyBuilder for configuring File properties
*/
static FilePropertyBuilder fileProperty(String name);
/**
* Create class type property builder
* @param name Property name
* @return ClassPropertyBuilder for configuring Class<?> properties
*/
static ClassPropertyBuilder typeProperty(String name);
}Usage Examples:
import net.sourceforge.pmd.properties.*;
import java.io.File;
import java.util.Arrays;
import java.util.regex.Pattern;
// Creating property descriptors with PropertyFactory
public class PropertyFactoryExamples {
public void createBasicProperties() {
// String properties with validation
PropertyDescriptor<String> nameProperty = PropertyFactory
.stringProperty("ruleName")
.desc("Name of the rule")
.defaultValue("CustomRule")
.build();
// Integer properties with range validation
PropertyDescriptor<Integer> thresholdProperty = PropertyFactory
.intProperty("threshold")
.desc("Threshold value for rule")
.defaultValue(10)
.range(1, 100)
.build();
// Boolean properties
PropertyDescriptor<Boolean> enabledProperty = PropertyFactory
.booleanProperty("enabled")
.desc("Whether rule is enabled")
.defaultValue(true)
.build();
// Double properties with precision constraints
PropertyDescriptor<Double> ratioProperty = PropertyFactory
.doubleProperty("ratio")
.desc("Ratio threshold")
.defaultValue(0.5)
.range(0.0, 1.0)
.build();
}
public void createAdvancedProperties() {
// Enum properties
enum Priority { LOW, MEDIUM, HIGH }
PropertyDescriptor<Priority> priorityProperty = PropertyFactory
.enumProperty("priority", Priority.class)
.desc("Rule priority level")
.defaultValue(Priority.MEDIUM)
.build();
// Regular expression properties
PropertyDescriptor<Pattern> patternProperty = PropertyFactory
.regexProperty("namePattern")
.desc("Pattern for valid names")
.defaultValue(Pattern.compile("[a-zA-Z][a-zA-Z0-9]*"))
.build();
// File properties
PropertyDescriptor<File> configFileProperty = PropertyFactory
.fileProperty("configFile")
.desc("Configuration file path")
.defaultValue(new File("config.properties"))
.build();
// Class type properties
PropertyDescriptor<Class<?>> handlerClassProperty = PropertyFactory
.typeProperty("handlerClass")
.desc("Handler class for processing")
.defaultValue(Object.class)
.build();
}
public void createCollectionProperties() {
// String list properties
PropertyDescriptor<List<String>> exclusionsProperty = PropertyFactory
.stringListProperty("exclusions")
.desc("List of patterns to exclude")
.defaultValue(Arrays.asList("test", "generated"))
.delim(',') // Custom delimiter
.build();
// Integer list properties
PropertyDescriptor<List<Integer>> thresholdsProperty = PropertyFactory
.intListProperty("thresholds")
.desc("List of threshold values")
.defaultValue(Arrays.asList(5, 10, 15))
.range(1, 50) // Range applies to each element
.build();
}
public void createConstrainedProperties() {
// String with length constraints
PropertyDescriptor<String> abbreviationProperty = PropertyFactory
.stringProperty("abbreviation")
.desc("Short abbreviation")
.defaultValue("ABC")
.range(2, 5) // Length range
.build();
// String with choice constraints
PropertyDescriptor<String> modeProperty = PropertyFactory
.stringProperty("mode")
.desc("Operation mode")
.defaultValue("strict")
.validChoices("strict", "lenient", "custom")
.build();
// Character with constraints
PropertyDescriptor<Character> separatorProperty = PropertyFactory
.charProperty("separator")
.desc("Field separator character")
.defaultValue(',')
.validChoices(',', ';', '|', '\t')
.build();
}
public void demonstratePropertyBuilder() {
// Complex property with multiple constraints
PropertyDescriptor<Integer> complexProperty = PropertyFactory
.intProperty("complexThreshold")
.desc("Complex threshold with multiple constraints")
.defaultValue(15)
.range(5, 50)
.require() // Make property required
.build();
// Multi-valued property with custom validation
PropertyDescriptor<List<String>> packagePrefixes = PropertyFactory
.stringListProperty("packagePrefixes")
.desc("Valid package prefixes")
.defaultValue(Arrays.asList("com.example", "org.project"))
.delim(';')
.emptyAllowed(false) // Don't allow empty list
.build();
}
public void usePropertiesInRule() {
// Example of defining properties in a custom rule
class CustomRule extends AbstractRule {
private static final PropertyDescriptor<Integer> COMPLEXITY_THRESHOLD =
PropertyFactory.intProperty("complexityThreshold")
.desc("Maximum allowed complexity")
.defaultValue(10)
.range(1, 50)
.build();
private static final PropertyDescriptor<List<String>> IGNORED_METHODS =
PropertyFactory.stringListProperty("ignoredMethods")
.desc("Method names to ignore")
.defaultValue(Arrays.asList("toString", "hashCode", "equals"))
.build();
public CustomRule() {
definePropertyDescriptor(COMPLEXITY_THRESHOLD);
definePropertyDescriptor(IGNORED_METHODS);
}
@Override
public void apply(List<? extends Node> nodes, RuleContext ctx) {
int threshold = getProperty(COMPLEXITY_THRESHOLD);
List<String> ignored = getProperty(IGNORED_METHODS);
// Use properties in rule logic
for (Node node : nodes) {
// Rule implementation using configured values
}
}
}
}
}Builder interfaces for fluent construction of property descriptors with type-specific constraints and validation.
/**
* Builder for string properties with length and choice constraints
*/
interface StringPropertyBuilder {
StringPropertyBuilder desc(String description);
StringPropertyBuilder defaultValue(String defaultValue);
StringPropertyBuilder require();
StringPropertyBuilder range(int minLength, int maxLength);
StringPropertyBuilder validChoices(String... choices);
PropertyDescriptor<String> build();
}
/**
* Builder for integer properties with range constraints
*/
interface IntegerPropertyBuilder {
IntegerPropertyBuilder desc(String description);
IntegerPropertyBuilder defaultValue(Integer defaultValue);
IntegerPropertyBuilder require();
IntegerPropertyBuilder range(int min, int max);
PropertyDescriptor<Integer> build();
}
/**
* Builder for boolean properties
*/
interface BooleanPropertyBuilder {
BooleanPropertyBuilder desc(String description);
BooleanPropertyBuilder defaultValue(Boolean defaultValue);
BooleanPropertyBuilder require();
PropertyDescriptor<Boolean> build();
}
/**
* Builder for double properties with range constraints
*/
interface DoublePropertyBuilder {
DoublePropertyBuilder desc(String description);
DoublePropertyBuilder defaultValue(Double defaultValue);
DoublePropertyBuilder require();
DoublePropertyBuilder range(double min, double max);
PropertyDescriptor<Double> build();
}
/**
* Builder for enum properties with type safety
*/
interface EnumPropertyBuilder<E extends Enum<E>> {
EnumPropertyBuilder<E> desc(String description);
EnumPropertyBuilder<E> defaultValue(E defaultValue);
EnumPropertyBuilder<E> require();
PropertyDescriptor<E> build();
}
/**
* Builder for regular expression pattern properties
*/
interface RegexPropertyBuilder {
RegexPropertyBuilder desc(String description);
RegexPropertyBuilder defaultValue(Pattern defaultValue);
RegexPropertyBuilder require();
PropertyDescriptor<Pattern> build();
}
/**
* Builder for file properties
*/
interface FilePropertyBuilder {
FilePropertyBuilder desc(String description);
FilePropertyBuilder defaultValue(File defaultValue);
FilePropertyBuilder require();
PropertyDescriptor<File> build();
}
/**
* Builder for list properties with element constraints
*/
interface ListPropertyBuilder<T> {
ListPropertyBuilder<T> desc(String description);
ListPropertyBuilder<T> defaultValue(List<T> defaultValue);
ListPropertyBuilder<T> require();
ListPropertyBuilder<T> delim(char delimiter);
ListPropertyBuilder<T> emptyAllowed(boolean allowed);
PropertyDescriptor<List<T>> build();
}/**
* Exception thrown when property operations fail
*/
class PropertyException extends RuntimeException {
PropertyException(String message);
PropertyException(String message, Throwable cause);
}
/**
* Property constraint for validation
*/
interface PropertyConstraint<T> {
/**
* Validate value against constraint
* @param value Value to validate
* @return Error message if invalid, null if valid
*/
String validate(T value);
/**
* Get constraint description
* @return Human-readable constraint description
*/
String getConstraintDescription();
}
/**
* Property serializer for custom types
*/
interface PropertySerializer<T> {
/**
* Serialize value to string
* @param value Value to serialize
* @return String representation
*/
String toString(T value);
/**
* Deserialize value from string
* @param str String representation
* @return Deserialized value
*/
T fromString(String str);
}
/**
* Property bundle for grouped configuration
*/
interface PropertyBundle extends PropertySource {
/**
* Get bundle name
* @return Name of the property bundle
*/
String getName();
/**
* Get bundle description
* @return Description of bundle purpose
*/
String getDescription();
/**
* Copy bundle with modifications
* @return New PropertyBundle copy
*/
PropertyBundle copy();
/**
* Merge with another bundle
* @param other PropertyBundle to merge
* @return New PropertyBundle with merged properties
*/
PropertyBundle merge(PropertyBundle other);
}
/**
* Property change listener for configuration updates
*/
interface PropertyChangeListener {
/**
* Handle property value change
* @param source PropertySource that changed
* @param property PropertyDescriptor that changed
* @param oldValue Previous value
* @param newValue New value
*/
<T> void propertyChanged(PropertySource source, PropertyDescriptor<T> property,
T oldValue, T newValue);
}
/**
* Manager for property configurations
*/
interface PropertyManager {
/**
* Register property change listener
* @param listener PropertyChangeListener to add
*/
void addPropertyChangeListener(PropertyChangeListener listener);
/**
* Remove property change listener
* @param listener PropertyChangeListener to remove
*/
void removePropertyChangeListener(PropertyChangeListener listener);
/**
* Load properties from configuration source
* @param source Configuration source (file, URL, etc.)
* @param target PropertySource to configure
*/
void loadProperties(String source, PropertySource target);
/**
* Save properties to configuration source
* @param target Configuration target (file, stream, etc.)
* @param source PropertySource to save
*/
void saveProperties(String target, PropertySource source);
}Install with Tessl CLI
npx tessl i tessl/maven-net-sourceforge-pmd--pmd-core