//****************************************************************** #region [BacktrackToOptionalNode() Method] //****************************************************************** /// <summary> /// Attempts to backtrack to a node pair on the MatchingNodes stack /// that (1) is a child of the given oParentNodePair and (2) has an /// optional FindPatternNode. If such a node pair is found, items /// are popped from the stack until this node pair is popped, and /// then this node pair is returned. If no such node pair is found, /// null is returned (and no items are popped from the stack). /// </summary> private SyntaxNodePair BacktrackToOptionalNode( SyntaxNodePair oParentNodePair) { Debug.Assert(oParentNodePair != null); Debug.Assert(oParentNodePair.ParseTreeNode != null); Debug.Assert(oParentNodePair.FindPatternNode != null); //************************************************************** // Search backwards through the MatchingNodes stack (starting // with the most recently pushed node pair), looking for a node // pair where (1) the ParseTreeNode is a child of the given // oParentNodePair.ParseTreeNode, (2) the FindPatternNode is a // child of the given oParentNodePair.FindPatternNode, and (3) // the FindPatternNode is optional. // // If such a node pair is found, pop items from the stack until // this node pair is popped, and then return this node pair. int iIndex = MatchingNodes.Count - 1; while (iIndex >= 0) { SyntaxNodePair oNodePair = MatchingNodes[iIndex]; if ((oNodePair.ParseTreeNode.ParentNode == oParentNodePair.ParseTreeNode) && (oNodePair.FindPatternNode.ParentNode == oParentNodePair.FindPatternNode) && (oNodePair.FindPatternNode.IsOptionalNode)) { //****************************************************** // Found a node pair that is a child of the given // oParentNodePair and has an optional FindPatternNode. // So pop items from the stack until this node pair is // popped, and then return this node pair. // // This restores the stack to the same state as when // this node pair was matched, but this time we can skip // the optional node and attempt to continue the match. ClearNodePairFromStack(oNodePair); Debug.WriteLineIf(VerboseDebugging, "Backtracking to optional node...."); return(oNodePair); } --iIndex; } //************************************************************** // The stack does not contain a child of the oParentNodePair // with an optional FindPatternNode, so return null. Debug.WriteLineIf(VerboseDebugging, "Backtracking... cannot find an optional child of " + oParentNodePair.ToString() + " on the stack."); return(null); }
//****************************************************************** #region [Pop() Method] //****************************************************************** /// <summary> /// Removes and returns the item at the top of the stack. /// </summary> public SyntaxNodePair Pop() { Debug.Assert(InnerList.Count > 0); SyntaxNodePair oNodePair = (SyntaxNodePair)InnerList[InnerList.Count - 1]; InnerList.RemoveAt(InnerList.Count - 1); return(oNodePair); }
//****************************************************************** #region [Push() Method] //****************************************************************** /// <summary> /// Inserts an item at the top of the stack. /// </summary> public void Push(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); if (oNodePair == null) { string sMessage = "Invalid argument: " + "SyntaxNodePairStack cannot push a null item."; throw new Exception(sMessage); } InnerList.Add(oNodePair); }
//****************************************************************** #region [FirstChildNodePair() Method] //****************************************************************** /// <summary> /// Returns a new node pair containing the first child of the /// ParseTreeNode and the first child of the FindPatternNode from /// the given oNodePair. /// </summary> private SyntaxNodePair FirstChildNodePair(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); Debug.Assert(oNodePair.ParseTreeNode != null); Debug.Assert(oNodePair.FindPatternNode != null); SyntaxNodePair oChildPair = new SyntaxNodePair(); oChildPair.ParseTreeNode = oNodePair.ParseTreeNode.FirstChild; oChildPair.FindPatternNode = oNodePair.FindPatternNode.FirstChild; return(oChildPair); }
//****************************************************************** #region [ClearNodePairFromStack() Method] //****************************************************************** /// <summary> /// Pops node pairs from the MatchingNodes stack until the indicated /// node pair (oNodePair) has been popped. (The indicated node pair /// must exist on the stack.) /// </summary> private void ClearNodePairFromStack(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); Debug.Assert(oNodePair.ParseTreeNode != null); Debug.Assert(oNodePair.FindPatternNode != null); while (MatchingNodes.Count > 0) { SyntaxNodePair oStackPair = MatchingNodes.Pop(); if (oStackPair == oNodePair) { return; } } Debug.Fail("Node pair not found in MatchingNodes stack."); }
//****************************************************************** #region [NextSiblingNodePair() Method] //****************************************************************** /// <summary> /// Returns a new node pair containing the next sibling of the /// ParseTreeNode and the next sibling of the FindPatternNode from /// the given oNodePair. /// </summary> private SyntaxNodePair NextSiblingNodePair(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); Debug.Assert(oNodePair.ParseTreeNode != null); Debug.Assert(oNodePair.FindPatternNode != null); Debug.Assert(oNodePair.ParseTreeNode != CurrentParseTreeNode); Debug.Assert(oNodePair.FindPatternNode != FindPatternRoot); SyntaxNodePair oSiblingPair = new SyntaxNodePair(); oSiblingPair.ParseTreeNode = oNodePair.ParseTreeNode.NextSibling; oSiblingPair.FindPatternNode = oNodePair.FindPatternNode.NextSibling; return(oSiblingPair); }
//****************************************************************** #region [SkipOptionalNode() Method] //****************************************************************** /// <summary> /// Returns a new node pair containing the same ParseTreeNode as the /// given oNodePair and the next sibling of the FindPatternNode from /// the given oNodePair. (The given oNodePair.FindPatternNode must /// be an optional node, so by moving to its next sibling we skip /// the optional node.) /// </summary> private SyntaxNodePair SkipOptionalNode(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); Debug.Assert(oNodePair.FindPatternNode != null); Debug.Assert(oNodePair.FindPatternNode.IsOptionalNode); Debug.Assert(oNodePair.FindPatternNode != FindPatternRoot); SyntaxNodePair oNextNodePair = new SyntaxNodePair(); oNextNodePair.ParseTreeNode = oNodePair.ParseTreeNode; oNextNodePair.FindPatternNode = oNodePair.FindPatternNode.NextSibling; Debug.WriteLineIf(VerboseDebugging, "Skipping optional node...."); return(oNextNodePair); }
//****************************************************************** /// <summary> /// Compares the trees dominated by the CurrentParseTreeNode and the /// FindPatternRoot. If these trees match, true is returned and the /// MatchingNodes stack contains the list of matching node pairs. /// Otherwise, false is returned. /// </summary> public bool CurrentBranchMatchesPattern() { Debug.WriteLineIf(VerboseDebugging, "MatchAlgorithm.CurrentBranchMatchesPattern() called."); //************************************************************** // Validate the current state. if (FindPatternRoot == null) { string sMessage = "CurrentBranchMatchesPattern() " + "called with an invalid state: " + "FindPatternRoot is null."; throw new Exception(sMessage); } if (CurrentParseTreeNode == null) { string sMessage = "CurrentBranchMatchesPattern() " + "called with an invalid state: " + "CurrentParseTreeNode is null."; throw new Exception(sMessage); } //************************************************************** // Clear the stack of matching node pairs. MatchingNodes.Clear(); //************************************************************** // Call FeaturesAndChildrenMatch() to check if the current // parse-tree branch matches the find-pattern tree. If so, // return true. SyntaxNodePair oNodePair = new SyntaxNodePair(); oNodePair.ParseTreeNode = CurrentParseTreeNode; oNodePair.FindPatternNode = FindPatternRoot; if (FeaturesAndChildrenMatch(oNodePair)) { Debug.Assert(MatchingNodes.Count > 0); Debug.WriteLineIf(VerboseDebugging, "MatchAlgorithm.CurrentBranchMatchesPattern() returns " + "true."); return true; } //************************************************************** // The current parse-tree branch does not match the find-pattern // tree, so return false. // // Note: The matching procedure should have cleared all node // pairs from the MatchingNodes stack if the match failed. Debug.Assert(MatchingNodes.Count == 0); MatchingNodes.Clear(); Debug.WriteLineIf(VerboseDebugging, "MatchAlgorithm.CurrentBranchMatchesPattern() returns " + "false."); return false; }
//****************************************************************** #region [LeafChildrenMatch() Method] //****************************************************************** /// <summary> /// Compares each child of oNodePair.ParseTreeNode by making /// recursive calls to FeaturesAndChildrenMatch() to see if it /// matches any child of oNodePair.FindPatternNode. Returns true if /// at least one child pair matched successfully. Returns false /// otherwise. (This is not an ordered comparison. The given /// oNodePair.ParseTreeNode must be a syntax leaf.) /// </summary> private bool LeafChildrenMatch(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); Debug.Assert(oNodePair.ParseTreeNode != null); Debug.Assert(oNodePair.FindPatternNode != null); Debug.Assert(oNodePair.ParseTreeNode.IsSyntaxLeaf); Debug.Assert(oNodePair.FindPatternNode.ChildNodes.Count > 0); //************************************************************** // Since oNodePair.ParseTreeNode is a syntax leaf, each of its // children is the root of a morphology parse. (A syntax leaf // may have one or more alternative morphology parses.) // // For each of these morphology-parse children of // oNodePair.ParseTreeNode, see if it matches any child of // oNodePair.FindPatternNode. If so, increment the counter. int iMatchCount = 0; foreach (SyntaxNode oParseChild in oNodePair.ParseTreeNode.ChildNodes) { //********************************************************** // Loop through the oNodePair.FindPatternNode children. // Stop if we find a match to the morphology-parse child of // oNodePair.ParseTreeNode. SyntaxNode oFindChild = oNodePair.FindPatternNode.FirstChild; while (oFindChild != null) { SyntaxNodePair oChildPair = new SyntaxNodePair(); oChildPair.ParseTreeNode = oParseChild; oChildPair.FindPatternNode = oFindChild; if (FeaturesAndChildrenMatch(oChildPair)) { //************************************************** // Found a match. Increment the counter and end the // loop (by setting oFindChild to null). ++iMatchCount; oFindChild = null; } else { //************************************************** // Continue looping (by setting oFindChild to the // next sibling). Debug.Assert(oFindChild != FindPatternRoot); oFindChild = oFindChild.NextSibling; } } } //************************************************************** // If at least one child of oNodePair.ParseTreeNode matched, // return true. Otherwise, return false. if (iMatchCount > 0) { return(true); } return(false); }
//****************************************************************** #region [NodeChildrenMatch() Method] //****************************************************************** // EXAMPLE WALKTHROUGH OF BACKTRACKING // // Suppose NodeChildrenMatch() is called with the following state: // // MatchingNodes = {(VP,VP)} // oNodePair.ParseTreeNode.ChildNodes = {V,PP} // oNodePair.FindPatternNode.ChildNodes = {V,(NP),(PP),PP} // // 1. Compare the first child pair (V,V). They match, so push the // pair onto the stack and move to the next sibling pair (PP,(NP)). // The stack now contains: // // MatchingNodes = {(V,V)(VP,VP)} // // 2. Compare the child pair (PP,(NP)). They do not match. Since // (NP) is optional, skip this node to get the next child pair to // compare (PP,(PP)). // // 3. Compare the child pair (PP,(PP)). They match, so push the pair // onto the stack and move to the next sibling pair (null,PP). The // stack now contains: // // MatchingNodes = {(PP,(PP))(V,V)(VP,VP)} // // 4. Compare the child pair (null,PP). They do not match. Since PP // is not optional, we need to backtrack to find a child pair with // an optional find-pattern node. The child pair (PP,(PP)) is found // on the stack. Skip the optional node to get the next child pair // to compare (PP,PP). Backtracking removed one pair from the stack: // // MatchingNodes = {(V,V)(VP,VP)} // // 5. Compare the child pair (PP,PP). They match, so push the pair // onto the stack and move to the next sibling pair (null,null). The // stack now contains: // // MatchingNodes = {(PP,PP)(V,V)(VP,VP)} // // 6. We reached the end of both ChildNodes collections at the same // time. This means we successfully matched all the children, so // return true. //****************************************************************** /// <summary> /// Compares the children of oNodePair.ParseTreeNode to the children /// of oNodePair.FindPatternNode by making recursive calls to /// FeaturesAndChildrenMatch() for each pair of child nodes. /// Backtracks and/or skips optional nodes if necessary to find /// matching pairs of child nodes. Returns true if all the children /// are matched successfully. Returns false otherwise. (This is an /// ordered comparison. The given oNodePair.ParseTreeNode must not /// be a syntax leaf.) /// </summary> private bool NodeChildrenMatch(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); Debug.Assert(oNodePair.ParseTreeNode != null); Debug.Assert(oNodePair.FindPatternNode != null); Debug.Assert(!oNodePair.ParseTreeNode.IsSyntaxLeaf); Debug.Assert(oNodePair.FindPatternNode.ChildNodes.Count > 0); //************************************************************** // Iterate through the children of the oNodePair.ParseTreeNode // and the children of the oNodePair.FindPatternNode, starting // with the first child in each collection. // // For each child pair, compare their features and children. // // If the pair matches: // // Get the next child pair to compare by moving forward to // the next sibling in each collection. // // Otherwise: // // Get the next child pair to compare by backtracking and/or // skipping an optional node. // // If we reach the end of both collections at the same time (the // child ParseTreeNode and the child FindPatternNode are both // null), we have successfully matched all the children. SyntaxNodePair oChildPair = FirstChildNodePair(oNodePair); while ((oChildPair.ParseTreeNode != null) || (oChildPair.FindPatternNode != null)) { if (FeaturesAndChildrenMatch(oChildPair)) { //****************************************************** // The pair matches. // // Move forward to the next sibling pair. oChildPair = NextSiblingNodePair(oChildPair); } else { //****************************************************** // The pair does not match. // // Try to find another child pair to compare by skipping // an optional node. // // If the child FindPatternNode is not optional, we need // to backtrack to a node pair on the stack that (1) is // a child of oNodePair and (2) has a FindPatternNode // that is optional. If backtracking fails to find such // a node pair, return false. // // Once we have a child FindPatternNode that is // optional, skip the optional node to get the next // child pair to compare. bool bChildFindPatternNodeIsOptional = false; if (oChildPair.FindPatternNode != null) { if (oChildPair.FindPatternNode.IsOptionalNode) { bChildFindPatternNodeIsOptional = true; } } if (!bChildFindPatternNodeIsOptional) { oChildPair = BacktrackToOptionalNode(oNodePair); if (oChildPair == null) { return(false); } } Debug.Assert(oChildPair.FindPatternNode != null); Debug.Assert(oChildPair.FindPatternNode.IsOptionalNode); oChildPair = SkipOptionalNode(oChildPair); } } //************************************************************** // We have successfully matched all the children, so return // true. return(true); }
//****************************************************************** #region [FeaturesAndChildrenMatch() Method] //****************************************************************** /// <summary> /// Compares the sub-trees dominated by oNodePair.ParseTreeNode and /// oNodePair.FindPatternNode. If the features and children of the /// pattern sub-tree match the features and children of the parse /// sub-tree, the matching node pairs are pushed onto the /// MatchingNodes stack and true is returned. Otherwise, false is /// returned (after any added partial matches are popped of the /// MatchingNodes stack, restoring it to the same state as when this /// method was called). /// </summary> private bool FeaturesAndChildrenMatch(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); //************************************************************** // Compare the features of oNodePair.ParseTreeNode and // oNodePair.FindPatternNode. // // If the features do not match (or if either node is null), // return false. // // Otherwise, push the oNodePair onto the MatchingNodes stack. if (!FeaturesMatch(oNodePair)) { return(false); } Debug.Assert(oNodePair.ParseTreeNode != null); Debug.Assert(oNodePair.FindPatternNode != null); MatchingNodes.Push(oNodePair); Debug.WriteLineIf(VerboseDebugging, "MatchingNodes: " + MatchingNodes.ToString() + "."); //************************************************************** // If oNodePair.FindPatternNode has children, compare these // children to the children of oNodePair.ParseTreeNode. // // (Note that the children of the ParseTreeNode do not need to // be matched if the FindPatternNode does not have children.) // // If the children do not match, pop everything off the // MatchingNodes stack up to and including the oNodePair, and // then return false. if (oNodePair.FindPatternNode.ChildNodes.Count > 0) { //********************************************************** // If oNodePair.ParseTreeNode is a syntax leaf node, call // LeafChildrenMatch() to compare the children. // // Otherwise, call NodeChildrenMatch() to compare the // children. bool bChildrenMatch = false; if (oNodePair.ParseTreeNode.IsSyntaxLeaf) { bChildrenMatch = LeafChildrenMatch(oNodePair); } else { bChildrenMatch = NodeChildrenMatch(oNodePair); } //********************************************************** // If the children do not match, pop everything off the // MatchingNodes stack up to and including the oNodePair, // and then return false. if (!bChildrenMatch) { ClearNodePairFromStack(oNodePair); return(false); } } //************************************************************** // The features and children match, so return true. return(true); }
//****************************************************************** #region [CopyLeafAndChildren() Method] //****************************************************************** /// <summary> /// For the given oReplacePatternNode, this method finds the /// corresponding find-pattern node and the parse-tree node it /// matched. A new node is created by copying features from the /// parse-tree node and merging features from the /// oReplacePatternNode. Then children are copied recursively from /// the parse-tree node. Children are also copied recursively from /// the oReplacePatternNode if they do not correspond to children of /// the parse-tree node. Returns null if the oReplacePatternNode was /// optional and the parse-tree node was not found. Otherwise, /// returns the new node after adding it to the ReplacedNodes list. /// (Note that this method is only for copying leaf nodes.) /// </summary> SyntaxNode CopyLeafAndChildren(SyntaxNode oReplacePatternNode) { Debug.Assert(oReplacePatternNode != null); //************************************************************** // For the given oReplacePatternNode, find the corresponding // find-pattern node and the parse-tree node it matched: // // Search the MatchingNodes list for a node pair where the // .FindPatternNode has the same label as the given // oReplacePatternNode. // // If a matching node pair is found, set oParseTreeNode and // oFindPatternNode to the node-pair values. Otherwise, set // oParseTreeNode and oFindPatternNode both to null. SyntaxNode oParseTreeNode = null; SyntaxNode oFindPatternNode = null; SyntaxNodePair oNodePair = FindMatchingPairFromLabel(oReplacePatternNode.Label); if (oNodePair != null) { oParseTreeNode = oNodePair.ParseTreeNode; oFindPatternNode = oNodePair.FindPatternNode; } //************************************************************** // If a matching node pair was not found and oReplacePatternNode // is optional, nothing will be copied, so return null. if ((oNodePair == null) && (oReplacePatternNode.IsOptionalNode)) { return(null); } //************************************************************** // This method should only be called for leaf nodes. if (oParseTreeNode != null) { Debug.Assert(oParseTreeNode.IsSyntaxLeaf || oReplacePatternNode.IsSyntaxLeaf); } else { Debug.Assert(oReplacePatternNode.IsSyntaxLeaf); } //************************************************************** // Create a new parse-tree node by copying the features from the // matching oParseTreeNode (if found) and then merging the // features from oReplacePatternNode. if (VerboseDebugging) { string sParseString = "null"; if (oParseTreeNode != null) { sParseString = oParseTreeNode.ToString(); } string sReplaceString = "null"; if (oReplacePatternNode != null) { sReplaceString = oReplacePatternNode.ToString(); } Debug.WriteLine("Copying leaf: CopyFeatures(" + sParseString + "," + sReplaceString + ")."); } SyntaxNode oNewNode = CopyFeatures(oParseTreeNode, oReplacePatternNode); //************************************************************** // Each child of a syntax leaf is the root of a morphology // parse. (A syntax leaf may have one or more alternative // morphology parses.) // // Each child will be copied recursively from oParseTreeNode. // Additional children will also be copied recursively from // oReplacePatternNode if they do not correspond to children of // oParseTreeNode. // // Create a list to keep track of the oReplacePatternNode // children as they are used. (A replace-pattern child is marked // as used if it has the same label as a find-pattern child // matching one of the oParseTreeNode children.) ArrayList oUsedReplaceChildren = new ArrayList(); //************************************************************** // Copy the oParseTreeNode children. if (oParseTreeNode != null) { foreach (SyntaxNode oParseChild in oParseTreeNode.ChildNodes) { //****************************************************** // Look for the find-pattern node that matched this // parse-tree child: // // Search the MatchingNodes list for a node pair where // the .ParseTreeNode is the same as oParseChild. // // If found, set oFindChild to the .FindPatternNode // value from the node pair. Otherwise, set oFindChild // to null. SyntaxNodePair oChildNodePair = FindMatchingPairFromNode(oParseChild); SyntaxNode oFindChild = null; if (oChildNodePair != null) { oFindChild = oChildNodePair.FindPatternNode; } //****************************************************** // If oFindChild is null (no find-pattern matched // this parse-tree child), copy oParseChild. // // Otherwise, copy each oReplacePatternNode child that // has the same label as oFindChild. if (oFindChild == null) { //************************************************** // Copy oParseChild. SyntaxNode oNewChild = oParseChild.CloneBranch(); oNewNode.ChildNodes.Add(oNewChild); } else { //************************************************** // Copy each oReplacePatternNode child that has the // same label as oFindChild. foreach (SyntaxNode oReplaceChild in oReplacePatternNode.ChildNodes) { if (oReplaceChild.Label == oFindChild.Label) { //****************************************** // Copy the oReplacePatternNode child. // // Since oParseTreeNode is a syntax leaf, // each of its children is the root of a // morphology parse. So oParseChild is used // as the oMorphologyParseRoot argument to // the CopyNodeAndChildren() method. SyntaxNode oNewChild = CopyNodeAndChildren( oReplaceChild, oParseChild); if (oNewChild != null) { oNewNode.ChildNodes.Add(oNewChild); } //****************************************** // Add this oReplaceChild to the list of // oUsedReplaceChildren (if not already // there). if (!oUsedReplaceChildren .Contains(oReplaceChild)) { oUsedReplaceChildren.Add(oReplaceChild); } } } } } } //************************************************************** // Copy any oReplacePatternNode children that have not yet been // used. foreach (SyntaxNode oReplaceChild in oReplacePatternNode.ChildNodes) { if (!oUsedReplaceChildren.Contains(oReplaceChild)) { SyntaxNode oNewChild = CopyNodeAndChildren(oReplaceChild); if (oNewChild != null) { oNewNode.ChildNodes.Add(oNewChild); } } } //************************************************************** // Return the new parse-tree node after adding it to the // ReplacedNodes list. SyntaxNodeTriple oNodeTriple = new SyntaxNodeTriple(); oNodeTriple.ParseTreeNode = oNewNode; oNodeTriple.FindPatternNode = oFindPatternNode; oNodeTriple.ReplacePatternNode = oReplacePatternNode; ReplacedNodes.Push(oNodeTriple); return(oNewNode); }
//****************************************************************** /// <summary> /// Compares the sub-trees dominated by oNodePair.ParseTreeNode and /// oNodePair.FindPatternNode. If the features and children of the /// pattern sub-tree match the features and children of the parse /// sub-tree, the matching node pairs are pushed onto the /// MatchingNodes stack and true is returned. Otherwise, false is /// returned (after any added partial matches are popped of the /// MatchingNodes stack, restoring it to the same state as when this /// method was called). /// </summary> private bool FeaturesAndChildrenMatch(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); //************************************************************** // Compare the features of oNodePair.ParseTreeNode and // oNodePair.FindPatternNode. // // If the features do not match (or if either node is null), // return false. // // Otherwise, push the oNodePair onto the MatchingNodes stack. if (! FeaturesMatch(oNodePair)) { return false; } Debug.Assert(oNodePair.ParseTreeNode != null); Debug.Assert(oNodePair.FindPatternNode != null); MatchingNodes.Push(oNodePair); Debug.WriteLineIf(VerboseDebugging, "MatchingNodes: " + MatchingNodes.ToString() + "."); //************************************************************** // If oNodePair.FindPatternNode has children, compare these // children to the children of oNodePair.ParseTreeNode. // // (Note that the children of the ParseTreeNode do not need to // be matched if the FindPatternNode does not have children.) // // If the children do not match, pop everything off the // MatchingNodes stack up to and including the oNodePair, and // then return false. if (oNodePair.FindPatternNode.ChildNodes.Count > 0) { //********************************************************** // If oNodePair.ParseTreeNode is a syntax leaf node, call // LeafChildrenMatch() to compare the children. // // Otherwise, call NodeChildrenMatch() to compare the // children. bool bChildrenMatch = false; if (oNodePair.ParseTreeNode.IsSyntaxLeaf) { bChildrenMatch = LeafChildrenMatch(oNodePair); } else { bChildrenMatch = NodeChildrenMatch(oNodePair); } //********************************************************** // If the children do not match, pop everything off the // MatchingNodes stack up to and including the oNodePair, // and then return false. if (! bChildrenMatch) { ClearNodePairFromStack(oNodePair); return false; } } //************************************************************** // The features and children match, so return true. return true; }
//****************************************************************** #region [FeaturesMatch() Method] //****************************************************************** /// <summary> /// Compares the ParseTreeNode and the FindPatternNode specified by /// the given oNodePair. If both nodes are not null, the node /// features are compared, and true is returned if the ParseTreeNode /// has a matching feature value for each feature in the /// FindPatternNode.Features collection. Otherwise, false is /// returned. /// </summary> private bool FeaturesMatch(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); //************************************************************** // For verbose debugging output, determine if the parse-tree // node is a syntax leaf. string sPair = "Node pair"; if (VerboseDebugging) { if (oNodePair.ParseTreeNode != null) { if (oNodePair.ParseTreeNode.IsSyntaxLeaf) { sPair = "Leaf pair"; } } } //************************************************************** // Return false if either node in the node pair is null. if ((oNodePair.ParseTreeNode == null) || (oNodePair.FindPatternNode == null)) { Debug.WriteLineIf(VerboseDebugging, sPair + " does not match: " + oNodePair.ToString() + "."); return(false); } //************************************************************** // For each feature in the find-pattern node (except for the // node label), check if the parse-tree node has a matching // value for that feature. foreach (SyntaxFeature oFeature in oNodePair.FindPatternNode.Features) { if (oFeature.Name != TreeTranEngineString.NodeLabel) { //****************************************************** // Return false if the parse-tree feature does not have // a matching value. // // Note: If the parse-tree node does not have a feature // with the same name, the default feature value is // used: an empty string (""). Because of this, we can // check if a feature matches a pattern like "!xxx", and // the match will succeed if (1) the parse-tree node has // that feature with any value other than "xxx" or (2) // the parse-tree node does not have that feature. string sString = oNodePair.ParseTreeNode.Features[oFeature.Name]; string sPattern = oFeature.Value; if (!StringMatchesPattern(sString, sPattern)) { Debug.WriteLineIf(VerboseDebugging, sPair + " does not match: " + oNodePair.ToString() + " " + oFeature.Name + " = " + "\"" + sString + "\" vs " + "\"" + sPattern + "\"."); return(false); } } } //************************************************************** // Return true if all of the features matched. Debug.WriteLineIf(VerboseDebugging, sPair + " matches: " + oNodePair.ToString() + "."); return(true); }
//****************************************************************** // EXAMPLE WALKTHROUGH OF BACKTRACKING // // Suppose NodeChildrenMatch() is called with the following state: // // MatchingNodes = {(VP,VP)} // oNodePair.ParseTreeNode.ChildNodes = {V,PP} // oNodePair.FindPatternNode.ChildNodes = {V,(NP),(PP),PP} // // 1. Compare the first child pair (V,V). They match, so push the // pair onto the stack and move to the next sibling pair (PP,(NP)). // The stack now contains: // // MatchingNodes = {(V,V)(VP,VP)} // // 2. Compare the child pair (PP,(NP)). They do not match. Since // (NP) is optional, skip this node to get the next child pair to // compare (PP,(PP)). // // 3. Compare the child pair (PP,(PP)). They match, so push the pair // onto the stack and move to the next sibling pair (null,PP). The // stack now contains: // // MatchingNodes = {(PP,(PP))(V,V)(VP,VP)} // // 4. Compare the child pair (null,PP). They do not match. Since PP // is not optional, we need to backtrack to find a child pair with // an optional find-pattern node. The child pair (PP,(PP)) is found // on the stack. Skip the optional node to get the next child pair // to compare (PP,PP). Backtracking removed one pair from the stack: // // MatchingNodes = {(V,V)(VP,VP)} // // 5. Compare the child pair (PP,PP). They match, so push the pair // onto the stack and move to the next sibling pair (null,null). The // stack now contains: // // MatchingNodes = {(PP,PP)(V,V)(VP,VP)} // // 6. We reached the end of both ChildNodes collections at the same // time. This means we successfully matched all the children, so // return true. //****************************************************************** /// <summary> /// Compares the children of oNodePair.ParseTreeNode to the children /// of oNodePair.FindPatternNode by making recursive calls to /// FeaturesAndChildrenMatch() for each pair of child nodes. /// Backtracks and/or skips optional nodes if necessary to find /// matching pairs of child nodes. Returns true if all the children /// are matched successfully. Returns false otherwise. (This is an /// ordered comparison. The given oNodePair.ParseTreeNode must not /// be a syntax leaf.) /// </summary> private bool NodeChildrenMatch(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); Debug.Assert(oNodePair.ParseTreeNode != null); Debug.Assert(oNodePair.FindPatternNode != null); Debug.Assert(! oNodePair.ParseTreeNode.IsSyntaxLeaf); Debug.Assert(oNodePair.FindPatternNode.ChildNodes.Count > 0); //************************************************************** // Iterate through the children of the oNodePair.ParseTreeNode // and the children of the oNodePair.FindPatternNode, starting // with the first child in each collection. // // For each child pair, compare their features and children. // // If the pair matches: // // Get the next child pair to compare by moving forward to // the next sibling in each collection. // // Otherwise: // // Get the next child pair to compare by backtracking and/or // skipping an optional node. // // If we reach the end of both collections at the same time (the // child ParseTreeNode and the child FindPatternNode are both // null), we have successfully matched all the children. SyntaxNodePair oChildPair = FirstChildNodePair(oNodePair); while ((oChildPair.ParseTreeNode != null) || (oChildPair.FindPatternNode != null)) { if (FeaturesAndChildrenMatch(oChildPair)) { //****************************************************** // The pair matches. // // Move forward to the next sibling pair. oChildPair = NextSiblingNodePair(oChildPair); } else { //****************************************************** // The pair does not match. // // Try to find another child pair to compare by skipping // an optional node. // // If the child FindPatternNode is not optional, we need // to backtrack to a node pair on the stack that (1) is // a child of oNodePair and (2) has a FindPatternNode // that is optional. If backtracking fails to find such // a node pair, return false. // // Once we have a child FindPatternNode that is // optional, skip the optional node to get the next // child pair to compare. bool bChildFindPatternNodeIsOptional = false; if (oChildPair.FindPatternNode != null) { if (oChildPair.FindPatternNode.IsOptionalNode) { bChildFindPatternNodeIsOptional = true; } } if (! bChildFindPatternNodeIsOptional) { oChildPair = BacktrackToOptionalNode(oNodePair); if (oChildPair == null) { return false; } } Debug.Assert(oChildPair.FindPatternNode != null); Debug.Assert(oChildPair.FindPatternNode.IsOptionalNode); oChildPair = SkipOptionalNode(oChildPair); } } //************************************************************** // We have successfully matched all the children, so return // true. return true; }
//****************************************************************** /// <summary> /// Returns a new node pair containing the next sibling of the /// ParseTreeNode and the next sibling of the FindPatternNode from /// the given oNodePair. /// </summary> private SyntaxNodePair NextSiblingNodePair(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); Debug.Assert(oNodePair.ParseTreeNode != null); Debug.Assert(oNodePair.FindPatternNode != null); Debug.Assert(oNodePair.ParseTreeNode != CurrentParseTreeNode); Debug.Assert(oNodePair.FindPatternNode != FindPatternRoot); SyntaxNodePair oSiblingPair = new SyntaxNodePair(); oSiblingPair.ParseTreeNode = oNodePair.ParseTreeNode.NextSibling; oSiblingPair.FindPatternNode = oNodePair.FindPatternNode.NextSibling; return oSiblingPair; }
//****************************************************************** /// <summary> /// Compares each child of oNodePair.ParseTreeNode by making /// recursive calls to FeaturesAndChildrenMatch() to see if it /// matches any child of oNodePair.FindPatternNode. Returns true if /// at least one child pair matched successfully. Returns false /// otherwise. (This is not an ordered comparison. The given /// oNodePair.ParseTreeNode must be a syntax leaf.) /// </summary> private bool LeafChildrenMatch(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); Debug.Assert(oNodePair.ParseTreeNode != null); Debug.Assert(oNodePair.FindPatternNode != null); Debug.Assert(oNodePair.ParseTreeNode.IsSyntaxLeaf); Debug.Assert(oNodePair.FindPatternNode.ChildNodes.Count > 0); //************************************************************** // Since oNodePair.ParseTreeNode is a syntax leaf, each of its // children is the root of a morphology parse. (A syntax leaf // may have one or more alternative morphology parses.) // // For each of these morphology-parse children of // oNodePair.ParseTreeNode, see if it matches any child of // oNodePair.FindPatternNode. If so, increment the counter. int iMatchCount = 0; foreach (SyntaxNode oParseChild in oNodePair.ParseTreeNode.ChildNodes) { //********************************************************** // Loop through the oNodePair.FindPatternNode children. // Stop if we find a match to the morphology-parse child of // oNodePair.ParseTreeNode. SyntaxNode oFindChild = oNodePair.FindPatternNode.FirstChild; while (oFindChild != null) { SyntaxNodePair oChildPair = new SyntaxNodePair(); oChildPair.ParseTreeNode = oParseChild; oChildPair.FindPatternNode = oFindChild; if (FeaturesAndChildrenMatch(oChildPair)) { //************************************************** // Found a match. Increment the counter and end the // loop (by setting oFindChild to null). ++iMatchCount; oFindChild = null; } else { //************************************************** // Continue looping (by setting oFindChild to the // next sibling). Debug.Assert(oFindChild != FindPatternRoot); oFindChild = oFindChild.NextSibling; } } } //************************************************************** // If at least one child of oNodePair.ParseTreeNode matched, // return true. Otherwise, return false. if (iMatchCount > 0) { return true; } return false; }
//****************************************************************** /// <summary> /// Returns a new node pair containing the first child of the /// ParseTreeNode and the first child of the FindPatternNode from /// the given oNodePair. /// </summary> private SyntaxNodePair FirstChildNodePair(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); Debug.Assert(oNodePair.ParseTreeNode != null); Debug.Assert(oNodePair.FindPatternNode != null); SyntaxNodePair oChildPair = new SyntaxNodePair(); oChildPair.ParseTreeNode = oNodePair.ParseTreeNode.FirstChild; oChildPair.FindPatternNode = oNodePair.FindPatternNode.FirstChild; return oChildPair; }
//****************************************************************** /// <summary> /// Compares the ParseTreeNode and the FindPatternNode specified by /// the given oNodePair. If both nodes are not null, the node /// features are compared, and true is returned if the ParseTreeNode /// has a matching feature value for each feature in the /// FindPatternNode.Features collection. Otherwise, false is /// returned. /// </summary> private bool FeaturesMatch(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); //************************************************************** // For verbose debugging output, determine if the parse-tree // node is a syntax leaf. string sPair = "Node pair"; if (VerboseDebugging) { if (oNodePair.ParseTreeNode != null) { if (oNodePair.ParseTreeNode.IsSyntaxLeaf) { sPair = "Leaf pair"; } } } //************************************************************** // Return false if either node in the node pair is null. if ((oNodePair.ParseTreeNode == null) || (oNodePair.FindPatternNode == null)) { Debug.WriteLineIf(VerboseDebugging, sPair + " does not match: " + oNodePair.ToString() + "."); return false; } //************************************************************** // For each feature in the find-pattern node (except for the // node label), check if the parse-tree node has a matching // value for that feature. foreach (SyntaxFeature oFeature in oNodePair.FindPatternNode.Features) { if (oFeature.Name != TreeTranEngineString.NodeLabel) { //****************************************************** // Return false if the parse-tree feature does not have // a matching value. // // Note: If the parse-tree node does not have a feature // with the same name, the default feature value is // used: an empty string (""). Because of this, we can // check if a feature matches a pattern like "!xxx", and // the match will succeed if (1) the parse-tree node has // that feature with any value other than "xxx" or (2) // the parse-tree node does not have that feature. string sString = oNodePair.ParseTreeNode.Features[oFeature.Name]; string sPattern = oFeature.Value; if (! StringMatchesPattern(sString,sPattern)) { Debug.WriteLineIf(VerboseDebugging, sPair + " does not match: " + oNodePair.ToString() + " " + oFeature.Name + " = " + "\"" + sString + "\" vs " + "\"" + sPattern + "\"."); return false; } } } //************************************************************** // Return true if all of the features matched. Debug.WriteLineIf(VerboseDebugging, sPair + " matches: " + oNodePair.ToString() + "."); return true; }
//****************************************************************** /// <summary> /// Attempts to backtrack to a node pair on the MatchingNodes stack /// that (1) is a child of the given oParentNodePair and (2) has an /// optional FindPatternNode. If such a node pair is found, items /// are popped from the stack until this node pair is popped, and /// then this node pair is returned. If no such node pair is found, /// null is returned (and no items are popped from the stack). /// </summary> private SyntaxNodePair BacktrackToOptionalNode( SyntaxNodePair oParentNodePair) { Debug.Assert(oParentNodePair != null); Debug.Assert(oParentNodePair.ParseTreeNode != null); Debug.Assert(oParentNodePair.FindPatternNode != null); //************************************************************** // Search backwards through the MatchingNodes stack (starting // with the most recently pushed node pair), looking for a node // pair where (1) the ParseTreeNode is a child of the given // oParentNodePair.ParseTreeNode, (2) the FindPatternNode is a // child of the given oParentNodePair.FindPatternNode, and (3) // the FindPatternNode is optional. // // If such a node pair is found, pop items from the stack until // this node pair is popped, and then return this node pair. int iIndex = MatchingNodes.Count - 1; while (iIndex >= 0) { SyntaxNodePair oNodePair = MatchingNodes[iIndex]; if ((oNodePair.ParseTreeNode.ParentNode == oParentNodePair.ParseTreeNode) && (oNodePair.FindPatternNode.ParentNode == oParentNodePair.FindPatternNode) && (oNodePair.FindPatternNode.IsOptionalNode)) { //****************************************************** // Found a node pair that is a child of the given // oParentNodePair and has an optional FindPatternNode. // So pop items from the stack until this node pair is // popped, and then return this node pair. // // This restores the stack to the same state as when // this node pair was matched, but this time we can skip // the optional node and attempt to continue the match. ClearNodePairFromStack(oNodePair); Debug.WriteLineIf(VerboseDebugging, "Backtracking to optional node...."); return oNodePair; } --iIndex; } //************************************************************** // The stack does not contain a child of the oParentNodePair // with an optional FindPatternNode, so return null. Debug.WriteLineIf(VerboseDebugging, "Backtracking... cannot find an optional child of " + oParentNodePair.ToString() + " on the stack."); return null; }
//****************************************************************** /// <summary> /// Inserts an item at the top of the stack. /// </summary> public void Push(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); if (oNodePair == null) { string sMessage = "Invalid argument: " + "SyntaxNodePairStack cannot push a null item."; throw new Exception(sMessage); } InnerList.Add(oNodePair); }
//****************************************************************** /// <summary> /// For the given oReplacePatternNode, this method finds the /// corresponding find-pattern node and the parse-tree node it /// matched. (The parse-tree node must be in the branch indicated by /// oMorphologyParseRoot if this optional argument is not null.) A /// new node is created by copying features from the parse-tree node /// and merging features from the oReplacePatternNode. Then children /// are copied recursively either from the parse-tree node or from /// the oReplacePatternNode. Returns null if the oReplacePatternNode /// was optional and the parse-tree node was not found. Otherwise, /// returns the new node after adding it to the ReplacedNodes list. /// (Note that this method calls the CopyLeafAndChildren() method if /// it determines that the node is a leaf node.) /// </summary> SyntaxNode CopyNodeAndChildren(SyntaxNode oReplacePatternNode, SyntaxNode oMorphologyParseRoot) { Debug.Assert(oReplacePatternNode != null); //************************************************************** // For the given oReplacePatternNode, find the corresponding // find-pattern node and the parse-tree node it matched: // // Search the MatchingNodes list for a node pair where the // .FindPatternNode has the same label as the given // oReplacePatternNode. // // If oMorphologyParseRoot is not null, restrict this search to // node pairs where the .ParseTreeNode is contained in the // branch dominated by this oMorphologyParseRoot. // // If a matching node pair is found, set oParseTreeNode and // oFindPatternNode to the node-pair values. Otherwise, set // oParseTreeNode and oFindPatternNode both to null. SyntaxNode oParseTreeNode = null; SyntaxNode oFindPatternNode = null; SyntaxNodePair oNodePair = FindMatchingPairFromLabel( oReplacePatternNode.Label, oMorphologyParseRoot); if (oNodePair != null) { oParseTreeNode = oNodePair.ParseTreeNode; oFindPatternNode = oNodePair.FindPatternNode; } //************************************************************** // If a matching node pair was not found and oReplacePatternNode // is optional, nothing will be copied, so return null. if ((oNodePair == null) && (oReplacePatternNode.IsOptionalNode)) { return(null); } //************************************************************** // If this is a leaf node, call CopyLeafAndChildren() instead. // // Each child of a syntax leaf is the root of a morphology // parse. (A syntax leaf may have one or more alternative // morphology parses.) CopyLeafAndChildren() is called because // a different algorithm is needed to copy these children. bool bIsSyntaxLeaf = false; if (oParseTreeNode != null) { if (oParseTreeNode.IsSyntaxLeaf) { bIsSyntaxLeaf = true; } } if (oReplacePatternNode.IsSyntaxLeaf) { bIsSyntaxLeaf = true; } if (bIsSyntaxLeaf) { return(CopyLeafAndChildren(oReplacePatternNode)); } //************************************************************** // Create a new parse-tree node by copying the features from the // matching oParseTreeNode (if found) and then merging the // features from oReplacePatternNode. if (VerboseDebugging) { string sParseString = "null"; if (oParseTreeNode != null) { sParseString = oParseTreeNode.ToString(); } string sReplaceString = "null"; if (oReplacePatternNode != null) { sReplaceString = oReplacePatternNode.ToString(); } Debug.WriteLine("Copying node: CopyFeatures(" + sParseString + "," + sReplaceString + ")."); } SyntaxNode oNewNode = CopyFeatures(oParseTreeNode, oReplacePatternNode); //************************************************************** // Check if any children are specified by oFindPatternNode or // oReplacePatternNode. bool bFindPatternHasChildren = false; if (oFindPatternNode != null) { if (oFindPatternNode.ChildNodes.Count > 0) { bFindPatternHasChildren = true; } } bool bReplacePatternHasChildren = false; if (oReplacePatternNode.ChildNodes.Count > 0) { bReplacePatternHasChildren = true; } //************************************************************** // Copy children either from the matching oParseTreeNode or from // the oReplacePatternNode: // // If oParseTreeNode is not null and no children are specified // by oFindPatternNode or oReplacePatternNode, copy the // oParseTreeNode children. // // Otherwise, copy the oReplacePatternNode children. if ((oParseTreeNode != null) && (!bFindPatternHasChildren) && (!bReplacePatternHasChildren)) { //********************************************************** // Copy the oParseTreeNode children. foreach (SyntaxNode oParseChild in oParseTreeNode.ChildNodes) { SyntaxNode oNewChild = oParseChild.CloneBranch(); oNewNode.ChildNodes.Add(oNewChild); } } else { //********************************************************** // Copy the oReplacePatternNode children. foreach (SyntaxNode oReplaceChild in oReplacePatternNode.ChildNodes) { SyntaxNode oNewChild = CopyNodeAndChildren( oReplaceChild, oMorphologyParseRoot); if (oNewChild != null) { oNewNode.ChildNodes.Add(oNewChild); } } } //************************************************************** // Return the new parse-tree node after adding it to the // ReplacedNodes list. SyntaxNodeTriple oNodeTriple = new SyntaxNodeTriple(); oNodeTriple.ParseTreeNode = oNewNode; oNodeTriple.FindPatternNode = oFindPatternNode; oNodeTriple.ReplacePatternNode = oReplacePatternNode; ReplacedNodes.Push(oNodeTriple); return(oNewNode); }
//****************************************************************** /// <summary> /// Returns a new node pair containing the same ParseTreeNode as the /// given oNodePair and the next sibling of the FindPatternNode from /// the given oNodePair. (The given oNodePair.FindPatternNode must /// be an optional node, so by moving to its next sibling we skip /// the optional node.) /// </summary> private SyntaxNodePair SkipOptionalNode(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); Debug.Assert(oNodePair.FindPatternNode != null); Debug.Assert(oNodePair.FindPatternNode.IsOptionalNode); Debug.Assert(oNodePair.FindPatternNode != FindPatternRoot); SyntaxNodePair oNextNodePair = new SyntaxNodePair(); oNextNodePair.ParseTreeNode = oNodePair.ParseTreeNode; oNextNodePair.FindPatternNode = oNodePair.FindPatternNode.NextSibling; Debug.WriteLineIf(VerboseDebugging, "Skipping optional node...."); return oNextNodePair; }
//****************************************************************** /// <summary> /// Pops node pairs from the MatchingNodes stack until the indicated /// node pair (oNodePair) has been popped. (The indicated node pair /// must exist on the stack.) /// </summary> private void ClearNodePairFromStack(SyntaxNodePair oNodePair) { Debug.Assert(oNodePair != null); Debug.Assert(oNodePair.ParseTreeNode != null); Debug.Assert(oNodePair.FindPatternNode != null); while (MatchingNodes.Count > 0) { SyntaxNodePair oStackPair = MatchingNodes.Pop(); if (oStackPair == oNodePair) { return; } } Debug.Fail("Node pair not found in MatchingNodes stack."); }
//****************************************************************** #region [CurrentBranchMatchesPattern() Method] //****************************************************************** /// <summary> /// Compares the trees dominated by the CurrentParseTreeNode and the /// FindPatternRoot. If these trees match, true is returned and the /// MatchingNodes stack contains the list of matching node pairs. /// Otherwise, false is returned. /// </summary> public bool CurrentBranchMatchesPattern() { Debug.WriteLineIf(VerboseDebugging, "MatchAlgorithm.CurrentBranchMatchesPattern() called."); //************************************************************** // Validate the current state. if (FindPatternRoot == null) { string sMessage = "CurrentBranchMatchesPattern() " + "called with an invalid state: " + "FindPatternRoot is null."; throw new Exception(sMessage); } if (CurrentParseTreeNode == null) { string sMessage = "CurrentBranchMatchesPattern() " + "called with an invalid state: " + "CurrentParseTreeNode is null."; throw new Exception(sMessage); } //************************************************************** // Clear the stack of matching node pairs. MatchingNodes.Clear(); //************************************************************** // Call FeaturesAndChildrenMatch() to check if the current // parse-tree branch matches the find-pattern tree. If so, // return true. SyntaxNodePair oNodePair = new SyntaxNodePair(); oNodePair.ParseTreeNode = CurrentParseTreeNode; oNodePair.FindPatternNode = FindPatternRoot; if (FeaturesAndChildrenMatch(oNodePair)) { Debug.Assert(MatchingNodes.Count > 0); Debug.WriteLineIf(VerboseDebugging, "MatchAlgorithm.CurrentBranchMatchesPattern() returns " + "true."); return(true); } //************************************************************** // The current parse-tree branch does not match the find-pattern // tree, so return false. // // Note: The matching procedure should have cleared all node // pairs from the MatchingNodes stack if the match failed. Debug.Assert(MatchingNodes.Count == 0); MatchingNodes.Clear(); Debug.WriteLineIf(VerboseDebugging, "MatchAlgorithm.CurrentBranchMatchesPattern() returns " + "false."); return(false); }
//****************************************************************** /// <summary> /// Creates a branch of SyntaxNode objects representing the nodes /// and features displayed by the given oTreeNode and its children /// (recursively). If the optional oTreeTransfer argument is given, /// its CurrentParseTreeNode is set to indicate the selected node /// (if any) in the branch, and items are added to its MatchingNodes /// and ReplacedNodes collections to indicate highlighted nodes in /// the branch. The root node of the created SyntaxNode branch is /// returned. /// </summary> private SyntaxNode CloneBranch(TreeViewerNode oTreeNode, TreeTransfer oTreeTransfer) { Debug.Assert(oTreeNode != null); Debug.Assert(oTreeTransfer != null); //************************************************************** // Clone the SyntaxNode branch associated with the given // oTreeNode. SyntaxNode oSyntaxNode = CloneNode(oTreeNode); //************************************************************** // The oTreeNode can have child nodes. Or the oSyntaxNode can // have child nodes (representing morphology nodes that were not // shown). But the oTreeNode and oSyntaxNode cannot both have // child nodes. if (oTreeNode.ChildNodes.Count > 0) { Debug.Assert(oSyntaxNode.ChildNodes.Count == 0); } if (oSyntaxNode.ChildNodes.Count > 0) { Debug.Assert(oTreeNode.ChildNodes.Count == 0); } //************************************************************** // Recursively clone each child branch of oTreeNode, and add // each cloned branch as a child of oSyntaxNode. foreach (TreeViewerNode oTreeChild in oTreeNode.ChildNodes) { SyntaxNode oSyntaxChild = CloneBranch(oTreeChild,oTreeTransfer); oSyntaxNode.ChildNodes.Add(oSyntaxChild); } //************************************************************** // If this node is the SelectedNode, set the // oTreeTransfer.CurrentParseTreeNode property to indicate this // node. if (oTreeNode == moTreeViewer.SelectedNode) { oTreeTransfer.CurrentParseTreeNode = oSyntaxNode; } //************************************************************** // If this node is highlighted as a matching node, add an item // indicating this node to the oTreeTransfer.MatchingNodes // collection. // // If this node is highlighted as a replaced node, add an item // indicating this node to the oTreeTransfer.ReplacedNodes // collection. if ((! DisplayFindPattern) && (! DisplayReplacePattern)) { if (oTreeNode.BackColor == FindPatternColor) { SyntaxNodePair oNodePair = new SyntaxNodePair(); oNodePair.ParseTreeNode = oSyntaxNode; oTreeTransfer.MatchingNodes.Push(oNodePair); } if (oTreeNode.BackColor == ReplacePatternColor) { SyntaxNodeTriple oNodeTriple = new SyntaxNodeTriple(); oNodeTriple.ParseTreeNode = oSyntaxNode; oTreeTransfer.ReplacedNodes.Push(oNodeTriple); } } //************************************************************** // Return the cloned SyntaxNode branch. return oSyntaxNode; }