File: System\Data\Query\InternalTrees\Dump.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="Dump.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner  Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
 
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Query.PlanCompiler;
using System.Globalization;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Xml;
using md = System.Data.Metadata.Edm;
 
//
// This module serves as a dump routine for an IQT
// The output is a weird form of Sql - closer to Quel (and perhaps, C#
// comprehensions)
//
namespace System.Data.Query.InternalTrees {
 
    /// <summary>
    /// A dump module for the Iqt
    /// </summary>
    internal class Dump : BasicOpVisitor, IDisposable {
 
        #region private state
 
        private XmlWriter _writer;
 
        #endregion
 
        #region constructors
 
        private Dump(Stream stream)
            : this(stream, Dump.DefaultEncoding, true) { }
 
        private Dump(Stream stream, Encoding encoding, bool indent)
            : base() {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.CheckCharacters = false;
            settings.Indent = true;
            settings.Encoding = encoding;
            _writer = XmlWriter.Create(stream, settings);
            _writer.WriteStartDocument(true);
        }
 
        #endregion
 
        #region "public" surface
 
        internal static readonly Encoding DefaultEncoding = Encoding.UTF8;
 
        /// <summary>
        /// Driver method to dump the entire tree
        /// </summary>
        /// <param name="itree"></param>
        /// <returns></returns>
        static internal string ToXml(Command itree) {
            return ToXml(itree, itree.Root);
        }
 
