/*
* 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.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
/**
* Index-based reference in a {@link Tree} that only takes care of trees satisfying a given
* predicate.
*
* @author Laurent Caillette
*/
public class RobustPath< T extends Tree< T > > {
private final int[] indexes ;
final Predicate< T > treeFilter ;
private RobustPath( final int[] indexes, final Predicate< T > treeFilter ) {
this.indexes = indexes ;
this.treeFilter = Preconditions.checkNotNull( treeFilter ) ;
}
public final Treepath< T > apply( final T root ) {
if( indexes == null) {
return Treepath.create( root ) ;
}
Treepath< T > result = Treepath.create( root ) ;
loop: for( int treepathIndex = 0 ; treepathIndex < indexes.length ; treepathIndex ++ ) {
final T tree = result.getTreeAtEnd() ;
int translatedIndex = -1 ;
for( int childIndex = 0 ; childIndex < tree.getChildCount() ; childIndex ++ ) {
if( treeFilter.apply( tree.getChildAt( childIndex ) ) ) {
translatedIndex ++ ;
}
if( translatedIndex == indexes[ treepathIndex ] ) {
result = Treepath.create( result, childIndex ) ;
continue loop ;
}
}
throw new FilterException( result, treeFilter ) ;
}
return result ;
}
public static < T extends Tree< T > > RobustPath< T > create( final Treepath< T > treepath ) {
return create( treepath, Predicates.< T >alwaysTrue() ) ;
}
public static < T extends Tree< T > > RobustPath< T > create(
final Treepath< T > treepath,
final Predicate< T > filter
) {
if( treepath.getLength() == 1 ) {
return new RobustPath< T >( null, filter ) ;
} else {
final int[] indexes = new int[ treepath.getLength() - 1 ] ;
for( int treepathIndex = 1 ; treepathIndex <= treepath.getLength() - 1 ; treepathIndex ++ ) {
final Treepath intermediate = treepath.getTreepathAtDistanceFromStart( treepathIndex ) ;
final int naturalIndexInPrevious = intermediate.getIndexInPrevious() ;
int filteredIndexInPrevious = -1 ;
final T parentTree = ( T ) intermediate.getPrevious().getTreeAtEnd() ;
for( int childIndex = 0 ; childIndex <= naturalIndexInPrevious ; childIndex ++ ) {
if( filter.apply( parentTree.getChildAt( childIndex ) ) ) {
filteredIndexInPrevious ++ ;
}
}
if( filteredIndexInPrevious < 0 ) {
throw new FilterException( intermediate, filter ) ;
}
indexes[ treepathIndex - 1 ] = filteredIndexInPrevious ;
}
return new RobustPath< T >( indexes, filter ) ;
}
}
}