CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-net-sourceforge-pmd--pmd-java

Java language support module for the PMD static code analyzer with AST processing, symbol resolution, type system, metrics, and 400+ built-in rules

Pending
Overview
Eval results
Files

metrics.mddocs/

Code Metrics

PMD Java provides a comprehensive metrics system with 12 built-in metrics for measuring code quality, complexity, and maintainability. These metrics follow established standards and can be configured with various options.

Capabilities

Metrics System

The metrics system is built on the Metric interface and can be used to calculate various code quality measures.

/**
 * Built-in Java metrics for code quality analysis
 */
public final class JavaMetrics {
    // Complexity metrics
    public static final Metric<ASTExecutableDeclaration, Integer> CYCLO;
    public static final Metric<ASTExecutableDeclaration, Integer> COGNITIVE_COMPLEXITY;
    public static final Metric<ASTExecutableDeclaration, BigInteger> NPATH;
    
    // Size metrics
    public static final Metric<JavaNode, Integer> LINES_OF_CODE;
    public static final Metric<JavaNode, Integer> NCSS;
    
    // Class metrics
    public static final Metric<ASTTypeDeclaration, Integer> NUMBER_OF_ACCESSORS;
    public static final Metric<ASTTypeDeclaration, Integer> NUMBER_OF_PUBLIC_FIELDS;
    public static final Metric<ASTTypeDeclaration, Double> TIGHT_CLASS_COHESION;
    public static final Metric<ASTTypeDeclaration, Integer> WEIGHED_METHOD_COUNT;
    public static final Metric<ASTTypeDeclaration, Double> WEIGHT_OF_CLASS;
    
    // Coupling metrics
    public static final Metric<JavaNode, Integer> ACCESS_TO_FOREIGN_DATA;
    public static final Metric<JavaNode, Integer> FAN_OUT;
}

/**
 * Generic metric interface
 */
public interface Metric<T, R> {
    /**
     * Compute the metric value for the given node with options
     */
    R computeFor(T node, MetricOptions options);
    
    /**
     * Get the display name of this metric
     */
    String getDisplayName();
    
    /**
     * Get the abbreviated name of this metric
     */
    String getAbbreviatedName();
}

/**
 * Options for configuring metric calculations
 */
public interface MetricOptions {
    /**
     * Check if a specific option is enabled
     */
    boolean isEnabled(MetricOption option);
}

Complexity Metrics

Cyclomatic Complexity (CYCLO)

Measures the number of independent paths through a block of code using McCabe's original definition.

/**
 * Cyclomatic Complexity metric
 * Counts independent paths through code
 */
public static final Metric<ASTExecutableDeclaration, Integer> CYCLO;

/**
 * Options for cyclomatic complexity calculation
 */
public enum CycloOption implements MetricOption {
    /** Do not count the paths in boolean expressions as decision points */
    IGNORE_BOOLEAN_PATHS("ignoreBooleanPaths"),
    
    /** Consider assert statements as if they were if (..) throw new AssertionError(..) */
    CONSIDER_ASSERT("considerAssert");
    
    String valueName();
}

Usage Example:

// Calculate cyclomatic complexity for a method
ASTMethodDeclaration method = /* ... */;
int complexity = JavaMetrics.CYCLO.computeFor(method, MetricOptions.emptyOptions());

// With options to ignore boolean paths
MetricOptions options = MetricOptions.ofOption(CycloOption.IGNORE_BOOLEAN_PATHS);
int simpleComplexity = JavaMetrics.CYCLO.computeFor(method, options);

Cognitive Complexity (COGNITIVE_COMPLEXITY)

Measures how difficult it is for humans to read and understand a method, with emphasis on nesting.

/**
 * Cognitive Complexity metric
 * Measures human readability complexity with nesting emphasis
 */
public static final Metric<ASTExecutableDeclaration, Integer> COGNITIVE_COMPLEXITY;

Usage Example:

// Calculate cognitive complexity
ASTMethodDeclaration method = /* ... */;
int cognitiveComplexity = JavaMetrics.COGNITIVE_COMPLEXITY.computeFor(method, MetricOptions.emptyOptions());

NPath Complexity (NPATH)

Counts the number of acyclic execution paths through a piece of code.

/**
 * NPath Complexity metric
 * Counts acyclic execution paths (exponential growth with sequential decisions)
 */
