or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ast-processing.mdindex.mdlanguage-support.mdmetrics.mdrule-framework.mdsymbols-types.md

metrics.mddocs/

0

# Code Metrics

1

2

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.

3

4

## Capabilities

5

6

### Metrics System

7

8

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

9

10

```java { .api }

11

/**

12

* Built-in Java metrics for code quality analysis

13

*/

14

public final class JavaMetrics {

15

// Complexity metrics

16

public static final Metric<ASTExecutableDeclaration, Integer> CYCLO;

17

public static final Metric<ASTExecutableDeclaration, Integer> COGNITIVE_COMPLEXITY;

18

public static final Metric<ASTExecutableDeclaration, BigInteger> NPATH;

19

20

// Size metrics

21

public static final Metric<JavaNode, Integer> LINES_OF_CODE;

22

public static final Metric<JavaNode, Integer> NCSS;

23

24

// Class metrics

25

public static final Metric<ASTTypeDeclaration, Integer> NUMBER_OF_ACCESSORS;

26

public static final Metric<ASTTypeDeclaration, Integer> NUMBER_OF_PUBLIC_FIELDS;

27

public static final Metric<ASTTypeDeclaration, Double> TIGHT_CLASS_COHESION;

28

public static final Metric<ASTTypeDeclaration, Integer> WEIGHED_METHOD_COUNT;

29

public static final Metric<ASTTypeDeclaration, Double> WEIGHT_OF_CLASS;

30

31

// Coupling metrics

32

public static final Metric<JavaNode, Integer> ACCESS_TO_FOREIGN_DATA;

33

public static final Metric<JavaNode, Integer> FAN_OUT;

34

}

35

36

/**

37

* Generic metric interface

38

*/

39

public interface Metric<T, R> {

40

/**

41

* Compute the metric value for the given node with options

42

*/

43

R computeFor(T node, MetricOptions options);

44

45

/**

46

* Get the display name of this metric

47

*/

48

String getDisplayName();

49

50

/**

51

* Get the abbreviated name of this metric

52

*/

53

String getAbbreviatedName();

54

}

55

56

/**

57

* Options for configuring metric calculations

58

*/

59

public interface MetricOptions {

60

/**

61

* Check if a specific option is enabled

62

*/

63

boolean isEnabled(MetricOption option);

64

}

65

```

66

67

### Complexity Metrics

68

69

#### Cyclomatic Complexity (CYCLO)

70

71

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

72

73

```java { .api }

74

/**

75

* Cyclomatic Complexity metric

76

* Counts independent paths through code

77

*/

78

public static final Metric<ASTExecutableDeclaration, Integer> CYCLO;

79

80

/**

81

* Options for cyclomatic complexity calculation

82

*/

83

public enum CycloOption implements MetricOption {

84

/** Do not count the paths in boolean expressions as decision points */

85

IGNORE_BOOLEAN_PATHS("ignoreBooleanPaths"),

86

87

/** Consider assert statements as if they were if (..) throw new AssertionError(..) */

88

CONSIDER_ASSERT("considerAssert");

89

90

String valueName();

91

}

92

```

93

94

**Usage Example:**

95

```java

96

// Calculate cyclomatic complexity for a method

97

ASTMethodDeclaration method = /* ... */;

98

int complexity = JavaMetrics.CYCLO.computeFor(method, MetricOptions.emptyOptions());

99

100

// With options to ignore boolean paths

101

MetricOptions options = MetricOptions.ofOption(CycloOption.IGNORE_BOOLEAN_PATHS);

102

int simpleComplexity = JavaMetrics.CYCLO.computeFor(method, options);

103

```

104

105

#### Cognitive Complexity (COGNITIVE_COMPLEXITY)

106

107

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

108

109

```java { .api }

110

/**

111

* Cognitive Complexity metric

112

* Measures human readability complexity with nesting emphasis

113

*/

114

public static final Metric<ASTExecutableDeclaration, Integer> COGNITIVE_COMPLEXITY;

115

```

116

117

**Usage Example:**

118

```java

119

// Calculate cognitive complexity

120

ASTMethodDeclaration method = /* ... */;

121

int cognitiveComplexity = JavaMetrics.COGNITIVE_COMPLEXITY.computeFor(method, MetricOptions.emptyOptions());

122

```

123

124

#### NPath Complexity (NPATH)

125

126

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

127

128

```java { .api }

129

/**

130

* NPath Complexity metric

131

* Counts acyclic execution paths (exponential growth with sequential decisions)

132

*/

133

public static final Metric<ASTExecutableDeclaration, BigInteger> NPATH;

134

```

135

136

**Usage Example:**

137

