File: System\Xml\Xsl\QIL\QilCloneVisitor.cs
Web Access
Project: src\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Xml;
using System.Xml.Xsl;
 
namespace System.Xml.Xsl.Qil
{
    // Create an exact replica of a QIL graph
    internal class QilCloneVisitor : QilScopedVisitor
    {
        private readonly QilFactory _fac;
        private readonly SubstitutionList _subs;
 
 
        //-----------------------------------------------
        // Constructors
        //-----------------------------------------------
 
        public QilCloneVisitor(QilFactory fac) : this(fac, new SubstitutionList())
        {
        }
 
        public QilCloneVisitor(QilFactory fac, SubstitutionList subs)
        {
            _fac = fac;
            _subs = subs;
        }
 
 
        //-----------------------------------------------
        // Entry
        //-----------------------------------------------
 
        public QilNode Clone(QilNode node)
        {
            QilDepthChecker.Check(node);
            // Assume that iterator nodes at the top-level are references rather than definitions
            return VisitAssumeReference(node);
        }
 
 
        //-----------------------------------------------
        // QilVisitor overrides
        //-----------------------------------------------
 
        /// <summary>
        /// Visit all children of "parent", replacing each child with a copy of each child.
        /// </summary>
        protected override QilNode Visit(QilNode oldNode)
        {
            QilNode? newNode = null;
 
            if (oldNode == null)
                return null!;
 
            // ShallowClone any nodes which have not yet been cloned
            if (oldNode is QilReference)
            {
                // Reference nodes may have been cloned previously and put into scope
                newNode = FindClonedReference(oldNode);
            }
 
            newNode ??= oldNode.ShallowClone(_fac);
 
            return base.Visit(newNode);
        }
 
        /// <summary>
        /// Visit all children of "parent", replacing each child with a copy of each child.
        /// </summary>
        protected override QilNode VisitChildren(QilNode parent)
        {
            // Visit children
            for (int i = 0; i < parent.Count; i++)
            {
                QilNode child = parent[i];
 
                // If child is a reference,
                if (IsReference(parent, i))
                {
                    // Visit the reference and substitute its copy
                    parent[i] = VisitReference(child);
 
                    // If no substutition found, then use original child
                    parent[i] ??= child;
                }
                else
                {
                    // Otherwise, visit the node and substitute its copy
                    parent[i] = Visit(child)!;
                }
            }
 
            return parent;
        }
 
        /// <summary>
        /// If a cloned reference is in scope, replace "oldNode".  Otherwise, return "oldNode".
        /// </summary>
        protected override QilNode VisitReference(QilNode oldNode)
        {
            QilNode? newNode = FindClonedReference(oldNode);
            return base.VisitReference(newNode ?? oldNode);
        }
 
 
        //-----------------------------------------------
        // QilScopedVisitor methods
        //-----------------------------------------------
 
        /// <summary>
        /// Push node and its shallow clone onto the substitution list.
        /// </summary>
        protected override void BeginScope(QilNode node)
        {
            _subs.AddSubstitutionPair(node, node.ShallowClone(_fac));
        }
 
        /// <summary>
        /// Pop entry from substitution list.
        /// </summary>
        protected override void EndScope(QilNode node)
        {
            _subs.RemoveLastSubstitutionPair();
        }
 
 
        //-----------------------------------------------
        // QilCloneVisitor methods
        //-----------------------------------------------
 
        /// <summary>
        /// Find the clone of an in-scope reference.
        /// </summary>
        protected QilNode? FindClonedReference(QilNode node)
        {
            return _subs.FindReplacement(node);
        }
    }
}