Class JavaMetrics
Metric
and MetricsUtil
for usage doc.- See Also:
-
Nested Class Summary
Nested Classes -
Field Summary
FieldsModifier and TypeFieldDescriptionNumber of usages of foreign attributes, both directly and through accessors.static final net.sourceforge.pmd.lang.metrics.Metric
<ASTExecutableDeclaration, Integer> Cognitive complexity is a measure of how difficult it is for humans to read and understand a method.static final net.sourceforge.pmd.lang.metrics.Metric
<ASTExecutableDeclaration, Integer> Number of independent paths through a block of code.This counts the number of other classes a given class or operation relies on.Simply counts the number of lines of code the operation or class takes up in the source.Number of statements in a class or operation.static final net.sourceforge.pmd.lang.metrics.Metric
<ASTExecutableDeclaration, BigInteger> Deprecated.Since 7.14.0.static final net.sourceforge.pmd.lang.metrics.Metric
<ReturnScopeNode, Long> Number of acyclic execution paths through a piece of code.static final net.sourceforge.pmd.lang.metrics.Metric
<ASTTypeDeclaration, Integer> static final net.sourceforge.pmd.lang.metrics.Metric
<ASTTypeDeclaration, Integer> static final net.sourceforge.pmd.lang.metrics.Metric
<ASTTypeDeclaration, Double> The relative number of method pairs of a class that access in common at least one attribute of the measured class.static final net.sourceforge.pmd.lang.metrics.Metric
<ASTTypeDeclaration, Integer> Sum of the statistical complexity of the operations in the class.static final net.sourceforge.pmd.lang.metrics.Metric
<ASTTypeDeclaration, Double> Number of “functional” public methods divided by the total number of public methods. -
Method Summary
-
Field Details
-
ACCESS_TO_FOREIGN_DATA
public static final net.sourceforge.pmd.lang.metrics.Metric<JavaNode,Integer> ACCESS_TO_FOREIGN_DATANumber of usages of foreign attributes, both directly and through accessors. "Foreign" hier means "not belonging tothis
", although field accesses to fields declared in the enclosing class are not considered foreign.High values of ATFD (> 3 for an operation) may suggest that the class or operation breaks encapsulation by relying on the internal representation of the classes it uses instead of the services they provide.
-
CYCLO
Number of independent paths through a block of code. Formally, given that the control flow graph of the block has n vertices, e edges and p connected components, the cyclomatic complexity of the block is given byCYCLO = e - n + 2p
. In practice it can be calculated by counting control flow statements following the standard rules given below.The standard version of the metric complies with McCabe’s original definition:
- Methods have a base complexity of 1.
- +1 for every control flow statement (if, case, catch, throw, do, while, for, break, continue) and conditional expression (?:). Notice switch cases count as one, but not the switch itself: the point is that a switch should have the same complexity value as the equivalent series of if statements.
- else, finally and default do not count;
- +1 for every boolean operator (
&&
,||
) in the guard condition of a control flow statement. That’s because Java has short-circuit evaluation semantics for boolean operators, which makes every boolean operator kind of a control flow statement in itself.
Code example:
class Foo { void baseCyclo() { // Cyclo = 1 highCyclo(); } void highCyclo() { // Cyclo = 10 int x = 0, y = 2; boolean a = false, b = true; if (a && (y == 1 ? b : true)) { // +3 if (y == x) { // +1 while (true) { // +1 if (x++ < 20) { // +1 break; // +1 } } } else if (y == t && !d) { // +2 x = a ? y : x; // +1 } else { x = 2; } } } }
- See Also:
-
COGNITIVE_COMPLEXITY
public static final net.sourceforge.pmd.lang.metrics.Metric<ASTExecutableDeclaration,Integer> COGNITIVE_COMPLEXITYCognitive complexity is a measure of how difficult it is for humans to read and understand a method. Code that contains a break in the control flow is more complex, whereas the use of language shorthands doesn't increase the level of complexity. Nested control flows can make a method more difficult to understand, with each additional nesting of the control flow leading to an increase in cognitive complexity.Information about Cognitive complexity can be found in the original paper here: CognitiveComplexity
Basic Idea
- Ignore structures that allow multiple statements to be readably shorthanded into one
- Increment (add one) for each break in the linear flow of the code
- Increment when flow-breaking structures are nested
Increments
There is an increment for each of the following:- `if`, `else if`, `else`, ternary operator
- `switch`
- `for`, `foreach`
- `while`, `do while`
- `catch`
- `goto LABEL`, `break LABEL`, `continue LABEL`
- sequences of binary logical operators
- each method in a recursion cycle
Nesting level
The following structures increment the nesting level:- `if`, `else if`, `else`, ternary operator
- `switch`
- `for`, `foreach`
- `while`, `do while`
- `catch`
- nested methods and method-like structures such as lambdas
Nesting increments
The following structures receive a nesting increment commensurate with their nested depth inside nested structures:- `if`, ternary operator
- `switch`
- `for`, `foreach`
- `while`, `do while`
- `catch`
Code example
class Foo { void myMethod () { try { if (condition1) { // +1 for (int i = 0; i < 10; i++) { // +2 (nesting=1) while (condition2) { } // +3 (nesting=2) } } } catch (ExcepType1 | ExcepType2 e) { // +1 if (condition2) { } // +2 (nesting=1) } } // Cognitive Complexity 9 }
-
FAN_OUT
This counts the number of other classes a given class or operation relies on. Classes from the packagejava.lang
are ignored by default (can be changed via options). Also primitives are not included into the count.Code example:
import java.util.*; import java.io.IOException; public class Foo { // total 8 public Set set = new HashSet(); // +2 public Map map = new HashMap(); // +2 public String string = ""; // from java.lang -> does not count by default public Double number = 0.0; // from java.lang -> does not count by default public int[] intArray = new int[3]; // primitive -> does not count \@Deprecated // from java.lang -> does not count by default public void foo(List list) throws Exception { // +1 (Exception is from java.lang) throw new IOException(); // +1 } public int getMapSize() { return map.size(); // +1 because it uses the Class from the 'map' field } }
- See Also:
-
LINES_OF_CODE
Simply counts the number of lines of code the operation or class takes up in the source. This metric doesn’t discount comments or blank lines. SeeNCSS
for a less biased metric. -
NCSS
Number of statements in a class or operation. That’s roughly equivalent to counting the number of semicolons and opening braces in the program. Comments and blank lines are ignored, and statements spread on multiple lines count as only one (e.g.int\n a;
counts a single statement).The standard version of the metric is based off [JavaNCSS](http://www.kclee.de/clemens/java/javancss/):
- +1 for any of the following statements:
if
,else
,while
,do
,for
,switch
,break
,continue
,return
,throw
,synchronized
,catch
,finally
. - +1 for each assignment, variable declaration (except for loop initializers)
or statement expression. We count variables declared on the same line (e.g.
int a, b, c;
) as a single statement. - Contrary to Sonarqube, but as JavaNCSS, we count type declarations (class, interface, enum, annotation), and method and field declarations.
- Contrary to JavaNCSS, but as Sonarqube, we do not count package
declaration and import declarations as statements. This makes it
easier to compare nested classes to outer classes. Besides, it makes
for class metric results that actually represent the size of the class
and not of the file. If you don’t like that behaviour, use the
JavaMetrics.NcssOption.COUNT_IMPORTS
option.
import java.util.Collections; // +0 import java.io.IOException; // +0 class Foo { // +1, total Ncss = 12 public void bigMethod() // +1 throws IOException { int x = 0, y = 2; // +1 boolean a = false, b = true; // +1 if (a || b) { // +1 try { // +1 do { // +1 x += 2; // +1 } while (x < 12); System.exit(0); // +1 } catch (IOException ioe) { // +1 throw new PatheticFailException(ioe); // +1 } } else { assert false; // +1 } } }
- +1 for any of the following statements:
-
NPATH_COMP
Number of acyclic execution paths through a piece of code. This is related to cyclomatic complexity, but the two metrics don’t count the same thing: NPath counts the number of distinct full paths from the beginning to the end of the method, while Cyclo only counts the number of decision points. NPath is not computed as simply asCYCLO
. With NPath, two decision points appearing sequentially have their complexity multiplied.The fact that NPath multiplies the complexity of statements makes it grow exponentially: 10
if .. else
statements in a row would give an NPath of 1024, while Cyclo would evaluate to 20. Methods with an NPath complexity over 200 are generally considered too complex.NPath is computed through control flow analysis. We walk a block and keep track of how many control flow paths lead to the current program point. We make sure to separate paths that end abruptly (for instance because of a throw, or return). This accurately counts the number of paths that lead out of a given method. For instance:
Here there are two paths from the entry to the exit of the method: one that ends after doSomething(), and another that ends with the return statement. Complexity can snowball rapidly. For instance:// entry if (foo) return foo; doSomething(); // exit
Here there are two paths from// entry if (foo) a = 1; else a = 2; // join if (bar) b = 3; else b = 4; // exit
entry
tojoin
: one that goes through each branch of the if/else. Then there are two paths fromjoin
toexit
, for the same reason. The total number of paths fromentry
toexit
is2 * 2 = 4
. Since complexities multiply in this way, it can grow exponentially. This is not the case with early-return patterns for instance:
Here there is only one path from// entry if (foo) return x; // join if (bar) = 3; // exit
entry
tojoin
, because theif
branch returns early. There are two paths fromjoin
toexit
(and notice, that's true even if there is no else branch, because the path where the if condition is falso must be taken into account anyway). So in total there are1*2 + 1 = 3
paths fromentry
to the end of the block or function (the return statement still counts).Note that shortcut boolean operators are counted as control flow branches, especially if they happen in the condition of a control flow statement. For instance
How many paths are there here? There is one path that goes from// entry if (foo || bar) return foo ? a() : b(); doSomething(); // exit
entry
toexit
, which is taken if!foo && !bar
. There is one path that leads to the return statement if foo is true, and another if foo is false and bar is true. In the return statement, there is one path that executes a() and another that executes b(). In total, there are 2 * 2 paths that start atentry
and end at the return statement, and 1 path that goes fromentry
toexit
, so the total is 5 paths. -
NPATH
@Deprecated public static final net.sourceforge.pmd.lang.metrics.Metric<ASTExecutableDeclaration,BigInteger> NPATHDeprecated.Since 7.14.0. UseNPATH_COMP
, which is available on more nodes, and uses Long instead of BigInteger. -
NUMBER_OF_ACCESSORS
public static final net.sourceforge.pmd.lang.metrics.Metric<ASTTypeDeclaration,Integer> NUMBER_OF_ACCESSORS -
NUMBER_OF_PUBLIC_FIELDS
public static final net.sourceforge.pmd.lang.metrics.Metric<ASTTypeDeclaration,Integer> NUMBER_OF_PUBLIC_FIELDS -
TIGHT_CLASS_COHESION
public static final net.sourceforge.pmd.lang.metrics.Metric<ASTTypeDeclaration,Double> TIGHT_CLASS_COHESIONThe relative number of method pairs of a class that access in common at least one attribute of the measured class. TCC only counts direct attribute accesses, that is, only those attributes that are accessed in the body of the method.The value is a double between 0 and 1.
TCC is taken to be a reliable cohesion metric for a class. High values (>70%) indicate a class with one basic function, which is hard to break into subcomponents. On the other hand, low values (<50%) may indicate that the class tries to do too much and defines several unrelated services, which is undesirable.
-
WEIGHED_METHOD_COUNT
public static final net.sourceforge.pmd.lang.metrics.Metric<ASTTypeDeclaration,Integer> WEIGHED_METHOD_COUNTSum of the statistical complexity of the operations in the class. We use CYCLO to quantify the complexity of an operation.WMC uses the same options as CYCLO, which are provided to CYCLO when computing it (
JavaMetrics.CycloOption
). -
WEIGHT_OF_CLASS
public static final net.sourceforge.pmd.lang.metrics.Metric<ASTTypeDeclaration,Double> WEIGHT_OF_CLASSNumber of “functional” public methods divided by the total number of public methods. Our definition of “functional method” excludes constructors, getters, and setters.The value is a double between 0 and 1.
This metric tries to quantify whether the measured class’ interface reveals more data than behaviour. Low values (less than 30%) indicate that the class reveals much more data than behaviour, which is a sign of poor encapsulation.
-