DynamicActivityXamlReader.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / NetFx40 / System.Activities / System / Activities / XamlIntegration / DynamicActivityXamlReader.cs / 1305376 / DynamicActivityXamlReader.cs

                            //---------------------------------------------------------------- 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//---------------------------------------------------------------

namespace System.Activities.XamlIntegration 
{
    using System; 
    using System.Collections.Generic; 
    using System.Runtime;
    using System.Xaml; 
    using System.Xaml.Schema;

    // This Xaml Reader converts an .
    // 
    // This Xaml Reader also supports ActivityBuilder, which has the same basic node structure
    class DynamicActivityXamlReader : XamlReader, IXamlLineInfo
    {
        internal static readonly XamlMember xPropertyType = XamlLanguage.Property.GetMember("Type"); 
        internal static readonly XamlMember xPropertyName = XamlLanguage.Property.GetMember("Name");
        internal static readonly XamlMember xPropertyAttributes = XamlLanguage.Property.GetMember("Attributes"); 
 
        // These may be a closed generic types in the Activity case, so we compute them dynamically
        XamlType activityReplacementXamlType; 
        XamlType activityXamlType;

        readonly XamlType baseActivityXamlType;
        readonly XamlType activityPropertyXamlType; 
        readonly XamlType xamlTypeXamlType;
        readonly XamlType typeXamlType; 
 
        readonly XamlMember activityPropertyType;
        readonly XamlMember activityPropertyName; 
        readonly XamlMember activityPropertyAttributes;
        readonly XamlMember activityPropertyValue;

        readonly XamlReader innerReader; 
        readonly NamespaceTable namespaceTable;
 
        int depth; 
        bool notRewriting;
 
        int inXClassDepth;

        XamlTypeName xClassName;
        IXamlLineInfo nodeReaderLineInfo; 
        IXamlLineInfo innerReaderLineInfo;
 
        bool frontLoadedDirectives; 
        XamlSchemaContext schemaContext;
        bool isBuilder; 
        bool hasLineInfo;

        // we pull off of the innerReader and into this nodeList, where we use its reader
        XamlNodeQueue nodeQueue; 
        XamlReader nodeReader;
 
        // Properties are tricky since they support default values, and those values 
        // can appear anywhere in the XAML document. So we need to buffer their XAML
        // nodes and present them only at the end of the document (right before the 
        // document end tag), when we have both the declaration and the value realized.
        BufferedPropertyList bufferedProperties;

        // in the ActivityBuilder case we need to jump through some extra hoops to 
        // support PropertyReferenceExtension, since in the ActivityBuilder case
        // Implementation isn't a template (Func), so we need to map 
        // such members into attached properties on their parent object 
        bool bufferMembers;
        BuilderMemberNode currentBuilderMember; 

        public DynamicActivityXamlReader(XamlReader innerReader)
            : this(innerReader, null)
        { 
        }
 
        public DynamicActivityXamlReader(XamlReader innerReader, XamlSchemaContext schemaContext) 
            : this(false, innerReader, schemaContext)
        { 
        }

        public DynamicActivityXamlReader(bool isBuilder, XamlReader innerReader, XamlSchemaContext schemaContext)
            : base() 
        {
            this.isBuilder = isBuilder; 
            this.innerReader = innerReader; 
            this.schemaContext = schemaContext ?? innerReader.SchemaContext;
 
            this.xamlTypeXamlType = this.schemaContext.GetXamlType(typeof(XamlType));
            this.typeXamlType = this.schemaContext.GetXamlType(typeof(Type));

            this.baseActivityXamlType = this.schemaContext.GetXamlType(typeof(Activity)); 
            this.activityPropertyXamlType = this.schemaContext.GetXamlType(typeof(DynamicActivityProperty));
            this.activityPropertyType = this.activityPropertyXamlType.GetMember("Type"); 
            this.activityPropertyName = this.activityPropertyXamlType.GetMember("Name"); 
            this.activityPropertyValue = this.activityPropertyXamlType.GetMember("Value");
            this.activityPropertyAttributes = this.activityPropertyXamlType.GetMember("Attributes"); 

            this.namespaceTable = new NamespaceTable();
            this.frontLoadedDirectives = true;
 
            // we pump items through this node-list when rewriting
            this.nodeQueue = new XamlNodeQueue(this.schemaContext); 
            this.nodeReader = this.nodeQueue.Reader; 
            IXamlLineInfo lineInfo = innerReader as IXamlLineInfo;
            if (lineInfo != null && lineInfo.HasLineInfo) 
            {
                this.innerReaderLineInfo = lineInfo;
                this.nodeReaderLineInfo = (IXamlLineInfo)nodeQueue.Reader;
                this.hasLineInfo = true; 
            }
        } 
 
        public override XamlType Type
        { 
            get
            {
                return this.nodeReader.Type;
            } 
        }
 
        public override NamespaceDeclaration Namespace 
        {
            get 
            {
                return this.nodeReader.Namespace;
            }
        } 

