Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Review: to fix issue #1979 #1980

Merged
merged 5 commits into from
May 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
}
}