```java

138

// Calculate NPath complexity

139

ASTMethodDeclaration method = /* ... */;

140

BigInteger npathComplexity = JavaMetrics.NPATH.computeFor(method, MetricOptions.emptyOptions());

141

142

// Check if complexity is over threshold

143

if (npathComplexity.compareTo(BigInteger.valueOf(200)) > 0) {

144

System.out.println("Method is too complex: " + npathComplexity);

145

}

146

```

147

148

### Size Metrics

149

150

#### Lines of Code (LINES_OF_CODE)

151

152

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

153

154

```java { .api }

155

/**

156

* Lines of Code metric

157

* Counts total lines including comments and blank lines

158

*/

159

public static final Metric<JavaNode, Integer> LINES_OF_CODE;

160

```

161

162

#### Non-Commenting Source Statements (NCSS)

163

164

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

165

166

```java { .api }

167

/**

168

* Non-Commenting Source Statements metric

169

* Counts actual statements excluding comments and blank lines

170

*/

171

public static final Metric<JavaNode, Integer> NCSS;

172

173

/**

174

* Options for NCSS calculation

175

*/

176

public enum NcssOption implements MetricOption {

177

/** Counts import and package statements (makes metric JavaNCSS compliant) */

178

COUNT_IMPORTS("countImports");

179

180

String valueName();

181

}

182

```

183

184

**Usage Example:**

185

```java

186

// Calculate NCSS for a class

187

ASTClassDeclaration clazz = /* ... */;

188

int statements = JavaMetrics.NCSS.computeFor(clazz, MetricOptions.emptyOptions());

189

190

// With imports counted

191

MetricOptions options = MetricOptions.ofOption(NcssOption.COUNT_IMPORTS);

192

int statementsWithImports = JavaMetrics.NCSS.computeFor(clazz, options);

193

```

194

195

### Coupling Metrics

196

197

#### Access to Foreign Data (ACCESS_TO_FOREIGN_DATA)

198

199

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

200

201

```java { .api }

202

/**

203

* Access to Foreign Data metric

204

* Counts usages of foreign attributes through direct access or accessors

205

*/

206

public static final Metric<JavaNode, Integer> ACCESS_TO_FOREIGN_DATA;

207

```

208

209

#### Fan-Out (FAN_OUT)

210

211

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

212

213

```java { .api }

214

/**

215

* Class Fan-Out metric

216

* Counts dependencies on other classes (excludes java.lang by default)

217

*/

218

public static final Metric<JavaNode, Integer> FAN_OUT;

219

220

/**

221

* Options for fan-out calculation

222

*/

223

public enum ClassFanOutOption implements MetricOption {

224

/** Whether to include classes in the java.lang package */

225

INCLUDE_JAVA_LANG("includeJavaLang");

226

227

String valueName();

228

}

229

```

230

231

**Usage Example:**

232

```java

233

// Calculate fan-out excluding java.lang

234

ASTClassDeclaration clazz = /* ... */;

235

int fanOut = JavaMetrics.FAN_OUT.computeFor(clazz, MetricOptions.emptyOptions());

236

237

// Include java.lang dependencies

238

MetricOptions options = MetricOptions.ofOption(ClassFanOutOption.INCLUDE_JAVA_LANG);

239

int totalFanOut = JavaMetrics.FAN_OUT.computeFor(clazz, options);

240

```

241

242

### Object-Oriented Metrics

243

244

#### Weighed Method Count (WEIGHED_METHOD_COUNT)

245

246

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

247

248

```java { .api }

249

/**

250

* Weighed Method Count metric

251

* Sum of cyclomatic complexity of all methods in a class

252

*/

253

public static final Metric<ASTTypeDeclaration, Integer> WEIGHED_METHOD_COUNT;

254

```

255

256

#### Tight Class Cohesion (TIGHT_CLASS_COHESION)

257

258

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

259

260

```java { .api }

261

/**

262

* Tight Class Cohesion metric

263

* Measures method pairs that access common attributes (0.0 to 1.0)

264

*/

265

public static final Metric<ASTTypeDeclaration, Double> TIGHT_CLASS_COHESION;

266

```

267

268

**Usage Example:**

269

```java

270

// Calculate class cohesion

271

ASTClassDeclaration clazz = /* ... */;

272

double cohesion = JavaMetrics.TIGHT_CLASS_COHESION.computeFor(clazz, MetricOptions.emptyOptions());

273

274

if (cohesion > 0.7) {

275

System.out.println("High cohesion class");

276

} else if (cohesion < 0.5) {

277

System.out.println("Low cohesion class - consider refactoring");

278

}

279

```

280

281

