pmd/pmd_userdocs_extending_metrics_howto.html

1814 lines
68 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="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...">
<meta name="keywords" content="extendinguserdocsmetrics, ">
<title>Using and defining code metrics for custom rules | PMD Source Code Analyzer</title>
<link rel="stylesheet" href="css/syntax.css">
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
<!--<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">-->
<link rel="stylesheet" href="css/modern-business.css">
<link rel="stylesheet" href="css/lavish-bootstrap.css">
<link rel="stylesheet" href="css/customstyles.css">
<link rel="stylesheet" href="css/theme-blue.css">
<link rel="stylesheet" href="css/pmd-customstyles.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
<script src="js/jquery.navgoco.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/anchor-js/2.0.0/anchor.min.js"></script>
<script src="js/toc.js"></script>
<script src="js/customscripts.js"></script>
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<link rel="icon" href="images/favicon.ico" type="image/x-icon">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<link rel="alternate" type="application/rss+xml" title="" href="https://pmd.github.io/pmd/feed.xml">
<script>
$(document).ready(function() {
// Initialize navgoco with default options
$("#mysidebar").navgoco({
caretHtml: '',
accordion: true,
openClass: 'active', // open
save: false, // leave false or nav highlighting doesn't work right
cookie: {
name: 'navgoco',
expires: false,
path: '/'
},
slide: {
duration: 400,
easing: 'swing'
}
});
$("#collapseAll").click(function(e) {
e.preventDefault();
$("#mysidebar").navgoco('toggle', false);
});
$("#expandAll").click(function(e) {
e.preventDefault();
$("#mysidebar").navgoco('toggle', true);
});
});
</script>
<script>
$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
</script>
<script>
$(document).ready(function() {
$("#tg-sb-link").click(function() {
$("#tg-sb-sidebar").toggle();
$("#tg-sb-content").toggleClass('col-md-9');
$("#tg-sb-content").toggleClass('col-md-12');
$("#tg-sb-icon").toggleClass('fa-toggle-on');
$("#tg-sb-icon").toggleClass('fa-toggle-off');
});
});
</script>
</head>
<body>
<!-- Content is offset by the height of the topnav bar. -->
<!-- There's already a padding-top rule in modern-business.css, but it apparently doesn't work on Firefox 60 and Chrome 67 -->
<div id="topbar-content-offset">
<!-- Navigation -->
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container topnavlinks">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="fa fa-home fa-lg navbar-brand" href="index.html">&nbsp;<span class="projectTitle"> PMD Source Code Analyzer Project</span></a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<!-- toggle sidebar button -->
<li><a id="tg-sb-link" href="#"><i id="tg-sb-icon" class="fa fa-toggle-on"></i> Nav</a></li>
<!-- entries without drop-downs appear here -->
<li><a href="https://github.com/pmd/pmd/releases/latest" target="_blank">Download</a></li>
<li><a href="https://github.com/pmd/pmd" target="_blank">Fork us on github</a></li>
<!-- entries with drop-downs appear here -->
<!-- conditional logic to control which topnav appears for the audience defined in the configuration file.-->
<!--comment out this block if you want to hide search-->
<li>
<!--start search-->
<div id="search-demo-container">
<input type="text" id="search-input" placeholder="search...">
<ul id="results-container"></ul>
</div>
<script src="js/jekyll-search.js" type="text/javascript"></script>
<script type="text/javascript">
SimpleJekyllSearch.init({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
dataSource: 'search.json',
searchResultTemplate: '<li><a href="{url}" title="Using and defining code metrics for custom rules">{title}</a></li>',
noResultsText: 'No results found.',
limit: 10,
fuzzy: true,
})
</script>
<!--end search-->
</li>
</ul>
</div>
</div>
<!-- /.container -->
</nav>
<!-- Page Content -->
<div class="container">
<div class="col-lg-12">&nbsp;</div>
<!-- Content Row -->
<div class="row">
<!-- Sidebar Column -->
<div class="col-md-3" id="tg-sb-sidebar">
<ul id="mysidebar" class="nav">
<li class="sidebarTitle">PMD 6.42.0-SNAPSHOT</li>
<li>
<a href="#">About</a>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="pmd_release_notes.html">Release notes</a></li>
<li><a href="pmd_next_major_development.html">PMD 7.0.0 development</a></li>
<li><a href="pmd_about_help.html">Getting help</a></li>
</ul>
</li>
<li>
<a href="#">User Documentation</a>
<ul>
<li><a href="pmd_userdocs_installation.html">Installation and basic CLI usage</a></li>
<li><a href="pmd_userdocs_making_rulesets.html">Making rulesets</a></li>
<li><a href="pmd_userdocs_configuring_rules.html">Configuring rules</a></li>
<li><a href="pmd_userdocs_best_practices.html">Best practices</a></li>
<li><a href="pmd_userdocs_suppressing_warnings.html">Suppressing warnings</a></li>
<li><a href="pmd_userdocs_incremental_analysis.html">Incremental analysis</a></li>
<li><a href="pmd_userdocs_cli_reference.html">PMD CLI reference</a></li>
<li><a href="pmd_userdocs_report_formats.html">PMD Report formats</a></li>
<li class="subfolders">
<a href="#">CPD reference</a>
<ul>
<li><a href="pmd_userdocs_cpd.html">Copy-paste detection</a></li>
<li><a href="pmd_userdocs_cpd_report_formats.html">CPD Report formats</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Extending PMD</a>
<ul>
<li><a href="pmd_userdocs_extending_writing_rules_intro.html">Introduction to writing rules</a></li>
<li><a href="pmd_userdocs_extending_your_first_rule.html">Your first rule</a></li>
<li><a href="pmd_userdocs_extending_writing_xpath_rules.html">XPath rules</a></li>
<li><a href="pmd_userdocs_extending_writing_java_rules.html">Java rules</a></li>
<li><a href="pmd_userdocs_extending_designer_reference.html">Rule designer reference</a></li>
<li><a href="pmd_userdocs_extending_defining_properties.html">Defining rule properties</a></li>
<li class="active"><a href="pmd_userdocs_extending_metrics_howto.html">Using and defining code metrics</a></li>
<li><a href="pmd_userdocs_extending_rule_guidelines.html">Rule guidelines</a></li>
<li><a href="pmd_userdocs_extending_testing.html">Testing your rules</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Tools / Integrations</a>
<ul>
<li><a href="pmd_userdocs_tools_maven.html">Maven PMD Plugin</a></li>
<li><a href="pmd_userdocs_tools_gradle.html">Gradle</a></li>
<li><a href="pmd_userdocs_tools_ant.html">Ant</a></li>
<li><a href="pmd_userdocs_tools_java_api.html">PMD Java API</a></li>
<li><a href="pmd_userdocs_tools_ci.html">CI integrations</a></li>
<li><a href="pmd_userdocs_tools.html">Other Tools / Integrations</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#">Rule Reference</a>
<ul>
<li class="subfolders">
<a href="#">Apex Rules</a>
<ul>
<li><a href="pmd_rules_apex.html">Index</a></li>
<li><a href="pmd_rules_apex_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_apex_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_apex_design.html">Design</a></li>
<li><a href="pmd_rules_apex_documentation.html">Documentation</a></li>
<li><a href="pmd_rules_apex_errorprone.html">Error Prone</a></li>
<li><a href="pmd_rules_apex_performance.html">Performance</a></li>
<li><a href="pmd_rules_apex_security.html">Security</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Ecmascript Rules</a>
<ul>
<li><a href="pmd_rules_ecmascript.html">Index</a></li>
<li><a href="pmd_rules_ecmascript_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_ecmascript_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_ecmascript_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Java Rules</a>
<ul>
<li><a href="pmd_rules_java.html">Index</a></li>
<li><a href="pmd_rules_java_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_java_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_java_design.html">Design</a></li>
<li><a href="pmd_rules_java_documentation.html">Documentation</a></li>
<li><a href="pmd_rules_java_errorprone.html">Error Prone</a></li>
<li><a href="pmd_rules_java_multithreading.html">Multithreading</a></li>
<li><a href="pmd_rules_java_performance.html">Performance</a></li>
<li><a href="pmd_rules_java_security.html">Security</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Java Server Pages Rules</a>
<ul>
<li><a href="pmd_rules_jsp.html">Index</a></li>
<li><a href="pmd_rules_jsp_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_jsp_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_jsp_design.html">Design</a></li>
<li><a href="pmd_rules_jsp_errorprone.html">Error Prone</a></li>
<li><a href="pmd_rules_jsp_security.html">Security</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Maven POM Rules</a>
<ul>
<li><a href="pmd_rules_pom.html">Index</a></li>
<li><a href="pmd_rules_pom_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Modelica Rules</a>
<ul>
<li><a href="pmd_rules_modelica.html">Index</a></li>
<li><a href="pmd_rules_modelica_bestpractices.html">Best Practices</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">PLSQL Rules</a>
<ul>
<li><a href="pmd_rules_plsql.html">Index</a></li>
<li><a href="pmd_rules_plsql_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_plsql_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_plsql_design.html">Design</a></li>
<li><a href="pmd_rules_plsql_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Salesforce VisualForce Rules</a>
<ul>
<li><a href="pmd_rules_vf.html">Index</a></li>
<li><a href="pmd_rules_vf_security.html">Security</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">VM Rules</a>
<ul>
<li><a href="pmd_rules_vm.html">Index</a></li>
<li><a href="pmd_rules_vm_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_vm_design.html">Design</a></li>
<li><a href="pmd_rules_vm_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">XML Rules</a>
<ul>
<li><a href="pmd_rules_xml.html">Index</a></li>
<li><a href="pmd_rules_xml_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">XSL Rules</a>
<ul>
<li><a href="pmd_rules_xsl.html">Index</a></li>
<li><a href="pmd_rules_xsl_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_xsl_performance.html">Performance</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#">Language Specific Documentation</a>
<ul>
<li><a href="pmd_languages_jsp.html">JSP Support</a></li>
<li><a href="pmd_java_metrics_index.html">Java code metrics</a></li>
<li><a href="pmd_apex_metrics_index.html">Apex code metrics</a></li>
<li><a href="pmd_languages_plsql.html">PLSQL</a></li>
<li><a href="pmd_languages_visualforce.html">Visualforce</a></li>
</ul>
</li>
<li>
<a href="#">Developer Documentation</a>
<ul>
<li><a href="pmd_devdocs_development.html">Developer resources</a></li>
<li><a href="pmd_devdocs_building.html">Building PMD from source</a></li>
<li><a href="https://github.com/pmd/pmd/blob/master/CONTRIBUTING.md" target="_blank">Contributing</a></li>
<li><a href="pmd_devdocs_writing_documentation.html">Writing documentation</a></li>
<li><a href="pmd_devdocs_roadmap.html">Roadmap</a></li>
<li><a href="pmd_devdocs_how_pmd_works.html">How PMD works</a></li>
<li><a href="pmd_devdocs_pmdtester.html">Pmdtester</a></li>
<li><a href="pmd_devdocs_rule_deprecation_policy.html">Rule Deprecation Policy</a></li>
<li class="subfolders">
<a href="#">Major contributions</a>
<ul>
<li><a href="pmd_devdocs_major_rule_guidelines.html">Rule Guidelines</a></li>
<li><a href="pmd_devdocs_major_adding_new_language.html">Adding a new language</a></li>
<li><a href="pmd_devdocs_major_adding_new_cpd_language.html">Adding a new CPD language</a></li>
<li><a href="pmd_devdocs_major_adding_new_metrics_framework.html">Adding metrics support to a language</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Experimental features</a>
<ul>
<li><a href="pmd_devdocs_experimental_ast_dump.html">Creating (XML) dump of the AST</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#">Project documentation</a>
<ul>
<li class="subfolders">
<a href="#">Trivia about PMD</a>
<ul>
<li><a href="pmd_projectdocs_trivia_news.html">PMD in the press</a></li>
<li><a href="pmd_projectdocs_trivia_products.html">Products & books related to PMD</a></li>
<li><a href="pmd_projectdocs_trivia_similarprojects.html">Similar projects</a></li>
<li><a href="pmd_projectdocs_trivia_meaning.html">What does 'PMD' mean?</a></li>
</ul>
</li>
<li><a href="pmd_projectdocs_faq.html">FAQ</a></li>
<li><a href="license.html">License</a></li>
<li><a href="pmd_projectdocs_credits.html">Credits</a></li>
<li><a href="pmd_release_notes_old.html">Old release notes</a></li>
<li class="subfolders">
<a href="#">Project management</a>
<ul>
<li><a href="pmd_projectdocs_committers_infrastructure.html">Infrastructure</a></li>
<li><a href="pmd_projectdocs_committers_releasing.html">Release process</a></li>
<li><a href="pmd_projectdocs_committers_merging_pull_requests.html">Merging pull requests</a></li>
<li><a href="pmd_projectdocs_committers_main_landing_page.html">Main Landing page</a></li>
</ul>
</li>
</ul>
</li>
<!-- if you aren't using the accordion, uncomment this block:
<p class="external">
<a href="#" id="collapseAll">Collapse All</a> | <a href="#" id="expandAll">Expand All</a>
</p>
-->
</ul>
<!-- 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.-->
<script>$("li.active").parents('li').toggleClass("active");</script>
</div>
<!-- Content Column -->
<div class="col-md-9" id="tg-sb-content">
<div class="post-header">
<h1 class="post-title-main">Using and defining code metrics for custom rules</h1>
</div>
<div class="post-content">
<div class="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. -->
<script>
$( document ).ready(function() {
// Handler for .ready() called.
$('#toc').toc({ minimumHeaders: 0, listType: 'ul', showSpeed: 0, headers: 'h2,h3,h4' });
});
</script>
<div id="toc"></div>
<a target="_blank" href="https://github.com/pmd/pmd/blob/master/docs/pages/pmd/userdocs/extending/metrics_howto.md" class="btn btn-default githubEditButton" role="button"><i class="fa fa-github fa-lg"></i> Edit me</a>
<h2 id="using-the-metrics-framework">Using the metrics framework</h2>
<div class="alert alert-info" role="alert"><i class="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 PMDs 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 <a href="https://docs.pmd-code.org/apidocs/pmd-core/6.42.0-SNAPSHOT/net/sourceforge/pmd/lang/metrics/MetricKey.html#"><code>MetricKey</code></a> objects,
which are listed in two public enums: <a href="https://docs.pmd-code.org/apidocs/pmd-java/6.42.0-SNAPSHOT/net/sourceforge/pmd/lang/java/metrics/api/JavaClassMetricKey.html#"><code>JavaClassMetricKey</code></a> and <a href="https://docs.pmd-code.org/apidocs/pmd-java/6.42.0-SNAPSHOT/net/sourceforge/pmd/lang/java/metrics/api/JavaOperationMetricKey.html#"><code>JavaOperationMetricKey</code></a>.
Metric keys wrap a metric, and know which type of node their metric can be computed on. That way, you cannot compute an operation metric on a class
declaration node. Metrics that can be computed on both operation and type declarations (e.g. NCSS) have one metric key in
each enum.</p>
<h2 id="for-xpath-rules">For XPath rules</h2>
<p>XPath rules can compute metrics using the <code class="language-plaintext 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 <code class="language-plaintext highlighter-rouge">JavaClassMetricKey</code> or <code class="language-plaintext 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>
<ul>
<li>The context node is neither an instance of <a href="https://docs.pmd-code.org/apidocs/pmd-java/6.42.0-SNAPSHOT/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.html#"><code>ASTAnyTypeDeclaration</code></a> or <a href="https://docs.pmd-code.org/apidocs/pmd-java/6.42.0-SNAPSHOT/net/sourceforge/pmd/lang/java/ast/MethodLikeNode.html#"><code>MethodLikeNode</code></a>, that is,
its not one of <a href="https://docs.pmd-code.org/apidocs/pmd-java/6.42.0-SNAPSHOT/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclaration.html#"><code>ASTClassOrInterfaceDeclaration</code></a>, <a href="https://docs.pmd-code.org/apidocs/pmd-java/6.42.0-SNAPSHOT/net/sourceforge/pmd/lang/java/ast/ASTEnumDeclaration.html#"><code>ASTEnumDeclaration</code></a>, <a href="https://docs.pmd-code.org/apidocs/pmd-java/6.42.0-SNAPSHOT/net/sourceforge/pmd/lang/java/ast/ASTMethodDeclaration.html#"><code>ASTMethodDeclaration</code></a>,
<a href="https://docs.pmd-code.org/apidocs/pmd-java/6.42.0-SNAPSHOT/net/sourceforge/pmd/lang/java/ast/ASTConstructorDeclaration.html#"><code>ASTConstructorDeclaration</code></a>, or <a href="https://docs.pmd-code.org/apidocs/pmd-java/6.42.0-SNAPSHOT/net/sourceforge/pmd/lang/java/ast/ASTLambdaExpression.html#"><code>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>
<div class="alert alert-info" role="alert"><i class="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.
The API is thus subject to change.</div>
<h3 id="examples">Examples</h3>
<ul>
<li><code class="language-plaintext highlighter-rouge">//ClassOrInterfaceDeclaration[metric('NCSS') &gt; 200]</code></li>
<li><code class="language-plaintext highlighter-rouge">//MethodDeclaration[metric('CYCLO') &gt; 10 and metric('NCSS') &gt; 20]</code></li>
<li><code class="language-plaintext highlighter-rouge">//ClassOrInterfaceDeclaration[metric('CYCLO') &gt; 50]</code>: IllegalArgumentException!
CYCLOs only defined for methods and constructors.</li>
</ul>
<h2 id="for-java-rules">For Java Rules</h2>
<p>The static façade class <a href="https://docs.pmd-code.org/apidocs/pmd-java/6.42.0-SNAPSHOT/net/sourceforge/pmd/lang/java/metrics/JavaMetrics.html#"><code>JavaMetrics</code></a> is the single entry point to compute metrics in the Java framework.</p>
<p>This class provides the method <code class="language-plaintext highlighter-rouge">get</code> and its overloads. The following sections describes the interface of this class.</p>
<h3 id="basic-usage">Basic usage</h3>
<p>The simplest overloads of <code class="language-plaintext highlighter-rouge">JavaMetrics.get</code> take two parameters: <strong>a <code class="language-plaintext 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 rules visitor, you
can get the value of Cyclo for a method node like so:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nc">Object</span> <span class="nf">visit</span><span class="o">(</span><span class="nc">ASTMethodDeclaration</span> <span class="n">method</span><span class="o">,</span> <span class="nc">Object</span> <span class="n">data</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">cyclo</span> <span class="o">=</span> <span class="o">(</span><span class="kt">int</span><span class="o">)</span> <span class="nc">JavaMetrics</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="nc">JavaOperationMetricKey</span><span class="o">.</span><span class="na">CYCLO</span><span class="o">,</span> <span class="n">method</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">cyclo</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// add violation</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">data</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The same goes for class metrics: you select one among <code class="language-plaintext highlighter-rouge">JavaClassMetricKey</code>s constants and pass it along with the node
to <code class="language-plaintext highlighter-rouge">JavaMetrics.get</code>.</p>
<div class="alert alert-success" role="alert"><i class="fa fa-check-square-o"></i> <b>Tip:</b> A specific base rule class (<code class="language-plaintext highlighter-rouge">AbstractJavaMetricsRule</code>) exists
to e.g. check constructors and method nodes completely alike. This comes
in handy for metrics, as they usually dont make the distinction</div>
<h3 id="capability-checking">Capability checking</h3>
<p>Metrics are not necessarily computable on any node of the type they handle. For example, Cyclo cannot be computed on
abstract methods. Metric keys provides a <a href="https://docs.pmd-code.org/apidocs/pmd-core/6.42.0-SNAPSHOT/net/sourceforge/pmd/lang/metrics/MetricKey.html#supports(net.sourceforge.pmd.lang.ast.Node)"><code>supports(Node)</code></a> boolean method
to find out if the metric can be computed on
the specified node. <strong>If the metric cannot be computed on the given node, <code class="language-plaintext highlighter-rouge">JavaMetrics.get</code> will return <code class="language-plaintext highlighter-rouge">Double.NaN</code> .</strong>
If youre concerned about that, you can condition your call on whether the node is supported or not:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nc">Object</span> <span class="nf">visit</span><span class="o">(</span><span class="nc">ASTMethodDeclaration</span> <span class="n">method</span><span class="o">,</span> <span class="nc">Object</span> <span class="n">data</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="nc">JavaOperationMetricKey</span><span class="o">.</span><span class="na">CYCLO</span><span class="o">.</span><span class="na">supports</span><span class="o">(</span><span class="n">node</span><span class="o">))</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">cyclo</span> <span class="o">=</span> <span class="o">(</span><span class="kt">int</span><span class="o">)</span> <span class="nc">JavaMetrics</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="nc">JavaOperationMetricKey</span><span class="o">.</span><span class="na">CYCLO</span><span class="o">,</span> <span class="n">method</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">cyclo</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// add violation</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">data</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<h3 id="metric-options">Metric options</h3>
<p>Some metrics define options that can be used to slightly modify the computation. Youll typically see these options
gathered inside an enum in the implementation class of the metric, for example <code class="language-plaintext highlighter-rouge">CycloMetric.CycloOption</code>. Theyre
also documented on the <a href="pmd_java_metrics_index.html">index of metrics</a>.</p>
<p>To use options with a metric, you must first bundle them into a <a href="https://docs.pmd-code.org/apidocs/pmd-core/6.42.0-SNAPSHOT/net/sourceforge/pmd/lang/metrics/MetricOptions.html#"><code>MetricOptions</code></a> object. <code class="language-plaintext highlighter-rouge">MetricOptions</code> provides the
utility method <code class="language-plaintext highlighter-rouge">ofOptions</code> to get a <code class="language-plaintext highlighter-rouge">MetricOptions</code> bundle from a collection or with varargs parameters. You can then
pass this bundle as a parameter to <code class="language-plaintext highlighter-rouge">JavaMetrics.get</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nc">Object</span> <span class="nf">visit</span><span class="o">(</span><span class="nc">ASTMethodDeclaration</span> <span class="n">method</span><span class="o">,</span> <span class="nc">Object</span> <span class="n">data</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">cyclo</span> <span class="o">=</span> <span class="o">(</span><span class="kt">int</span><span class="o">)</span> <span class="nc">JavaMetrics</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="nc">JavaOperationMetricKey</span><span class="o">.</span><span class="na">CYCLO</span><span class="o">,</span> <span class="n">method</span><span class="o">,</span>
<span class="nc">MetricOptions</span><span class="o">.</span><span class="na">ofOptions</span><span class="o">(</span><span class="nc">CycloOptions</span><span class="o">.</span><span class="na">IGNORE_BOOLEAN_PATHS</span><span class="o">));</span>
<span class="k">if</span> <span class="o">(</span><span class="n">cyclo</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// add violation</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">data</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The version of <code class="language-plaintext highlighter-rouge">MetricOptions.ofOptions</code> using a collection is useful when youre building a <code class="language-plaintext highlighter-rouge">MetricOptions</code> from eg
the value of an <code class="language-plaintext highlighter-rouge">EnumeratedMultiProperty</code>, which gives users control of the options they use. See
<a href="https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CyclomaticComplexityRule.java#L35">CyclomaticComplexityRule</a>
for an example usage.</p>
<h3 id="result-options">Result options</h3>
<p>The Metrics API also gives you the possibility to aggregate the result of an operation metric on all operations of a
class very simply. You can for example get the highest value of the metric over a class that way:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nc">Object</span> <span class="nf">visit</span><span class="o">(</span><span class="nc">ASTClassOrInterfaceDeclaration</span> <span class="n">clazz</span><span class="o">,</span> <span class="nc">Object</span> <span class="n">data</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">highest</span> <span class="o">=</span> <span class="o">(</span><span class="kt">int</span><span class="o">)</span> <span class="nc">JavaMetrics</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="nc">JavaOperationMetricKey</span><span class="o">.</span><span class="na">CYCLO</span><span class="o">,</span> <span class="n">clazz</span><span class="o">,</span>
<span class="nc">ResultOption</span><span class="o">.</span><span class="na">HIGHEST</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">highest</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// add violation</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">data</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Notice that <strong>we use an operation metric and a class node</strong>. The <code class="language-plaintext highlighter-rouge">ResultOption</code> parameter controls what result will be
computed: you can choose among <code class="language-plaintext highlighter-rouge">HIGHEST</code>, <code class="language-plaintext highlighter-rouge">SUM</code> and <code class="language-plaintext highlighter-rouge">AVERAGE</code>. You can use metric options together with a result
option too.</p>
<h3 id="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
<a href="https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CyclomaticComplexityRule.java#L35">CyclomaticComplexityRule</a>,
<a href="https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NcssCountRule.java#L30">NcssCountRule</a>,
or <a href="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>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CycloRule</span> <span class="kd">extends</span> <span class="nc">AbstractJavaMetricsRule</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">BooleanProperty</span> <span class="no">COUNT_BOOLEAN_PATHS</span>
<span class="o">=</span> <span class="nc">BooleanProperty</span><span class="o">.</span><span class="na">named</span><span class="o">(</span><span class="s">"countBooleanPaths"</span><span class="o">)</span>
<span class="o">.</span><span class="na">desc</span><span class="o">(</span><span class="s">"Count boolean paths"</span><span class="o">)</span>
<span class="o">.</span><span class="na">defaultValue</span><span class="o">(</span><span class="kc">true</span><span class="o">).</span><span class="na">build</span><span class="o">();</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">MetricOptions</span> <span class="n">options</span><span class="o">;</span>
<span class="kd">public</span> <span class="nf">CycloRule</span><span class="o">()</span> <span class="o">{</span>
<span class="n">definePropertyDescriptor</span><span class="o">(</span><span class="no">COUNT_BOOLEAN_PATHS</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="nc">Object</span> <span class="nf">visit</span><span class="o">(</span><span class="nc">ASTCompilationUnit</span> <span class="n">node</span><span class="o">,</span> <span class="nc">Object</span> <span class="n">data</span><span class="o">)</span> <span class="o">{</span>
<span class="n">options</span> <span class="o">=</span> <span class="n">getProperty</span><span class="o">(</span><span class="no">COUNT_BOOLEAN_PATHS</span><span class="o">)</span>
<span class="o">?</span> <span class="nc">MetricOptions</span><span class="o">.</span><span class="na">ofOptions</span><span class="o">(</span><span class="nc">CycloOptions</span><span class="o">.</span><span class="na">IGNORE_BOOLEAN_PATHS</span><span class="o">)</span>
<span class="o">:</span> <span class="nc">MetricOptions</span><span class="o">.</span><span class="na">emptyOptions</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="nc">Object</span> <span class="nf">visit</span><span class="o">(</span><span class="nc">ASTAnyTypeDeclaration</span> <span class="n">clazz</span><span class="o">,</span> <span class="nc">Object</span> <span class="n">data</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">total</span> <span class="o">=</span> <span class="o">(</span><span class="kt">int</span><span class="o">)</span> <span class="nc">JavaMetrics</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="nc">JavaOperationMetricKey</span><span class="o">.</span><span class="na">CYCLO</span><span class="o">,</span> <span class="n">clazz</span><span class="o">,</span>
<span class="n">options</span><span class="o">,</span> <span class="nc">ResultOption</span><span class="o">.</span><span class="na">SUM</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">total</span> <span class="o">&gt;</span> <span class="mi">50</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// add violation</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">data</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="nc">Object</span> <span class="nf">visit</span><span class="o">(</span><span class="nc">ASTMethodDeclaration</span> <span class="n">method</span><span class="o">,</span> <span class="nc">Object</span> <span class="n">data</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">cyclo</span> <span class="o">=</span> <span class="o">(</span><span class="kt">int</span><span class="o">)</span> <span class="nc">JavaMetrics</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="nc">JavaOperationMetricKey</span><span class="o">.</span><span class="na">CYCLO</span><span class="o">,</span> <span class="n">method</span><span class="o">,</span>
<span class="n">options</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">cyclo</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// this is safe if the node is not supported, as (Double.NaN &gt; 10) == false</span>
<span class="c1">// add violation</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">data</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<h2 id="available-metrics">Available metrics</h2>
<p>There are already many metrics ready to use. We maintain the following documentation
pages to describe them all, including their usage and options:</p>
<ul>
<li><a href="pmd_java_metrics_index.html">Java metrics</a></li>
<li><a href="pmd_apex_metrics_index.html">Apex metrics</a></li>
</ul>
<h2 id="writing-custom-metrics">Writing custom metrics</h2>
<p>You can use the framework to customize the existing metrics at will, or define
new ones quite easily. Heres some info to get you started. Again, the examples are for
the Java framework but its symmetrical in the Apex framework.</p>
<h3 id="the-really-short-guide">The really short guide</h3>
<ol>
<li>Determine whether your metric is an operation metric or a class metric and
<strong>extend the correct base class</strong> (<code class="language-plaintext highlighter-rouge">AbstractJavaClassMetric</code> or
<code class="language-plaintext highlighter-rouge">AbstractJavaOperationMetric</code>)</li>
<li>Youre immediately prompted by your IDE to <strong>implement the <code class="language-plaintext highlighter-rouge">computeFor</code> method</strong>.
This method takes a node of the type you want to handle, a bundle of options,
and returns the result of the metric.</li>
<li>Optionally specify a predicate to check if a node can be handled by <strong>overriding
the <code class="language-plaintext highlighter-rouge">supports</code> method</strong>.</li>
<li>Optionally define options (implementing <a href="https://github.com/pmd/pmd/blob/master/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/MetricOption.java"><code class="language-plaintext highlighter-rouge">MetricOption</code></a>)
and handle them as you see fit in your <code class="language-plaintext highlighter-rouge">computeFor</code> method</li>
<li><strong>Create a metric key</strong> using <code class="language-plaintext highlighter-rouge">MetricKeyUtil</code>s <code class="language-plaintext highlighter-rouge">of</code> method, specifying a name
for your metric and an instance of your metric. Youre done and can use your
metric key as if it were a standard one.</li>
</ol>
<h3 id="best-practices">Best practices</h3>
<ul>
<li><strong>Metrics should be stateless</strong>. In any case, instances of the same metric class
are considered <code class="language-plaintext 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 <code class="language-plaintext 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. Thats especially good to implement metrics that count some kind of node,
e.g. <a href="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 <a href="https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/NcssMetric.java">NCSS</a>.
Additionally, it makes your metric more easily generalisable to other node types.</p>
</li>
<li>
<p><em>Signature matching metrics:</em> Thats 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.
<code class="language-plaintext highlighter-rouge">AbstractJavaClassMetric</code> has a few methods that allow you to count signatures
directly, see e.g. the metrics <a href="https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/NopaMetric.java">NOPA</a>
and <a href="https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/WocMetric.java">WOC</a>.</p>
</li>
</ul>
</li>
</ul>
<h3 id="capability-checking-1">Capability checking</h3>
<p>You may have noticed that when you extend e.g. <code class="language-plaintext highlighter-rouge">AbstractJavaClassMetric</code>, the
<code class="language-plaintext highlighter-rouge">computeFor</code> method youre prompted to implement takes a node of type
<code class="language-plaintext highlighter-rouge">ASTAnyTypeDeclaration</code> as a parameter. Thats not a concrete node type, but
an interface, implemented by several concrete node types. Basically thats done
so that class metrics are given the ability to be computed on any type
declaration, and operation metrics on constructors and methods. Here are the
concrete node types you can target with class and operation metrics, by language:</p>
<table>
<thead>
<tr>
<th>Language</th>
<th>Java</th>
<th>Apex</th>
</tr>
</thead>
<tbody>
<tr>
<td>Operation declaration</td>
<td><code class="language-plaintext highlighter-rouge">ASTMethodOrConstructorDeclaration</code><br />&gt;: <code class="language-plaintext highlighter-rouge">ASTMethodDeclaration</code>, <code class="language-plaintext highlighter-rouge">ASTConstructorDeclaration</code></td>
<td><code class="language-plaintext highlighter-rouge">ASTMethod</code>*</td>
</tr>
<tr>
<td>Type declaration</td>
<td><code class="language-plaintext highlighter-rouge">ASTAnyTypeDeclaration</code> &gt;: <code class="language-plaintext highlighter-rouge">ASTEnumDeclaration</code>, <br /> <code class="language-plaintext highlighter-rouge">ASTAnnotationDeclaration</code>, <code class="language-plaintext highlighter-rouge">ASTClassOrInterfaceDeclaration</code></td>
<td><code class="language-plaintext highlighter-rouge">ASTUserClassOrInterface</code> &gt;: <code class="language-plaintext highlighter-rouge">ASTUserClass</code>, <code class="language-plaintext highlighter-rouge">ASTUserInterface</code></td>
</tr>
</tbody>
</table>
<p>*Apex method metrics are also applied to triggers by default (see <a href="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 dont want such a generalisation? The <code class="language-plaintext 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
like so:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">supports</span><span class="o">(</span><span class="nc">ASTAnyTypeDeclaration</span> <span class="n">node</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">node</span><span class="o">.</span><span class="na">getTypeKind</span><span class="o">()</span> <span class="o">==</span> <span class="nc">TypeKind</span><span class="o">.</span><span class="na">CLASS</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<div class="alert alert-success" role="alert"><i class="fa fa-check-square-o"></i> <b>Tip:</b> You can be sure that if your <code class="language-plaintext highlighter-rouge">supports</code> method returns <code class="language-plaintext highlighter-rouge">false</code> on a node, then
that node will never be passed as a parameter to <code class="language-plaintext highlighter-rouge">computeFor</code>. That allows you
to write your <code class="language-plaintext highlighter-rouge">computeFor</code> method without worrying about unsupported nodes.</div>
<p>The <code class="language-plaintext highlighter-rouge">supports</code> method already has a default implementation in the abstract base
classes. Heres 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 <code class="language-plaintext highlighter-rouge">&lt;init&gt;</code>, <code class="language-plaintext highlighter-rouge">&lt;clinit&gt;</code>, and <code class="language-plaintext highlighter-rouge">clone</code></td>
</tr>
<tr>
<td>Type declaration</td>
<td>supports classes and enums</td>
<td>supports classes</td>
</tr>
</tbody>
</table>
<div class="tags">
<b>Tags: </b>
<a href="tag_extending.html" class="btn btn-default navbar-btn cursorNorm" role="button">extending</a>
<a href="tag_userdocs.html" class="btn btn-default navbar-btn cursorNorm" role="button">userdocs</a>
<a href="tag_metrics.html" class="btn btn-default navbar-btn cursorNorm" role="button">metrics</a>
</div>
</div>
<hr class="shaded"/>
<footer>
<div class="row">
<div class="col-lg-12 footer">
&copy;2021 PMD Open Source Project. All rights reserved. <br />
<span>Page last updated:</span> December 18, 2017<br/> Site last generated: Dec 13, 2021 <br />
<p><img src="images/pmd-logo-small.png" alt="Company logo"/></p>
</div>
</div>
</footer>
</div>
<!-- /.row -->
</div>
<!-- /.container -->
</div>
</div>
</body>
</html>