Once you have written a predicate (e.g. the System.out.println 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) 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 System.out.println detector from our this tutorial.
% The predicate analysis_definition/5 logically belongs to the % analysis_api module but clauses can be defined in many files: :- multifile(analysis_api:analysis_definition/5). % The clause defining the System.out.println detector: analysis_api:analysis_definition( 'sysout_call', % Name onSave, % Trigger warning, % Severity 'Logging', % Category 'Call to System.out.println' % Description ).
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 System.out.println detector implemented in the sysout_call/6 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('sysout_call', _, Result) :-
% Call the System.out.println detector that you implemented: sysout_call(CallId, _, _, _, _, _) , % Create a description Description = 'Call to System.out.println', % Wrap everything into a result term for the GUI: make_result_term(sysout_call(CallId), Description, Result).
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 from our Tutorial project 1) 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.
Sometimes a generic description in your marker is not enough and you want to add dynamically computed values (e.g. names of referenced elements, results of metrics …). For the “Depth of Inheritance (DOI)” metric from our Tutorial project 2) it is helpful to add the computed value to the marker. <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 the call to format/3 in line 6 that creates the description dynamically, depending on the value of the DOI variable.
There is also a way to create the description in the make_result_term call. Just delete line 6 and replace line 9 by:
make_result_term(stinky_doi(Id), 'The Depth of Inheritance of this class is ~w'-[DOI], Result).
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)