        public override object Value 
        { 
            get
            { 
                return this.nodeReader.Value;
            }
        }
 
        public override bool IsEof
        { 
            get 
            {
                return this.nodeReader.IsEof; 
            }
        }

        public override XamlMember Member 
        {
            get 
            { 
                return this.nodeReader.Member;
            } 
        }

        public override XamlSchemaContext SchemaContext
        { 
            get
            { 
                return this.schemaContext; 
            }
        } 

        public override XamlNodeType NodeType
        {
            get 
            {
                return this.nodeReader.NodeType; 
            } 
        }
 
        public bool HasLineInfo
        {
            get
            { 
                return this.hasLineInfo;
            } 
        } 

        public int LineNumber 
        {
            get
            {
                if (this.hasLineInfo) 
                {
                    return this.nodeReaderLineInfo.LineNumber; 
                } 
                else
                { 
                    return 0;
                }
            }
        } 

        public int LinePosition 
        { 
            get
            { 
                if (this.hasLineInfo)
                {
                    return this.nodeReaderLineInfo.LinePosition;
                } 
                else
                { 
                    return 0; 
                }
            } 
        }

        protected override void Dispose(bool disposing)
        { 
            try
            { 
                if (disposing) 
                {
                    this.innerReader.Close(); 
                }
            }
            finally
            { 
                base.Dispose(disposing);
            } 
        } 

        static XamlException CreateXamlException(string message, IXamlLineInfo lineInfo) 
        {
            if (lineInfo != null && lineInfo.HasLineInfo)
            {
                return new XamlException(message, null, lineInfo.LineNumber, lineInfo.LinePosition); 
            }
            else 
            { 
                return new XamlException(message);
            } 
        }

        // perf optimization to efficiently support non-Activity types
        void DisableRewrite() 
        {
            this.notRewriting = true; 
            this.nodeReader = this.innerReader; 
            this.nodeReaderLineInfo = this.innerReader as IXamlLineInfo;
        } 

        public override bool Read()
        {
            if (this.notRewriting) 
            {
                Fx.Assert(object.ReferenceEquals(this.innerReader, this.nodeReader), "readers must match at this point"); 
                return this.nodeReader.Read(); 
            }
 
            // for properties, we'll store nodes "on the side"
            bool innerReaderResult = this.innerReader.Read();
            bool continueProcessing = true;
            while (continueProcessing && !this.innerReader.IsEof) 
            {
                // ProcessCurrentNode will only return true if it has advanced the innerReader 
                continueProcessing = ProcessCurrentNode(); 
            }
 
            // rewriting may have been disabled under ProcessCurrentNode
            if (this.notRewriting)
            {
                return innerReaderResult; 
            }
            else 
            { 
                // now that we've mapped the innerReader into (at least) one node entry, pump that reader as well
                return this.nodeReader.Read(); 
            }
        }

