/* * 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.designator; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Set; import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.novelang.common.SimpleTree; import org.novelang.common.SyntacticTree; import org.novelang.common.TagBehavior; import org.novelang.common.tree.Traversal; import org.novelang.common.tree.Treepath; import org.novelang.common.tree.TreepathTools; import org.novelang.designator.FragmentIdentifier; import org.novelang.parser.NodeKind; import org.novelang.rendering.RenderingTools; /** * * * @author Laurent Caillette */ public class DesignatorTools { public static final Predicate< SyntacticTree > IDENTIFIER_TREE_FILTER = new Predicate< SyntacticTree >() { @Override public boolean apply( final SyntacticTree tree ) { return tree.isOneOf( NodeKind._LEVEL, NodeKind.NOVELLA, NodeKind.OPUS ) ; } } ; public static final Traversal.MirroredPostorder< SyntacticTree > TRAVERSAL = Traversal.MirroredPostorder.create( IDENTIFIER_TREE_FILTER ) ; private DesignatorTools() { } public static final EnumSet< NodeKind > IDENTIFIER_BEARING_NODEKINDS ; static { final EnumSet< TagBehavior > BEHAVIORS = EnumSet.complementOf( EnumSet.of( TagBehavior.NON_TRAVERSABLE ) ) ; final List< NodeKind > nodeKinds = Lists.newArrayList() ; for( final NodeKind nodeKind : NodeKind.values() ) { if( BEHAVIORS.contains( nodeKind.getTagBehavior() ) ) { nodeKinds.add( nodeKind ) ; } } IDENTIFIER_BEARING_NODEKINDS = EnumSet.copyOf( nodeKinds ) ; } public static String getMarkerText( final SyntacticTree tree ) throws Exception { return RenderingTools.toImplicitIdentifier( tree ) ; } public static SyntacticTree findTitleTree( final SyntacticTree levelTree ) { if( levelTree.isOneOf( NodeKind._LEVEL ) ) { for( final SyntacticTree child : levelTree.getChildren() ) { if( child.isOneOf( NodeKind.LEVEL_TITLE ) ) { return child ; } } } return null ; } /** * Extracts an identifier (either {@link org.novelang.parser.NodeKind#ABSOLUTE_IDENTIFIER} * if there is one as immediate child. */ public static Treepath< SyntacticTree > findPathToExplicitIdentifier( final Treepath< SyntacticTree > parentOfIdentifier ) { final SyntacticTree parentTree = parentOfIdentifier.getTreeAtEnd() ; for( int i = 0 ; i < parentTree.getChildCount() ; i ++ ) { final SyntacticTree child = parentTree.getChildAt( i ) ; if( child.isOneOf( NodeKind.ABSOLUTE_IDENTIFIER ) ) { return Treepath.create( parentOfIdentifier, i ) ; } } return null ; } public static void dumpIdentifierMap( final StringBuilder stringBuilder, final Map< FragmentIdentifier, Treepath< SyntacticTree > > identifierMap, final String prefix ) { for( final Map.Entry< FragmentIdentifier, Treepath< SyntacticTree > > fragmentIdentifierTreepathEntry : identifierMap.entrySet() ) { stringBuilder.append( prefix ) ; stringBuilder.append( fragmentIdentifierTreepathEntry.getKey() ) ; stringBuilder.append( " -> " ) ; stringBuilder.append( fragmentIdentifierTreepathEntry.getValue() ) ; } } public static IdentifierCollisions findCollisions( Treepath< SyntacticTree > treepath ) { final Set< String > implicitIdentifiers = Sets.newHashSet() ; final Set< String > implicitIdentifierCollisions = Sets.newHashSet() ; final Set< String > explicitIdentifiers = Sets.newHashSet() ; final Set< String > explicitIdentifierCollisions = Sets.newHashSet() ; treepath = TRAVERSAL.first( treepath ) ; // No need for reverse preorder but reusing filter. while( true ) { detectCollisions( treepath, implicitIdentifiers, implicitIdentifierCollisions, NodeKind._IMPLICIT_IDENTIFIER ) ; detectCollisions( treepath, explicitIdentifiers, explicitIdentifierCollisions, NodeKind._EXPLICIT_IDENTIFIER ) ; final Treepath< SyntacticTree > next = TRAVERSAL.next( treepath ) ; if( next == null ) { return new IdentifierCollisions() { @Override public boolean implicitIdentifierCollides( final SyntacticTree tree ) { return implicitIdentifierCollisions.contains( tree.getChildAt( 0 ).getText() ) ; } @Override public boolean explicitIdentifierCollides( final SyntacticTree tree ) { return explicitIdentifierCollisions.contains( tree.getChildAt( 0 ).getText() ) ; } } ; } else { treepath = next ; } } } private static void detectCollisions( final Treepath< SyntacticTree > treepath, final Set< String > identifierSet, final Set< String > collisionSet, final NodeKind nodeKind ) { final SyntacticTree tree = treepath.getTreeAtEnd(); if( tree.isOneOf( nodeKind ) ) { final String identifierAsString = tree.getChildAt( 0 ).getText() ; if( identifierSet.contains( identifierAsString ) ) { collisionSet.add( identifierAsString ) ; } else { identifierSet.add( identifierAsString ) ; } } } public static Treepath< SyntacticTree > removeCollidingImplicitIdentifiers( final IdentifierCollisions identifierCollisions, Treepath< SyntacticTree > treepath ) { treepath = TRAVERSAL.first( treepath ) ; while( true ) { final SyntacticTree parentTree = treepath.getTreeAtEnd() ; for( int i = 0 ; i < parentTree.getChildCount() ; i ++ ) { final SyntacticTree child = parentTree.getChildAt( i ) ; if( child.isOneOf( NodeKind._IMPLICIT_IDENTIFIER ) && identifierCollisions.implicitIdentifierCollides( child ) ) { treepath = TreepathTools.removeEnd( Treepath.create( treepath, i ) ) ; } } final Treepath< SyntacticTree > next = TRAVERSAL.next( treepath ) ; if( next == null ) { return treepath ; } else { treepath = next ; } } } public static Treepath< SyntacticTree > tagCollidingExplicitIdentifiers( final IdentifierCollisions identifierCollisions, Treepath< SyntacticTree > treepath ) { treepath = TRAVERSAL.first( treepath ) ; while( true ) { final SyntacticTree parentTree = treepath.getTreeAtEnd() ; for( int i = 0 ; i < parentTree.getChildCount() ; i ++ ) { final SyntacticTree child = parentTree.getChildAt( i ) ; if( child.isOneOf( NodeKind._EXPLICIT_IDENTIFIER ) && identifierCollisions.explicitIdentifierCollides( child ) ) { final SyntacticTree newChild = new SimpleTree( NodeKind._COLLIDING_EXPLICIT_IDENTIFIER, child.getChildAt( 0 ) ) ; treepath = TreepathTools.replaceTreepathEnd( Treepath.create( treepath, i ), newChild ) ; } } final Treepath< SyntacticTree > next = TRAVERSAL.next( treepath ) ; if( next == null ) { return treepath ; } else { treepath = next ; } } } }