public static final Metric<ASTExecutableDeclaration, BigInteger> NPATH;

Usage Example:

// Calculate NPath complexity
ASTMethodDeclaration method = /* ... */;
BigInteger npathComplexity = JavaMetrics.NPATH.computeFor(method, MetricOptions.emptyOptions());

// Check if complexity is over threshold
if (npathComplexity.compareTo(BigInteger.valueOf(200)) > 0) {
    System.out.println("Method is too complex: " + npathComplexity);
}

Size Metrics

Lines of Code (LINES_OF_CODE)

Simply counts the number of lines of code including comments and blank lines.

/**
 * Lines of Code metric
 * Counts total lines including comments and blank lines
 */
public static final Metric<JavaNode, Integer> LINES_OF_CODE;

Non-Commenting Source Statements (NCSS)

Counts the number of statements, roughly equivalent to counting semicolons and opening braces.

/**
 * Non-Commenting Source Statements metric
 * Counts actual statements excluding comments and blank lines
 */
public static final Metric<JavaNode, Integer> NCSS;

/**
 * Options for NCSS calculation
 */
public enum NcssOption implements MetricOption {
    /** Counts import and package statements (makes metric JavaNCSS compliant) */
    COUNT_IMPORTS("countImports");
    
    String valueName();
}

Usage Example:

// Calculate NCSS for a class
ASTClassDeclaration clazz = /* ... */;
int statements = JavaMetrics.NCSS.computeFor(clazz, MetricOptions.emptyOptions());

// With imports counted
MetricOptions options = MetricOptions.ofOption(NcssOption.COUNT_IMPORTS);
int statementsWithImports = JavaMetrics.NCSS.computeFor(clazz, options);

Coupling Metrics

Access to Foreign Data (ACCESS_TO_FOREIGN_DATA)

Counts usages of foreign attributes (not belonging to the current class).

/**
 * Access to Foreign Data metric
 * Counts usages of foreign attributes through direct access or accessors
 */
public static final Metric<JavaNode, Integer> ACCESS_TO_FOREIGN_DATA;

Fan-Out (FAN_OUT)

Counts the number of other classes a given class or operation relies on.

/**
 * Class Fan-Out metric
 * Counts dependencies on other classes (excludes java.lang by default)
 */
public static final Metric<JavaNode, Integer> FAN_OUT;

/**
 * Options for fan-out calculation
 */
public enum ClassFanOutOption implements MetricOption {
    /** Whether to include classes in the java.lang package */
    INCLUDE_JAVA_LANG("includeJavaLang");
    
    String valueName();
}

Usage Example:

// Calculate fan-out excluding java.lang
ASTClassDeclaration clazz = /* ... */;
int fanOut = JavaMetrics.FAN_OUT.computeFor(clazz, MetricOptions.emptyOptions());

// Include java.lang dependencies
MetricOptions options = MetricOptions.ofOption(ClassFanOutOption.INCLUDE_JAVA_LANG);
int totalFanOut = JavaMetrics.FAN_OUT.computeFor(clazz, options);

Object-Oriented Metrics

Weighed Method Count (WEIGHED_METHOD_COUNT)

Sum of the statistical complexity of the operations in the class using CYCLO.

/**
 * Weighed Method Count metric
 * Sum of cyclomatic complexity of all methods in a class
 */
public static final Metric<ASTTypeDeclaration, Integer> WEIGHED_METHOD_COUNT;

Tight Class Cohesion (TIGHT_CLASS_COHESION)

Measures the relative number of method pairs that access common attributes.

/**
 * Tight Class Cohesion metric
 * Measures method pairs that access common attributes (0.0 to 1.0)
 */
public static final Metric<ASTTypeDeclaration, Double> TIGHT_CLASS_COHESION;

Usage Example:

// Calculate class cohesion
ASTClassDeclaration clazz = /* ... */;
double cohesion = JavaMetrics.TIGHT_CLASS_COHESION.computeFor(clazz, MetricOptions.emptyOptions());

if (cohesion > 0.7) {
    System.out.println("High cohesion class");
} else if (cohesion < 0.5) {
    System.out.println("Low cohesion class - consider refactoring");
}

Weight of Class (WEIGHT_OF_CLASS)

Ratio of "functional" public methods to total public methods.