        // pull on our inner reader, map the results as necessary, and pump 
        // mapped results into the streaming node reader that we're offering up.
        // return true if we need to keep pumping (because we've buffered some nodes on the side) 
        bool ProcessCurrentNode() 
        {
            bool processedNode = false; 
            this.namespaceTable.ManageNamespace(this.innerReader);

            switch (this.innerReader.NodeType)
            { 
                case XamlNodeType.StartMember:
                    XamlMember currentMember = this.innerReader.Member; 
                    // find out if the member is a default value for one of 
                    // our declared properties. If it is, then we have a complex case
                    // where we need to: 
                    // 1) read the nodes into a side list
                    // 2) interleave these nodes with the DynamicActivityProperty nodes
                    //    since they need to appear as DynamicActivityProperty.Value
                    // 3) right before we hit the last node, we'll dump the side node-lists 
                    //    reflecting a zipped up representation of the Properties
                    if (IsXClassName(currentMember.DeclaringType)) 
                    { 
                        if (this.bufferedProperties == null)
                        { 
                            this.bufferedProperties = new BufferedPropertyList(this);
                        }
                        this.bufferedProperties.BufferDefaultValue(currentMember.Name, this.activityPropertyValue, this.innerReader, this.innerReaderLineInfo);
                        return true; // output cursor didn't move forward 
                    }
                    else if (this.frontLoadedDirectives && currentMember == XamlLanguage.FactoryMethod) 
                    { 
                        DisableRewrite();
                        return false; 
                    }
                    else
                    {
                        this.depth++; 
                        if (this.depth == 2)
                        { 
                            if (currentMember.DeclaringType == this.activityXamlType || currentMember.DeclaringType == this.baseActivityXamlType) 
                            {
                                // Rewrite "" to "" 
                                XamlMember member = this.activityReplacementXamlType.GetMember(currentMember.Name);
                                if (member == null)
                                {
                                    throw FxTrace.Exception.AsError(CreateXamlException(SR.MemberNotSupportedByActivityXamlServices(currentMember.Name), this.innerReaderLineInfo)); 
                                }
 
                                this.nodeQueue.Writer.WriteStartMember(member, this.innerReaderLineInfo); 

                                if (member.Name == "Constraints") 
                                {
                                    WriteWrappedMember(true);
                                    processedNode = true;
                                    return true; 
                                }
 
                                processedNode = true; 

                                // if we're in ActivityBuilder.Implementation, start buffering nodes 
                                if (this.isBuilder && member.Name == "Implementation")
                                {
                                    this.bufferMembers = true;
                                } 
                            }
                            else if (currentMember == XamlLanguage.Class) 
                            { 
                                this.inXClassDepth = this.depth;
 
                                // Rewrite x:Class to DynamicActivity.Name
                                this.nodeQueue.Writer.WriteStartMember(this.activityReplacementXamlType.GetMember("Name"), this.innerReaderLineInfo);
                                processedNode = true;
                            } 
                            else if (currentMember == XamlLanguage.Members)
                            { 
                                // Rewrite "" to "" 
                                if (this.bufferedProperties == null)
                                { 
                                    this.bufferedProperties = new BufferedPropertyList(this);
                                }
                                this.bufferedProperties.BufferDefinitions(this);
                                this.depth--; 
                                return true; // output cursor didn't move forward
                            } 
                            else if (currentMember == XamlLanguage.ClassAttributes) 
                            {
                                // Rewrite x:ClassAttributes to DynamicActivity.Attributes 
                                this.nodeQueue.Writer.WriteStartMember(this.activityReplacementXamlType.GetMember("Attributes"), this.innerReaderLineInfo);
                                // x:ClassAttributes directive has no following GetObject, but Attributes does since it's not a directive
                                WriteWrappedMember(false);
                                processedNode = true; 
                                return true;
                            } 
                        } 
                        else if (this.bufferMembers)
                        { 
                            Fx.Assert(this.currentBuilderMember == null, "should only have one BuildMember active");
                            this.currentBuilderMember = new BuilderMemberNode(this, this.depth);
                            // read past this node
                            this.innerReader.Read(); 
                            return true; // output cursor didn't move forward
                        } 
                    } 
                    break;
 
                case XamlNodeType.StartObject:
                    {
                        EnterObject();
                        if (this.depth == 1) 
                        {
                            // see if we're deserializing an Activity 
                            if (this.innerReader.Type.UnderlyingType == typeof(Activity)) 
                            {
                                // Rewrite "" to "" 
                                this.activityXamlType = this.innerReader.Type;
                                if (this.isBuilder)
                                {
                                    this.activityReplacementXamlType = SchemaContext.GetXamlType(typeof(ActivityBuilder)); 
                                }
                                else 
                                { 
                                    this.activityReplacementXamlType = SchemaContext.GetXamlType(typeof(DynamicActivity));
                                } 
                            }
                            // or an Activity
                            else if (this.innerReader.Type.IsGeneric && this.innerReader.Type.UnderlyingType != null
                                && this.innerReader.Type.UnderlyingType.GetGenericTypeDefinition() == typeof(Activity<>)) 
                            {
                                // Rewrite "" to "" 
                                this.activityXamlType = this.innerReader.Type; 

                                Type activityType = this.innerReader.Type.TypeArguments[0].UnderlyingType; 
                                Type activityReplacementGenericType;
                                if (this.isBuilder)
                                {
                                    activityReplacementGenericType = typeof(ActivityBuilder<>).MakeGenericType(activityType); 
                                }
                                else 
                                { 
                                    activityReplacementGenericType = typeof(DynamicActivity<>).MakeGenericType(activityType);
                                } 

                                this.activityReplacementXamlType = SchemaContext.GetXamlType(activityReplacementGenericType);
                            }
                            // otherwise disable rewriting so that we're a pass through 
                            else
                            { 
                                DisableRewrite(); 
                                return false;
                            } 

                            this.nodeQueue.Writer.WriteStartObject(this.activityReplacementXamlType, this.innerReaderLineInfo);
                            processedNode = true;
                        } 

                    } 
                    break; 

                case XamlNodeType.GetObject: 
                    EnterObject();
                    break;

                case XamlNodeType.EndObject: 
                case XamlNodeType.EndMember:
                    ExitObject(); 
                    break; 

                case XamlNodeType.Value: 
                    if (this.inXClassDepth >= this.depth && this.xClassName == null)
                    {
                        string fullName = (string)this.innerReader.Value;
                        string xClassNamespace = ""; 
                        string xClassName = fullName;
 
                        int nameStartIndex = fullName.LastIndexOf('.'); 
                        if (nameStartIndex > 0)
                        { 
                            xClassNamespace = fullName.Substring(0, nameStartIndex);
                            xClassName = fullName.Substring(nameStartIndex + 1);
                        }
 
                        this.xClassName = new XamlTypeName(xClassNamespace, xClassName);
                    } 
                    break; 
            }
 
            if (!processedNode)
            {
                if (this.currentBuilderMember != null && this.depth >= this.currentBuilderMember.Depth)
                { 
                    bool result = this.currentBuilderMember.ProcessNode(this.innerReader, this.nodeQueue.Writer, this.depth, this.innerReaderLineInfo);
                    if (!result) // pop a builder member 
                    { 
                        bool exitObject = this.currentBuilderMember.ExitObject;
                        this.currentBuilderMember = null; 
                        if (exitObject)
                        {
                            // need to pop 2 levels for ExitObject/ExitMember
                            this.ExitObject(); 
                            this.ExitObject();
                        } 
                    } 
                    return result;
                } 
                else
                {
                    this.nodeQueue.Writer.WriteNode(this.innerReader, this.innerReaderLineInfo);
                } 
            }
            return false; 
        } 

