//******************************************************************
        /// <summary>
        /// Pastes the current contents of the clipboard.
        /// </summary>
        public void Paste()
        {
            //**************************************************************
            // Validate the current state.

            if (! CanPaste())
            {
                string sMessage = "Invalid state: "
                    + "A call to ParseTreeViewer.Paste() is not allowed "
                    + "if ParseTreeViewer.CanPaste() returns false.";
                throw new Exception(sMessage);
            }

            //**************************************************************
            // Prevent modification to a read-only tree.

            if (ReadOnly)
            {
                return;
            }

            //**************************************************************
            // Use the current contents of the tree (before the paste
            // operation) to set the undo information.

            TreeTransferForUndo = RecreateTreeTransfer();

            //**************************************************************
            // Get the string data on the clipboard, or return (without
            // reporting an error) if there is no string data on the
            // clipboard.

            IDataObject oDataObject = Clipboard.GetDataObject();
            if (oDataObject == null)
            {
                return;
            }
            if (! oDataObject.GetDataPresent(typeof(string)))
            {
                return;
            }
            string sString = (string) oDataObject.GetData(typeof(string));
            if (sString == null)
            {
                return;
            }

            //**************************************************************
            // Return (without reporting an error) if the string data does
            // not look at all like XML.

            if (sString.IndexOf("<") < 0)
            {
                return;
            }
            if (sString.IndexOf("/") < 0)
            {
                return;
            }
            if (sString.IndexOf(">") < 0)
            {
                return;
            }

            //**************************************************************
            // Get the branch to paste from the first tree represented by
            // the string data.

            SyntaxNode oSyntaxBranchToPaste = null;
            try
            {
                //**********************************************************
                // Use a RuleReader to read the first rule from the string.
                // Get the rule's FindPatternRoot as the branch to paste.

                TextReader oTextReader = new StringReader(sString);
                RuleReader oRuleReader = new RuleReader(oTextReader);
                if (oRuleReader.Read())
                {
                    oSyntaxBranchToPaste = oRuleReader.FindPatternRoot;
                }
                oRuleReader.Close();
            }
            catch (Exception oException)
            {
                string sCaption = "Paste";
                string sMessage = "Cannot paste because "
                    + "the clipboard data is not compatible."
                    + Environment.NewLine + Environment.NewLine
                    + "(" + oException.Message  + ")";
                MessageBox.Show(sMessage,sCaption,MessageBoxButtons.OK,
                    MessageBoxIcon.Warning);
                return;
            }

            //**************************************************************
            // Return (without reporting an error) if the branch to paste
            // (from the string data) is null.

            if (oSyntaxBranchToPaste == null)
            {
                return;
            }

            //**************************************************************
            // If the displayed tree is a find pattern or a replace pattern,
            // the labels of the new nodes should all be unique.

            if ((DisplayFindPattern) || (DisplayReplacePattern))
            {
                //**********************************************************
                // Collect all the node labels in the current tree. Then go
                // through the new branch and make sure all the nodes have
                // labels that are not already in use.

                SyntaxNode oSyntaxRoot = CloneTree();
                if (oSyntaxRoot != null)
                {
                    StringCollection oLabelsInUse = new StringCollection();
                    CollectLabelsInUse(oSyntaxRoot,oLabelsInUse);
                    RenameLabelsInUse(oSyntaxBranchToPaste,oLabelsInUse);
                }
            }

            //**************************************************************
            // Call BeginUpdate() to disable redrawing the tree, but not if
            // redrawing was already disabled by the calling code.

            bool bCallEndUpdate = false;
            if (! IsUpdating)
            {
                BeginUpdate();
                bCallEndUpdate = true;
            }

            //**************************************************************
            // If the root node is null or empty, paste a new tree.
            // Otherwise, paste a new child of the selected node.

            if (NodeIsNullOrEmpty(moTreeViewer.RootNode))
            {
                //**********************************************************
                // Paste the branch as the root of the tree. Select the new
                // root node.

                moTreeViewer.RootNode = new TreeViewerNode();
                PopulateBranch(moTreeViewer.RootNode,oSyntaxBranchToPaste);
                moTreeViewer.SelectedNode = moTreeViewer.RootNode;
            }
            else
            {
                //**********************************************************
                // Paste the branch as a child of the selected node. Select
                // the new child node that was pasted.

                if (moTreeViewer.SelectedNode != null)
                {
                    //******************************************************
                    // The entire branch dominated by the selected node will
                    // be repopulated. (This is because we want the pasted
                    // branch to be displayed, even if ShowMorphology is
                    // false and we are pasting a new morphology child of a
                    // leaf node).
                    //
                    // First, clone the entire branch dominated by the
                    // selected node. Delete the children of the selected
                    // node in the tree.

                    SyntaxNode oSyntaxParent
                        = CloneBranch(moTreeViewer.SelectedNode);
                    moTreeViewer.SelectedNode.ChildNodes.Clear();

                    //******************************************************
                    // Add the oSyntaxBranchToPaste as a child of the cloned
                    // branch. Then use the cloned branch to repopulate the
                    // branch in the tree dominated by the selected node.
                    //
                    // The oTreeTransfer.CurrentParseTreeNode is set to the
                    // oSyntaxBranchToPaste, so this new child will be the
                    // new selected node (even if it is a morphology node
                    // and ShowMorphology is false).

                    oSyntaxParent.ChildNodes.Add(oSyntaxBranchToPaste);
                    TreeTransfer oTreeTransfer = new TreeTransfer();
                    oTreeTransfer.CurrentParseTreeNode
                        = oSyntaxBranchToPaste;
                    PopulateBranch(moTreeViewer.SelectedNode,oSyntaxParent,
                        oTreeTransfer);
                }
            }

            //**************************************************************
            // Call EndUpdate() to redraw the tree, but not if redrawing was
            // already disabled by the calling code.

            if (bCallEndUpdate)
            {
                EndUpdate();
            }

            //**************************************************************
            // Set the Modified property to true and raise the TreeChanged
            // event.

            Modified = true;
            OnTreeChanged(new EventArgs());
        }
        //******************************************************************
        /// <summary>
        /// If the TreeTransferData object is null, it is set to a new
        /// TreeTransfer object. If the TreeTransferData object's
        /// .CurrentParseTreeNode is null, its .ParseTreeRoot is set to a
        /// copy of the parse tree in the linked ParseTreeViewer, and its
        /// .FindPatternRoot and .ReplacePatternRoot are set to the find
        /// and replace patterns indicated by the SelectedRule.
        /// </summary>
        private void InitializeTreeTransferData()
        {
            //**************************************************************
            // Set the TreeTransferData property to a new TreeTransfer
            // object if the TreeTransferData property is currently null.

            if (TreeTransferData == null)
            {
                TreeTransferData = new TreeTransfer();
            }

            //**************************************************************
            // If TreeTransferData.CurrentParseTreeNode is null, set the
            // .ParseTreeRoot, .FindPatternRoot and .ReplacePatternRoot
            // properties of the TreeTransferData object.
            //
            // If TreeTransferData.CurrentParseTreeNode is not null, these
            // properties are not changed, because setting these properties
            // would clear the .CurrentParseTreeNode property (resetting the
            // current search position in the tree).

            if (TreeTransferData.CurrentParseTreeNode == null)
            {
                //**********************************************************
                // Set the TreeTransferData.ParseTreeRoot property to a copy
                // of the parse tree in the linked ParseTreeViewer.

                TreeTransferData.ParseTreeRoot = null;
                if (LinkedParseTreeViewer != null)
                {
                    TreeTransferData.ParseTreeRoot
                        = LinkedParseTreeViewer.CloneTree();
                }

                //**********************************************************
                // Set the TreeTransferData.FindPatternRoot property to the
                // find-pattern of the SelectedRule.

                TreeTransferData.FindPatternRoot = null;
                if (SelectedRule != null)
                {
                    TreeTransferData.FindPatternRoot
                        = SelectedRule.FindPatternRoot;
                }
                if (TreeTransferData.FindPatternRoot == null)
                {
                    TreeTransferData.FindPatternRoot = new SyntaxNode();
                }

                //**********************************************************
                // Set the TreeTransferData.ReplacePatternRoot property to
                // the replace-pattern of the SelectedRule.

                TreeTransferData.ReplacePatternRoot = null;
                if (SelectedRule != null)
                {
                    TreeTransferData.ReplacePatternRoot
                        = SelectedRule.ReplacePatternRoot;
                }
                if (TreeTransferData.ReplacePatternRoot == null)
                {
                    TreeTransferData.ReplacePatternRoot = new SyntaxNode();
                }
            }
        }
        //******************************************************************
        /// <summary>
        /// Initializes a new instance of the MatchAlgorithm class, which
        /// will access the tree data through the given TreeTransfer object.
        /// </summary>
        public MatchAlgorithm(TreeTransfer oTreeTransfer)
        {
            Debug.Assert(oTreeTransfer != null);

            moTreeTransfer = oTreeTransfer;
        }
        //******************************************************************
        /// <summary>
        /// Opens the FeaturesForm to display the syntax features of the
        /// selected node.
        /// </summary>
        public void OpenFeatures()
        {
            //**************************************************************
            // Validate the current state.

            if (! CanOpenFeatures())
            {
                string sMessage = "Invalid state: "
                    + "A call to ParseTreeViewer.OpenFeatures() is not "
                    + "allowed if ParseTreeViewer.CanOpenFeatures() "
                    + "returns false.";
                throw new Exception(sMessage);
            }

            //**************************************************************
            // Clone the selected branch.
            //
            // We clone the entire branch dominated by the selected node so
            // we can repopulate this entire branch if the node's features
            // are changed. (We need to do this because the user could edit
            // the node-type feature, which determines if the node is a leaf
            // node whose morphology children might be hidden.)

            TreeViewerNode oTreeNode = moTreeViewer.SelectedNode;
            TreeTransfer oTreeTransfer = new TreeTransfer();
            SyntaxNode oSyntaxNode = CloneBranch(oTreeNode,oTreeTransfer);

            //**************************************************************
            // Set the font for displaying features in the FeaturesForm to
            // the same font that this control uses to display parse trees.

            FeaturesForm.FeatureFont = Font;

            //**************************************************************
            // Open the FeaturesForm to display the features of the selected
            // node. (If ReadOnly is true, the user can view the features.
            // If ReadOnly is false, the user can also edit the features.)

            DialogResult iResult = DialogResult.None;
            if (ReadOnly)
            {
                iResult = FeaturesForm.OpenFeaturesReadOnly(oSyntaxNode);
            }
            else
            {
                iResult = FeaturesForm.OpenFeatures(oSyntaxNode);
            }

            //**************************************************************
            // Return without making any changes if the user canceled out of
            // the FeaturesForm.

            if (iResult != DialogResult.OK)
            {
                return;
            }

            //**************************************************************
            // Clear the undo information.

            TreeTransferForUndo = null;

            //**************************************************************
            // Determine if the FeaturesForm.FavoriteTreeFeatures list is
            // now different from the private FavoriteTreeFeatures copy of
            // the list. (If so, we will need to refresh the whole tree to
            // show the new set of features.)

            bool bTreeFeaturesChanged = false;
            if (FavoriteTreeFeatures.Count
                == FeaturesForm.FavoriteTreeFeatures.Count)
            {
                for (int iIndex = 0;
                    iIndex < FavoriteTreeFeatures.Count; ++iIndex)
                {
                    if (FavoriteTreeFeatures[iIndex]
                        != FeaturesForm.FavoriteTreeFeatures[iIndex])
                    {
                        bTreeFeaturesChanged = true;
                    }
                }
            }
            else
            {
                bTreeFeaturesChanged = true;
            }

            //**************************************************************
            // Call BeginUpdate() to disable redrawing the tree, but not if
            // redrawing was already disabled by the calling code.

            bool bCallEndUpdate = false;
            if (! IsUpdating)
            {
                BeginUpdate();
                bCallEndUpdate = true;
            }

            //**************************************************************
            // Update the selected node. (But not if ReadOnly is true.)

            if (! ReadOnly)
            {
                //**********************************************************
                // Repopulate the entire branch dominated by the selected
                // node.
                //
                // First, delete the children of the selected node in the
                // tree. Then repopulate the branch to display any changes
                // to the features of the selected node (and any changes to
                // whether its children are hidden).

                oTreeNode.ChildNodes.Clear();
                PopulateBranch(oTreeNode,oSyntaxNode,oTreeTransfer);
            }

            //**************************************************************
            // If the FeaturesForm.FavoriteTreeFeatures list was changed,
            // refresh the whole tree to show the new set of features.

            if (bTreeFeaturesChanged)
            {
                oTreeTransfer = RecreateTreeTransfer();
                PopulateRoot(oTreeTransfer);
            }

            //**************************************************************
            // Call EndUpdate() to redraw the tree, but not if redrawing was
            // already disabled by the calling code.

            if (bCallEndUpdate)
            {
                EndUpdate();
            }

            //**************************************************************
            // Set the Modified property to true and raise the TreeChanged
            // event. (But not if ReadOnly is true.)

            if (! ReadOnly)
            {
                Modified = true;
                OnTreeChanged(new EventArgs());
            }
        }
        //******************************************************************
        /// <summary>
        /// Uses the .ParseTreeRoot property of the given oSelectedParse
        /// object to populate the linked ParseTreeViewer. The SelectedParse
        /// property is set to the given oSelectedParse object.
        /// </summary>
        private void UpdateParseTreeViewer(
			ParseListViewerItem oSelectedParse)
        {
            //**************************************************************
            // Use the given oSelectedParse to initialize a TreeTransfer
            // object, which will be used to populate the parse-tree viewer.
            //
            // If oSelectedParse is null, a blank node will be used to
            // populate the parse-tree viewer.
            //
            // If oSelectedParse is not null, its .ParseTreeRoot tree will
            // be used to populate the parse-tree viewer.

            TreeTransfer oTreeTransfer = new TreeTransfer();
            if (oSelectedParse == null)
            {
                oTreeTransfer.ParseTreeRoot = new SyntaxNode();
                oTreeTransfer.FindPatternRoot = null;
                oTreeTransfer.ReplacePatternRoot = null;
            }
            else
            {
                oTreeTransfer.ParseTreeRoot = oSelectedParse.ParseTreeRoot;
                oTreeTransfer.FindPatternRoot = null;
                oTreeTransfer.ReplacePatternRoot = null;
            }

            //**************************************************************
            // Temporarily set the SelectedParse property to null, because
            // we do not want the LinkedParseTreeViewer TreeChanged event to
            // update the SelectedParse (and set the Modified property to
            // true) when we populate the parse-tree viewer.

            moSelectedParse = null;

            //**************************************************************
            // Populate the linked parse-tree viewer.

            if (LinkedParseTreeViewer != null)
            {
                LinkedParseTreeViewer.PopulateTree(oTreeTransfer);
            }

            //**************************************************************
            // Now set the SelectedParse property to the given
            // oSelectedParse object, because we want the TreeChanged events
            // to update the SelectedParse (and set the Modified property to
            // true) when the user edits the parse tree.

            moSelectedParse = oSelectedParse;
        }
        //******************************************************************
        /// <summary>
        /// Initializes a new instance of the MatchAlgorithm class, which
        /// will access the tree data through the given TreeTransfer object.
        /// </summary>
        public MatchAlgorithm(TreeTransfer oTreeTransfer)
        {
            Debug.Assert(oTreeTransfer != null);

            moTreeTransfer = oTreeTransfer;
        }
        //******************************************************************
        /// <summary>
        /// Uses the given oTreeNode to display features of the indicated
        /// oSyntaxNode. Children are then recursively added to oTreeNode to
        /// represent the children of oSyntaxNode (unless it is a leaf node
        /// and morphology nodes are hidden). If the optional oTreeTransfer
        /// argument is given, its CurrentParseTreeNode indicates the node
        /// to select, and items in its MatchingNodes and ReplacedNodes
        /// collections indicate nodes to highlight.
        /// </summary>
        private void PopulateBranch(TreeViewerNode oTreeNode,
			SyntaxNode oSyntaxNode)
        {
            TreeTransfer oTreeTransfer = new TreeTransfer();
            PopulateBranch(oTreeNode,oSyntaxNode,oTreeTransfer);
        }
        //******************************************************************
        /// <summary>
        /// Uses the TreeTransferData object to populate the linked
        /// ParseTreeViewer.
        /// </summary>
        private void UpdateParseTreeViewer()
        {
            Debug.Assert(TreeTransferData != null);

            //**************************************************************
            // The LinkedParseTreeViewer TreeChanged event handler resets
            // the TreeTransferData object. But we do not want this to
            // happen when we populate the parse-tree viewer, because this
            // would reset the current search position in the tree.
            //
            // So, to prevent this, we save the TreeTransferData object, and
            // then temporarily set the TreeTransferData property to null.

            TreeTransfer oTreeTransferData = TreeTransferData;
            TreeTransferData = null;

            //**************************************************************
            // Populate the linked parse-tree viewer.

            if (LinkedParseTreeViewer != null)
            {
                LinkedParseTreeViewer.PopulateTree(oTreeTransferData);
            }

            //**************************************************************
            // Now we set the TreeTransferData property back to the saved
            // oTreeTransferData object.

            TreeTransferData = oTreeTransferData;
        }
 //******************************************************************
 /// <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 = new TreeTransfer();
     return CloneBranch(oTreeNode,oTreeTransfer);
 }
        //******************************************************************
        /// <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;
        }
        //******************************************************************
        /// <summary>
        /// Returns true if the specified tree branch (dominated by
        /// oSyntaxNode) contains a node to be selected or highlighted (as
        /// indicated by the CurrentParseTreeNode, MatchingNodes and
        /// ReplacedNodes properties of the given oTreeTransfer object).
        /// Returns false otherwise.
        /// </summary>
        private bool BranchContainsSelectionOrHighlight(
			SyntaxNode oSyntaxNode,TreeTransfer oTreeTransfer)
        {
            Debug.Assert(oSyntaxNode != null);
            Debug.Assert(oTreeTransfer != null);

            //**************************************************************
            // Use the oTreeTransfer.CurrentParseTreeNode property to
            // determine if this node is selected.

            if (oSyntaxNode == oTreeTransfer.CurrentParseTreeNode)
            {
                return true;
            }

            //**************************************************************
            // Use the oTreeTransfer.MatchingNodes and .ReplacedNodes
            // collections to determine if this node is highlighted.

            foreach (SyntaxNodePair oNodePair in
                oTreeTransfer.MatchingNodes)
            {
                if (oSyntaxNode == oNodePair.ParseTreeNode)
                {
                    return true;
                }
            }
            foreach (SyntaxNodeTriple oNodeTriple in
                oTreeTransfer.ReplacedNodes)
            {
                if (oSyntaxNode == oNodeTriple.ParseTreeNode)
                {
                    return true;
                }
            }

            //**************************************************************
            // Search the child branches recursively.

            foreach (SyntaxNode oSyntaxChild in oSyntaxNode.ChildNodes)
            {
                if (BranchContainsSelectionOrHighlight(
                    oSyntaxChild,oTreeTransfer))
                {
                    return true;
                }
            }
            return false;
        }
        //******************************************************************
        /// <summary>
        /// Undoes the last edit operation.
        /// </summary>
        public void Undo()
        {
            //**************************************************************
            // Validate the current state.

            if (! CanUndo())
            {
                string sMessage = "Invalid state: "
                    + "A call to ParseTreeViewer.Undo() is not allowed "
                    + "if ParseTreeViewer.CanUndo() returns false.";
                throw new Exception(sMessage);
            }

            //**************************************************************
            // Prevent modification to a read-only tree.

            if (ReadOnly)
            {
                return;
            }

            //**************************************************************
            // Get the undo information and then clear the
            // TreeTransferForUndo property.

            TreeTransfer oTreeTransfer = TreeTransferForUndo;
            TreeTransferForUndo = null;

            //**************************************************************
            // Populate the tree using the undo information.

            PopulateRoot(oTreeTransfer);

            //**************************************************************
            // Set the Modified property to true and raise the TreeChanged
            // event.

            Modified = true;
            OnTreeChanged(new EventArgs());
        }
        //******************************************************************
        /// <summary>
        /// Displays the parse tree specified by the ParseTreeRoot property
        /// of the given TreeTransfer object. The TreeTransfer object also
        /// specifies the node to select (CurrentParseTreeNode property) and
        /// the nodes to highlight (in the MatchingNodes and ReplacedNodes
        /// collections).
        /// </summary>
        public void PopulateTree(TreeTransfer oTreeTransfer)
        {
            //**************************************************************
            // Validate the parameters.

            if (oTreeTransfer == null)
            {
                string sMessage = "Invalid argument: "
                    + "ParseTreeViewer.PopulateTree() requires "
                    + "a TreeTransfer object that is not null.";
                throw new Exception(sMessage);
            }

            //**************************************************************
            // Clear the undo information.

            TreeTransferForUndo = null;

            //**************************************************************
            // Populate the tree.

            PopulateRoot(oTreeTransfer);

            //**************************************************************
            // Set the Modified property to false and raise the TreeChanged
            // event.

            Modified = false;
            OnTreeChanged(new EventArgs());
        }
 //******************************************************************
 /// <summary>
 /// Sets the TreeTransferData object to null and then calls the
 /// InitializeTreeTransferData() method to reinitialize the
 /// TreeTransferData object.
 /// </summary>
 private void ResetTreeTransferData()
 {
     TreeTransferData = null;
     InitializeTreeTransferData();
 }
        //******************************************************************
        /// <summary>
        /// Uses the given oTreeNode to display features of the indicated
        /// oSyntaxNode. Children are then recursively added to oTreeNode to
        /// represent the children of oSyntaxNode (unless it is a leaf node
        /// and morphology nodes are hidden). If the optional oTreeTransfer
        /// argument is given, its CurrentParseTreeNode indicates the node
        /// to select, and items in its MatchingNodes and ReplacedNodes
        /// collections indicate nodes to highlight.
        /// </summary>
        private void PopulateBranch(TreeViewerNode oTreeNode,
			SyntaxNode oSyntaxNode,TreeTransfer oTreeTransfer)
        {
            Debug.Assert(oTreeNode != null);
            Debug.Assert(oSyntaxNode != null);
            Debug.Assert(oTreeTransfer != null);

            //**************************************************************
            // The given oTreeNode should not already have children.

            Debug.Assert(oTreeNode.ChildNodes.Count == 0);

            //**************************************************************
            // Set the node's context menu.

            oTreeNode.ContextMenu = moMenu;

            //**************************************************************
            // Use the oTreeTransfer.CurrentParseTreeNode property to
            // determine if the node is selected.

            if (oSyntaxNode == oTreeTransfer.CurrentParseTreeNode)
            {
                moTreeViewer.SelectedNode = oTreeNode;
            }

            //**************************************************************
            // Use the oTreeTransfer.MatchingNodes and .ReplacedNodes
            // collections to determine the node's background color.

            Color oForeColor = SystemColors.WindowText;
            Color oBackColor = SystemColors.Window;

            foreach (SyntaxNodePair oNodePair in
                oTreeTransfer.MatchingNodes)
            {
                if (oSyntaxNode == oNodePair.ParseTreeNode)
                {
                    oBackColor = FindPatternColor;
                }
            }
            foreach (SyntaxNodeTriple oNodeTriple in
                oTreeTransfer.ReplacedNodes)
            {
                if (oSyntaxNode == oNodeTriple.ParseTreeNode)
                {
                    oBackColor = ReplacePatternColor;
                }
            }

            if (DisplayFindPattern)
            {
                oBackColor = FindPatternColor;
            }
            if (DisplayReplacePattern)
            {
                oBackColor = ReplacePatternColor;
            }

            oTreeNode.ForeColor = oForeColor;
            oTreeNode.BackColor = oBackColor;

            //**************************************************************
            // Determine if the node's children should be shown.
            //
            // A node's children are usually shown, but if the node is a
            // leaf node and ShowMorphology is false, the leaf node's
            // children (representing morphology) are not shown (unless the
            // leaf node's children are selected or highlighted).

            bool bShowChildNodes = true;
            if (oSyntaxNode.IsSyntaxLeaf)
            {
                if (! ShowMorphology)
                {
                    bShowChildNodes = false;

                    //******************************************************
                    // Even if ShowMorphology is false, a leaf node's
                    // children are shown if any of the child branches
                    // contains a selected or highlighted node.

                    foreach (SyntaxNode oSyntaxChild in
                        oSyntaxNode.ChildNodes)
                    {
                        if (BranchContainsSelectionOrHighlight(
                            oSyntaxChild,oTreeTransfer))
                        {
                            bShowChildNodes = true;
                        }
                    }
                }
            }

            //**************************************************************
            // Display the node (showing or hiding its children).

            if (bShowChildNodes)
            {
                //**********************************************************
                // Display the node and its children.
                //
                // Clone the oSyntaxNode (without including child nodes) and
                // call PopulateNode().

                PopulateNode(oTreeNode,oSyntaxNode.CloneNode());

                //**********************************************************
                // For each child node, add a new TreeViewerNode to the tree
                // and make a recursive call to PopulateBranch().

                foreach (SyntaxNode oSyntaxChild in oSyntaxNode.ChildNodes)
                {
                    TreeViewerNode oTreeChild = new TreeViewerNode();
                    oTreeNode.ChildNodes.Add(oTreeChild);
                    PopulateBranch(oTreeChild,oSyntaxChild,oTreeTransfer);
                }
            }
            else
            {
                //**********************************************************
                // Display the node, but hide its children.
                //
                // Clone the entire oSyntaxNode branch (so the cloned branch
                // includes child nodes that represent morphology) and call
                // PopulateNode().

                PopulateNode(oTreeNode,oSyntaxNode.CloneBranch());
            }
        }
        //******************************************************************
        /// <summary>
        /// Uses the .FindPatternRoot and .ReplacePatternRoot properties of
        /// the given oSelectedRule object to populate the linked
        /// FindPatternViewer and ReplacePatternViewer. The SelectedRule
        /// property is set to the given oSelectedRule object.
        /// </summary>
        private void UpdateFindAndReplacePatternViewers(
			RuleListViewerItem oSelectedRule)
        {
            //**************************************************************
            // Use the given oSelectedRule to initialize a TreeTransfer
            // object, which will be used to populate the find-pattern and
            // replace-pattern viewers.
            //
            // If oSelectedRule is null, blank nodes will be used to
            // populate the find-pattern and replace-pattern viewers.
            //
            // If oSelectedRule is not null, its .FindPatternRoot tree and
            // .ReplacePatternRoot tree will be used to populate the
            // find-pattern and replace-pattern viewers.

            TreeTransfer oTreeTransfer = new TreeTransfer();
            if (oSelectedRule == null)
            {
                oTreeTransfer.ParseTreeRoot = null;
                oTreeTransfer.FindPatternRoot = new SyntaxNode();
                oTreeTransfer.ReplacePatternRoot = new SyntaxNode();
            }
            else
            {
                oTreeTransfer.ParseTreeRoot = null;
                oTreeTransfer.FindPatternRoot
                    = oSelectedRule.FindPatternRoot;
                oTreeTransfer.ReplacePatternRoot
                    = oSelectedRule.ReplacePatternRoot;
            }

            //**************************************************************
            // Temporarily set the SelectedRule property to null, because we
            // do not want the LinkedFindPatternViewer TreeChanged event and
            // the LinkedReplacePatternViewer TreeChanged event to update
            // the SelectedRule (and set the Modified property to true) when
            // we populate the find-pattern and replace-pattern trees.

            moSelectedRule = null;

            //**************************************************************
            // Populate the linked find-pattern viewer.

            if (LinkedFindPatternViewer != null)
            {
                LinkedFindPatternViewer.PopulateTree(oTreeTransfer);
            }

            //**************************************************************
            // Populate the linked replace-pattern viewer.

            if (LinkedReplacePatternViewer != null)
            {
                LinkedReplacePatternViewer.PopulateTree(oTreeTransfer);
            }

            //**************************************************************
            // Now set the SelectedRule property to the given oSelectedRule
            // object, because we want the TreeChanged events to update the
            // SelectedRule (and set the Modified property to true) when the
            // user edits the find-pattern and replace-pattern trees.

            moSelectedRule = oSelectedRule;
        }
        //******************************************************************
        /// <summary>
        /// Displays the parse tree specified by the ParseTreeRoot property
        /// (or the FindPatternRoot or the DisplayPatternRoot) of the given
        /// TreeTransfer object. The TreeTransfer object also specifies the
        /// node to select (CurrentParseTreeNode property) and the nodes to
        /// highlight (in the MatchingNodes and ReplacedNodes collections).
        /// This method does not change the Modified property, raise the
        /// TreeChanged event or clear the undo information.
        /// </summary>
        private void PopulateRoot(TreeTransfer oTreeTransfer)
        {
            Debug.Assert(oTreeTransfer != null);

            //**************************************************************
            // Make a private copy of the FeaturesForm.FavoriteTreeFeatures
            // collection. This private copy will be used until the
            // PopulateRoot() method is called again, so this tree will
            // display a consistent set of features when parts of the tree
            // are edited (even if the FeaturesForm collection changes).

            FavoriteTreeFeatures.Clear();
            foreach (string sName in FeaturesForm.FavoriteTreeFeatures)
            {
                FavoriteTreeFeatures.Add(sName);
            }

            //**************************************************************
            // Call BeginUpdate() to disable redrawing the tree, but not if
            // redrawing was already disabled by the calling code.

            bool bCallEndUpdate = false;
            if (! IsUpdating)
            {
                BeginUpdate();
                bCallEndUpdate = true;
            }

            //**************************************************************
            // Get the root node that specifies the tree to display.
            //
            // Display the find-pattern tree if DisplayFindPattern is true.
            // Display the replace-pattern tree if DisplayReplacePattern is
            // true. Display the parse tree otherwise.

            SyntaxNode oSyntaxRoot = oTreeTransfer.ParseTreeRoot;
            if (DisplayFindPattern)
            {
                oSyntaxRoot = oTreeTransfer.FindPatternRoot;
            }
            if (DisplayReplacePattern)
            {
                oSyntaxRoot = oTreeTransfer.ReplacePatternRoot;
            }

            //**************************************************************
            // If the specified root node is null, display a blank node.

            if (oSyntaxRoot == null)
            {
                oSyntaxRoot = new SyntaxNode();
            }

            //**************************************************************
            // Create the root TreeViewerNode and then call PopulateBranch()
            // to display the tree.

            TreeViewerNode oTreeRoot = new TreeViewerNode();
            moTreeViewer.RootNode = oTreeRoot;
            PopulateBranch(oTreeRoot,oSyntaxRoot,oTreeTransfer);

            //**************************************************************
            // If no node is selected, select the root node.

            if (moTreeViewer.SelectedNode == null)
            {
                moTreeViewer.SelectedNode = moTreeViewer.RootNode;
            }

            //**************************************************************
            // Call EndUpdate() to redraw the tree, but not if redrawing was
            // already disabled by the calling code.

            if (bCallEndUpdate)
            {
                EndUpdate();
            }
        }
        //******************************************************************
        /// <summary>
        /// Displays the find-pattern tree specified by the FindPatternRoot
        /// property of the given TreeTransfer object.
        /// </summary>
        public void PopulateTree(TreeTransfer oTreeTransfer)
        {
            //**************************************************************
            // The viewer must be set up to display find-pattern trees.

            Debug.Assert(moParseTreeViewer.DisplayFindPattern);
            Debug.Assert(moParseTreeViewer.ShowFeatures);
            Debug.Assert(moParseTreeViewer.ShowMorphology);

            //**************************************************************
            // Display the find-pattern tree.

            moParseTreeViewer.PopulateTree(oTreeTransfer);
        }
        //******************************************************************
        /// <summary>
        /// Returns a new TreeTransfer object that can be used to repopulate
        /// the tree. This TreeTransfer object points to a cloned copy of
        /// the current state of the tree. It also specifies the node to
        /// select (CurrentParseTreeNode property) and the nodes to
        /// highlight (in the MatchingNodes and ReplacedNodes collections).
        /// </summary>
        private TreeTransfer RecreateTreeTransfer()
        {
            //**************************************************************
            // Create a new TreeTransfer object.

            TreeTransfer oTreeTransfer = new TreeTransfer();

            //**************************************************************
            // Set the TreeTransfer properties to represent the current
            // state of the tree.

            if (moTreeViewer.RootNode != null)
            {
                //**********************************************************
                // Clone the whole tree.

                SyntaxNode oSyntaxRoot
                    = CloneBranch(moTreeViewer.RootNode,oTreeTransfer);

                //**********************************************************
                // Copy the CurrentParseTreeNode property and the
                // MatchingNodes and ReplacedNodes collections (since these
                // may be cleared when the ParseTreeRoot, FindPatternRoot
                // and ReplacePatternRoot properties of the TreeTransfer
                // object are set).

                SyntaxNode oCurrentParseTreeNode
                    = oTreeTransfer.CurrentParseTreeNode;

                SyntaxNodePairStack oMatchingNodes
                    = new SyntaxNodePairStack();
                foreach (SyntaxNodePair oNodePair in
                    oTreeTransfer.MatchingNodes)
                {
                    oMatchingNodes.Push(oNodePair);
                }

                SyntaxNodeTripleStack oReplacedNodes
                    = new SyntaxNodeTripleStack();
                foreach (SyntaxNodeTriple oNodeTriple in
                    oTreeTransfer.ReplacedNodes)
                {
                    oReplacedNodes.Push(oNodeTriple);
                }

                //**********************************************************
                // If the parse tree is displayed, set the ParseTreeRoot
                // property to the root of the cloned tree.
                //
                // If the find-pattern tree is displayed, set the
                // FindPatternRoot property to the root of the cloned tree.
                //
                // If the replace-pattern tree is displayed, set the
                // ReplacePatternRoot property to the root of the cloned
                // tree.

                oTreeTransfer.ParseTreeRoot = oSyntaxRoot;
                oTreeTransfer.FindPatternRoot = null;
                oTreeTransfer.ReplacePatternRoot = null;
                if (DisplayFindPattern)
                {
                    oTreeTransfer.ParseTreeRoot = null;
                    oTreeTransfer.FindPatternRoot = oSyntaxRoot;
                }
                if (DisplayReplacePattern)
                {
                    oTreeTransfer.ParseTreeRoot = null;
                    oTreeTransfer.ReplacePatternRoot = oSyntaxRoot;
                }

                //**********************************************************
                // Set the CurrentParseTreeNode property and the items in
                // the MatchingNodes and ReplacedNodes collections, using
                // the values that were copied earlier.

                oTreeTransfer.CurrentParseTreeNode = oCurrentParseTreeNode;

                oTreeTransfer.MatchingNodes.Clear();
                foreach (SyntaxNodePair oNodePair in oMatchingNodes)
                {
                    oTreeTransfer.MatchingNodes.Push(oNodePair);
                }

                oTreeTransfer.ReplacedNodes.Clear();
                foreach (SyntaxNodeTriple oNodeTriple in oReplacedNodes)
                {
                    oTreeTransfer.ReplacedNodes.Push(oNodeTriple);
                }
            }

            //**************************************************************
            // Return the TreeTransfer object.

            return oTreeTransfer;
        }
        //******************************************************************
        /// <summary>
        /// Initializes a new instance of the ReplaceAlgorithm class, which
        /// will access the tree data through the given TreeTransfer object.
        /// </summary>
        public ReplaceAlgorithm(TreeTransfer oTreeTransfer)
        {
            Debug.Assert(oTreeTransfer != null);

            moTreeTransfer = oTreeTransfer;
        }
        //******************************************************************
        /// <summary>
        /// Deletes the current selection.
        /// </summary>
        public void Delete()
        {
            //**************************************************************
            // Validate the current state.

            if (! CanDelete())
            {
                string sMessage = "Invalid state: "
                    + "A call to ParseTreeViewer.Delete() is not allowed "
                    + "if ParseTreeViewer.CanDelete() returns false.";
                throw new Exception(sMessage);
            }

            //**************************************************************
            // Prevent modification to a read-only tree.

            if (ReadOnly)
            {
                return;
            }

            //**************************************************************
            // Use the current contents of the tree (before the delete
            // operation) to set the undo information.

            TreeTransferForUndo = RecreateTreeTransfer();

            //**************************************************************
            // Call BeginUpdate() to disable redrawing the tree, but not if
            // redrawing was already disabled by the calling code.

            bool bCallEndUpdate = false;
            if (! IsUpdating)
            {
                BeginUpdate();
                bCallEndUpdate = true;
            }

            //**************************************************************
            // Delete the branch dominated by the selected node.

            if (moTreeViewer.SelectedNode != null)
            {
                if (moTreeViewer.SelectedNode == moTreeViewer.RootNode)
                {
                    //******************************************************
                    // The selected node is the root node.
                    //
                    // Replace the root node with an empty node. Select the
                    // new root node.

                    moTreeViewer.RootNode = new TreeViewerNode();
                    SyntaxNode oSyntaxNode = new SyntaxNode();
                    PopulateBranch(moTreeViewer.RootNode,oSyntaxNode);
                    moTreeViewer.SelectedNode = moTreeViewer.RootNode;
                }
                else
                {
                    //******************************************************
                    // The selected node is not the root node.
                    //
                    // Remove the selected node. Select the parent of the
                    // removed node.

                    TreeViewerNode oTreeParent
                        = moTreeViewer.SelectedNode.ParentNode;
                    if (oTreeParent != null)
                    {
                        oTreeParent.ChildNodes
                            .Remove(moTreeViewer.SelectedNode);
                        moTreeViewer.SelectedNode = oTreeParent;
                    }
                }
            }

            //**************************************************************
            // Call EndUpdate() to redraw the tree, but not if redrawing was
            // already disabled by the calling code.

            if (bCallEndUpdate)
            {
                EndUpdate();
            }

            //**************************************************************
            // Set the Modified property to true and raise the TreeChanged
            // event.

            Modified = true;
            OnTreeChanged(new EventArgs());
        }
        //******************************************************************
        /// <summary>
        /// Initializes a new instance of the ReplaceAlgorithm class, which
        /// will access the tree data through the given TreeTransfer object.
        /// </summary>
        public ReplaceAlgorithm(TreeTransfer oTreeTransfer)
        {
            Debug.Assert(oTreeTransfer != null);

            moTreeTransfer = oTreeTransfer;
        }
