/*
* Copyright (C) 2011 Laurent Caillette
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.novelang.common.tree;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
/**
* {@link Treepath}-based tree traversal functions.
*
* @author Laurent Caillette
*/
public abstract class Traversal< T extends Tree< T > > {
protected final Predicate< T > treeFilter ;
private Traversal( final Predicate< T > treeFilter ) {
this.treeFilter = treeFilter ;
}
/**
* Returns the {@link Treepath} to the next {@link Tree} object for this traversal
* algorithm, or null if there is none.
*
* @param treepath a non-null object.
* @return a possibly null object.
*/
public abstract Treepath< T > next( final Treepath< T > treepath ) ;
/**
* Returns the first eleemnt for this traversal, or null if there is none.
* @param treepath a non-null object.
* @return a possibly null object.
*/
public abstract Treepath< T > first( final Treepath< T > treepath ) ;
// =================
// MirroredPostorder
// =================
/**
* Function object capturing a tree filter.
*/
public static final class MirroredPostorder< T extends Tree< T > > extends Traversal< T > {
private MirroredPostorder( final Predicate< T > treeFilter ) {
super( treeFilter ) ;
}
private MirroredPostorder() {
this( Predicates.< T >alwaysTrue() ) ;
}
/**
* Factory method for type inference.
*/
public static< T extends Tree< T > > MirroredPostorder< T > create() {
return new MirroredPostorder< T >() ;
}
/**
* Factory method for type inference.
*/
public static< T extends Tree< T > > MirroredPostorder< T > create(
final Predicate< T > treeFilter
) {
return new MirroredPostorder< T >( treeFilter ) ;
}
/**
* Returns a {@code Treepath} corresponding to the last tree in a
* {@link #next(org.novelang.common.tree.Treepath) postorder traversal}
* in the {@link Treepath#getTreeAtEnd()} end} tree.
*
* @see #next (Treepath)
*/
@Override
public Treepath< T > first( final Treepath< T > treepath ) {
Treepath< T > result = treepath ;
while( true ) {
final T tree = result.getTreeAtEnd() ;
final int childCount = tree.getChildCount() ;
if( childCount > 0 && treeFilter.apply( tree ) ) {
result = Treepath.create( result, childCount - 1 ) ;
} else {
return result ;
}
}
}
/**
* Returns a {@code Treepath} object corresponding to the previous tree in a
* <a href="http://en.wikipedia.org/wiki/Tree_traversal">postorder</a> traversal.
* <pre>
* *t0 *t0 *t0 *t0
* | next | next | next | next
* *t1 --> *t1 --> *t1 --> t1 --> null
* / \ / \ / \ / \
* t2 *t3 *t2 t3 t2 t3 t2 t3
* </pre>
*
* This is a valuable traversal algorithm that preserves indexes of unmodified treepaths.
*
* @param treepath a non-null object.
* @return the treepath to the next tree, or null.
*/
@Override
public Treepath< T > next( final Treepath< T > treepath ) {
if( treepath.getLength() > 1 ) {
if( TreepathTools.hasPreviousSibling( treepath ) ) {
final Treepath< T > previousSibling = TreepathTools.getPreviousSibling( treepath ) ;
if( previousSibling.getTreeAtEnd().getChildCount() == 0 ) {
return previousSibling ;
} else {
return first(previousSibling) ;
}
} else {
return treepath.getPrevious() ;
}
} else {
return null ;
}
}
}
// ========
// Preorder
// ========
/**
* Function object capturing a tree filter.
*/
public static final class Preorder< T extends Tree< T > > extends Traversal< T > {
private Preorder( final Predicate< T > treeFilter ) {
super( treeFilter ) ;
}
private Preorder() {
this( Predicates.< T >alwaysTrue() ) ;
}
/**
* Factory method for type inference.
*/
public static< T extends Tree< T > > Preorder< T > create() {
return new Preorder< T >() ;
}
@Override
public Treepath< T > first( final Treepath< T > treepath ) {
return treepath.getStart() ;
}
/**
* Returns a {@code Treepath} object to the next tree in a
* <a href="http://en.wikipedia.org/wiki/Tree_traversal">preorder</a> traversal.
* <pre>
* *t0 *t0 *t0 *t0
* | next | next | next | next
* t1 --> *t1 --> *t1 --> *t1 --> null
* / \ / \ / \ / \
* t2 t3 t2 t3 *t2 t3 t2 *t3
* </pre>
*
* @param treepath a non-null object.
* @return the treepath to the next tree, or null.
*/
@Override
public Treepath< T > next( final Treepath< T > treepath ) {
final T tree = treepath.getTreeAtEnd();
if( tree.getChildCount() > 0 ) {
return Treepath.create( treepath, 0 ) ;
}
return nextUp( treepath ) ;
}
private static < T extends Tree< T > > Treepath< T > upNext( final Treepath< T > treepath ) {
Treepath< T > previousTreepath = treepath.getPrevious() ;
while( previousTreepath != null && previousTreepath.getPrevious() != null ) {
if( TreepathTools.hasNextSibling( previousTreepath ) ) {
return TreepathTools.getNextSibling( previousTreepath ) ;
} else {
previousTreepath = previousTreepath.getPrevious() ;
}
}
return null ;
}
/**
* Navigates towards the next sibling or the next sibling of a parent tree.
* @param treepath a non-null object.
* @return the next tree, or null if there is no other tree to navigate to.
*
*/
public static < T extends Tree< T > > Treepath< T > nextUp( final Treepath< T > treepath ) {
if( TreepathTools.hasNextSibling( treepath ) ) {
return TreepathTools.getNextSibling( treepath ) ;
} else {
return upNext( treepath ) ;
}
}
}
}