/* * Copyright 2015 Lukas Krejci * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package org.revapi.query; import java.util.Deque; import java.util.Iterator; import java.util.LinkedList; import java.util.NoSuchElementException; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.revapi.Element; /** * Recursively walks an element forest in a depth-first manner leaving out elements not matching the optionally provided * filter. * * @author Lukas Krejci * @since 0.1 */ public class DFSFilteringIterator<E extends Element> implements Iterator<E> { private final Class<? extends E> resultClass; private final Deque<Iterator<? extends Element>> dfsStack = new LinkedList<>(); private final Filter<? super E> filter; private E current; /** * Constructor. * * @param rootIterator the iterator over the root elements of the forest * @param resultClass the class of the elements to look for in the forest. All the returned elements will be * assignable to this class. * @param filter optional filter that further filters out unwanted elements. */ public DFSFilteringIterator(@Nonnull Iterator<? extends Element> rootIterator, @Nonnull Class<? extends E> resultClass, @Nullable Filter<? super E> filter) { dfsStack.push(rootIterator); this.resultClass = resultClass; this.filter = filter; } @Override public boolean hasNext() { if (current != null) { return true; } else { while (true) { while (!dfsStack.isEmpty() && !dfsStack.peek().hasNext()) { dfsStack.pop(); } if (dfsStack.isEmpty()) { return false; } Iterator<? extends Element> currentIterator = dfsStack.peek(); while (currentIterator.hasNext()) { Element next = currentIterator.next(); while (next == null && currentIterator.hasNext()) { next = currentIterator.next(); } if (next == null) { break; } boolean found = false; if (resultClass.isAssignableFrom(next.getClass())) { E cur = resultClass.cast(next); if (filter == null || filter.applies(cur)) { current = cur; found = true; } } // we're doing DFS, so once we report this element, we want to start reporting its children // even if we don't report the current element, we might report one of its children so we base // our decision on whether to descend or not regardless of whether we're reporting the current // element or not boolean descend = filter == null || filter.shouldDescendInto(next); if (descend) { Iterator<? extends Element> childIterator = next.getChildren().iterator(); if (childIterator.hasNext()) { dfsStack.push(childIterator); } } if (found) { return true; } if (descend) { break; } } } } } @Override public E next() { if (current == null && !hasNext()) { throw new NoSuchElementException(); } E ret = current; current = null; return ret; } /** * @throws UnsupportedOperationException This is not supported. */ @Override public void remove() { //is this worth implementing? throw new UnsupportedOperationException(); } }