/**
 * Weight of Class metric
 * Ratio of functional methods to total public methods (0.0 to 1.0)
 */
public static final Metric<ASTTypeDeclaration, Double> WEIGHT_OF_CLASS;

Method and Field Count Metrics

Number of Accessors (NUMBER_OF_ACCESSORS)

Counts getter and setter methods in a class.

/**
 * Number of Accessor Methods metric
 * Counts getter and setter methods
 */
public static final Metric<ASTTypeDeclaration, Integer> NUMBER_OF_ACCESSORS;

Number of Public Fields (NUMBER_OF_PUBLIC_FIELDS)

Counts public fields in a class.

/**
 * Number of Public Attributes metric
 * Counts public fields
 */
public static final Metric<ASTTypeDeclaration, Integer> NUMBER_OF_PUBLIC_FIELDS;

Usage Examples

Basic Metric Calculation

import net.sourceforge.pmd.lang.java.metrics.JavaMetrics;
import net.sourceforge.pmd.lang.metrics.MetricOptions;

// Calculate complexity for a method
public void analyzeMethod(ASTMethodDeclaration method) {
    // Cyclomatic complexity
    int cyclo = JavaMetrics.CYCLO.computeFor(method, MetricOptions.emptyOptions());
    
    // Cognitive complexity
    int cognitive = JavaMetrics.COGNITIVE_COMPLEXITY.computeFor(method, MetricOptions.emptyOptions());
    
    // NPath complexity
    BigInteger npath = JavaMetrics.NPATH.computeFor(method, MetricOptions.emptyOptions());
    
    // Lines of code
    int loc = JavaMetrics.LINES_OF_CODE.computeFor(method, MetricOptions.emptyOptions());
    
    System.out.println("Method: " + method.getName());
    System.out.println("  Cyclomatic Complexity: " + cyclo);
    System.out.println("  Cognitive Complexity: " + cognitive);
    System.out.println("  NPath Complexity: " + npath);
    System.out.println("  Lines of Code: " + loc);
}

Class-Level Analysis

// Comprehensive class analysis
public void analyzeClass(ASTClassDeclaration clazz) {
    MetricOptions emptyOptions = MetricOptions.emptyOptions();
    
    // Size metrics
    int loc = JavaMetrics.LINES_OF_CODE.computeFor(clazz, emptyOptions);
    int ncss = JavaMetrics.NCSS.computeFor(clazz, emptyOptions);
    
    // Coupling metrics
    int atfd = JavaMetrics.ACCESS_TO_FOREIGN_DATA.computeFor(clazz, emptyOptions);
    int fanOut = JavaMetrics.FAN_OUT.computeFor(clazz, emptyOptions);
    
    // Object-oriented metrics
    int wmc = JavaMetrics.WEIGHED_METHOD_COUNT.computeFor(clazz, emptyOptions);
    double tcc = JavaMetrics.TIGHT_CLASS_COHESION.computeFor(clazz, emptyOptions);
    double woc = JavaMetrics.WEIGHT_OF_CLASS.computeFor(clazz, emptyOptions);
    
    // Method and field counts
    int accessors = JavaMetrics.NUMBER_OF_ACCESSORS.computeFor(clazz, emptyOptions);
    int publicFields = JavaMetrics.NUMBER_OF_PUBLIC_FIELDS.computeFor(clazz, emptyOptions);
    
    System.out.println("Class: " + clazz.getSimpleName());
    System.out.println("Size Metrics:");
    System.out.println("  Lines of Code: " + loc);
    System.out.println("  NCSS: " + ncss);
    System.out.println("Coupling Metrics:");
    System.out.println("  ATFD: " + atfd);
    System.out.println("  Fan-Out: " + fanOut);
    System.out.println("OO Metrics:");
    System.out.println("  WMC: " + wmc);
    System.out.println("  TCC: " + String.format("%.2f", tcc));
    System.out.println("  WOC: " + String.format("%.2f", woc));
    System.out.println("Counts:");
    System.out.println("  Accessors: " + accessors);
    System.out.println("  Public Fields: " + publicFields);
}

Metrics with Options