#### Weight of Class (WEIGHT_OF_CLASS)

282

283

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

284

285

```java { .api }

286

/**

287

* Weight of Class metric

288

* Ratio of functional methods to total public methods (0.0 to 1.0)

289

*/

290

public static final Metric<ASTTypeDeclaration, Double> WEIGHT_OF_CLASS;

291

```

292

293

### Method and Field Count Metrics

294

295

#### Number of Accessors (NUMBER_OF_ACCESSORS)

296

297

Counts getter and setter methods in a class.

298

299

```java { .api }

300

/**

301

* Number of Accessor Methods metric

302

* Counts getter and setter methods

303

*/

304

public static final Metric<ASTTypeDeclaration, Integer> NUMBER_OF_ACCESSORS;

305

```

306

307

#### Number of Public Fields (NUMBER_OF_PUBLIC_FIELDS)

308

309

Counts public fields in a class.

310

311

```java { .api }

312

/**

313

* Number of Public Attributes metric

314

* Counts public fields

315

*/

316

public static final Metric<ASTTypeDeclaration, Integer> NUMBER_OF_PUBLIC_FIELDS;

317

```

318

319

## Usage Examples

320

321

### Basic Metric Calculation

322

323

```java

324

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

325

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

326

327

// Calculate complexity for a method

328

public void analyzeMethod(ASTMethodDeclaration method) {

329

// Cyclomatic complexity

330

int cyclo = JavaMetrics.CYCLO.computeFor(method, MetricOptions.emptyOptions());

331

332

// Cognitive complexity

333

int cognitive = JavaMetrics.COGNITIVE_COMPLEXITY.computeFor(method, MetricOptions.emptyOptions());

334

335

// NPath complexity

336

BigInteger npath = JavaMetrics.NPATH.computeFor(method, MetricOptions.emptyOptions());

337

338

// Lines of code

339

int loc = JavaMetrics.LINES_OF_CODE.computeFor(method, MetricOptions.emptyOptions());

340

341

System.out.println("Method: " + method.getName());

342

System.out.println(" Cyclomatic Complexity: " + cyclo);

343

System.out.println(" Cognitive Complexity: " + cognitive);

344

System.out.println(" NPath Complexity: " + npath);

345

System.out.println(" Lines of Code: " + loc);

346

}

347

```

348

349

### Class-Level Analysis

350

351

```java

352

// Comprehensive class analysis

353

public void analyzeClass(ASTClassDeclaration clazz) {

354

MetricOptions emptyOptions = MetricOptions.emptyOptions();

355

356

// Size metrics

357

int loc = JavaMetrics.LINES_OF_CODE.computeFor(clazz, emptyOptions);

358

int ncss = JavaMetrics.NCSS.computeFor(clazz, emptyOptions);

359

360

// Coupling metrics

361

int atfd = JavaMetrics.ACCESS_TO_FOREIGN_DATA.computeFor(clazz, emptyOptions);

362

int fanOut = JavaMetrics.FAN_OUT.computeFor(clazz, emptyOptions);

363

364

// Object-oriented metrics

365

int wmc = JavaMetrics.WEIGHED_METHOD_COUNT.computeFor(clazz, emptyOptions);

366

double tcc = JavaMetrics.TIGHT_CLASS_COHESION.computeFor(clazz, emptyOptions);

367

double woc = JavaMetrics.WEIGHT_OF_CLASS.computeFor(clazz, emptyOptions);

368

369

// Method and field counts

370

int accessors = JavaMetrics.NUMBER_OF_ACCESSORS.computeFor(clazz, emptyOptions);

371

int publicFields = JavaMetrics.NUMBER_OF_PUBLIC_FIELDS.computeFor(clazz, emptyOptions);

372

373

System.out.println("Class: " + clazz.getSimpleName());

374

System.out.println("Size Metrics:");

375

System.out.println(" Lines of Code: " + loc);

376

System.out.println(" NCSS: " + ncss);

377

System.out.println("Coupling Metrics:");

378

System.out.println(" ATFD: " + atfd);

379

System.out.println(" Fan-Out: " + fanOut);

380

System.out.println("OO Metrics:");

381

System.out.println(" WMC: " + wmc);

382

System.out.println(" TCC: " + String.format("%.2f", tcc));

383

System.out.println(" WOC: " + String.format("%.2f", woc));

384

System.out.println("Counts:");

385

System.out.println(" Accessors: " + accessors);

386

System.out.println(" Public Fields: " + publicFields);

387

}

388

```

389

390

### Metrics with Options

391

392