        // used for a number of cases when wrapping we need to add a GetObject/StartMember(_Items) since XAML directives intrinsically 
        // take care of it
        void WriteWrappedMember(bool stripWhitespace)
        {
            this.nodeQueue.Writer.WriteGetObject(this.innerReaderLineInfo); 
            this.nodeQueue.Writer.WriteStartMember(XamlLanguage.Items, this.innerReaderLineInfo);
            XamlReader subReader = this.innerReader.ReadSubtree(); 
 
            // 1) Read past the start member since we wrote it above
            subReader.Read(); 

            // 2) copy over the rest of the subnodes, possibly discarding top-level whitespace from WhitespaceSignificantCollection
            subReader.Read();
            while(!subReader.IsEof) 
            {
                bool isWhitespaceNode = false; 
                if(subReader.NodeType == XamlNodeType.Value) 
                {
                    string stringValue = subReader.Value as string; 
                    if(stringValue != null && stringValue.Trim().Length == 0)
                    {
                        isWhitespaceNode = true;
                    } 
                }
 
                if(isWhitespaceNode && stripWhitespace) 
                {
                    subReader.Read(); 
                }
                else
                {
                    XamlWriterExtensions.Transform(subReader.ReadSubtree(), this.nodeQueue.Writer, this.innerReaderLineInfo, false); 
                }
            } 
 

            // close the GetObject added above. Note that we are doing EndObject/EndMember after the last node (EndMember) 
            // rather than inserting EndMember/EndObject before the last EndMember since all EndMembers are interchangable from a state perspective
            this.nodeQueue.Writer.WriteEndObject(this.innerReaderLineInfo);
            this.nodeQueue.Writer.WriteEndMember(this.innerReaderLineInfo);
 
            subReader.Close();
 
            // we hand exited a member where we had increased the depth manually, so record that fact 
            ExitObject();
        } 

        // when Read hits StartObject or GetObject
        void EnterObject()
        { 
            this.depth++;
            if (this.depth >= 2) 
            { 
                this.frontLoadedDirectives = false;
            } 
        }

        // when Read hits EndObject or EndMember
        void ExitObject() 
        {
            if (this.depth < this.inXClassDepth) 
            { 
                this.inXClassDepth = 0;
            } 

            this.depth--;
            if (this.currentBuilderMember != null && this.currentBuilderMember.Depth == this.depth)
            { 
                this.currentBuilderMember.FlushBuffer(this.nodeQueue.Writer);
                this.currentBuilderMember = null; 
            } 
            this.frontLoadedDirectives = false;
 
            if (this.depth == 0)
            {
                // we're about to write out the last tag. Dump our accrued properties
                // as no more property values are forthcoming. 
                if (this.bufferedProperties != null)
                { 
                    this.bufferedProperties.FlushTo(this.nodeQueue, this); 
                }
            } 
        }

        bool IsXClassName(XamlType xamlType)
        { 
            if (xamlType == null || this.xClassName == null || xamlType.Name != this.xClassName.Name)
            { 
                return false; 
            }
 
            const string clrNamespacePart = "clr-namespace:";
            string clrNamespace = xamlType.PreferredXamlNamespace;
            if (clrNamespace.Contains(clrNamespacePart))
            { 
                clrNamespace = clrNamespace.Substring(clrNamespacePart.Length);
 
                int lastIndex = clrNamespace.IndexOf(';'); 
                if (lastIndex < 0 || lastIndex > clrNamespace.Length)
                { 
                    lastIndex = clrNamespace.Length;
                }

                string @namespace = clrNamespace.Substring(0, lastIndex); 
                return this.xClassName.Namespace == @namespace;
            } 
 
            return false;
        } 

        static void IncrementIfPositive(ref int a)
        {
            if (a > 0) 
            {
                a++; 
            } 
        }
 
        static void DecrementIfPositive(ref int a)
        {
            if (a > 0)
            { 
                a--;
            } 
        } 

        class BuilderMemberNode 
        {
            XamlMember currentMember;
            XamlNodeQueue bufferedNodes;
 
