Skip to content

Commit

Permalink
feature: add CtIterator for dfs-based iteration of CtElement's descen…
Browse files Browse the repository at this point in the history
…dants (#1980)
  • Loading branch information
msramalho authored and monperrus committed May 3, 2018
1 parent 094d7f5 commit fc55d9a
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 0 deletions.
77 changes: 77 additions & 0 deletions src/main/java/spoon/reflect/visitor/CtIterator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package spoon.reflect.visitor;

import spoon.reflect.declaration.CtElement;

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Iterator;

/**
* A class to be able to iterate over the children elements in the tree of a given node, in depth-first order.
*/
public class CtIterator extends CtScanner implements Iterator {
/**
* A deque containing the elements the iterator has seen but not expanded
*/
private ArrayDeque<CtElement> deque = new ArrayDeque<CtElement>() {
/**
* add a collection of elements with addFirst instead of default add() which defaults to addLast()
* @param c Collection of CtElements
* @return true if this deque has changed, in accordance with original method
*/
@Override
public boolean addAll(Collection c) {
for (Object aC : c) {
this.addFirst((CtElement) aC);
}
return c.size() > 0;
}
};

/**
* A deque to be used when scanning an element so that @deque preserves the elements in dfs without complete expansion
*/
private ArrayDeque<CtElement> current_children = new ArrayDeque<>();

/**
* CtIterator constructor, prepares the iterator from the @root node
*
* @param root the initial node to expand
*/
public CtIterator(CtElement root) {
if (root != null) {
deque.add(root);
}
}

/**
* prevent scanner from going down the tree, instead save with other CtElement children of the current node
*
* @param element the next direct child of the current node being expanded
*/
@Override
public void scan(CtElement element) {
if (element != null) {
current_children.addFirst(element);
}
}

@Override
public boolean hasNext() {
return deque.size() > 0;
}

/**
* Dereference the "iterator"
*
* @return CtElement the next element in DFS order without going down the tree
*/
@Override
public Object next() {
CtElement next = deque.pollFirst(); // get the element to expand from the deque
current_children.clear(); // clear for this scan
next.accept(this); // call @scan for each direct child of the node
deque.addAll(current_children); // overridden method to add all to first
return next;
}
}
50 changes: 50 additions & 0 deletions src/test/java/spoon/reflect/visitor/CtIteratorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package spoon.reflect.visitor;

import org.junit.Test;
import spoon.Launcher;
import spoon.reflect.declaration.CtElement;

import java.util.ArrayDeque;

import static org.junit.Assert.assertEquals;

public class CtIteratorTest {
@Test
public void testMethodsInIterator() throws Exception {
// contract: CtIterator must go over all nodes in dfs order
final Launcher launcher = new Launcher();
launcher.setArgs(new String[] {"--output-type", "nooutput"});
launcher.getEnvironment().setNoClasspath(true);
// resources to iterate
launcher.addInputResource("./src/main/java/spoon/reflect/visitor/CtScanner.java");
launcher.buildModel();

// get the first Type
CtElement root = launcher.getFactory().getModel().getAllTypes().iterator().next();

// use custom CtScanner to assert the proper behaviour
CtScannerList counter = new CtScannerList();

// scan the root to get the elements in DFS order
root.accept(counter);

// test the iterator by testing that it matches the DFS order as expected
CtIterator iterator = new CtIterator(root);
while (iterator.hasNext()) {
assertEquals(counter.nodes.pollFirst(), iterator.next());
}
}

/**
* Class that saves a deque with all the nodes the {@link CtScanner} visits,
* in DFS order, for the {@link CtIterator} test
*/
class CtScannerList extends CtScanner {
public ArrayDeque<CtElement> nodes = new ArrayDeque<>();

@Override
protected void enter(CtElement e) {
nodes.addLast(e);
}
}
}

0 comments on commit fc55d9a

Please sign in to comment.