```java

393

// Using metric options for customized calculations

394

public void analyzeWithOptions(JavaNode node) {

395

// Cyclomatic complexity ignoring boolean paths

396

MetricOptions cycloOptions = MetricOptions.ofOption(

397

JavaMetrics.CycloOption.IGNORE_BOOLEAN_PATHS,

398

JavaMetrics.CycloOption.CONSIDER_ASSERT

399

);

400

401

if (node instanceof ASTExecutableDeclaration) {

402

ASTExecutableDeclaration method = (ASTExecutableDeclaration) node;

403

int cyclo = JavaMetrics.CYCLO.computeFor(method, cycloOptions);

404

System.out.println("Simplified Cyclomatic Complexity: " + cyclo);

405

}

406

407

// NCSS including imports

408

MetricOptions ncssOptions = MetricOptions.ofOption(JavaMetrics.NcssOption.COUNT_IMPORTS);

409

int ncss = JavaMetrics.NCSS.computeFor(node, ncssOptions);

410

System.out.println("NCSS with imports: " + ncss);

411

412

// Fan-out including java.lang

413

MetricOptions fanOutOptions = MetricOptions.ofOption(JavaMetrics.ClassFanOutOption.INCLUDE_JAVA_LANG);

414

int fanOut = JavaMetrics.FAN_OUT.computeFor(node, fanOutOptions);

415

System.out.println("Total Fan-Out: " + fanOut);

416

}

417

```

418

419

### Custom Metrics Analysis Visitor

420

421

```java

422

// Visitor to collect metrics across an entire compilation unit

423

public class MetricsCollector extends JavaVisitorBase<Void, Void> {

424

private List<ClassMetrics> classMetrics = new ArrayList<>();

425

private List<MethodMetrics> methodMetrics = new ArrayList<>();

426

427

@Override

428

public Void visit(ASTClassDeclaration node, Void data) {

429

// Collect class-level metrics

430

ClassMetrics metrics = new ClassMetrics();

431

metrics.className = node.getSimpleName();

432

metrics.loc = JavaMetrics.LINES_OF_CODE.computeFor(node, MetricOptions.emptyOptions());

433

metrics.ncss = JavaMetrics.NCSS.computeFor(node, MetricOptions.emptyOptions());

434

metrics.wmc = JavaMetrics.WEIGHED_METHOD_COUNT.computeFor(node, MetricOptions.emptyOptions());

435

metrics.tcc = JavaMetrics.TIGHT_CLASS_COHESION.computeFor(node, MetricOptions.emptyOptions());

436

437

classMetrics.add(metrics);

438

439

return super.visit(node, data);

440

}

441

442

@Override

443

public Void visit(ASTMethodDeclaration node, Void data) {

444

// Collect method-level metrics

445

MethodMetrics metrics = new MethodMetrics();

446

metrics.methodName = node.getName();

447

metrics.cyclo = JavaMetrics.CYCLO.computeFor(node, MetricOptions.emptyOptions());

448

metrics.cognitive = JavaMetrics.COGNITIVE_COMPLEXITY.computeFor(node, MetricOptions.emptyOptions());

449

metrics.npath = JavaMetrics.NPATH.computeFor(node, MetricOptions.emptyOptions());

450

metrics.loc = JavaMetrics.LINES_OF_CODE.computeFor(node, MetricOptions.emptyOptions());

451

452

methodMetrics.add(metrics);

453

454

return super.visit(node, data);

455

}

456

457

public List<ClassMetrics> getClassMetrics() { return classMetrics; }

458

public List<MethodMetrics> getMethodMetrics() { return methodMetrics; }

459

460

static class ClassMetrics {

461

String className;

462

int loc, ncss, wmc;

463

double tcc;

464

}

465

466

static class MethodMetrics {

467

String methodName;

468

int cyclo, cognitive, loc;

469

BigInteger npath;

470

}

471

}

472

```

473

474

## Metric Interpretation Guidelines

475

476

### Complexity Thresholds

477

478

- **Cyclomatic Complexity**: Methods with CYCLO > 10 should be considered for refactoring

479

- **Cognitive Complexity**: Methods with > 15 cognitive complexity are hard to understand

480

- **NPath**: Methods with NPath > 200 are generally too complex

481

482

### Object-Oriented Quality

483

484

- **Tight Class Cohesion**:

485

- > 70%: High cohesion, single responsibility

486

- < 50%: Low cohesion, possibly doing too much

487

- **Weight of Class**:

488

- < 30%: Poor encapsulation, reveals too much data

489

- > 70%: Good behavioral interface

490

491

### Coupling Guidelines

492

493

- **Access to Foreign Data**: > 3 for operations suggests encapsulation issues

494

- **Fan-Out**: High values indicate tight coupling to many other classes