diff -r 000000000000 -r 818e61de6cd1 crashanalysercmd/PerfToolsSharedLibraries/Engine/SymBuildParsingLib/Token/SymTokenBalancer.cs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/crashanalysercmd/PerfToolsSharedLibraries/Engine/SymBuildParsingLib/Token/SymTokenBalancer.cs Thu Feb 11 15:50:58 2010 +0200 @@ -0,0 +1,812 @@ +/* +* Copyright (c) 2004-2008 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* +*/ + +using System; +using System.Text; +using System.Collections; +using SymBuildParsingLib.Tree; +using SymbianTree; + +namespace SymBuildParsingLib.Token +{ + public class SymTokenBalancer + { + #region Observer interface + public delegate void LevelsBalancedEventHandler(); + public delegate void LevelsImbalancedEventHandler(); + public delegate void LevelChangeEventHandler( int aLevelCount, SymNode aOldLevel, SymNode aNewLevel ); + #endregion + + #region Events + public event LevelChangeEventHandler EventLevelStarted; + public event LevelChangeEventHandler EventLevelFinished; + public event LevelsBalancedEventHandler EventLevelsBalanced; + public event LevelsImbalancedEventHandler EventLevelsImbalanced; + #endregion + + #region Constructors & destructor + public SymTokenBalancer() + { + Reset(); + System.Diagnostics.Debug.IndentSize = 1; + } + #endregion + + #region API + public void RegisterBalancerTokens() + { + RegisterBalancerTokens( false ); + } + + public virtual void RegisterBalancerTokens( bool aEmitLevelZeroBrackets ) + { + // These are the important tokens relating to function arguments + SymToken bracketTokenOpening = new SymToken( "(", SymToken.TClass.EClassSymbol, SymToken.TType.ETypeUnidentified ); + SymToken bracketTokenClosing = new SymToken( ")", SymToken.TClass.EClassSymbol, SymToken.TType.ETypeUnidentified ); + + // When parsing a function name and arguments, we want to start a new level when we see the first opening + // brace, but we don't want to emit the token. + RegisterOpeningToken( bracketTokenOpening, bracketTokenClosing, aEmitLevelZeroBrackets, true, TLevelExpectations.ELevelExpectationsAtLevel, 0, TAssociatedBehaviour.EBehaviourRemoveReduntantBracketing ); + RegisterClosingToken( bracketTokenClosing, bracketTokenOpening, aEmitLevelZeroBrackets, true, TLevelExpectations.ELevelExpectationsAtLevel, 0, TAssociatedBehaviour.EBehaviourRemoveReduntantBracketing ); + + // For other brackets, we want to reduce the complexity by removing any redundant entries + RegisterOpeningToken( bracketTokenOpening, bracketTokenClosing, true, true, TLevelExpectations.ELevelExpectationsAboveLevelNumber, 0, TAssociatedBehaviour.EBehaviourRemoveReduntantBracketing ); + RegisterClosingToken( bracketTokenClosing, bracketTokenOpening, true, true, TLevelExpectations.ELevelExpectationsAboveLevelNumber, 0, TAssociatedBehaviour.EBehaviourRemoveReduntantBracketing ); + } + + public virtual bool OfferToken( SymToken aToken ) + { + bool consumed = false; + int currentLevelNumber = CurrentLevelNumber; + SymTokenBalancerMatchCriteria tokenExtendedInfo; + + // Check for bracket matches + if ( IsOpeningTokenMatch( aToken, out tokenExtendedInfo, currentLevelNumber ) ) + { + LevelStarted( aToken, tokenExtendedInfo ); + consumed = true; + } + else if ( IsClosingTokenMatch( aToken, out tokenExtendedInfo, currentLevelNumber ) ) + { + LevelFinished( aToken, tokenExtendedInfo ); + consumed = true; + } + + // Always consume the token if it didn't match + if ( ! consumed ) + { + AddToCurrentLevel( aToken ); + consumed = true; + } + // + return consumed; + } + + public virtual void Reset() + { + iTree = new SymTokenBalancerDocument(); + } + #endregion + + #region API - registration + public void RegisterOpeningToken( SymToken aToken, SymToken aDiametricToken, bool aEmit, bool aStartsNewLevel, TLevelExpectations aLevelExpectations, int aAssociatedLevel, TAssociatedBehaviour aBehaviour ) + { + SymTokenBalancerMatchCriteria criteria = new SymTokenBalancerMatchCriteria( aDiametricToken, aEmit, aStartsNewLevel, aLevelExpectations, aAssociatedLevel, aBehaviour ); + RegisterOpeningToken( aToken, criteria ); + } + + public void RegisterOpeningToken( SymToken aToken, SymTokenBalancerMatchCriteria aCriteria ) + { + SymToken copy = new SymToken( aToken ); + copy.Tag = aCriteria; + // + if ( IsTokenExactMatch( copy, iOpeningTokens ) == false ) + { + iOpeningTokens.Append( copy ); + } + } + + public void RegisterClosingToken( SymToken aToken, SymToken aDiametricToken, bool aEmit, bool aStartsNewLevel, TLevelExpectations aLevelExpectations, int aAssociatedLevel, TAssociatedBehaviour aBehaviour ) + { + SymTokenBalancerMatchCriteria criteria = new SymTokenBalancerMatchCriteria( aDiametricToken, aEmit, aStartsNewLevel, aLevelExpectations, aAssociatedLevel, aBehaviour ); + RegisterClosingToken( aToken, criteria ); + } + + public void RegisterClosingToken( SymToken aToken, SymTokenBalancerMatchCriteria aCriteria ) + { + SymToken copy = new SymToken( aToken ); + copy.Tag = aCriteria; + // + if ( IsTokenExactMatch( copy, iClosingTokens ) == false ) + { + iClosingTokens.Append( copy ); + } + } + #endregion + + #region Properties - level related + public int CurrentLevelNumber + { + get + { + int depth = iTree.CurrentNode.Depth; + return depth; + } + } + + public SymNode CurrentNode + { + get { return iTree.CurrentNode; } + } + + public SymTokenBalancerDocument DocumentTree + { + get { return iTree; } + } + #endregion + + #region Notification framework + protected virtual void NotifyEventLevelStarted( int aLevelCount, SymNode aOldLevel, SymNode aNewLevel ) + { + if ( EventLevelStarted != null ) + { + EventLevelStarted( aLevelCount, aOldLevel, aNewLevel ); + } + } + + protected virtual void NotifyEventLevelFinished( int aLevelCount, SymNode aOldLevel, SymNode aNewLevel ) + { + if ( EventLevelFinished != null ) + { + EventLevelFinished( aLevelCount, aOldLevel, aNewLevel ); + } + } + + protected virtual void NotifyEventLevelsImbalanced() + { + if ( EventLevelsImbalanced != null ) + { + EventLevelsImbalanced(); + } + } + #endregion + + #region Utility methods + protected bool IsTokenExactMatch( SymToken aToken, SymTokenContainer aContainerToSearch ) + { + bool ret = false; + // + foreach( SymToken t in aContainerToSearch ) + { + if ( t.Equals( aToken ) ) + { + if ( aToken.Tag != null && t.Tag != null ) + { + if ( ( aToken.Tag is SymTokenBalancerMatchCriteria ) && ( t.Tag is SymTokenBalancerMatchCriteria ) ) + { + SymTokenBalancerMatchCriteria extendedInfo1 = (SymTokenBalancerMatchCriteria) aToken.Tag; + SymTokenBalancerMatchCriteria extendedInfo2 = (SymTokenBalancerMatchCriteria) t.Tag; + // + if ( extendedInfo1.Equals( extendedInfo2 ) ) + { + ret = true; + break; + } + } + } + } + } + // + return ret; + } + + protected bool IsOpeningTokenMatch( SymToken aToken, out SymTokenBalancerMatchCriteria aCriteria, int aLevelNumber ) + { + aCriteria = null; + bool matchFound = false; + // + int index = iOpeningTokens.IndexOf( aToken ); + while( index >= 0 && matchFound == false ) + { + SymToken token = iOpeningTokens[ index ]; + System.Diagnostics.Debug.Assert ( token.Tag != null && token.Tag is SymTokenBalancerMatchCriteria ); + SymTokenBalancerMatchCriteria criteria = (SymTokenBalancerMatchCriteria) token.Tag; + + if ( criteria.Matches( aLevelNumber ) ) + { + aCriteria = criteria; + matchFound = true; + } + else + { + index = iOpeningTokens.IndexOf( aToken, index+1 ); + } + } + + return matchFound; + } + + protected bool IsClosingTokenMatch( SymToken aToken, out SymTokenBalancerMatchCriteria aCriteria, int aLevelNumber ) + { + aCriteria = null; + bool matchFound = false; + // + int index = iClosingTokens.IndexOf( aToken ); + while( index >= 0 && matchFound == false ) + { + SymToken token = iClosingTokens[ index ]; + System.Diagnostics.Debug.Assert ( token.Tag != null && token.Tag is SymTokenBalancerMatchCriteria ); + SymTokenBalancerMatchCriteria criteria = (SymTokenBalancerMatchCriteria) token.Tag; + + if ( criteria.Matches( aLevelNumber ) ) + { + aCriteria = criteria; + matchFound = true; + } + else + { + index = iClosingTokens.IndexOf( aToken, index+1 ); + } + } + + return matchFound; + } + + protected static int CountTokenByType( SymNode aNodeWithChildren, SymToken.TClass aClass ) + { + int count = 0; + // + foreach( SymNode n in aNodeWithChildren ) + { + bool isNodeToken = ( n is SymNodeToken ); + // + if ( isNodeToken ) + { + bool isSpecial = ( ( n is SymTokenBalancerNode ) || ( n is SymTokenBalancerNodeEmittedElement ) ); + // + if ( isSpecial == false ) + { + SymToken t = ((SymNodeToken) n).Token; + // + if ( t.Class == aClass ) + { + ++count; + } + } + } + } + // + return count; + } + + protected bool LevelCanBeMergedWithParent( SymTokenBalancerMarkerLevelNode aLevelNode ) + { + #region Example + // We can replace the opening bracket, the level marker and the closing bracket + // with the level marker's child. + // + // E.g. + // ( = opening bracket + // * = level marker + // ) = closing bracket + // V = 'real' node value + // + // This: + // + // ( -- * -- ) + // | + // V + // + // Can become: + // + // V + // + // + // Or this: + // + // ( -- * -- ) + // | + // [ -- * -- ] + // | + // V + // + // Can become: + // + // [ -- * -- ] + // | + // V + // + #endregion + System.Diagnostics.Debug.Assert( aLevelNode.HasPrevious && aLevelNode.HasNext ); + System.Diagnostics.Debug.Assert( aLevelNode.Previous is SymTokenBalancerNodeEmittedElement && aLevelNode.Next is SymTokenBalancerNodeEmittedElement ); + // + bool ret = false; + // + SymTokenBalancerNodeEmittedElement previous = (SymTokenBalancerNodeEmittedElement) aLevelNode.Previous; + SymTokenBalancerNodeEmittedElement next = (SymTokenBalancerNodeEmittedElement) aLevelNode.Next; + + // We should be able to remove these brackets, providing they are diametrically oposites. + SymTokenBalancerMatchCriteria matchInfo; + if ( IsOpeningTokenMatch( previous.Token, out matchInfo, CurrentLevelNumber ) ) + { + // We've now got the match info for this opening token. We can compare the + // match info's diametric token against the closing token to check that they + // really are equal and oposite. + if ( matchInfo.DiametricToken.Equals( next.Token ) ) + { + // Check whether the children are suitable for coalescing + int grandChildCount = aLevelNode.ChildCount; + int numberOfWhiteSpaceGrandChildren = CountTokenByType( aLevelNode, SymToken.TClass.EClassWhiteSpace ); + int nonWhiteSpaceGrandChildrenCount = grandChildCount - numberOfWhiteSpaceGrandChildren; + + if ( nonWhiteSpaceGrandChildrenCount == 1 ) + { + // If there is just a single non-whitespace token then we are able to + // coalesce this branch + ret = true; + } + else if ( nonWhiteSpaceGrandChildrenCount == 3 ) + { + // Branch contains several non-whitespace children. Must check further + // to see if they are simply bracketed regions themselves + object levelObject = aLevelNode.ChildByType( typeof( SymTokenBalancerMarkerLevelNode ) ); + if ( levelObject != null ) + { + SymTokenBalancerMarkerLevelNode childLevel = (SymTokenBalancerMarkerLevelNode) levelObject; + // + if ( childLevel.HasPrevious && childLevel.HasNext ) + { + ret = ( childLevel.Previous is SymTokenBalancerNodeEmittedElement && childLevel.Next is SymTokenBalancerNodeEmittedElement ); + } + } + else + { + ret = true; + } + } + } + } + // + return ret; + } + + protected void SimplifyLevel( SymTokenBalancerMarkerLevelNode aLevel ) + { + System.Diagnostics.Debug.Assert( aLevel.IsRoot == false && aLevel.HasParent ); + SymNode parent = aLevel.Parent; + int levelNumber = parent.Depth; + // + int childCount = parent.ChildCount; + while( --childCount >= 0 ) + { + SymNode possibleLevelNode = parent[ childCount ]; + + // We're looking to remove redundant bracketing from either side of the level + // node. First check if we have a level node... + if ( possibleLevelNode is SymTokenBalancerMarkerLevelNode ) + { + // Then check whether it has a previous and a next node. These should + // be the SymTokenBalancerNodeEmittedElement nodes + if ( possibleLevelNode.HasPrevious && possibleLevelNode.HasNext ) + { + if ( possibleLevelNode.Previous is SymTokenBalancerNodeEmittedElement && possibleLevelNode.Next is SymTokenBalancerNodeEmittedElement ) + { + if ( LevelCanBeMergedWithParent( possibleLevelNode as SymTokenBalancerMarkerLevelNode ) ) + { + SymTokenBalancerNodeEmittedElement previous = (SymTokenBalancerNodeEmittedElement) possibleLevelNode.Previous; + SymTokenBalancerNodeEmittedElement next = (SymTokenBalancerNodeEmittedElement) possibleLevelNode.Next; + + // Insert value node prior to previous node (which is the opening bracket token). + parent.InsertChildrenFrom( possibleLevelNode, previous.Previous ); + + // Remove the opening bracket token + previous.Remove(); + + // Remove the level marker token + possibleLevelNode.Remove(); + + // Remove the closing bracket token. + next.Remove(); + } + } + } + } + } + } + #endregion + + #region Internal level manipulation methods + protected virtual void LevelStarted( SymToken aToken, SymTokenBalancerMatchCriteria aMatchCriteria ) + { + System.Diagnostics.Debug.Write( System.Environment.NewLine + aToken.Value ); + + // Always store the node element so that we can balance brackets + SymTokenBalancerNodeEmittedElement node = new SymTokenBalancerNodeEmittedElement( aToken, aMatchCriteria ); + iTree.CurrentNode.Add( node ); + + // Store the token (with the level) so we can check for open/close mis-matches + SymTokenBalancerMarkerLevelNode levelNode = new SymTokenBalancerMarkerLevelNode( aMatchCriteria ); + iTree.CurrentNode.Add( levelNode ); + + SymNode oldLevel = iTree.CurrentNode; + SymNode newLevel = levelNode; + NotifyEventLevelStarted( CurrentLevelNumber, oldLevel, newLevel ); + + iTree.CurrentNode = levelNode; + } + + protected virtual void LevelFinished( SymToken aToken, SymTokenBalancerMatchCriteria aMatchCriteria ) + { + #region Example + + // #define TEST1((( B )+( C ))+(E)) + + #region Key + // |y| = level y + // [x] = token of value 'x' + // [*] = level marker node + // [@] = (current) level marker node + // [,] = argument node + // [(] = opening level emitted token node + // [)] = closing level emitted token node + #endregion + + #region 1) First wave of opening level tokens processed... + // + // |0| [ROOT] + // | + // |1| [TEST] [(] [*] + // | + // |2| [(] [*] + // | + // |3| [(] [@] + // | + // |4| B + // + #endregion + + #region 2) Add closing level token to level |3|, which means that we can now + // attempt to simplify level |4|. Level |4| does not contain any child + // level nodes, so there is nothing to do here. + // + // |0| [ROOT] + // | + // |1| [TEST] [(] [*] + // | + // |2| [(] [@] + // | + // |3| [(] [*] [)] + // | + // |4| [ ] [B] [ ] + // + #endregion + + #region 3) Add plus symbol. This obviously becomes a child of the current node, + // i.e. a child of level |2|. + // + // |0| [ROOT] + // | + // |1| [TEST] [(] [*] + // | + // |2| [(] [@] + // | + // |3| [(] [*] [)] [+] + // | + // |4| [ ] [B] [ ] + // + #endregion + + #region 4) Start new opening level node on level |3|. The newly added level node + // on level |3| becomes the current node. + // + // |0| [ROOT] + // | + // |1| [TEST] [(] [*] + // | + // |2| [(] [*] + // | + // |3| [(] [*] [)] [+] [(] [@] + // | | + // |4| [ ] [B] [ ] + // + #endregion + + #region 5) Add the tokens near the 'C' to level |4| + // + // |0| [ROOT] + // | + // |1| [TEST] [(] [*] + // | + // |2| [(] [*] + // | + // |3| [(] [*] [)] [+] [(] [@] + // | | + // |4| [ ] [B] [ ] [ ] [C] [ ] + // + #endregion + + #region 6) Add closing level token to level |3|, which means that we can now + // attempt to simplify level |4|, this time the [C] branch. + // Since this branch does not contain any sub-level nodes, there is nothing + // to be done and therefore levels |3| and |4| remain unchanged. + // The level node at level |2| becomes the current node. + // + // |0| [ROOT] + // | + // |1| [TEST] [(] [*] + // | + // |2| [(] [@] + // | + // |3| [(] [*] [)] [+] [(] [*] [)] + // | | + // |4| [ ] [B] [ ] [ ] [C] [ ] + // + #endregion + + #region 7a) Add closing level token to level |2|, which means that we can now + // attempt to simplify level |3|. + // + // |0| [ROOT] + // | + // |1| [TEST] [(] [*] + // | + // |2| [(] [@] [)] + // | + // |3| [(] [*] [)] [+] [(] [*] [)] + // | | + // |4| [ ] [B] [ ] [ ] [C] [ ] + // + #endregion + + #region 7b) We iterate through all the child level nodes of level |3| looking + // to see if any of their children are simple enough to merge up into level + // |3| itself. There are two level nodes in level |3| and both contain + // children that are considered simple enough to merge up. This is because + // they contain only a single non-whitespace node so we can simplify the level by + // merging [B] from level |4| into level |3|. Likewise for [C]. + // + // This means that the bracketry on level |3| is redundant since level + // |4| contains two sets of only a single non-whitespace token. Level |4| + // is therefore entirely removed. + // + // The node at level |1| becomes the current node now. + // + // |0| [ROOT] + // | + // |1| [TEST] [(] [@] + // | + // |2| [(] [*] [)] + // | + // |3| [ ] [B] [ ] [+] [ ] [C] [ ] + // + #endregion + + #region 8) Add the plus symbol to level |2|. Then we start a new level + // as a result of adding the opening bracket prior to 'E.' + // This new level node at level |2| now becomes the current node. + // + // |0| [ROOT] + // | + // |1| [TEST] [(] [*] + // | + // |2| [(] [*] [)] [+] [(] [@] + // | | + // |3| [ ] [B] [ ] [+] [ ] [C] [ ] + // + #endregion + + #region 9) Now add the 'E' token to level |3|. + // + // |0| [ROOT] + // | + // |1| [TEST] [(] [*] + // | + // |2| [(] [*] [)] [+] [(] [@] + // | | + // |3| [ ] [B] [ ] [+] [ ] [C] [ ] [E] + #endregion + + #region 10) We then add the closing bracket to level |2| which means + // that we can attempt to simplify level |3|'s 'E' branch. There's nothing + // to do though, since it doesn't contain any child level nodes. + // The level node at level |1| again becomes current. + // + // |0| [ROOT] + // | + // |1| [TEST] [(] [@] + // | + // |2| [(] [*] [)] [+] [(] [*] [)] + // | | + // |3| [ ] [B] [ ] [+] [ ] [C] [ ] [E] + #endregion + + #region 11a) We then add the closing bracket to level |1| which means + // that we can attempt to simplify level |2| entirely. This is the + // situation prior to simplification. + // + // |0| [ROOT] + // | + // |1| [TEST] [(] [@] [)] + // | + // |2| [(] [*] [)] [+] [(] [*] [)] + // | | + // |3| [ ] [B] [ ] [+] [ ] [C] [ ] [E] + #endregion + + #region 11b) The two level nodes have been taged as *1 and *2 to make it + // easier to explain the process. First we attempt to simplify level *1. + // However, since its children consist of more than a single non-whitespace + // token, we cannot make level |3|'s " B + C " branch as a replacement + // for the bracket *1 tokens. Therefore this remains unchanged + // + // + // |0| [ROOT] + // | + // |1| [TEST] [(] [@] [)] + // | + // |2| [(] [*1] [)] [+] [(] [*2] [)] + // | | + // |3| [ ] [B] [ ] [+] [ ] [C] [ ] [E] + #endregion + + #region 11c) The level *2 node, however, contains only a single non-whitespace + // child token, so it can be simplified. The level *2 node is replaced by + // its child (E). + // + // The final tree looks like this, with the root as the current node once more: + // + // + // |0| [@ROOT@] + // | + // |1| [TEST] [(] [*] [)] + // | + // |2| [(] [*] [)] [+] [E] + // | + // |3| [ ] [B] [ ] [+] [ ] [C] [ ] + #endregion + + #endregion + + System.Diagnostics.Debug.Write( aToken.Value ); + System.Diagnostics.Debug.WriteLine( " " ); + + // First of all, check if the current node has a parent. If we're at the root + // node and we see a closing token, then we've got an imbalanced stack. + if ( iTree.CurrentNode.IsRoot ) + { + NotifyEventLevelsImbalanced(); + } + else + { + // We expect the parent to be a level node + System.Diagnostics.Debug.Assert( iTree.CurrentNode is SymTokenBalancerMarkerLevelNode ); + + // Notify that we're about to change levels + SymTokenBalancerMarkerLevelNode currentLevel = (SymTokenBalancerMarkerLevelNode) iTree.CurrentNode; + SymNode newLevel = currentLevel.Parent; + + // The new level should not be null in this case + System.Diagnostics.Debug.Assert( newLevel != null ); + + // Check whether the closing token type is the same type as was used to start + // the level. E.g. for this case is "([ANDMORE)]" which has a mis-match + // between the opening and closing braces on each level. We can't simplify this + // during the 'end level behaviour' stage. + bool levelsBalanced = currentLevel.MatchCriteria.DiametricToken.Equals( aToken ); + + // Switch levels + iTree.CurrentNode = newLevel; + + // We have to refetch up-to-date match info, since we've changed levels, and the match + // info that was used to enter this method is associated with the previous level + // (not the new level number, which is now one less). + SymTokenBalancerMatchCriteria matchCriteria; + if ( IsClosingTokenMatch( aToken, out matchCriteria, CurrentLevelNumber ) == false ) + { + matchCriteria = aMatchCriteria; + } + + // Always store the node element so that we can balance brackets + SymTokenBalancerNodeEmittedElement node = new SymTokenBalancerNodeEmittedElement( aToken, matchCriteria ); + iTree.CurrentNode.Add( node ); + + // We have finished the current level. E.g. see step 6. Need to simplify level |2|, where possible. + PerformEndLevelBehaviour( currentLevel ); + + if ( levelsBalanced ) + { + // Notify that we've finished some level + NotifyEventLevelFinished( CurrentLevelNumber, currentLevel, newLevel ); + } + else + { + // Imbalance + NotifyEventLevelsImbalanced(); + } + } + } + + protected virtual void AddToCurrentLevel( SymToken aToken ) + { + System.Diagnostics.Debug.Write( aToken.Value ); + + SymNodeToken node = new SymNodeToken( aToken ); + iTree.CurrentNode.Add( node ); + } + + #endregion + + #region End level behaviour related + protected void PerformEndLevelBehaviour( SymTokenBalancerMarkerLevelNode aLevel ) + { + PerformEndLevelBehaviour( aLevel, aLevel.MatchCriteria ); + } + + protected virtual void PerformEndLevelBehaviour( SymNode aLevel, SymTokenBalancerMatchCriteria aCriteria ) + { + #region Example step (11a) from LevelFinished method + // + // We then add the closing bracket to level |1| which means + // that we can attempt to simplify level |2| entirely. This is the + // situation prior to simplification. + // + // |0| [ROOT] + // | + // |1| [TEST] [(] [@] [)] + // | + // |2| [(] [*] [)] [+] [(] [*] [)] + // | | + // |3| [ ] [B] [ ] [+] [ ] [C] [ ] [E] + // + // aLevel would be the @ node at level |1|. + // + // We remove redundant bracketing from our children, i.e. those on level |2|, not from our own level. + // Our parent takes care of removing this level's redundant bracketing (when it's level is completed) + // or then when an argument separator token is handled. + // + // We must iterate through level |1|'s children to find other level nodes. We check whether each + // child level node can be simplified by checking its children (i.e. our grandchildren). + // + #endregion + + if ( aCriteria.IsAssociatedBehaviourRemoveRedundantBracketing ) + { + int index = 0; + object childLevelNodeObject = aLevel.ChildByType( typeof(SymTokenBalancerMarkerLevelNode), ref index ); + while( childLevelNodeObject != null ) + { + SymTokenBalancerMarkerLevelNode childLevelNode = (SymTokenBalancerMarkerLevelNode) childLevelNodeObject; + if ( childLevelNode.CanBeSubsumed ) + { + childLevelNode.Subsume(); + } + + // Try to find next level node + ++index; + childLevelNodeObject = aLevel.ChildByType( typeof(SymTokenBalancerMarkerLevelNode), ref index ); + } + } + } + #endregion + + #region Data members + private SymTokenBalancerDocument iTree; + private SymTokenContainer iOpeningTokens = new SymTokenContainer(); + private SymTokenContainer iClosingTokens = new SymTokenContainer(); + #endregion + } +}