<metaname="description"content="Since version 6.0.0, PMD is enhanced with the ability to compute code metrics on Java and Apex source (the so-called Metrics Framework). This framework provi...">
<!-- this highlights the active parent class in the navgoco sidebar. this is critical so that the parent expands when you're viewing a page. This must appear below the sidebar code above. Otherwise, if placed inside customscripts.js, the script runs before the sidebar code runs and the class never gets inserted.-->
<h1class="post-title-main">Using and defining code metrics for custom rules</h1>
</div>
<divclass="post-content">
<divclass="summary">Since version 6.0.0, PMD is enhanced with the ability to compute code metrics on Java and Apex source (the so-called Metrics Framework). This framework provides developers with a straightforward interface to use code metrics in their rules, and to extend the framework with their own custom metrics.</div>
<!-- this handles the automatic toc. use ## for subheads to auto-generate the on-page minitoc. if you use html tags, you must supply an ID for the heading element in order for it to appear in the minitoc. -->
<divclass="alert alert-info"role="alert"><iclass="fa fa-info-circle"></i><b>Note:</b> The following explains how to use the Java metrics framework. The Apex framework
differs only by the name of its classes.</div>
<p>In PMD’s Metrics framework, a metric is an operation that can be carried out on nodes of a certain type and produces
a numeric result. In the Java framework, metrics can be computed on operation declaration nodes (constructor and
method declaration), and type declaration nodes (class, interface, enum, and annotation declarations). A metric
object in the framework can only handle either types or operations, but not both.</p>
<p>PMD ships with a library of already implemented metrics. These metrics are referenced by <ahref="https://javadoc.io/page/net.sourceforge.pmd/pmd-core/6.21.0/net/sourceforge/pmd/lang/metrics/MetricKey.html#"><codeclass="highlighter-rouge">MetricKey</code></a> objects,
which are listed in two public enums: <ahref="https://javadoc.io/page/net.sourceforge.pmd/pmd-java/6.21.0/net/sourceforge/pmd/lang/java/metrics/api/JavaClassMetricKey.html#"><codeclass="highlighter-rouge">JavaClassMetricKey</code></a> and <ahref="https://javadoc.io/page/net.sourceforge.pmd/pmd-java/6.21.0/net/sourceforge/pmd/lang/java/metrics/api/JavaOperationMetricKey.html#"><codeclass="highlighter-rouge">JavaOperationMetricKey</code></a>.
declaration node. Metrics that can be computed on both operation and type declarations (e.g. NCSS) have one metric key in
each enum.</p>
<h2id="for-xpath-rules">For XPath rules</h2>
<p>XPath rules can compute metrics using the <codeclass="highlighter-rouge">metric</code> function. This function takes a single <strong>string argument</strong>,
which is the name of the metric key as defined in <codeclass="highlighter-rouge">JavaClassMetricKey</code> or <codeclass="highlighter-rouge">JavaOperationMetricKey</code>. The metric
will be <strong>computed on the context node</strong>.</p>
<p>The function will throw an exception in the following cases:</p>
<li>The context node is neither an instance of <ahref="https://javadoc.io/page/net.sourceforge.pmd/pmd-java/6.21.0/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.html#"><codeclass="highlighter-rouge">ASTAnyTypeDeclaration</code></a> or <ahref="https://javadoc.io/page/net.sourceforge.pmd/pmd-java/6.21.0/net/sourceforge/pmd/lang/java/ast/MethodLikeNode.html#"><codeclass="highlighter-rouge">MethodLikeNode</code></a>, that is,
it’s not one of <ahref="https://javadoc.io/page/net.sourceforge.pmd/pmd-java/6.21.0/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.html#"><codeclass="highlighter-rouge">ASTClassOrInterfaceDeclaration</code></a>, <ahref="https://javadoc.io/page/net.sourceforge.pmd/pmd-java/6.21.0/net/sourceforge/pmd/lang/java/ast/ASTEnumDeclaration.html#"><codeclass="highlighter-rouge">ASTEnumDeclaration</code></a>, <ahref="https://javadoc.io/page/net.sourceforge.pmd/pmd-java/6.21.0/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclaration.html#"><codeclass="highlighter-rouge">ASTMethodDeclaration</code></a>,
<ahref="https://javadoc.io/page/net.sourceforge.pmd/pmd-java/6.21.0/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclaration.html#"><codeclass="highlighter-rouge">ASTConstructorDeclaration</code></a>, or <ahref="https://javadoc.io/page/net.sourceforge.pmd/pmd-java/6.21.0/net/sourceforge/pmd/lang/java/ast/ASTLambdaExpression.html#"><codeclass="highlighter-rouge">ASTLambdaExpression</code></a>.</li>
<li>The metric key does not exist (the name is case insensitive) or is not defined for the type of the context node.</li>
</ul>
<divclass="alert alert-info"role="alert"><iclass="fa fa-info-circle"></i><b>Note:</b> More advanced features of the API are not accessible yet, but may be supported in the future.
<p>The static façade class <ahref="https://javadoc.io/page/net.sourceforge.pmd/pmd-java/6.21.0/net/sourceforge/pmd/lang/java/metrics/JavaMetrics.html#"><codeclass="highlighter-rouge">JavaMetrics</code></a> is the single entry point to compute metrics in the Java framework.</p>
<p>This class provides the method <codeclass="highlighter-rouge">get</code> and its overloads. The following sections describes the interface of this class.</p>
<h3id="basic-usage">Basic usage</h3>
<p>The simplest overloads of <codeclass="highlighter-rouge">JavaMetrics.get</code> take two parameters: <strong>a <codeclass="highlighter-rouge">MetricKey</code> and a node of the corresponding type.</strong>
Say you want to write a rule to report methods that have a high cyclomatic complexity. In your rule’s visitor, you
can get the value of Cyclo for a method node like so:</p>
<p>The same goes for class metrics: you select one among <codeclass="highlighter-rouge">JavaClassMetricKey</code>’s constants and pass it along with the node
to <codeclass="highlighter-rouge">JavaMetrics.get</code>.</p>
<divclass="alert alert-success"role="alert"><iclass="fa fa-check-square-o"></i><b>Tip:</b> A specific base rule class (<codeclass="highlighter-rouge">AbstractJavaMetricsRule</code>) exists
to e.g. check constructors and method nodes completely alike. This comes
in handy for metrics, as they usually don’t make the distinction</div>
the specified node. <strong>If the metric cannot be computed on the given node, <codeclass="highlighter-rouge">JavaMetrics.get</code> will return <codeclass="highlighter-rouge">Double.NaN</code> .</strong>
If you’re concerned about that, you can condition your call on whether the node is supported or not:</p>
<p>To use options with a metric, you must first bundle them into a <ahref="https://javadoc.io/page/net.sourceforge.pmd/pmd-core/6.21.0/net/sourceforge/pmd/lang/metrics/MetricOptions.html#"><codeclass="highlighter-rouge">MetricOptions</code></a> object. <codeclass="highlighter-rouge">MetricOptions</code> provides the
utility method <codeclass="highlighter-rouge">ofOptions</code> to get a <codeclass="highlighter-rouge">MetricOptions</code> bundle from a collection or with varargs parameters. You can then
pass this bundle as a parameter to <codeclass="highlighter-rouge">JavaMetrics.get</code>:</p>
<p>The version of <codeclass="highlighter-rouge">MetricOptions.ofOptions</code> using a collection is useful when you’re building a <codeclass="highlighter-rouge">MetricOptions</code> from eg
the value of an <codeclass="highlighter-rouge">EnumeratedMultiProperty</code>, which gives users control of the options they use. See
<p>Notice that <strong>we use an operation metric and a class node</strong>. The <codeclass="highlighter-rouge">ResultOption</code> parameter controls what result will be
computed: you can choose among <codeclass="highlighter-rouge">HIGHEST</code>, <codeclass="highlighter-rouge">SUM</code> and <codeclass="highlighter-rouge">AVERAGE</code>. You can use metric options together with a result
option too.</p>
<h3id="complete-use-case">Complete use case</h3>
<p>The following is a sample code for a rule reporting methods with a cyclomatic
complexity over 10 and classes with a total cyclo over 50. A metric option can be
user-configured with a rule property. More complete examples can be found in
or <ahref="https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/GodClassRule.java#L24">GodClassRule</a>.</p>
<spanclass="k">if</span><spanclass="o">(</span><spanclass="n">cyclo</span><spanclass="o">></span><spanclass="mi">10</span><spanclass="o">)</span><spanclass="o">{</span><spanclass="c1">// this is safe if the node is not supported, as (Double.NaN > 10) == false</span>
and handle them as you see fit in your <codeclass="highlighter-rouge">computeFor</code> method</li>
<li><strong>Create a metric key</strong> using <codeclass="highlighter-rouge">MetricKeyUtil</code>’s <codeclass="highlighter-rouge">of</code> method, specifying a name
for your metric and an instance of your metric. You’re done and can use your
metric key as if it were a standard one.</li>
</ol>
<h3id="best-practices">Best practices</h3>
<ul>
<li><strong>Metrics should be stateless</strong>. In any case, instances of the same metric class
are considered <codeclass="highlighter-rouge">equals</code>. The same instance of your metric will be used to
compute the metric on the AST of different nodes so it should really be
“functionnally pure”. That rule also makes you keep it simple and understandable
which is nice.</li>
<li><strong>Implementation patterns:</strong> You can implement your <codeclass="highlighter-rouge">computeFor</code> method as you
like it. But most metrics in our library are implemented following a few
patterns you may want to look at:
<ul>
<li>
<p><em>Visitor metrics:</em> Those metrics use one or more AST visitor to compute their
value. That’s especially good to implement metrics that count some kind of node,
e.g. <ahref="https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/NpathMetric.java">NPath complexity</a>
or <ahref="https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/NcssMetric.java">NCSS</a>.
Additionnally, it makes your metric more easily generalisable to other node types.</p>
</li>
<li>
<p><em>Signature matching metrics:</em> That’s even more straightforward when you want
to count the number of methods or fields that match a specific signature, e.g.
public static final fields. Basically a signature is an object that describes
a field or method, with info about its modifers and other node-specific info.
<codeclass="highlighter-rouge">AbstractJavaClassMetric</code> has a few methods that allow you to count signatures
directly, see e.g. the metrics <ahref="https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/NopaMetric.java">NOPA</a>
and <ahref="https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/WocMetric.java">WOC</a>.</p>
<p>*Apex method metrics are also applied to triggers by default (see <ahref="https://github.com/pmd/pmd/pull/771">#771</a>). Finer capability checking is not available out of the box for now.</p>
<p>What if you don’t want such a generalisation? The <codeclass="highlighter-rouge">supports</code> method lets you
define a predicate to check that the node is supported by your metric. For example,
if your metric can only be computed on classes, you may override the default behaviour
<divclass="alert alert-success"role="alert"><iclass="fa fa-check-square-o"></i><b>Tip:</b> You can be sure that if your <codeclass="highlighter-rouge">supports</code> method returns <codeclass="highlighter-rouge">false</code> on a node, then
that node will never be passed as a parameter to <codeclass="highlighter-rouge">computeFor</code>. That allows you
to write your <codeclass="highlighter-rouge">computeFor</code> method without worrying about unsupported nodes.</div>
<p>The <codeclass="highlighter-rouge">supports</code> method already has a default implementation in the abstract base
classes. Here’s the default behaviour by language and type of metric:</p>
<table>
<thead>
<tr>
<th>Language</th>
<th>Java</th>
<th>Apex</th>
</tr>
</thead>
<tbody>
<tr>
<td>Operation metrics</td>
<td>supports constructors and non abstract methods</td>
<td>supports any non abstract method (including triggers), except <codeclass="highlighter-rouge"><init></code>, <codeclass="highlighter-rouge"><clinit></code>, and <codeclass="highlighter-rouge">clone</code></td>