Exemple #23
0
        //******************************************************************
        /// <summary>
        /// The timer is fired once after the form is loaded, and this event
        /// handler performs the tree-transfer processing. It uses the
        /// collection of transfer rules to process the parse trees in the
        /// input file, and it writes the modified parse trees to the output
        /// file.
        /// </summary>
        private void moTimer_Tick(object oSender,EventArgs oArgs)
        {
            ParseReader oParseReader = null;
            ParseWriter oParseWriter = null;
            try
            {
                //**********************************************************
                // Stop the timer (so it only fires once after the form is
                // loaded).

                moTimer.Stop();

                //**********************************************************
                // Initialize the information displayed on the form.

                Text = "Processing Parse File...";

                moInputFileTextBox.Text = InputFileName;
                moOutputFileTextBox.Text = OutputFileName;
                moRuleCountTextBox.Text = TransferRules.Count.ToString();
                moParseCountTextBox.Text = "0";

                moInputFileTextBox.Select(
                    moInputFileTextBox.Text.Length,0);
                moOutputFileTextBox.Select(
                    moOutputFileTextBox.Text.Length,0);
                moRuleCountTextBox.Select(
                    moRuleCountTextBox.Text.Length,0);
                moParseCountTextBox.Select(
                    moParseCountTextBox.Text.Length,0);

                moParseCountTextBox.Focus();

                moOKButton.Enabled = false;
                moCancelButton.Enabled = true;

                //**********************************************************
                // Make sure the form is visible.

                Show();
                Application.DoEvents();

                //**********************************************************
                // Open the input and output files.

                StreamReader oStreamReader
                    = new StreamReader(InputFileName);
                oParseReader = new ParseReader(oStreamReader);

                StreamWriter oStreamWriter
                    = new StreamWriter(OutputFileName);
                oParseWriter = new ParseWriter(oStreamWriter);

                //**********************************************************
                // Read and process each parse tree from the input file.

                int iCount = 0;
                ProcessingWasCanceled = false;
                while ((oParseReader.Read()) && (! ProcessingWasCanceled))
                {
                    //******************************************************
                    // Create a TreeTransfer object and set its
                    // ParseTreeRoot to the parse tree that was read from
                    // the input file.

                    TreeTransfer oTransfer = new TreeTransfer();
                    oTransfer.ParseTreeRoot = oParseReader.ParseTreeRoot;

                    //******************************************************
                    // Apply each rule in the TransferRules collection.

                    foreach (TransferRule oRule in TransferRules)
                    {
                        oTransfer.FindPatternRoot
                            = oRule.FindPatternRoot;
                        oTransfer.ReplacePatternRoot
                            = oRule.ReplacePatternRoot;

                        //**************************************************
                        // Apply the rule to each matching branch.

                        oTransfer.CurrentParseTreeNode = null;
                        while (oTransfer.FindNextMatchingBranch())
                        {
                            oTransfer.ReplaceCurrentMatchingBranch();
                        }
                    }

                    //******************************************************
                    // Write the modified parse tree to the output file.

                    oParseWriter.ParseTreeRoot = oTransfer.ParseTreeRoot;
                    oParseWriter.Write();

                    //******************************************************
                    // Update the information displayed on the form.

                    ++iCount;
                    moParseCountTextBox.Text = iCount.ToString();
                    moParseCountTextBox.Select(
                        moParseCountTextBox.Text.Length,0);

                    //******************************************************
                    // Call DoEvents() so the form can update the display
                    // and check if the user clicked the Cancel button.

                    Application.DoEvents();
                }

                //**********************************************************
                // Close the input and output files.

                oParseReader.Close();
                oParseReader = null;
                oParseWriter.Close();
                oParseWriter = null;

                //**********************************************************
                // Update the final information displayed on the form.

                if (ProcessingWasCanceled)
                {
                    Text = "Processing Parse File... Canceled";
                }
                else
                {
                    Text = "Processing Parse File... Finished";
                }

                moOKButton.Enabled = true;
                moCancelButton.Enabled = false;
                moOKButton.Focus();

                //**********************************************************
                // If the Cancel button was clicked, set DialogResult to
                // DialogResult.Cancel, which will close the form.
                //
                // If the Cancel button was not clicked, but the
                // CloseWhenFinished property is true, set DialogResult to
                // DialogResult.OK, which will close the form.

                if (ProcessingWasCanceled)
                {
                    DialogResult = DialogResult.Cancel;
                }
                else
                {
                    if (CloseWhenFinished)
                    {
                        DialogResult = DialogResult.OK;
                    }
                }
            }
            catch (Exception oException)
            {
                ShowException(oException);
                ProcessingWasCanceled = true;
            }
            try
            {
                //**********************************************************
                // Make sure the output file is closed (even if an exception
                // was thrown).

                if (oParseWriter != null)
                {
                    oParseWriter.Close();
                    oParseWriter = null;
                }
            }
            catch (Exception oException)
            {
                ShowException(oException);
                ProcessingWasCanceled = true;
            }
            try
            {
                //**********************************************************
                // Make sure the OK button is enabled (even if an exception
                // was thrown).

                moOKButton.Enabled = true;
                moCancelButton.Enabled = false;
                moOKButton.Focus();
            }
            catch (Exception oException)
            {
                ShowException(oException);
                ProcessingWasCanceled = true;
            }
            try
            {
                //**********************************************************
                // If the Cancel button was clicked (or an exception was
                // thrown), make sure DialogResult is set to
                // DialogResult.Cancel, which will close the form.

                if (ProcessingWasCanceled)
                {
                    DialogResult = DialogResult.Cancel;
                }
            }
            catch (Exception oException)
            {
                ShowException(oException);
            }
        }