            int currentMemberLineNumber;
            int currentMemberLinePosition; 
            DynamicActivityXamlReader parent; 

            public BuilderMemberNode(DynamicActivityXamlReader parent, int depth) 
            {
                Fx.Assert(parent.isBuilder, "this path is only applicable for ActivityBuilder");
                Fx.Assert(parent.innerReader.NodeType == XamlNodeType.StartMember, "should only push for StartMember");
                this.parent = parent; 
                this.Depth = depth;
 
                // hang onto the initial StartMember separately from the other buffered nodes, since we may need 
                // to strip it in the PropertyReferenceExtension case
                this.currentMember = parent.innerReader.Member; 
                if (parent.hasLineInfo)
                {
                    this.currentMemberLineNumber = parent.innerReaderLineInfo.LineNumber;
                    this.currentMemberLinePosition = parent.innerReaderLineInfo.LinePosition; 
                }
                this.bufferedNodes = new XamlNodeQueue(parent.SchemaContext); 
            } 

            public int Depth 
            {
                get;
                private set;
            } 

            public bool ExitObject 
            { 
                get;
                private set; 
            }

            public void FlushBuffer(XamlWriter targetWriter)
            { 
                Fx.Assert(this.bufferedNodes != null, "Flush should only be called once");
                targetWriter.WriteStartMember(this.currentMember, this.currentMemberLineNumber, this.currentMemberLinePosition); 
                XamlServices.Transform(this.bufferedNodes.Reader, targetWriter, false); 
                this.bufferedNodes = null;
            } 

            // returns false if we've flushed our data into the targetWriter
            public bool ProcessNode(XamlReader reader, XamlWriter targetWriter, int currentDepth, IXamlLineInfo readerLineInfo)
            { 
                if (currentDepth == this.Depth &&
                    (reader.NodeType == XamlNodeType.NamespaceDeclaration || reader.NodeType == XamlNodeType.None)) 
                { 
                    this.bufferedNodes.Writer.WriteNode(reader, readerLineInfo);
                    reader.Read(); 
                    return true;
                }

                Fx.Assert(currentDepth <= this.Depth + 1, "only process up to depth + 1, since we know what we need at that point"); 

                // see if we're hosting a property reference extension 
                if (reader.NodeType == XamlNodeType.StartObject && reader.Type.IsGeneric && reader.Type.UnderlyingType != null 
                    && reader.Type.Name == "PropertyReferenceExtension"
                    && reader.Type.UnderlyingType.GetGenericTypeDefinition() == typeof(PropertyReferenceExtension<>)) 
                {
                    // first pump any buffered nodes (namespaces, etc)
                    if (this.bufferedNodes.Count > 0)
                    { 
                        XamlServices.Transform(this.bufferedNodes.Reader, targetWriter, false);
                        this.bufferedNodes = null; 
                    } 

                    //  maps to ... 
                    XamlType propertyReferenceExtensionXamlType = reader.Type;

                    XamlReader subReader = reader.ReadSubtree();
 
                    XamlType activityBuilderXamlType = reader.SchemaContext.GetXamlType(typeof(ActivityBuilder));
                    XamlType activityPropertyReferenceXamlType = reader.SchemaContext.GetXamlType(typeof(ActivityPropertyReference)); 
 
                    // 1) swap out the start member with 
                    targetWriter.WriteStartMember(activityBuilderXamlType.GetAttachableMember("PropertyReference"), readerLineInfo); 

                    // 2) swap out the start object with 
                    subReader.Read();
                    targetWriter.WriteStartObject(activityPropertyReferenceXamlType, readerLineInfo); 

                    // 3) Output our TargetProperty 
                    targetWriter.WriteStartMember(activityPropertyReferenceXamlType.GetMember("TargetProperty"), readerLineInfo); 
                    targetWriter.WriteValue(this.currentMember.Name, readerLineInfo);
                    targetWriter.WriteEndMember(readerLineInfo); 

                    // 4) process the subnodes to find our SourceProperty (i.e. PropertyReferenceExtension.PropertyName)
                    bool continueReading = subReader.Read();
                    bool inSourceProperty = false; 
                    while (continueReading)
                    { 
                        if (subReader.NodeType == XamlNodeType.StartMember 
                            && subReader.Member.DeclaringType == propertyReferenceExtensionXamlType
                            && subReader.Member.Name == "PropertyName") 
                        {
                            inSourceProperty = true;
                        }
                        else if (inSourceProperty) 
                        {
                            if (subReader.NodeType == XamlNodeType.EndMember) 
                            { 
                                inSourceProperty = false;
                            } 
                            else if (subReader.NodeType == XamlNodeType.Value)
                            {
                                // 3) Output our TargetProperty
                                targetWriter.WriteStartMember(activityPropertyReferenceXamlType.GetMember("SourceProperty"), readerLineInfo); 
                                targetWriter.WriteValue((string)subReader.Value, readerLineInfo);
                                targetWriter.WriteEndMember(readerLineInfo); 
 
                            }
                        } 
                        continueReading = subReader.Read();
                    }

                    // 5)  
                    targetWriter.WriteEndObject(readerLineInfo);
                    targetWriter.WriteEndMember(readerLineInfo); 
                    this.ExitObject = true; 
                    subReader.Close();
                } 
                else
                {
                    FlushBuffer(targetWriter);
                    targetWriter.WriteNode(reader, readerLineInfo); 
                }
 
                return false; // we're done 
            }
        } 

        // This class exists to "zip" together  property definitions (to be rewritten as  nodes)
        // with their corresponding default values  (to be rewritten as  nodes).
        // Definitions come all at once, but values could come anywhere in the XAML document, so we save them all almost until the end of 
        // the document and write them all out at once using BufferedPropertyList.CopyTo().
        class BufferedPropertyList 
        { 
            Dictionary propertyHolders;
            Dictionary valueHolders; 
            XamlNodeQueue outerNodes;
            DynamicActivityXamlReader parent;

            bool alreadyBufferedDefinitions; 

            public BufferedPropertyList(DynamicActivityXamlReader parent) 
            { 
                this.parent = parent;
                this.outerNodes = new XamlNodeQueue(parent.SchemaContext); 
            }

            // Called inside of an x:Members--read up to , buffering definitions
            public void BufferDefinitions(DynamicActivityXamlReader parent) 
            {
                XamlReader subReader = parent.innerReader.ReadSubtree(); 
                IXamlLineInfo readerLineInfo = parent.innerReaderLineInfo; 

                // 1) swap out the start member with  
                subReader.Read();
                Fx.Assert(subReader.NodeType == XamlNodeType.StartMember && subReader.Member == XamlLanguage.Members,
                    "Should be inside of x:Members before calling BufferDefinitions");
                this.outerNodes.Writer.WriteStartMember(parent.activityReplacementXamlType.GetMember("Properties"), readerLineInfo); 

                // x:Members directive has no following GetObject, but Properties does since it's not a directive 
                this.outerNodes.Writer.WriteGetObject(readerLineInfo); 
                this.outerNodes.Writer.WriteStartMember(XamlLanguage.Items, readerLineInfo);
 
                // 2) process the subnodes and store them in either ActivityPropertyHolders,
                // or exigent nodes in the outer node list
                bool continueReading = subReader.Read();
                while (continueReading) 
                {
                    if (subReader.NodeType == XamlNodeType.StartObject 
                        && subReader.Type == XamlLanguage.Property) 
                    {
                        // we found an x:Property. Store it in an ActivityPropertyHolder 
                        ActivityPropertyHolder newProperty = new ActivityPropertyHolder(parent, subReader.ReadSubtree());
                        this.PropertyHolders.Add(newProperty.Name, newProperty);

                        // and stash away a proxy node to map later 
                        this.outerNodes.Writer.WriteValue(newProperty, readerLineInfo);
 
                        // ActivityPropertyHolder consumed the subtree, so we don't need to pump a Read() in this path 
                    }
                    else 
                    {
                        // it's not an x:Property. Store it in our extra node list
                        this.outerNodes.Writer.WriteNode(subReader, readerLineInfo);
                        continueReading = subReader.Read(); 
                    }
                } 
 
                // close the GetObject added above. Note that we are doing EndObject/EndMember after the last node (EndMember)
                // rather than inserting EndMember/EndObject before the last EndMember since all EndMembers are interchangable from a state perspective 
                this.outerNodes.Writer.WriteEndObject(readerLineInfo);
                this.outerNodes.Writer.WriteEndMember(readerLineInfo);
                subReader.Close();
 
                this.alreadyBufferedDefinitions = true;
                FlushValueHolders(parent); 
            } 

            void FlushValueHolders(DynamicActivityXamlReader parent) 
            {
                // We've seen all the property definitions we're going to see. Write out any values already accumulated.

                // If we have picked up any values already before definitions, process them immediately 
                // (and throw as usual if corresponding definition doesn't exist)
                if (this.valueHolders != null) 
                { 
                    foreach (KeyValuePair propertyNameAndValue in this.valueHolders)
                    { 
                        ProcessDefaultValue(propertyNameAndValue.Key, propertyNameAndValue.Value.PropertyValue, propertyNameAndValue.Value.ValueReader, parent.innerReaderLineInfo);
                    }
                    this.valueHolders = null; // So we don't flush it again at close
                } 
            }
 
            Dictionary PropertyHolders 
            {
                get 
                {
                    if (this.propertyHolders == null)
                    {
                        this.propertyHolders = new Dictionary(); 
                    }
 
                    return this.propertyHolders; 
                }
            } 

            public void BufferDefaultValue(string propertyName, XamlMember propertyValue, XamlReader reader, IXamlLineInfo lineInfo)
            {
                if (this.alreadyBufferedDefinitions) 
                {
                    ProcessDefaultValue(propertyName, propertyValue, reader.ReadSubtree(), lineInfo); 
                } 
                else
                { 
                    if (this.valueHolders == null)
                    {
                        this.valueHolders = new Dictionary();
                    } 
                    ValueHolder savedValue = new ValueHolder(this.parent.SchemaContext, propertyValue, reader, lineInfo);
                    valueHolders[propertyName] = savedValue; 
                } 
            }
 
            public void ProcessDefaultValue(string propertyName, XamlMember propertyValue, XamlReader reader, IXamlLineInfo lineInfo)
            {
                ActivityPropertyHolder propertyHolder;
                if (!this.PropertyHolders.TryGetValue(propertyName, out propertyHolder)) 
                {
                    throw FxTrace.Exception.AsError(CreateXamlException(SR.InvalidProperty(propertyName), lineInfo)); 
                } 

                propertyHolder.ProcessDefaultValue(propertyValue, reader, lineInfo); 
            }

            public void FlushTo(XamlNodeQueue targetNodeQueue, DynamicActivityXamlReader parent)
            { 
                FlushValueHolders(parent);
 
                XamlReader sourceReader = this.outerNodes.Reader; 
                IXamlLineInfo sourceReaderLineInfo = parent.hasLineInfo ? sourceReader as IXamlLineInfo : null;
                while (sourceReader.Read()) 
                {
                    if (sourceReader.NodeType == XamlNodeType.Value)
                    {
                        ActivityPropertyHolder propertyHolder = sourceReader.Value as ActivityPropertyHolder; 
                        if (propertyHolder != null)
                        { 
                            // replace ActivityPropertyHolder with its constituent nodes 
                            propertyHolder.CopyTo(targetNodeQueue, sourceReaderLineInfo);
                            continue; 
                        }
                    }

                    targetNodeQueue.Writer.WriteNode(sourceReader, sourceReaderLineInfo); 
                }
            } 
 
            // Buffer property values until we can match them with definitions
            class ValueHolder 
            {
                XamlNodeQueue nodes;

                public ValueHolder(XamlSchemaContext schemaContext, XamlMember propertyValue, XamlReader reader, IXamlLineInfo lineInfo) 
                {
                    this.nodes = new XamlNodeQueue(schemaContext); 
                    this.PropertyValue = propertyValue; 
                    XamlWriterExtensions.Transform(reader.ReadSubtree(), this.nodes.Writer, lineInfo, true);
                } 

                public XamlMember PropertyValue { get; private set; }

                public XamlReader ValueReader 
                {
                    get 
                    { 
                        return this.nodes.Reader;
                    } 
                }
            }

            class ActivityPropertyHolder 
            {
                // the nodes that we'll pump at the end 
                XamlNodeQueue nodes; 
                DynamicActivityXamlReader parent;
 
                public ActivityPropertyHolder(DynamicActivityXamlReader parent, XamlReader reader)
                {
                    this.parent = parent;
                    this.nodes = new XamlNodeQueue(parent.SchemaContext); 
                    IXamlLineInfo readerLineInfo = parent.innerReaderLineInfo;
 
                    // parse the subtree, and extract out the Name and Type for now. 
                    // keep the node-list open for now, just in case a default value appears
                    // later in the document 

                    // Rewrite "" to ""
                    reader.Read();
                    this.nodes.Writer.WriteStartObject(parent.activityPropertyXamlType, readerLineInfo); 
                    int depth = 1;
                    int nameDepth = 0; 
                    int typeDepth = 0; 
                    bool continueReading = reader.Read();
                    while (continueReading) 
                    {
                        switch (reader.NodeType)
                        {
                            case XamlNodeType.StartMember: 
                                // map  membes to the appropriate  members
                                if (reader.Member.DeclaringType == XamlLanguage.Property) 
                                { 
                                    XamlMember mappedMember = reader.Member;
 
                                    if (mappedMember == xPropertyName)
                                    {
                                        mappedMember = parent.activityPropertyName;
                                        if (nameDepth == 0) 
                                        {
                                            nameDepth = 1; 
                                        } 
                                    }
                                    else if (mappedMember == xPropertyType) 
                                    {
                                        mappedMember = parent.activityPropertyType;
                                        if (typeDepth == 0)
                                        { 
                                            typeDepth = 1;
                                        } 
                                    } 
                                    else if (mappedMember == xPropertyAttributes)
                                    { 
                                        mappedMember = parent.activityPropertyAttributes;
                                    }
                                    else
                                    { 
                                        throw FxTrace.Exception.AsError(CreateXamlException(SR.PropertyMemberNotSupportedByActivityXamlServices(mappedMember.Name), readerLineInfo));
                                    } 
                                    this.nodes.Writer.WriteStartMember(mappedMember, readerLineInfo); 
                                    continueReading = reader.Read();
                                    continue; 
                                }
                                break;

                            case XamlNodeType.Value: 
                                if (nameDepth == 1)
                                { 
                                    // We only support property name as an attribute (nameDepth == 1) 
                                    this.Name = reader.Value as string;
                                } 
                                else if (typeDepth == 1)
                                {
                                    // We only support property type as an attribute (typeDepth == 1)
                                    XamlTypeName xamlTypeName = XamlTypeName.Parse(reader.Value as string, parent.namespaceTable); 
                                    XamlType xamlType = parent.SchemaContext.GetXamlType(xamlTypeName);
                                    if (xamlType == null) 
                                    { 
                                        throw FxTrace.Exception.AsError(CreateXamlException(SR.InvalidPropertyType(reader.Value as string, this.Name), readerLineInfo));
                                    } 
                                    this.Type = xamlType;
                                }
                                break;
 
                            case XamlNodeType.StartObject:
                            case XamlNodeType.GetObject: 
                                depth++; 
                                IncrementIfPositive(ref nameDepth);
                                IncrementIfPositive(ref typeDepth); 
                                if (typeDepth > 0 && reader.Type == parent.xamlTypeXamlType)
                                {
                                    this.nodes.Writer.WriteStartObject(parent.typeXamlType, readerLineInfo);
                                    continueReading = reader.Read(); 
                                    continue;
                                } 
                                break; 

                            case XamlNodeType.EndObject: 
                                depth--;
                                if (depth == 0)
                                {
                                    continueReading = reader.Read(); 
                                    continue; // skip this node, we'll close it by hand in CopyTo()
                                } 
                                DecrementIfPositive(ref nameDepth); 
                                DecrementIfPositive(ref typeDepth);
                                break; 

                            case XamlNodeType.EndMember:
                                DecrementIfPositive(ref nameDepth);
                                DecrementIfPositive(ref typeDepth); 
                                break;
                        } 
 
                        // if we didn't continue (from a mapped case), just copy over
                        this.nodes.Writer.WriteNode(reader, readerLineInfo); 
                        continueReading = reader.Read();
                    }

                    reader.Close(); 
                }
 
                public string Name 
                {
                    get; 
                    private set;
                }

                public XamlType Type 
                {
                    get; 
                    private set; 
                }
 
                // called when we've reached the end of the activity and need
                // to extract out the resulting data into our activity-wide node list
                public void CopyTo(XamlNodeQueue targetNodeQueue, IXamlLineInfo readerInfo)
                { 
                    // first copy any buffered nodes
                    XamlServices.Transform(this.nodes.Reader, targetNodeQueue.Writer, false); 
 
                    // then write the end node for this property
                    targetNodeQueue.Writer.WriteEndObject(readerInfo); 
                }

                public void ProcessDefaultValue(XamlMember propertyValue, XamlReader subReader, IXamlLineInfo lineInfo)
                { 
                    bool addedStartObject = false;
 
                    // 1) swap out the start member with  
                    subReader.Read();
                    if (!subReader.Member.IsNameValid) 
                    {
                        throw FxTrace.Exception.AsError(CreateXamlException(SR.InvalidXamlMember(subReader.Member.Name), lineInfo));
                    }
 
                    this.nodes.Writer.WriteStartMember(propertyValue, lineInfo);
 
                    // temporary hack: read past GetObject/StartMember nodes that are added by 
                    // the XAML stack. This has been fixed in the WPF branch, but we haven't FI'ed that yet
                    XamlReader valueReader; 
                    subReader.Read();
                    if (subReader.NodeType == XamlNodeType.GetObject)
                    {
                        subReader.Read(); 
                        subReader.Read();
                        valueReader = subReader.ReadSubtree(); 
                        valueReader.Read(); 
                    }
                    else 
                    {
                        valueReader = subReader;
                    }
 
                    // Add SO tag if necessary UNLESS there's no value to wrap (which means we're already at EO)
                    if (valueReader.NodeType != XamlNodeType.EndMember && valueReader.NodeType != XamlNodeType.StartObject) 
                    { 
                        addedStartObject = true;
                        // Add  nodes so that type converters work correctly 
                        this.nodes.Writer.WriteStartObject(this.Type, lineInfo);
                        this.nodes.Writer.WriteStartMember(XamlLanguage.Initialization, lineInfo);
                    }
 
                    // 3) copy over the value
                    while(!valueReader.IsEof) 
                    { 
                        this.nodes.Writer.WriteNode(valueReader, lineInfo);
                        valueReader.Read(); 
                    }

                    valueReader.Close();
 
                    // 4) close up the extra nodes
                    if (!object.ReferenceEquals(valueReader, subReader)) 
                    { 
                        subReader.Read();
                        while (subReader.Read()) 
                        {
                            this.nodes.Writer.WriteNode(subReader, lineInfo);
                        }
 
                    }
 
                    if (addedStartObject) 
                    {
                        this.nodes.Writer.WriteEndObject(lineInfo); 
                        this.nodes.Writer.WriteEndMember(lineInfo);
                    }
                    subReader.Close();
                } 
            }
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK