/*******************************************************************************
* Copyright (c) 2002-2006 Innoopract Informationssysteme 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:
* Innoopract Informationssysteme GmbH - initial API and implementation
******************************************************************************/
package com.w4t.util;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.rwt.internal.util.ParamCheck;
import com.w4t.*;
import com.w4t.dhtml.Item;
import com.w4t.dhtml.Node;
/**
* <p>Simple utility class that provides a traversal through a component-tree
* using the visitor pattern. The component types of interest are
* <code>WebComponent</code>, <code>WebContainer</code>, <code>Node</code>
* and <code>Decorator</code>, since they represent the different nesting
* types used by W4 Toolkit.</p>
*
* <p>The traversal through the children will be ignored if the visit call
* on the parent node returns <code>false</code>.</p>
*
*/
public class ComponentTreeVisitor {
/**
* <p>The depth-first traversal first visits the root node and then
* the first child node of the search tree that appears and
* thus going deeper and deeper until it hits a node that has no children.
* Then the traversal backtracks and starts off on the next node.</p>
*
* <p>For example, the following component tree</p>
* <pre>
* +- root
* +- a
* +- a1
* +- a2
* +- b
* +- c
* +- c1
* </pre>
* <p>... will be visited in the following order: root, a, a1, a2,
* b, c, c1.</p>
*/
public static final int STRATEGY_DEPTH_FIRST = 0;
/**
* <p>The breadth-first traversal visits the nodes in the order of their depth
* in the component tree. Breadth-first traversal first visits all the nodes
* at depth zero (i.e., the root), then all the nodes at depth one, and
* so on.</p>
*
* <p>For example, the following component tree</p>
* <pre>
* +- root
* +- a
* +- a1
* +- a2
* +- b
* +- c
* +- c1
* </pre>
* <p>... will be visited in the following order: root, a, b, c, a1,
* a2, c1.</p>
*/
public static final int STRATEGY_BREADTH_FIRST = 1;
/**
* <p>The <code>AllComponentTreeVisitor</code> is an convenience
* <code>ComponentTreeVisitor</code> implementation which visits all
* different types that made up the component tree structure. This
* is useful, if the same operation of the <code>doVisit</code> method
* should be performed on each component in the component tree and
* only the common super type API is needed.</p>
*/
public static abstract class AllComponentVisitor
extends ComponentTreeVisitor
{
public boolean visit( final WebComponent leaf ) {
return doVisit( leaf );
}
public boolean visit( final WebContainer container ) {
return doVisit( container );
}
public boolean visit( final Decorator decorator ) {
return doVisit( decorator );
}
public boolean visit( final Node node ) {
return doVisit( node );
}
/**
* <p>Subclassses of <code>AllComponentVisitor</code> must implement
* this method to perform a common operation on each component
* of the component tree.</p>
*
* @param component The actual element of the component tree to process.
* @return Whether the traversal should continue with the component's
* children (if there are any) or not. If <code>true</code>
* children of the specified component will be visited.
*/
public abstract boolean doVisit( final WebComponent component );
}
/**
* <p>Starts the tree traversal with the specified component as root element
* of the component tree to visit using the
* {@link ComponentTreeVisitor#STRATEGY_DEPTH_FIRST} traversal
* algorithm.</p>
*
* @param root The root component of the tree traversal. Must not be null.
* @param visitor The actual <code>ComponentTreeVisitor</code> implementation
* that performs the operations on the visited component
* tree elements. Must not be null.
*/
public static void accept( final WebComponent root,
final ComponentTreeVisitor visitor )
{
accept( root, STRATEGY_DEPTH_FIRST, visitor );
}
/**
* <p>Starts the tree traversal with the specified component as root element
* of the component tree to visit.</p>
*
* @param root The root component of the tree traversal. Must not be null.
* @param strategy The traversal strategy that is used for the tree traversal.
* Must be one of
* {@link ComponentTreeVisitor#STRATEGY_DEPTH_FIRST} or
* {@link ComponentTreeVisitor#STRATEGY_BREADTH_FIRST}.
* @param visitor The actual <code>ComponentTreeVisitor</code> implementation
* that performs the operations on the visited component
* tree elements. Must not be null.
*/
public static void accept( final WebComponent root,
final int strategy,
final ComponentTreeVisitor visitor )
{
ParamCheck.notNull( root, "root" );
ParamCheck.notNull( visitor, "visitor" );
switch( strategy ) {
case STRATEGY_DEPTH_FIRST:
acceptDepthFirst( root , visitor );
break;
case STRATEGY_BREADTH_FIRST:
doAcceptBreadthFirst( root , visitor );
break;
default:
throw new IllegalArgumentException( "Unknown traversal strategy." );
}
}
private static void doAcceptBreadthFirst( final WebComponent root,
final ComponentTreeVisitor visitor )
{
List queue = new ArrayList();
queue.add( root );
while( !queue.isEmpty() ) {
WebComponent component = ( WebComponent )queue.remove( 0 );
if( performBFVisit( component, visitor ) ) {
if( component instanceof WebContainer ) {
WebContainer container = ( WebContainer )component;
WebComponent[] webComponents = container.get();
for( int i = 0; i < webComponents.length; i++ ) {
queue.add( webComponents[ i ] );
}
} else if( component instanceof Node ) {
Node node = ( Node )component;
Item[] items = node.getItem();
for( int i = 0; i < items.length; i++ ) {
queue.add( items[ i ] );
}
} else if( component instanceof Decorator ) {
Decorator decorator = ( Decorator )component;
queue.add( decorator.getContent() );
}
}
}
}
private static boolean performBFVisit( final WebComponent component,
final ComponentTreeVisitor visitor )
{
boolean result = false;
if( component instanceof WebContainer ) {
result = visitor.visit( ( WebContainer )component );
} else if( component instanceof Decorator ) {
result = visitor.visit( ( Decorator )component );
} else if( component instanceof Node ) {
result = visitor.visit( ( Node )component );
} else {
result = visitor.visit( component );
}
return result;
}
private static void acceptDepthFirst( final WebComponent root,
final ComponentTreeVisitor visitor )
{
if( root instanceof Decorator ) {
Decorator decorator = ( Decorator )root;
if( visitor.visit( decorator ) ) {
acceptDepthFirst( decorator.getContent(), visitor );
}
} else if( root instanceof WebContainer ) {
WebContainer container = ( WebContainer )root;
if( visitor.visit( container ) ) {
WebComponent[] webComponents = container.get();
for( int i = 0; i < webComponents.length; i++ ) {
acceptDepthFirst( webComponents[ i ], visitor );
}
}
} else if( root instanceof Node ) {
Node node = ( Node )root;
if( visitor.visit( node ) ) {
Item[] items = node.getItem();
for( int i = 0; i < items.length; i++ ) {
acceptDepthFirst( items[ i ], visitor );
}
}
} else {
visitor.visit( root );
}
}
/**
* <p>Subclassses of <code>ComponentTreeVisitor</code> may override
* this method to perform a <code>WebComponent</code> type specific
* operation.</p>
*
* <p>Note that only 'leaf' components, this means components that
* do not have potentially child components, will be visited by
* this method.</p>
*
* @param leaf A 'leaf' element of the component tree to process.
* @return As 'leaf' components do not have any child components the return
* value will not be evaluated. This exists only for method signature
* reasons.
*/
public boolean visit( final WebComponent leaf ) {
return true;
}
/**
* <p>Subclassses of <code>ComponentTreeVisitor</code> may override
* this method to perform a <code>WebContainer</code> type specific
* operation.</p>
*
* @param container A <code>WebContainer</code> instance of the component
* tree to process.
* @return Whether the traversal should continue with the component's
* children (if there are any) or not. If <code>true</code>
* children of the specified component will be visited.
*/
public boolean visit( final WebContainer container ) {
return true;
}
/**
* <p>Subclassses of <code>ComponentTreeVisitor</code> may override
* this method to perform a <code>Decorator</code> type specific
* operation.</p>
*
* @param decorator A <code>Decorator</code> instance of the component tree
* to process.
* @return Whether the traversal should continue with the component's
* children (if there are any) or not. If <code>true</code>
* children of the specified component will be visited.
*/
public boolean visit( final Decorator decorator ) {
return true;
}
/**
* <p>
* Subclassses of <code>ComponentTreeVisitor</code> may override this method
* to perform a <code>Node</code> type specific operation.
* </p>
*
* @param node A <code>Node</code> instance of the component tree to
* process.
* @return Whether the traversal should continue with the component's children
* (if there are any) or not. If <code>true</code> children of the
* specified component will be visited.
*/
public boolean visit( final Node node ) {
return true;
}
}