/*******************************************************************************
* Copyright (c) 2004, 2010 BREDEX GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BREDEX GmbH - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.jubula.client.core.utils;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* Traverses a tree of <code>T</code> instances top-down. The traversal starts
* at the root node passed to the constructor. The recursion is established by
* calling {@link #getChildIterator(Object)} on any node. On the way top-down,
* the <code>operate()</code> method of the passed operation is called for
* any node.
* <br/><br/>
* Based on {@link TreeTraverser}, which should eventually be replaced by a
* concrete class extending this class.
*
* @param <T> The class of object contained in the tree to be traversed.
*
* @author BREDEX GmbH
* @created 02.06.2010
*/
public abstract class AbstractTreeTraverser<T> {
/** constant for no maximum traversal depth */
public static final int NO_DEPTH_LIMIT = -1;
/**
* The tree operation.
*/
private List<ITreeNodeOperation<T>> m_operations =
new ArrayList<ITreeNodeOperation<T>>();
/**
* The root node.
*/
private T m_rootNode;
/**
* The maximum traversal depth. <code>NO_DEPTH_LIMIT</code> by default.
*/
private int m_maxDepth = NO_DEPTH_LIMIT;
/**
* The constructor.
*
* @param rootNode
* The node where the traversion starts
*/
public AbstractTreeTraverser(T rootNode) {
m_rootNode = rootNode;
}
/**
* The constructor.
*
* @param rootNode
* The node where the traversion starts
* @param operation
* The operation to call on any node
*/
public AbstractTreeTraverser(T rootNode,
ITreeNodeOperation<T> operation) {
this(rootNode);
m_operations.add(operation);
}
/**
* The constructor.
*
* @param rootNode
* The node where the traversion starts
* @param operation
* The operation to call on any node
* @param maxTraversalDepth The maximum depth of traversal.
*/
public AbstractTreeTraverser(T rootNode,
ITreeNodeOperation<T> operation, int maxTraversalDepth) {
this(rootNode, operation);
m_maxDepth = maxTraversalDepth;
}
/**
* Implements the recursive traversion.
*
* @param context
* The context
* @param parent
* The parent node
* @param node
* The current node
*/
protected void traverseImpl(ITreeTraverserContext<T> context,
T parent, T node) {
if (m_maxDepth == NO_DEPTH_LIMIT
|| m_maxDepth > context.getCurrentTreePath().size()) {
context.append(node);
Set<ITreeNodeOperation<T>> suspendedOps = null;
for (ITreeNodeOperation<T> operation : m_operations) {
boolean continueWork =
operation.operate(context, parent, node, false);
if (!continueWork) {
if (suspendedOps == null) {
suspendedOps = new HashSet<ITreeNodeOperation<T>>(
m_operations.size());
}
suspendedOps.add(operation);
}
}
if (suspendedOps != null) {
m_operations.removeAll(suspendedOps);
}
if (context.isContinue() && !m_operations.isEmpty()) {
for (Iterator<T> iter = getChildIterator(node);
iter.hasNext(); ) {
traverseImpl(context, node, iter.next());
}
}
if (suspendedOps != null) {
m_operations.addAll(suspendedOps);
}
if (context.isContinue()) {
for (ITreeNodeOperation<T> operation : m_operations) {
operation.postOperate(context, parent, node,
false);
}
}
context.removeLast();
}
}
/**
* @param node A node in the traversed tree.
* @return an iterator that returns the children of the given node.
*/
protected abstract Iterator<T> getChildIterator(T node);
/**
* Starts the traversion of the tree under the root node passed to the
* constructor. Event handlers are not included during the traversion.
*/
public void traverse() {
traverseImpl(new TreeTraverserContext<T>(m_rootNode), null, m_rootNode);
}
/**
*
* @return the tree node operation
*/
protected List<ITreeNodeOperation<T>> getOperations() {
return m_operations;
}
/**
* adds a <code>ITreeNodeOperation</code> to the list of operations
* that are executed on every step
*
* @param op
* <code>ITreeNodeOperation</code>
*/
public void addOperation(ITreeNodeOperation<T> op) {
m_operations.add(op);
}
}