/* * Copyright (c) 2013, SRI International * All rights reserved. * Licensed under the The BSD 3-Clause License; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://opensource.org/licenses/BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the aic-util nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.sri.ai.util.collect; import java.lang.reflect.InvocationTargetException; import java.util.Iterator; import com.google.common.annotations.Beta; import com.google.common.base.Predicate; /** * A facility for creating a depth-first iterator over the subtrees of a * user-defined tree (by default including the root subtree itself, which is the * same as the original tree). * <p> * The user defines the tree by providing a root object and a method * {@link #getChildrenIterator(Object)} returning an iterator for the children * of a given object (or <code>null</code> if there are no children). * <p> * Let C be the class extending {@link DepthFirstIterator}. Child nodes are * explored depth-first by constructing an instance of C for each of them. This * instance is constructed by using Java's reflection mechanism to find a * single-parameter constructor. If however C does not have a single-parameter * constructor, it needs to override * {@link #makeDepthFirstIteratorOfCurrentExtendingClass(Object child)} to * provide a depth-first iterator on a given child. Typically * {@link #makeDepthFirstIteratorOfCurrentExtendingClass(Object child)} will do * this by using a C constructor taking more than one parameter, but any * depth-first iterator based on the child will do (in fact, any iterator of * child, even if not depth-first, would work, but this would render the overall * iterator depth-first on the first level only). * <p> * The walk can be pruned by overriding the method {@link #pruneChildren()}, * which must return <code>true</code> if the <i>children</i> of {@link #root} * must not be traversed. Note that each visited node has a corresponding * DepthFirstIterator, so it will be its root. Therefore, {@link #pruneChildren()} will be * applied to each visited node. * <p> * Also note that, if prune returns <code>true</code> for a certain node, that * node is still visited, but not its children. This may contradict an intuition * that the node itself should not be visited. The reason for this design * decision is that, should we wish not to visit a node based on its parent's * properties, it would be difficult to write a prune method, which does not * have access to a node's parent node. The way prune works, one can easily do * that by using a node's property to avoid visiting its children. If we want to * avoid visiting the node itself, we can use {@link PredicateIterator} to * filter them out as well. * <p> * By default, {@link #pruneChildren()} returns the result of evaluating * {@link #pruneChildrenPredicate} on {@link #root} if that field is not * <code>null</code>, and <code>false</code> otherwise. Therefore, pruning * functionality can be implemented either by overriding * {@link #pruneChildren()} in an extending class, or assigning a new * {@link Predicate} to {@link #pruneChildrenPredicate} (using its setter * {@link #setPruneChildrenPredicate(Predicate)}). * * @author braz */ @Beta public abstract class DepthFirstIterator<E> extends EZIterator<E> { protected E root; private boolean hasToReturnRootStill; private Iterator<E> childrenIterator; private Iterator<E> currentChildIterator; protected Predicate<E> pruneChildrenPredicate = null; public DepthFirstIterator(E root, boolean mustReturnRootAsWell) { setUp(root); hasToReturnRootStill = mustReturnRootAsWell; } public DepthFirstIterator(E root) { this(root, true); } public abstract Iterator<E> getChildrenIterator(E object); public abstract DepthFirstIterator<E> newInstance(E object); /** * Given an object, makes a DepthFirstIterator (an instance of the invoking * extension, rather than DepthFirstIterator proper) with that object as * root. * * @param object * the root object for the iterator. * @return a new DepthFirstIterator with the given object as root. * @throws InstantiationException * an instantiation exception. * @throws IllegalAccessException * an illegal access exception. * @throws InvocationTargetException * an invocation target exception. */ protected DepthFirstIterator<E> makeDepthFirstIteratorOfCurrentExtendingClass(E object) throws InstantiationException, IllegalAccessException, InvocationTargetException { DepthFirstIterator<E> result = newInstance(object); result.pruneChildrenPredicate = pruneChildrenPredicate; return result; } private void setUp(E root) { this.root = root; this.childrenIterator = null; } @Override protected E calculateNext() { if (hasToReturnRootStill) { hasToReturnRootStill = false; return root; } // pruneChildren() decides whether children must be visited or not if (childrenIterator == null && !pruneChildren()) { childrenIterator = getChildrenIterator(root); } if (childrenIterator == null) { return null; } ensureCurrentChildIteratorHasNextOrIsNull(); if (currentChildIterator != null) { return currentChildIterator.next(); } return null; } private void ensureCurrentChildIteratorHasNextOrIsNull() { while (currentChildIterator == null || !currentChildIterator.hasNext()) { if ( ! childrenIterator.hasNext()) { currentChildIterator = null; return; } else { E child = childrenIterator.next(); try { currentChildIterator = makeDepthFirstIteratorOfCurrentExtendingClass(child); } catch (Exception e) { e.printStackTrace(); } } } } public boolean pruneChildren() { if (pruneChildrenPredicate == null) { return false; } else { boolean result = pruneChildrenPredicate.apply(root); return result; } } /** * @return the current prune children predicate, used by the default * implementation of {@link #pruneChildren()} to decide whether to * prune the children of the current node (that default * implementation returns <code>false</code> if the prune predicate * is <code>null</code>). */ public Predicate getPruneChildrenPredicate() { return pruneChildrenPredicate; } /** * Sets the prune children predicate, used by the default implementation of * {@link #pruneChildren()} to decide whether to prune the children of the * current node (that default implementation returns <code>false</code> if * the prune predicate is <code>null</code>). * * @param prunePredicate * the prune predicate to set. */ public void setPruneChildrenPredicate(Predicate<E> prunePredicate) { this.pruneChildrenPredicate = prunePredicate; } }