Once you have written a predicate (e.g. the Singleton Pattern Detector) you can query it interactively in the Prolog Console. However, that is not really nice since it means a lot of typing and the results are just text with lots of element IDs that are hard to interpret by a human.
This section tells you how to let JTransformer know that your predicate should be
and that its results should be
The visual effects of these declarations are shown in the Control Center section.
The predicate analysis_api:analysis_definition(Name, Trigger, Severity, Category, Description, QuickFix) tells the JT Control Center about an analysis that it should display. Its six arguments define properties of the analysis:
The following listing shows the definition of the Depth of inheritance analysis from our Tutorial project 1).
% The predicate analysis_definition/6 logically belongs to the % analysis_api module but clauses can be defined in many files: :- multifile(analysis_api:analysis_definition/6). % The clause defining the Stinky_DOI analysis: analysis_api:analysis_definition( 'Stinky_DOI', % Name onSave, % Trigger warning, % Severity 'Metrics', % Category 'Depth of Inheritance is too big', % Description false % QuickfixAvailable ).
Implementing a clause for the multifile predicate analysis_api:analysis_result/3 makes the results of you analysis predicate accessible to the GUI of JT. The predicate to implement has three arguments:
The first thing to do is to include the following declaration into your file: <code > :- multifile(analysis_api:analysis_result/3). % (Name, Group, Result) </Code> Then you implement a clause for the predicate. Two examples are shown below. Our tutorial project contains several more examples.
The Depth of Inheritance (DOI) analysis implemented in the stinky_doi/3 predicate finds individual results. This is indicated by the anonymous variable “_” in the second argument of the related clause for analysis_result/3: <code 1 > analysis_api:analysis_result('Stinky_DOI', _, Result) :-
% Call the DOI analysis predicate that you implemented: stinky_doi(DOI, Id, _), % Create a description that refers to the dynamically computed DOI value: format(atom(Description), 'The Depth of Inheritance of this class is ~w', [DOI]), % Wrap everything into a result term for the GUI: make_result_term(stinky_doi(Id), Description, Result).
Note that the call to format/3 in line 6 is only needed because the Description is created dynamically, depending on the value of the DOI variable. A statically known value could be passed directly as the Description argument of the call in line 9.
Note also that the functor of the first argument in the call to make make_result_term/3 can be anything, it does not need to be the predicate name. In our example, the decision to use the predicate name (compare lines 3 and 9) was a matter of personal taste. See also → generating a result term.
The Singleton Pattern Detection analysis finds groups of related results. Each occurrence of a singleton pattern consists of three elements:
Each element is represented by one result term. The terms are returned to the calling site (the GUI of JT) one by one, via backtracking.
To tell the GUI that all result that are related to the same class belong together the predicate uses the group identifier singleton(Class) in the second argument. So the GUI knows that is should present elements that share this identifier together, as parts of one result for the Singleton Pattern Detection analysis.
analysis_api:analysis_result('mine_singleton', singleton(Class), Result) :- % Call the Singleton Pattern detector predicate: classMethodReturnsOwnInstance(Class, Method, Field), % Wrap each element into a result term for the GUI, indicating which % element plays which role in the detected pattern occurrence. % The disjunction forces returning the results one by one, via backtracking: ( make_result_term(class(Class), 'This is a singleton', Result) ; make_result_term(getInstance(Method), 'This is the getInstance method of a singleton', Result) ; make_result_term(instance_field(Field), 'This is the instance field of a singleton', Result) ).
See also → generating a result term.
The predicate make_result_term/3 is used to generate a term describing a result. It has three arguments. The first two are required input, the third is bound to the generated term:
make_result_term(RoleTerm, Description, Result) is predefined. To use it you have to import it:
:- use_module(jt_analysis(make_result_terms), [
make_result_term/3 % (RoleTerm, Description, Result)
Transformations of program elements returned as analysis results are declared using the multifile predicate transformation_api:transformation/5. Its arguments are:
This predicate is called by JTransformer with the mode (+, +, -, -, -), i.e. Group and RoleTerm are bound.
The following listing shows the transformation defined for the “Singleton Constructor not private” analysis from our Tutorial project.
:- multifile(transformation_api:transformation/5). % Don't forget the declaration! transformation_api:transformation( _, % Individual result (No group) singleton_constructor_not_private(Id), % RoleTerm make_constructor_private(Id), % CTHead 'Change visibilty of constructor to private', % Description [preview]). % Option: Show Preview
The declared transformations for a particular analysis result are shown in the Java editor. The screenshot below shows the transformations for a result of the “Singleton Constructor not private” analysis. The first two offered transformations result from the transformation definition shown above.
This suggestion is always generated, independent of the options set in the transformation declaration.
This suggestion is generated if the 'preview' or the 'global' option is set in the transformation declaration (for analyses that apply globally, a preview is always generated).
The last four transformations are always offered, for each result: