Getters

All elements provide a set of appropriate getters to get the children of an element.

methods = ctClass.getMethods();

In addition, there exists a generic getter based on the role played by an element with respect to its parent. See CtRole (javadoc) for a complete list of roles.

methods = ctClass.getValueByRole(CtRole.METHOD);

While not recommended, it is also possible to get all direct children of an element

allDescendants = ctElement.getDirectChildren();

Filters

A Filter defines a predicate of the form of a matches method that returns true if an element has to be selected in the filtering operation. A Filter is given as parameter to CtElement#getElement(Filter) (or CtQueryable#filterChildren(Filter)) which implements a depth-first search algorithm. During AST traversal, the elements satisfying the matching predicate are selected by the filter.

Here are code examples about the usage of filters. The first example returns all AST nodes of type CtAssignment.

// collecting all assignments of a method body
list1 = methodBody.getElements(new TypeFilter(CtAssignment.class));

The second example selects all deprecated classes.

// collecting all deprecated classes
list2 = rootPackage.getElements(new AnnotationFilter(Deprecated.class));

Now let’s consider a user-defined filter that only matches public fields across all classes.

// creating a custom filter to select all public fields
list3 = rootPackage.filterChildren(
  new AbstractFilter<CtField>(CtField.class) {
    @Override
    public boolean matches(CtField field) {
      return field.getModifiers.contains(ModifierKind.PUBLIC);
    }
  }
).list();

Scanners

CtScanner provides a simple way to visit a node and its children.

//Scanner counting the number of CtFieldWrite
class CounterScanner extends CtScanner {
	private int visited = 0;
	@Override
	public <T> void visitCtFieldWrite(CtFieldWrite<T> fieldWrite) {
		visited++;
	}
}

CounterScanner scanner = new CounterScanner();

//Run the scanner on an element, here the CtClass representing FieldAccessRes
launcher.getFactory().Class().get("FieldAccessRes").accept(scanner);

//scanner.visited now contains the number of children of type CtFieldWrite 
assertEquals(1, scanner.visited);

EarlyTerminatingScanner is a specialized Class implementing CtScanner that stops once terminate() has been called.

See also CtVisitor (javadoc).

Iterator

CtIterator provides an iterator on all transitive children of a node in depth first order.

CtIterator iterator = new CtIterator(root);
while (iterator.hasNext()) {
	CtElement el = iterator.next();
	//do something on each child of root
}

CtBFSIterator is similar to CtIterator but in Breadth first order.

Queries

The Query, introduced in Spoon 5.5 by Pavel Vojtechovsky, is an improved filter mechanism:

  • queries can be done with a Java 8 lambda
  • queries can be chained
  • queries can be reused on multiple input elements

Queries with Java8 lambdas: CtQueryable#map(CtFunction)enables you to give Java 8 lambda as query.

// returns a list of String
list = package.map((CtClass c) -> c.getSimpleName()).list();

Compatibility with existing filters CtQueryable#filterChildren(Filter) is a filtering query that can be chained:

// collecting all methods of deprecated classes
list2 = rootPackage
    .filterChildren(new AnnotationFilter(Deprecated.class)).list()

A boolean return value of the lambda tells whether the elements are selected for inclusion or not.

// creating a custom filter to select all public fields using java 8 lambda
list3 = rootPackage.filterChildren((CtField field)->field.getModifiers.contains(ModifierKind.PUBLIC)).list();

Chaining If the CtFunction returns an object, this object is given as result to the next step of the query. All results of the last query step are results of the query. The results of intermediate steps are not results of the query.:

// a query which processes all not deprecated methods of all deprecated classes
rootPackage
  .filterChildren((CtClass clazz)->clazz.getAnnotation(Deprecated.class)!=null)
  .map((CtClass clazz)->clazz.getMethods())
  .map((CtMethod<?> method)->method.getAnnotation(Deprecated.class)==null)
  .list()
;

Finally, if the CtFunction returns and Iterable or an Array then each item of the collection/array is sent to next query step or result.

Query reuse. Method setInput allows you to reuse the same query over multiple inputs. In such case it makes sense to create unbound query using Factory#createQuery().

// here is the query
CtQuery q = factory.createQuery().map((CtClass c) -> c.getSimpleName());
// using it on a first input
String s1 = q.setInput(cls).list().get(0);
// using it on a second input
String s2 = q.setInput(cls2).list().get(0);

Query evaluation. Each example above use CtQuery#list() to evaluate the query. The list method evaluates the query and returns the List, which contains all the results of the query.

But it is not the only way how to evaluate query. There is CtQuery#forEach(CtConsumer), which sends each query result to the CtConsumer#accept function. It is more efficient in cases when query results can be immediately processed.

//prints each deprecated element
rootPackage
    .filterChildren(new AnnotationFilter(Deprecated.class)).forEach((CtElement ele)->System.out.println(ele));

Finally there is CtQuery#first(), which evaluates the query until first query result is found. Then the evaluation is terminated and first result is returned. It is faster than query.list().get(0), because query engine does not collect other results.

// returns first deprecated element
CtElement firstDeprecated = rootPackage.filterChildren(new AnnotationFilter(Deprecated.class)).first();