// Using metric options for customized calculations
public void analyzeWithOptions(JavaNode node) {
    // Cyclomatic complexity ignoring boolean paths
    MetricOptions cycloOptions = MetricOptions.ofOption(
        JavaMetrics.CycloOption.IGNORE_BOOLEAN_PATHS,
        JavaMetrics.CycloOption.CONSIDER_ASSERT
    );
    
    if (node instanceof ASTExecutableDeclaration) {
        ASTExecutableDeclaration method = (ASTExecutableDeclaration) node;
        int cyclo = JavaMetrics.CYCLO.computeFor(method, cycloOptions);
        System.out.println("Simplified Cyclomatic Complexity: " + cyclo);
    }
    
    // NCSS including imports
    MetricOptions ncssOptions = MetricOptions.ofOption(JavaMetrics.NcssOption.COUNT_IMPORTS);
    int ncss = JavaMetrics.NCSS.computeFor(node, ncssOptions);
    System.out.println("NCSS with imports: " + ncss);
    
    // Fan-out including java.lang
    MetricOptions fanOutOptions = MetricOptions.ofOption(JavaMetrics.ClassFanOutOption.INCLUDE_JAVA_LANG);
    int fanOut = JavaMetrics.FAN_OUT.computeFor(node, fanOutOptions);
    System.out.println("Total Fan-Out: " + fanOut);
}

Custom Metrics Analysis Visitor

// Visitor to collect metrics across an entire compilation unit
public class MetricsCollector extends JavaVisitorBase<Void, Void> {
    private List<ClassMetrics> classMetrics = new ArrayList<>();
    private List<MethodMetrics> methodMetrics = new ArrayList<>();
    
    @Override
    public Void visit(ASTClassDeclaration node, Void data) {
        // Collect class-level metrics
        ClassMetrics metrics = new ClassMetrics();
        metrics.className = node.getSimpleName();
        metrics.loc = JavaMetrics.LINES_OF_CODE.computeFor(node, MetricOptions.emptyOptions());
        metrics.ncss = JavaMetrics.NCSS.computeFor(node, MetricOptions.emptyOptions());
        metrics.wmc = JavaMetrics.WEIGHED_METHOD_COUNT.computeFor(node, MetricOptions.emptyOptions());
        metrics.tcc = JavaMetrics.TIGHT_CLASS_COHESION.computeFor(node, MetricOptions.emptyOptions());
        
        classMetrics.add(metrics);
        
        return super.visit(node, data);
    }
    
    @Override
    public Void visit(ASTMethodDeclaration node, Void data) {
        // Collect method-level metrics
        MethodMetrics metrics = new MethodMetrics();
        metrics.methodName = node.getName();
        metrics.cyclo = JavaMetrics.CYCLO.computeFor(node, MetricOptions.emptyOptions());
        metrics.cognitive = JavaMetrics.COGNITIVE_COMPLEXITY.computeFor(node, MetricOptions.emptyOptions());
        metrics.npath = JavaMetrics.NPATH.computeFor(node, MetricOptions.emptyOptions());
        metrics.loc = JavaMetrics.LINES_OF_CODE.computeFor(node, MetricOptions.emptyOptions());
        
        methodMetrics.add(metrics);
        
        return super.visit(node, data);
    }
    
    public List<ClassMetrics> getClassMetrics() { return classMetrics; }
    public List<MethodMetrics> getMethodMetrics() { return methodMetrics; }
    
    static class ClassMetrics {
        String className;
        int loc, ncss, wmc;
        double tcc;
    }
    
    static class MethodMetrics {
        String methodName;
        int cyclo, cognitive, loc;
        BigInteger npath;
    }
}

Metric Interpretation Guidelines

Complexity Thresholds

  • Cyclomatic Complexity: Methods with CYCLO > 10 should be considered for refactoring
  • Cognitive Complexity: Methods with > 15 cognitive complexity are hard to understand
  • NPath: Methods with NPath > 200 are generally too complex

Object-Oriented Quality

  • Tight Class Cohesion:
    • 70%: High cohesion, single responsibility

    • < 50%: Low cohesion, possibly doing too much
  • Weight of Class:
    • < 30%: Poor encapsulation, reveals too much data
    • 70%: Good behavioral interface

Coupling Guidelines

  • Access to Foreign Data: > 3 for operations suggests encapsulation issues
  • Fan-Out: High values indicate tight coupling to many other classes

Install with Tessl CLI

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

docs

ast-processing.md

index.md

language-support.md

metrics.md

rule-framework.md

symbols-types.md

tile.json