        /// <summary>
        /// Driver method to dump the a subtree of a tree
        /// </summary>
        /// <param name="itree"></param>
        /// <param name="subtree"></param>
        /// <returns></returns>
        static internal string ToXml(Command itree, Node subtree) {
            MemoryStream stream = new MemoryStream();
 
            using (Dump dumper = new Dump(stream)) {
                // Just in case the node we're provided doesn't dump as XML, we'll always stick
                // an XML wrapper around it -- this happens when we're dumping scalarOps, for
                // example, and it's unfortunate if you can't debug them using a dump...
                using (new AutoXml(dumper, "nodes")) {
                    dumper.VisitNode(subtree);
                }
            }
 
            return DefaultEncoding.GetString(stream.ToArray());
        }
 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "System.Data.Query.InternalTrees.Dump.ToXml")]
        static internal string ToXml(ColumnMap columnMap) {
            MemoryStream stream = new MemoryStream();
 
            using (Dump dumper = new Dump(stream)) {
                // Just in case the node we're provided doesn't dump as XML, we'll always stick
                // an XML wrapper around it -- this happens when we're dumping scalarOps, for
                // example, and it's unfortunate if you can't debug them using a dump...
                using (new AutoXml(dumper, "columnMap")) {
                    columnMap.Accept(ColumnMapDumper.Instance, dumper);
                }
            }
 
            return DefaultEncoding.GetString(stream.ToArray());
        }
 
        #endregion
 
        #region Begin/End management
 
        void IDisposable.Dispose() {
            // Technically, calling GC.SuppressFinalize is not required because the class does not
            // have a finalizer, but it does no harm, protects against the case where a finalizer is added
            // in the future, and prevents an FxCop warning.
            GC.SuppressFinalize(this);
            try {
                _writer.WriteEndDocument();
                _writer.Flush();
                _writer.Close();
            }
            catch (Exception e) {
                if (!EntityUtil.IsCatchableExceptionType(e)) {
                    throw;
                }
                // eat this exception; we don't care if the dumper is failing...
            }
        }
 
        internal void Begin(string name, Dictionary<string, object> attrs) {
            _writer.WriteStartElement(name);
            if (attrs != null) {
                foreach (KeyValuePair<string, object> attr in attrs) {
                    _writer.WriteAttributeString(attr.Key, attr.Value.ToString());
                }
            }
        }
 
        internal void BeginExpression() {
            WriteString("(");
        }
 
        internal void EndExpression() {
            WriteString(")");
        }
 
        internal void End(string name) {
            _writer.WriteEndElement();
        }
 
        internal void WriteString(string value) {
            _writer.WriteString(value);
        }
 
        #endregion
 
        #region VisitorMethods
 
        protected override void VisitDefault(Node n) {
            using (new AutoXml(this, n.Op)) {
                base.VisitDefault(n);
            }
        }
 
        protected override void VisitScalarOpDefault(ScalarOp op, Node n) {
            using (new AutoString(this, op)) {
                string separator = string.Empty;
                foreach (Node chi in n.Children) {
                    WriteString(separator);
                    VisitNode(chi);
                    separator = ",";
                }
            }
        }
 
        protected override void VisitJoinOp(JoinBaseOp op, Node n) {
            using (new AutoXml(this, op)) {
                if (n.Children.Count > 2) {
                    using (new AutoXml(this, "condition")) {
                        VisitNode(n.Child2);
                    }
                }
                using (new AutoXml(this, "input")) {
                    VisitNode(n.Child0);
                }
                using (new AutoXml(this, "input")) {
                    VisitNode(n.Child1);
                }
            }
        }
 
        public override void Visit(CaseOp op, Node n) {
            using (new AutoXml(this, op)) {
                int i = 0;
                while (i < n.Children.Count) {
                    if ((i + 1) < n.Children.Count) {
                        using (new AutoXml(this, "when")) {
                            VisitNode(n.Children[i++]);
                        }
                        using (new AutoXml(this, "then")) {
                            VisitNode(n.Children[i++]);
                        }
                    }
                    else {
                        using (new AutoXml(this, "else")) {
                            VisitNode(n.Children[i++]);
                        }
                    }
                }
            }
        }
 
        public override void Visit(CollectOp op, Node n) {
            using (new AutoXml(this, op)) {
                VisitChildren(n);
            }
        }
 
        protected override void VisitConstantOp(ConstantBaseOp op, Node n) {
            using (new AutoString(this, op)) {
                if (null == op.Value) {
                    WriteString("null");
                }
                else {
                    WriteString("(");
                    WriteString(op.Type.EdmType.FullName);
                    WriteString(")");
                    WriteString(String.Format(CultureInfo.InvariantCulture,"{0}",op.Value));
                }
                VisitChildren(n);
            }
        }
 
        public override void Visit(DistinctOp op, Node n) {
            Dictionary<string, object> attrs = new Dictionary<string, object>();
 
            StringBuilder sb = new StringBuilder();
            string separator = string.Empty;
 
            foreach (Var v in op.Keys) {
                sb.Append(separator);
                sb.Append(v.Id);
                separator = ",";
            }
            if (0 != sb.Length) {
                attrs.Add("Keys", sb.ToString());
            }
 
            using (new AutoXml(this, op, attrs)) {
                VisitChildren(n);
            }
        }
 
        protected override void VisitGroupByOp(GroupByBaseOp op, Node n) {
            Dictionary<string, object> attrs = new Dictionary<string, object>();
 
            StringBuilder sb = new StringBuilder();
            string separator = string.Empty;
 
            foreach (Var v in op.Keys) {
                sb.Append(separator);
                sb.Append(v.Id);
                separator = ",";
            }
            if (0 != sb.Length) {
                attrs.Add("Keys", sb.ToString());
            }
 
            using (new AutoXml(this, op, attrs)) {
                using (new AutoXml(this, "outputs")) {
                    foreach (Var v in op.Outputs) {
                        DumpVar(v);
                    }
                }
                VisitChildren(n);
            }
        }
 
        public override void Visit(IsOfOp op, Node n)
        {
            using (new AutoXml(this, ( op.IsOfOnly ? "IsOfOnly" : "IsOf" )))
            {
                string separator = string.Empty;
                foreach (Node chi in n.Children)
                {
                    WriteString(separator);
                    VisitNode(chi);
                    separator = ",";
                }
            }
        }
 
        protected override void VisitNestOp(NestBaseOp op, Node n) {
            Dictionary<string, object> attrs = new Dictionary<string, object>();
 
            SingleStreamNestOp ssnOp = op as SingleStreamNestOp;
            if (null != ssnOp) {
                attrs.Add("Discriminator", (ssnOp.Discriminator == null) ? "<null>" : ssnOp.Discriminator.ToString());
            }
 
            StringBuilder sb = new StringBuilder();
            string separator;
 
            if (null != ssnOp) {
                sb.Length = 0;
                separator = string.Empty;
                foreach (Var v in ssnOp.Keys) {
                    sb.Append(separator);
                    sb.Append(v.Id);
                    separator = ",";
                }
                if (0 != sb.Length) {
                    attrs.Add("Keys", sb.ToString());
                }
            }
 
            using (new AutoXml(this, op, attrs)) {
                using (new AutoXml(this, "outputs")) {
                    foreach (Var v in op.Outputs) {
                        DumpVar(v);
                    }
                }
                foreach (CollectionInfo ci in op.CollectionInfo) {
                    Dictionary<string, object> attrs2 = new Dictionary<string, object>();
                    attrs2.Add("CollectionVar", ci.CollectionVar);
 
                    if (null != ci.DiscriminatorValue) {
                        attrs2.Add("DiscriminatorValue", ci.DiscriminatorValue);
                    }
                    if (0 != ci.FlattenedElementVars.Count) {
                        attrs2.Add("FlattenedElementVars", FormatVarList(sb, ci.FlattenedElementVars));
                    }
                    if (0 != ci.Keys.Count) {
                        attrs2.Add("Keys", ci.Keys);
                    }
                    if (0 != ci.SortKeys.Count) {
                        attrs2.Add("SortKeys", FormatVarList(sb, ci.SortKeys));
                    } 
                    using (new AutoXml(this, "collection", attrs2)) {
                        ci.ColumnMap.Accept(ColumnMapDumper.Instance, this);
                    }
                }
                VisitChildren(n);
            }
        }
 
        private static string FormatVarList(StringBuilder sb, VarList varList) {
            string separator;
            sb.Length = 0;
            separator = string.Empty;
            foreach (Var v in varList) {
                sb.Append(separator);
                sb.Append(v.Id);
                separator = ",";
            }
            return sb.ToString();
        }
 
        private static string FormatVarList(StringBuilder sb, List<SortKey> varList) {
            string separator;
            sb.Length = 0;
            separator = string.Empty;
            foreach (SortKey v in varList) {
                sb.Append(separator);
                sb.Append(v.Var.Id);
                separator = ",";
            }
            return sb.ToString();
        }
 
        private void VisitNewOp(Op op, Node n) {
            using (new AutoXml(this, op)) {
                foreach (Node chi in n.Children) {
                    using (new AutoXml(this, "argument", null)) {
                        VisitNode(chi);
                    }
                }
            }
        }
        public override void Visit(NewEntityOp op, Node n) {
            VisitNewOp(op, n);
        }
        public override void Visit(NewInstanceOp op, Node n) {
            VisitNewOp(op, n);
        }
 
        public override void Visit(DiscriminatedNewEntityOp op, Node n) {
            VisitNewOp(op, n);
        }
 
        public override void Visit(NewMultisetOp op, Node n) {
            VisitNewOp(op, n);
        }
 
        public override void Visit(NewRecordOp op, Node n) {
            VisitNewOp(op, n);
        }
 
        public override void Visit(PhysicalProjectOp op, Node n) {
            using (new AutoXml(this, op)) {
                using (new AutoXml(this, "outputs")) {
                    foreach (Var v in op.Outputs) {
                        DumpVar(v);
                    }
                }
                using (new AutoXml(this, "columnMap")) {
                    op.ColumnMap.Accept(ColumnMapDumper.Instance, this);
                }
                using (new AutoXml(this, "input")) {
                    VisitChildren(n);
                }
            }
        }
        
        public override void Visit(ProjectOp op, Node n) {
 
            using (new AutoXml(this, op)) {
                using (new AutoXml(this, "outputs")) {
                    foreach (Var v in op.Outputs) {
                        DumpVar(v);
                    }
                }
                VisitChildren(n);
            }
        }
 
        public override void Visit(PropertyOp op, Node n) {
            using (new AutoString(this, op)) {
                VisitChildren(n);
                WriteString(".");
                WriteString(op.PropertyInfo.Name);
            }
        }
        public override void Visit(RelPropertyOp op, Node n) {
            using (new AutoString(this, op)) {
                VisitChildren(n);
                WriteString(".NAVIGATE(");
                WriteString(op.PropertyInfo.Relationship.Name);
                WriteString(",");
                WriteString(op.PropertyInfo.FromEnd.Name);
                WriteString(",");
                WriteString(op.PropertyInfo.ToEnd.Name);
                WriteString(")");
            }
        }
 
        public override void Visit(ScanTableOp op, Node n) {
            using (new AutoXml(this, op)) {
                DumpTable(op.Table);
                VisitChildren(n);
            }
        }
 
 
        public override void Visit(ScanViewOp op, Node n) {
            using (new AutoXml(this, op)) {
                DumpTable(op.Table);
                VisitChildren(n);
            }
        }
 
        protected override void VisitSetOp(SetOp op, Node n) {
            Dictionary<string, object> attrs = new Dictionary<string, object>();
            if (OpType.UnionAll == op.OpType) {
                UnionAllOp uallOp = (UnionAllOp)op;
                if (null != uallOp.BranchDiscriminator) {
                    attrs.Add("branchDiscriminator", uallOp.BranchDiscriminator);
                }
            }
            using (new AutoXml(this, op, attrs)) {
                using (new AutoXml(this, "outputs")) {
                    foreach (Var v in op.Outputs) {
                        DumpVar(v);
                    }
                } 
                int i = 0;
                foreach (Node chi in n.Children) {
                    Dictionary<string, object> attrs2 = new Dictionary<string, object>();
                    attrs2.Add("VarMap", op.VarMap[i++].ToString());
 
                    using (new AutoXml(this, "input", attrs2)) {
                        VisitNode(chi);
                    }
                }
            }
        }
 
        public override void Visit(SortOp op, Node n) {
            using (new AutoXml(this, op)) {
                base.Visit(op, n);
            }
        }
 
        public override void Visit(ConstrainedSortOp op, Node n) {
            Dictionary<string, object> attrs = new Dictionary<string, object>();
            attrs.Add("WithTies", op.WithTies);
            using (new AutoXml(this, op, attrs)) {
                base.Visit(op, n);
            }
        }
 
        protected override void VisitSortOp(SortBaseOp op, Node n)
        {
            using (new AutoXml(this, "keys")) {
                foreach (InternalTrees.SortKey sortKey in op.Keys) {
                    Dictionary<string, object> attrs = new Dictionary<string, object>();
                    attrs.Add("Var", sortKey.Var);
                    attrs.Add("Ascending", sortKey.AscendingSort);
                    attrs.Add("Collation", sortKey.Collation);
 
                    using (new AutoXml(this, "sortKey", attrs)) {
                    }
                }
            }
            VisitChildren(n);
        }
 
        public override void Visit(UnnestOp op, Node n) {
            Dictionary<string, object> attrs = new Dictionary<string, object>();
            if (null != op.Var) {
                attrs.Add("Var", op.Var.Id);
            }
            using (new AutoXml(this, op, attrs)) {
                DumpTable(op.Table);
                VisitChildren(n);
            }
        }
 
        public override void Visit(VarDefOp op, Node n) {
            Dictionary<string, object> attrs = new Dictionary<string, object>();
            attrs.Add("Var", op.Var.Id);
 
            using (new AutoXml(this, op, attrs)) {
                VisitChildren(n);
            }
        }
 
        public override void Visit(VarRefOp op, Node n) {
            using (new AutoString(this, op)) {
                VisitChildren(n);
                if (null != op.Type) {
                    WriteString("Type=");
                    WriteString(TypeHelpers.GetFullName(op.Type));
                    WriteString(", ");
                }
                WriteString("Var=");
                WriteString(op.Var.Id.ToString(CultureInfo.InvariantCulture));
            }
        }
        #endregion
 
        #region dumper helpers 
 
        private void DumpVar(Var v) {
            Dictionary<string, object> attrs = new Dictionary<string, object>();
 
            attrs.Add("Var", v.Id);
            ColumnVar cv = v as ColumnVar;
            if (null != cv) {
                attrs.Add("Name", cv.ColumnMetadata.Name);
                attrs.Add("Type", TypeHelpers.GetFullName(cv.ColumnMetadata.Type));
            }
            using (new AutoXml(this, v.GetType().Name, attrs)) {
            }
        }
 
        private void DumpVars(List<Var> vars) {
            foreach (Var v in vars) {
                DumpVar(v);
            }
        }
 
        private void DumpTable(Table table) {
            Dictionary<string, object> attrs = new Dictionary<string, object>();
 
            attrs.Add("Table", table.TableId);
            if (null != table.TableMetadata.Extent) {
                attrs.Add("Extent", table.TableMetadata.Extent.Name);
            }
 
            using (new AutoXml(this, "Table", attrs)) {
                DumpVars(table.Columns);
            }
        }
 
        #region ColumnMap dumper
        internal class ColumnMapDumper : ColumnMapVisitor<Dump> {
 
            static internal ColumnMapDumper Instance = new ColumnMapDumper();
 
            /// <summary>
            /// Private constructor
            /// </summary>
            private ColumnMapDumper() 
            { 
            }
 
            #region Helpers
            /// <summary>
            /// Common CollectionColumnMap code
            /// </summary>
            private void DumpCollection(CollectionColumnMap columnMap, Dump dumper) {
                if (columnMap.ForeignKeys.Length > 0) {
                    using (new AutoXml(dumper, "foreignKeys")) {
                        VisitList(columnMap.ForeignKeys, dumper);
                    }
                }
                if (columnMap.Keys.Length > 0) {
                    using (new AutoXml(dumper, "keys")) {
                        VisitList(columnMap.Keys, dumper);
                    }
                } 
                using (new AutoXml(dumper, "element")) {
                    columnMap.Element.Accept(this, dumper);
                }
            }
 
 
            /// <summary>
            /// Common code to produce an the attributes for the dumper's XML node
            /// </summary>
            /// <param name="columnMap"></param>
            /// <returns></returns>
            private static Dictionary<string, object> GetAttributes(ColumnMap columnMap) {
                Dictionary<string, object> attrs = new Dictionary<string, object>();
                attrs.Add("Type", columnMap.Type.ToString());
                return attrs;
            }
 
            #endregion
 
            /// <summary>
            /// ComplexTypeColumnMap
            /// </summary>
            /// <param name="columnMap"></param>
            /// <param name="dumper"></param>
            /// <returns></returns>
            internal override void Visit(ComplexTypeColumnMap columnMap, Dump dumper) {
                using (new AutoXml(dumper, "ComplexType", GetAttributes(columnMap))) {
                    if (columnMap.NullSentinel != null) {
                        using (new AutoXml(dumper, "nullSentinel")) {
                            columnMap.NullSentinel.Accept(this, dumper);
                        }
                    }
                    VisitList(columnMap.Properties, dumper);
                }
            }
 
            /// <summary>
            /// DiscriminatedCollectionColumnMap
            /// </summary>
            /// <param name="columnMap"></param>
            /// <param name="dumper"></param>
            /// <returns></returns>
            internal override void Visit(DiscriminatedCollectionColumnMap columnMap, Dump dumper) {
                using (new AutoXml(dumper, "DiscriminatedCollection", GetAttributes(columnMap))) {
                    Dictionary<string, object> attrs = new Dictionary<string,object>();
                    attrs.Add("Value", columnMap.DiscriminatorValue);
 
                    using (new AutoXml(dumper, "discriminator", attrs)) {
                        columnMap.Discriminator.Accept(this, dumper);
                    }
                    DumpCollection(columnMap, dumper);
                }
            }
 
            /// <summary>
            /// EntityColumnMap
            /// </summary>
            /// <param name="columnMap"></param>
            /// <param name="dumper"></param>
            /// <returns></returns>
            internal override void Visit(EntityColumnMap columnMap, Dump dumper) {
                using (new AutoXml(dumper, "Entity", GetAttributes(columnMap))) {
                    using (new AutoXml(dumper, "entityIdentity")) {
                        VisitEntityIdentity(columnMap.EntityIdentity, dumper);
                    }
                    VisitList(columnMap.Properties, dumper);
                }
            }
 
            /// <summary>
            /// PolymorphicColumnMap
            /// </summary>
            /// <param name="columnMap"></param>
            /// <param name="dumper"></param>
            /// <returns></returns>
            internal override void Visit(SimplePolymorphicColumnMap columnMap, Dump dumper) {
                using (new AutoXml(dumper, "SimplePolymorphic", GetAttributes(columnMap))) {
                    using (new AutoXml(dumper, "typeDiscriminator")) {
                        columnMap.TypeDiscriminator.Accept(this, dumper);
                    }
                    Dictionary<string, object> attrs = new Dictionary<string, object>();
                    foreach (KeyValuePair<object, TypedColumnMap> tc in columnMap.TypeChoices) {
                        attrs.Clear();
                        attrs.Add("DiscriminatorValue", tc.Key);
                        using (new AutoXml(dumper, "choice", attrs)) {
                            tc.Value.Accept(this, dumper);
                        }
                    }
                    using (new AutoXml(dumper, "default")) {
                        VisitList(columnMap.Properties, dumper);
                    }
                }
            }
 
            /// <summary>
            /// MultipleDiscriminatorPolymorphicColumnMap
            /// </summary>
            internal override void Visit(MultipleDiscriminatorPolymorphicColumnMap columnMap, Dump dumper) {
                using (new AutoXml(dumper, "MultipleDiscriminatorPolymorphic", GetAttributes(columnMap))) {
                    using (new AutoXml(dumper, "typeDiscriminators")) {
                        VisitList(columnMap.TypeDiscriminators, dumper);
                    }
                    Dictionary<string, object> attrs = new Dictionary<string, object>();
                    foreach (var tc in columnMap.TypeChoices) {
                        attrs.Clear();
                        attrs.Add("EntityType", tc.Key);
                        using (new AutoXml(dumper, "choice", attrs)) {
                            tc.Value.Accept(this, dumper);
                        }
                    }
                    using (new AutoXml(dumper, "default")) {
                        VisitList(columnMap.Properties, dumper);
                    }
                }
            }
 
            /// <summary>
            /// RecordColumnMap
            /// </summary>
            /// <param name="columnMap"></param>
            /// <param name="dumper"></param>
            /// <returns></returns>
            internal override void Visit(RecordColumnMap columnMap, Dump dumper) {
                using (new AutoXml(dumper, "Record", GetAttributes(columnMap))) {
                    if (columnMap.NullSentinel != null) {
                        using (new AutoXml(dumper, "nullSentinel")) {
                            columnMap.NullSentinel.Accept(this, dumper);
                        }
                    }
                    VisitList(columnMap.Properties, dumper);
                }
            }
 
            /// <summary>
            /// RefColumnMap
            /// </summary>
            /// <param name="columnMap"></param>
            /// <param name="dumper"></param>
            /// <returns></returns>
            internal override void Visit(RefColumnMap columnMap, Dump dumper) {
                using (new AutoXml(dumper, "Ref", GetAttributes(columnMap))) {
                    using (new AutoXml(dumper, "entityIdentity")) {
                        VisitEntityIdentity(columnMap.EntityIdentity, dumper);
                    }
                }
            }
 
            /// <summary>
            /// SimpleCollectionColumnMap
            /// </summary>
            /// <param name="columnMap"></param>
            /// <param name="dumper"></param>
            /// <returns></returns>
            internal override void Visit(SimpleCollectionColumnMap columnMap, Dump dumper) {
                using (new AutoXml(dumper, "SimpleCollection", GetAttributes(columnMap))) {
                    DumpCollection(columnMap, dumper);
                }
            }
 
            /// <summary>
            /// SimpleColumnMap
            /// </summary>
            /// <param name="columnMap"></param>
            /// <param name="dumper"></param>
            /// <returns></returns>
            internal override void Visit(ScalarColumnMap columnMap, Dump dumper) {
                Dictionary<string, object> attrs = GetAttributes(columnMap);
                attrs.Add("CommandId", columnMap.CommandId);
                attrs.Add("ColumnPos", columnMap.ColumnPos);
 
                using (new AutoXml(dumper, "AssignedSimple", attrs)) {
                }
            }
 
            /// <summary>
            /// SimpleColumnMap
            /// </summary>
            /// <param name="columnMap"></param>
            /// <param name="dumper"></param>
            /// <returns></returns>
            internal override void Visit(VarRefColumnMap columnMap, Dump dumper) {
                Dictionary<string, object> attrs = GetAttributes(columnMap);
                attrs.Add("Var", ((VarRefColumnMap)columnMap).Var.Id);
                using (new AutoXml(dumper, "VarRef", attrs)) {
                }
            }
 
 
            /// <summary>
            /// DiscriminatedEntityIdentity
            /// </summary>
            /// <param name="entityIdentity"></param>
            /// <param name="dumper"></param>
            /// <returns></returns>
            protected override void VisitEntityIdentity(DiscriminatedEntityIdentity entityIdentity, Dump dumper) {
                using (new AutoXml(dumper, "DiscriminatedEntityIdentity")) {
                    using (new AutoXml(dumper, "entitySetId")) {
                        entityIdentity.EntitySetColumnMap.Accept(this, dumper);
                    }
                    if (entityIdentity.Keys.Length > 0) {
                        using (new AutoXml(dumper, "keys")) {
                            VisitList(entityIdentity.Keys, dumper);
                        }
                    }
                }
            }
 
            /// <summary>
            /// SimpleEntityIdentity
            /// </summary>
            /// <param name="entityIdentity"></param>
            /// <param name="dumper"></param>
            /// <returns></returns>
            protected override void VisitEntityIdentity(SimpleEntityIdentity entityIdentity, Dump dumper) {
                using (new AutoXml(dumper, "SimpleEntityIdentity")) {
                    if (entityIdentity.Keys.Length > 0) {
                        using (new AutoXml(dumper, "keys")) {
                            VisitList(entityIdentity.Keys, dumper);
                        }
                    }
                }
            }
        }
        #endregion
 
        #endregion
 
        internal struct AutoString : IDisposable {
            private Dump _dumper;
 
            internal AutoString(Dump dumper, Op op) {
                _dumper = dumper;
                _dumper.WriteString(AutoString.ToString(op.OpType));
                _dumper.BeginExpression();
            }
 
            public void Dispose() {
                try {
                    _dumper.EndExpression();
                }
                catch (Exception e) {
                    if (!EntityUtil.IsCatchableExceptionType(e)) {
                        throw;
                    }
                    // eat this exception; we don't care if the dumper is failing...
                }
            }
 
            internal static string ToString(OpType op)
            {   // perf: Enum.ToString() actually is very perf intensive in time & memory
                switch (op)
                {
                case OpType.Aggregate:
                    return "Aggregate";
                case OpType.And:
                    return "And";
                case OpType.Case:
                    return "Case";
                case OpType.Cast:
                    return "Cast";
                case OpType.Collect:
                    return "Collect";
                case OpType.Constant:
                    return "Constant";
                case OpType.ConstantPredicate:
                    return "ConstantPredicate";
                case OpType.CrossApply:
                    return "CrossApply";
                case OpType.CrossJoin:
                    return "CrossJoin";
                case OpType.Deref:
                    return "Deref";
                case OpType.Distinct:
                    return "Distinct";
                case OpType.Divide:
                    return "Divide";
                case OpType.Element:
                    return "Element";
                case OpType.EQ:
                    return "EQ";
                case OpType.Except:
                    return "Except";
                case OpType.Exists:
                    return "Exists";
                case OpType.Filter:
                    return "Filter";
                case OpType.FullOuterJoin:
                    return "FullOuterJoin";
                case OpType.Function:
                    return "Function";
                case OpType.GE:
                    return "GE";
                case OpType.GetEntityRef:
                    return "GetEntityRef";
                case OpType.GetRefKey:
                    return "GetRefKey";
                case OpType.GroupBy:
                    return "GroupBy";
                case OpType.GroupByInto:
                    return "GroupByInto";
                case OpType.GT:
                    return "GT";
                case OpType.InnerJoin:
                    return "InnerJoin";
                case OpType.InternalConstant:
                    return "InternalConstant";
                case OpType.Intersect:
                    return "Intersect";
                case OpType.IsNull:
                    return "IsNull";
                case OpType.IsOf:
                    return "IsOf";
                case OpType.LE:
                    return "LE";
                case OpType.Leaf:
                    return "Leaf";
                case OpType.LeftOuterJoin:
                    return "LeftOuterJoin";
                case OpType.Like:
                    return "Like";
                case OpType.LT:
                    return "LT";
                case OpType.Minus:
                    return "Minus";
                case OpType.Modulo:
                    return "Modulo";
                case OpType.Multiply:
                    return "Multiply";
                case OpType.MultiStreamNest:
                    return "MultiStreamNest";
                case OpType.Navigate:
                    return "Navigate";
                case OpType.NE:
                    return "NE";
                case OpType.NewEntity:
                    return "NewEntity";
                case OpType.NewInstance:
                    return "NewInstance";
                case OpType.DiscriminatedNewEntity:
                    return "DiscriminatedNewEntity";
                case OpType.NewMultiset:
                    return "NewMultiset";
                case OpType.NewRecord:
                    return "NewRecord";
                case OpType.Not:
                    return "Not";
                case OpType.Null:
                    return "Null";
                case OpType.NullSentinel:
                    return "NullSentinel";
                case OpType.Or:
                    return "Or";
                case OpType.OuterApply:
                    return "OuterApply";
                case OpType.PhysicalProject:
                    return "PhysicalProject";
                case OpType.Plus:
                    return "Plus";
                case OpType.Project:
                    return "Project";
                case OpType.Property:
                    return "Property";
                case OpType.Ref:
                    return "Ref";
                case OpType.RelProperty:
                    return "RelProperty";
                case OpType.ScanTable:
                    return "ScanTable";
                case OpType.ScanView:
                    return "ScanView";
                case OpType.SingleRow:
                    return "SingleRow";
                case OpType.SingleRowTable:
                    return "SingleRowTable";
                case OpType.SingleStreamNest:
                    return "SingleStreamNest";
                case OpType.SoftCast:
                    return "SoftCast";
                case OpType.Sort:
                    return "Sort";
                case OpType.Treat:
                    return "Treat";
                case OpType.UnaryMinus:
                    return "UnaryMinus";
                case OpType.UnionAll:
                    return "UnionAll";
                case OpType.Unnest:
                    return "Unnest";
                case OpType.VarDef:
                    return "VarDef";
                case OpType.VarDefList:
                    return "VarDefList";
                case OpType.VarRef:
                    return "VarRef";
                case OpType.ConstrainedSort:
                    return "ConstrainedSort";
                default:
                    Debug.Assert(false, "need to special case enum->string: " + op.ToString());
                    return op.ToString();
                }
            }
        }
 
        internal struct AutoXml : IDisposable {
            private string _nodeName;
            private Dump _dumper;
 
            internal AutoXml(Dump dumper, Op op) {
                _dumper = dumper;
                _nodeName = AutoString.ToString(op.OpType);
 
                Dictionary<string, object> attrs = new Dictionary<string, object>();
                if (null != op.Type) {
                    attrs.Add("Type", TypeHelpers.GetFullName(op.Type));
                }
 
                _dumper.Begin(_nodeName, attrs);
            }
 
            internal AutoXml(Dump dumper, Op op, Dictionary<string, object> attrs) {
                _dumper = dumper;
                _nodeName = AutoString.ToString(op.OpType);
 
                Dictionary<string, object> attrs2 = new Dictionary<string, object>();
                if (null != op.Type) {
                    attrs2.Add("Type", TypeHelpers.GetFullName(op.Type));
                }
 
                foreach (KeyValuePair<string, object> kv in attrs) {
                    attrs2.Add(kv.Key, kv.Value);
                }
 
                _dumper.Begin(_nodeName, attrs2);
            }
 
            internal AutoXml(Dump dumper, string nodeName)
                : this(dumper, nodeName, null) {
            }
 
            internal AutoXml(Dump dumper, string nodeName, Dictionary<string, object> attrs) {
                _dumper = dumper;
                _nodeName = nodeName;
                _dumper.Begin(_nodeName, attrs);
            }
 
            public void Dispose() {
                _dumper.End(_nodeName);
            }
        }
    }
}