Creating a Custom Analyzer

Adding a custom analyzer to Audit requires the following:

Defining Rules

An analyzer must define the rules that it implements by creating instances of Rule and returning them from its getRules() method. The framework will configure the properties of those instances before beginning the audit. Even if the analyzer does not use any rule properties, it must retain references to its rule instances for use when reporting violations.

The most general Rule constructor takes the following properties:

category The category of this rule. See below.
name The name of this rule, unique across the categories, rules, assists, and metrics of this analyzer.
severity The severity of a violation of this rule:
ERROR The construct contains a likely error.
WARNING The construct contains a possible error.
ADVISORY The construct violates conventions or may have a better alternative (often with respect to performance).
localizer The localizer for this rule. See below.
transforms The transforms to offer as fixes for this rule.
defaultTranform The transform to use by default when fixing this rule.

Additionally, Rule defines an enabled property. An analyzer can choose to skip the analysis for a rule if it is disabled (reported violations of disabled rules are ignored in any case). By using a subclass of Rule, an analyzer can define a rule with additional properties. Presumably, the analyzer would use the property values to control its analysis.

Defining Categories

Defining a rule first requires defining a category. Audit uses category to organize rules in the user interface. A category is an instance of Category and the normal constructor takes the following properties:

name The name of this category, unique across the categories, rules, and metrics of this analyzer.
localizer The localizer for this category. See below.

By using a subclass of Category, an analyzer can define a category with additional properties (which would be expected to apply to all the rules in that category).

It seems to be fairly typical for one analyzer to define all the rules in a category. However, it is possible to define a rule in a category defined by another analyzer, by creating the category using the alternative constructor with the class of the other analyzer as the context class.

Localizers

Categories, rules, and metrics all have associated text that is displayed to a user. This text typically requires formatting, and it must be localizable. Formatting and localization are encapsulated in the Localizer class and the label(), tip(), description() and similar methods of Category, Rule, and Metric. Each of these methods invokes its localizer with a key constructed from the name property and with the object itself. The localizer gets the template string from resource bundle associated with the localizer and replaces any property references in the template with the actual property value from the object.

An Example

Here is an example of typical creation code for a trivial analyzer with one rule. It verifies that class names do not contain a dollar sign:


  public class DollarAnalyzer extends Analyzer {
  
    // Strings are in the DollarAnalyzerBundle list resource bundle.
    private static final   LOCALIZER = Localizer.instance(DollarAnalyzerBundle.class);
    
    // Neither category nor rule should be static.
    private final Category CATEGORY  = new Category("dollars", LOCALIZER);
    private final Rule     DOLLAR    = new Rule("dollar", Severity.ERROR, LOCALIZER);
    
    public Rule[] getRules () { 
      return new Rule[] {DOLLAR}; 
    }
    
    public enter(AuditContext context, SourceClass construct) {
      if (construct.getName().indexOf('$') > 0) context.report(DOLLAR);
    }
    
  }

Defining Assists

Assists are essentially Rules that extend Assist instead of Rule (note that Assist extends Rule) and that have a severity of ASSIST. Otherwise, assists are created, returned from the getRules methods, configured, and reported exactly as are rules.

Defining Metrics

Virtually everything said about defining rules also applies to defining metrics. A metric is an instance of Metric and its most general constructor takes the following properties:

category The category of this metric.
name The name of this metric, unique across the categories, rules, assists, and metrics of this analyzer.
type The type of the values measured by this metric. Typically int, but can be anything.
threshold The maximum normal value for this metric (if any).
localizer The localizer for this metric.

The Sequence of an Audit

At the beginning of an audit, Audit creates an instance of each analyzer class in the profile, gets the rule instances of each analyzer, and sets property values for which a modified value was saved in the profile. It scans the visitor methods of each analyzer class and organizes them into dispatch lists per construct type.

Regardless of the constructs selected to be audited, Audit begins its traversal at the singleton IDE Workspaces model. It invokes the enter methods which match this model, then visits any and all children which contain or are contained by the selected constructs, invoking matching enter methods before visiting children and matching exit methods after visiting children (i.e., depth-first, preorder with respect to enter methods and depth-first, postorder with respect to exit methods).

A typical analyzer collects information in enter methods, as the traversal descends, and analyzes it and reports violations or measurements in exit methods, as the traversal ascends. The methods share data through specific instance fields declared in the analyzer or through the getAttribute and setAttribute methods provided by the AuditContext object.

At the end of an audit, Audit keeps the AuditModel, with references to the instances of the rules violated and the metrics measured, and releases the analyzer instances (allowing any large data structures created by the analyzers to be collected).

An analyzer class should never share a category, rule, or metric instance between analyzer instances.

An analyzer class should never retain a construct after the traversal exits the model from which it came.

The Context Parameter

The first parameter of a visitor method is an AuditContext. The context provides the analyzer its only access to the Audit framework. Most importantly, it provides the report methods for reporting violations and measurements.

An analyzer should never access a context object once the visitor method to which it was passed returns.


 

Copyright © 1997, 2005, Oracle. All rights reserved.