CORPORATE RESOURCES…

Rules development for Kiuwan (V): Query API

Once again, we continue our posts series on rules development for KIUWAN.

In the last post, we saw the basic functionalities to navigate through the abstract sintax tree (AST): BaseNode, TreeNode, NodeVisitor and NodePredicate; and we also wrote about the two available AST versions:High-Level AST and Low-Level AST.

Today, we’ll see the use of another available API for implementing rules: Query API.

The com.optimyth.qaking.highlevelapi.dsl.Query class represents a query in the sintax tree, in this case, the High-Level Tree (HLT). Query provides a “fluent interface” to perform searches in the AST, specifying a sequence of operations (find, filter, navigate, visit…), which will be done starting from a given set of nodes. Each operation will be set passing primitive objects (NodePredicate, NodeVisitor, NodeSearch or Navigation) so then a call to one of the available run() methods makes to execute all the registered operations. Once found the nodes of interest, the report() operation will generate in the report one violation for each node reached.

Primitive objects

com.als.core.ast.NodeVisitor: apply some logic to the node. Execution sequence on a tree or sub-tree.

com.als.core.ast.NodePredicate: check if a node meets a series of clauses or conditions.

com.optimyth.qaking.highlevelapi.nodeset.NodeSearch: find some node starting from a given one. For example, find the declaration of a variable from its use, the definition of a function from its call…

com.optimyth.qaking.highlevelapi.navigation.Navigation: navigate through the AST from a given node, possibly applying some other primitive object.

com.optimyth.qaking.highlevelapi.dsl.QueryOperation: definition of a new operation.

The available operations to perform the queries can be classified as follows:

Most of these operations return the same Query instance, so that calls can be chained. In addtion, available actions and primitives are typically thread-safe, so you could use the Query object as rule instance field and make the query.run() call from the visit method of the rule to process each source file under analysis.

As a simple example, imagine you want to report the getter methods that return null. Using the Query API, your rule could have han implementation that:

1

2

3

4

5

6

7

8

9

10

Predicate isGetter=...;

NodePredicate returnsNull=...;

Queryq=Query.query()

.find(methods(isGetter))

.filter(returnsNull)

.report();

...// execute query from high-level root node

q.run(rule,ctx,ctx.getHighLevelTree());

The rule will be with a declarative format and coded in a few lines, leaving the thickest part of the implementationin the declaration of the appropriate primitives for each case.

For the supported languages (Abap, C#, Cobol, C++, Java, Javascript, PHP), we can find primitives, both predicates and navigations, already predefined, available to use directly in our rules. In each case, they will be found in the jar of the technology parser; for instance, we will have the classes com.optimyth.qaking.java.hla.JavaPredicates (where we can find the isGetter and returnsNull methods, used in the example above, already defined) and com.optimyth.qaking.java.hla.JavaNavigations.

Besides, we can always define those specific classes we need. To do that and specifically in the case of the predicates, it is possible to use both the own classcom.als.core.ast.NodePredicate, already mentioned, and the one defined in Google Guava library (com.google.common.base.Predicate), which is included in the classpath for developing.

Let’s see now a complete example; in this case, a rule that detects, in Java classes, not used variables (local, class fields or parameters).