/*
* 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.treemangling;
import static org.novelang.parser.NodeKind.*;
import org.novelang.common.SimpleTree;
import org.novelang.common.SyntacticTree;
import org.novelang.common.tree.Treepath;
import org.novelang.common.tree.TreepathTools;
import org.novelang.parser.NodeKind;
/**
* Adds or removes various kinds of separators under various conditions.
*
* @author Laurent Caillette
*/
public final class SeparatorsMangler {
private SeparatorsMangler() { }
// ==================
// Whitespace removal
// ==================
/**
* Removes {@link org.novelang.parser.NodeKind#WHITESPACE_} and
* {@link org.novelang.parser.NodeKind#LINE_BREAK_} tokens in order to ease comparison.
*/
public static SyntacticTree removeSeparators( final SyntacticTree tree ) {
return removeSeparators( Treepath.create( tree ) ).getTreeAtEnd() ;
}
public static Treepath< SyntacticTree > removeSeparators( Treepath< SyntacticTree > treepath ) {
int index = 0 ;
while( index < treepath.getTreeAtEnd().getChildCount() ) {
final SyntacticTree child = treepath.getTreeAtEnd().getChildAt( index ) ;
final Treepath< SyntacticTree > childTreepath = Treepath.create( treepath, index ) ;
if( child.isOneOf( NodeKind.WHITESPACE_, NodeKind.LINE_BREAK_ ) ) {
treepath = TreepathTools.removeEnd( childTreepath ) ;
} else {
treepath = removeSeparators( childTreepath ).getPrevious() ;
index++ ;
}
}
return treepath ;
}
// ==============================
// Mandatory whitespace insertion
// ==============================
private static final SimpleTree MANDATORY_WHITESPACE_TREE =
new SimpleTree( _PRESERVED_WHITESPACE ) ;
/**
* Inserts a {@link NodeKind#_PRESERVED_WHITESPACE} before a whitespace-preceded apostrophe.
*/
public static Treepath< SyntacticTree > insertMandatoryWhitespaceNearApostrophe(
Treepath< SyntacticTree > treepath
) {
final SyntacticTree tree = treepath.getTreeAtEnd() ;
if( tree.isOneOf( APOSTROPHE_WORDMATE ) ) {
treepath = insertMandatoryWhitespaceIfNeeded( treepath, SiblingTraverser.FORWARD ) ;
treepath = insertMandatoryWhitespaceIfNeeded( treepath, SiblingTraverser.BACKWARD ) ;
} else if( ! tree.isOneOf( TreeManglingConstants.NON_TRAVERSABLE_NODEKINDS ) ){
int childIndex = 0 ;
while( true ) {
if( childIndex < treepath.getTreeAtEnd().getChildCount() ) {
treepath = insertMandatoryWhitespaceNearApostrophe(
Treepath.create( treepath, childIndex ) ).getPrevious() ;
childIndex++ ;
} else {
break ;
}
}
}
return treepath ;
}
private static Treepath< SyntacticTree > insertMandatoryWhitespaceIfNeeded(
final Treepath< SyntacticTree > treepath,
final SiblingTraverser walker
) {
Treepath< SyntacticTree > preceding = treepath ;
boolean foundWhitespace = false ;
while( true ) {
preceding = walker.apply( preceding ) ;
if( null == preceding ) {
return treepath ;
} else {
if( preceding.getTreeAtEnd().isOneOf( WHITESPACE_ ) ) {
foundWhitespace = true ;
} else if( foundWhitespace ) {
if( preceding.getTreeAtEnd().isOneOf( WORD_ ) ) {
return TreepathTools.addChildAt(
treepath.getPrevious(),
MANDATORY_WHITESPACE_TREE,
treepath.getIndexInPrevious() + walker.getOffset()
) ;
} else {
return treepath ;
}
} else {
return treepath ;
}
}
}
}
}