Skip to content

Development and Maintenance

Madeline Kahn edited this page May 11, 2024 · 4 revisions

This guide shall detail how to develop additional checks for the linter.
It assumes that you have Java 11 installed, and have opened the project in your choice of modern Java IDE with support for Maven build configuration and JUnit testing.

Creating the Check Class

First, within the src/main/java directory, navigate to the domain.checks package and create a class named <NAME>Check, where <NAME> is what your check shall be known as. For example, a check for the usage of the template method pattern would have a fully qualified class name domain.checks.TemplateMethodPatternCheck.

Make your class inherit the domain.Check class, override the run method, and implement the zero-argument constructor to set the check's name. For your convenience, you can just copy and paste this check template (using your actual check name of course):

package domain.checks;

import java.util.HashSet;
import java.util.Set;

import datasource.Configuration;
import domain.Check;
import domain.Message;
import domain.javadata.ClassDataCollection;

public class FooBarCheck extends Check {
    private static final String NAME = "fooBar";

    public FooBarCheck() {
        super(NAME);
    }

    @Override
    public Set<Message> run(ClassDataCollection classes, Configuration config) {
        Set<Message> messages = new HashSet<Message>();
        // TODO: execute check behavior
        return messages;
    }
}

Check Roster

You must add your check to the check roster for it to be run in normal usage of the linter. Open the class domain.CheckRoster. All it contains is an array of linter checks, roughly ordered like this:

  • Cursory style checks
  • Principle violation checks
  • Design pattern checks
  • Additional features

Add your check to the array in an appropriate position.

Constructor

A couple properties are set through the constructor's super call:

  • The check's name is the first argument to super. It must be unique among all checks/features, and should be camelCase.
  • With no further modification, the check is enabled by default. If your check is relatively niche and should not run for all users by default, you can make the check disabled by default by passing the value false as a 2nd argument to super.

Execution

It is the behavior of a check to take an input ClassDataCollection and generate Messages based on its analysis of the classes therein. Start by iterating over the ClassData objects of the ClassDataCollection, like this:

for (ClassData classData : classes) {
    // ...
}

Within the loop's body, you can analyze each ClassData (which may be a class, interface, or enum) from the input codebase. ClassData and the rest of the domain.javadata package provide a powerful API for you to analyze Java bytecode; take a look at the interfaces in the domain.javadata package to discover what functionality is available. You should not need to directly invoke the ASM library.

Configuration

Your check is passed the user's entire configuration as run's second argument, from which you can read whatever configuration properties you may need. However, you'll need to add them to config-spec.json to make them appear in GUI settings. Actually, even if your check has no configuration properties, you still must add a section in config-spec.json so your check can be enabled and disabled through the GUI settings.

Open the file src/main/java/config-spec.json. The sections array has a couple general sections at the top, and then features a section for each check, in the same order they appear in CheckRoster. Add yours in the correct position. The list of properties of the section object follows.

Section object properties

  • title: Plain English name of the section (in this case, of the check). Required.
  • checkName: Name of the check in camelCase as declared in the check's class. Optional in general, but required if the section represents a check (which yours presumably does).
  • entityTypeOverride: If your "check" is not really a check, use this optional property to set the noun representing what it is. For example, the PlantUML Generator is not a check but a miscellaneous "feature".
  • settings: Optional array of setting objects. No setting name should appear more than once in config-spec.json. If a setting is applicable to multiple checks, don't put it in either of them; instead add it to the "Settings applied to multiple checks" section. Properties for setting objects follow next.

Setting object properties

  • name: Name of the setting, to be used as its key in Configuration. Required.
  • type: One of "boolean", "int", "String", "boolean[]", "int[]", or "String[]". Required.
  • desc: Plain English description to be displayed by the GUI. Optional.
  • select: If type is "String", you may optionally specify a set of permitted values. Name it here, and add an entry for it to the selects object at the end of the file. The GUI will display the setting as a dropdown instead of a textbox.