-
-
Notifications
You must be signed in to change notification settings - Fork 346
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: add CtIterator for dfs-based iteration of CtElement's descen…
…dants (#1980)
- Loading branch information
Showing
2 changed files
with
127 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |