All plugins are located in subdirectories of com/togethersoft/modules/qa directory.
Audit plugins are located in the audit subdirectory while metrics plugins in metrics subdirectory.
Each plugin is located in its own directory and consists of at least three files: class file (or files), description file in html format and properties file.
Class file contains java class of plugin. Description file is displayed in description pane of the audit or metrics dialogue. Properties file stores plugin options.
Plugin's class name must be exactly the same as the name of its directory.
Plugin's package name must be equal to
com.togethersoft.modules.qa.<subsystem>.<plugin_name>
,where <subsystem> is audit
for audit plugin and metrics for metrics plugin.
Plugin description and properties file names must be the same as plugin class file name
but with extension of "html" or "properties" accordingly.
metrics or audit directory.
It examines all its subdirectories. For each subdirectory, it tries to find file that have the same name
as this subdirectory and "class" extension. If such file is found, plugin loader loads this class and creates its instance.
Hence, plugin class must have public default constructor. Next, loader finds method with the signature public void load() in this class or its ancestors. If such method is found, plugin loader invokes it and then appends plugin to the plugins list. If any of the above-mentioned actions fails, plugin loader goes to the next subdirectory.
For each subsystem, audit, or metrics, this operation is performed only once, on the first invocation of subsystem.
Each audit plugin must implement
AuditPlugin interface.
Each metrics plugin must implement
MetricsPlugin interface
(all mentioned classes belong to the package com.togethersoft.modules.qa.api).
These interfaces are both derived from
Plugin
interfaces.
The entry point of the
AuditPlugin
is
run
method. It is invoked for every package and class selected in diagram
and for each subpackage and subclass. It examines class or package passed by parameter;
if it discovers audit rule violation, it adds it to the violations list with the help of the
static method
add
of AuditViolations class
MetricsPlugin entry point
has the same name
run
but a different signature. It has two parameters. The first represents class or package.
The second is an object of
MetricResult class.
Plugin stores the result value in this object.
Overload run method. Its signature is
public boolean run(SciObject obj)
for audit
and
public boolean run(SciObject obj, MetricsResult result)
for metrics plugin.
This method would be invoked for every package and class selected in diagram as well as for each their subpackage and subclass.
You can choose either of the following two ways to implement the run method.
You can process object yourself, enumerating given SciObject attributes, operations
and their code and so on via Together open API methods.
Alternatively, you can make AuditPluginImpl to do all work for you
and overload only methods processing entities, which are of interest for you
(it might be only declaration statements and loops, for example).
In this case, you run method simply call
PluginImpl.process
method. This method goes through the class attributes, operations, nested classes, their code and so on,
and for each kind of above-mentioned class elements call corresponding methods (
processAttrib
for each attribute,
processOper
for each operation,
processParam
for each operation parameter
and so on.
The example of the first way you can see in the ExmMt1 and ExmMt2 samples. They both are interested only in counting of methods, so it is much more effective and even easier to enumerate class methods via Together open API methods.
ExmAu2 sample uses the second way.
It must process all variable declarations,
so it is better to delegate all work of going through methods and their code
to PluginImpl in this case.
Therefore, it calls
process
method of the superclass and overloads processLoopStmt
and processDeclStmt methods.
The first is called for each loop (for or while) statement
while the second is called for each declaration.
To speed up the operation you may set procGoal variable to
PROC_OPERS
if you want PluginImpl to process only operations, or
PROC_ATTRS
if you are interested only in attributes, or both.
PluginImpl.process method, it examines whether the object passed by obj parameter represents a class
(i.e. is instance of SciClass class). If so,
PluginImpl.processClass
method is invoked. This method goes through all class elements (attributes, methods, nested classes, code and so on)
and invokes the following methods, which you can override.
processAttrib | for each attribute |
processLocalVar | for each local variable |
processConceptVar | for each attribute, parameter, or local variable |
processDeclStmt | for each declaration statement |
processInitValue | for each initialization expression of class attribute or local variable |
processCompStmt | for each compound statement |
processCodeBlock | for each code block |
processStatement | for each statement within code block |
processExpression | for each expression, including expressions in function calls, conditional or loop statements and so on |
processExprStmt | for each expression statement |
processSwitchStmt | for each switch statement |
processCaseStmt | for each case statement |
processIfStmt | for each if statement |
processLoopStmt | for each loop statement |
processOper | for each operation |
processParam | for each parameter of the operation |
processReturnStmt | for each return statement |
processThrowSpec | for each exception in the operation throw list |
processThrowStmt | for each throw statement |
processTryStmt | for each try-catch statement |
processCatchStmt | for each catch statement |
The following two methods are invoked for each class: | |
processAttribs | to process class attributes. Default implementation calls processAttrib for each attribute. |
processOpers | to process class operations. Default implementation calls processOper for each operation. |
The most of these methods have default implementation.
For example, processOper method calls
processParam for each parameter of operation,
processThrowSpec for each exception in the throw list of operation and
processCodeBlock for the body of operation.
So, don't forget to call superclass' method
if you are interested in further processing of current element.
Below you can see sequence diagram of the PluginImpl.processClass method.
Since this diagram is too complex, it is divided into three parts.
PluginImpl.processClass method consists of
1) processing class' attributes and
2) processing class' operations
Therefore the first part of diagram shows common schema without details,
the second shows attributes processing, and the third - operations processing.
Common sequence diagram (without details)
Attributes processing sequence diagram
Operations processing sequence diagram
public String getTitle()
to return the title of plugin. It is used to display plugin in list
public String getGroupName()
to set plugin group.
You may either return your own group name or return the name of any existing group.
It is also useful to override
getDefaultChoice()
method. It determines whether plugin would be initially chosen or not.
For audit plugin, you often want to override
getDefaultSeverity()
method (default severity is SEVERITY_NORMAL).
Values returned by metrics plugin are not aggregated by default, so as a rule, you also have to override
getDefaultAggregation()
method.
protected SciMember CurrMbr
contains member being processesprotected SciClass currCls
contains class being processedprotected boolean stopIter
allows to stop processing (if you will set it to true)MetricsResult
class stores the result of metrics plugin.
AuditViolation
class stores information about violation of audit plugin rules.
AuditViolations
class maintains the list of all such violations. (In fact, the only methods of AuditViolations class
we are interested in is method add).
For audit plugin, you can use static method of AuditViolations class
add(AuditPlugin plugin, SciObject node, SciObject item)
or
add(AuditPlugin plugin, SciObject node, SciObject item, String explanation)
to add found errors or warnings.
For metrics plugin, run method simply sets the
value attribute of the second parameter.
The
createOptionsPane
method is used
to create panel (the descendant of JComponent) with additional options.
It is called once, upon plugin loading.
The
getOptionsPane
method returns panel created earlier by createOptionsPane.
The
storeOptions
method is invoked when user presses 'Start' button.
It stores options selected with user in class variables and in the properties file.
The
restoreOptions
method is used to restore options from the properties file.
You need to add the following import directives in your plugin source code:
import com.togethersoft.modules.qa.api.*;
import com.togethersoft.modules.qa.utils.*;
import com.togethersoft.openapi.sci.*;
import com.togethersoft.openapi.sci.enum.*;
There are three samples of plugins - one for audit and two for metric plugins.
Audit plugin sample, ExmAu1 , inspects all variables declaration and warns if the declaration is within loop statement (as such declarations lower performance).
The first metrics plugin example, ExmMt1 , simply counts the number of operations of a class.
The second metrics plugin example, ExmMt2 , does the same as ExmMt1, but it allows user to choose whether he wants to count only public operations, or private, protected or package, or any combination of them.
We also provide source code of PluginImpl, AuditPluginImpl and MetricsPluginImpl classes.