ErrorTolerantObjectWriter.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 / Tools / System.Activities.Presentation / System / Activities / Presentation / Xaml / ErrorTolerantObjectWriter.cs / 1407647 / ErrorTolerantObjectWriter.cs

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

 
namespace System.Activities.Presentation.Xaml
{ 
    using System; 
    using System.Activities;
    using System.Activities.Debugger; 
    using System.Activities.XamlIntegration;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel; 
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization; 
    using System.Runtime; 
    using System.Text;
    using System.Xaml; 
    using System.Xaml.Schema;
    using System.Xml;
    using System.Linq;
    using NameReferenceConverter = System.Windows.Markup.NameReferenceConverter; 
    using XamlDeferLoad = System.Windows.Markup.XamlDeferLoadAttribute;
 
    // This XamlWriter wraps an ObjectWriter to provide limited error tolerance, as follows: 
    // - Buffer the node stream as a tree of fragments (one for each activity) and statically validate it.
    // - Write the node stream out to an ObjectWriter, wrapping any subtrees that failed validation 
    //   in an ErrorActivity.
    // - If validation fails at the root level, we don't load any object, we only provide validation errors.
    // We are only tolerant of errors that can be detected statically; i.e. we are not resillient
    // to XamlObjectWriter exceptions. 

    internal class ErrorTolerantObjectWriter : XamlWriter, IXamlLineInfoConsumer 
    { 
        // We store three main types of state:
        // - The current state of the nodestream (XamlFrames), for validation purposes. 
        // - Buffered XamlFragments.
        // - NameScopes, which have

        // Current state of the nodestream, for performing validation 
        WalkableStack xamlStack;
 
        // Root of the tree of completed fragments (except for the root, which may be in progress) 
        XamlFragment rootFragment;
 
        // Stack of in-progress fragments
        Stack fragmentStack;

        // Stack of in-progress namescopes; fragments can overlap multiple namescopes, and vice versa 
        Stack nameScopeStack;
 
        // Completed namescopes, saved so we can resolve all references at end of parse 
        List poppedNameScopes;
 
        // Pending NS declarations whose corresponding StartObject hasn't been written yet
        NamespaceStackNode pendingNamespaces;

        XamlObjectWriter objectWriter; 
        XamlType typeOfActivity;
        XamlMember nameOfReference; 
        XamlValueConverter activityLoader; 
        int lineNumber, linePosition;
 
        public string LocalAssemblyName { get; set; }

        public IList LoadErrors { get; private set; }
 
        public ErrorTolerantObjectWriter(XamlSchemaContext schemaContext)
        { 
            this.xamlStack = new WalkableStack(); 
            this.rootFragment = new XamlFragment(schemaContext);
            this.fragmentStack = new Stack(); 
            this.fragmentStack.Push(this.rootFragment);
            this.nameScopeStack = new Stack();
            this.nameScopeStack.Push(new NameScopeFrame(null));
            this.poppedNameScopes = new List(); 
            this.objectWriter = new XamlObjectWriter(schemaContext);
            this.typeOfActivity = objectWriter.SchemaContext.GetXamlType(typeof(Activity)); 
            this.nameOfReference = XamlLanguage.Reference.GetMember("Name"); 
            this.activityLoader = typeOfActivity.GetMember("Implementation").DeferringLoader;
        } 

        public object Result { get; private set; }

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

        public override void WriteNamespace(NamespaceDeclaration namespaceDeclaration) 
        { 
            if (this.rootFragment.HasError)
            { 
                return;
            }
            if (this.pendingNamespaces == null)
            { 
                this.pendingNamespaces = new NamespaceStackNode();
            } 
            this.pendingNamespaces.Add(namespaceDeclaration.Prefix, namespaceDeclaration.Namespace); 
            CurrentWriter.WriteNamespace(namespaceDeclaration);
        } 

        public override void WriteStartObject(XamlType type)
        {
            // This validation must be done before pushing the object frame, so that if there is an error, 
            // we treat the containing type as an error and not just the subtree.
            ValidateSetMember(); 
            PushXamlFrame(type); 

            // Pushing the activity frame must be done before the rest of validation, because if this 
            // is an unknown Activity, we want to treat just this subtree as an error, not its parent.
            PushNewActivityFrameIfNeeded();
            ValidateStartObject();
            if (this.rootFragment.HasError) 
            {
                return; 
            } 
            CurrentWriter.WriteStartObject(type);
            CurrentFragment.ObjectDepth++; 
        }

        public override void WriteGetObject()
        { 
            PushXamlFrame(null);
            ValidateGetObject(); 
            if (this.rootFragment.HasError) 
            {
                return; 
            }
            CurrentWriter.WriteGetObject();
            CurrentFragment.ObjectDepth++;
        } 

        public override void WriteEndObject() 
        { 
            this.xamlStack.Pop();
            ValidateEndObject(); 
            if (this.rootFragment.HasError)
            {
                return;
            } 
            CurrentWriter.WriteEndObject();
            CurrentFragment.ObjectDepth--; 
            if (CurrentFragment.ObjectDepth == 0) 
            {
                XamlFragment completedFragment = CurrentFragment; 
                this.fragmentStack.Pop();
                if (this.fragmentStack.Count == 0)
                {
                    Fx.Assert(completedFragment == this.rootFragment, "Base of stack should be root fragment"); 
                    CompleteLoad();
                } 
                else 
                {
                    CurrentFragment.AddChild(completedFragment); 
                }
            }
        }
 
        public override void WriteStartMember(XamlMember member)
        { 
            Fx.Assert(this.xamlStack.Count > 0 && this.xamlStack.Current.Member == null, "Unexpected StartMember"); 
            this.xamlStack.Current.Member = member;
            ValidateStartMember(); 
            if (this.rootFragment.HasError)
            {
                return;
            } 
            CurrentWriter.WriteStartMember(member);
        } 
 
        public override void WriteEndMember()
        { 
            Fx.Assert(this.xamlStack.Count > 0 && this.xamlStack.Current.Member != null, "Unexpected EndMember");
            this.xamlStack.Current.Member = null;
            this.xamlStack.Current.MemberIsSet = false;
            ValidateEndMember(); 
            if (this.rootFragment.HasError)
            { 
                return; 
            }
            CurrentWriter.WriteEndMember(); 
        }

        public override void WriteValue(object value)
        { 
            ValidateValue(value);
            if (this.rootFragment.HasError) 
            { 
                return;
            } 
            CurrentWriter.WriteValue(value);
        }

        public void SetLineInfo(int lineNumber, int linePosition) 
        {
            // We need to save the line info statically, for validation errors 
            this.lineNumber = lineNumber; 
            this.linePosition = linePosition;
 
            // But we also need to keep it in [....] with the nodestream, for XOW errors
            // XOW and XamlNodeQueue.Writer both implement IXamlLineInfoConsumer, so we can do a straight cast
            if (this.rootFragment.HasError)
            { 
                return;
            } 
            ((IXamlLineInfoConsumer)CurrentWriter).SetLineInfo(lineNumber, linePosition); 
        }
 
        // ObjectWriter always wants LineInfo
        public bool ShouldProvideLineInfo { get { return true; } }

        internal static bool IsErrorActivity(Type objectType) 
        {
            return objectType == typeof(ErrorActivity) || 
                (objectType != null && objectType.IsGenericType && 
                 objectType.GetGenericTypeDefinition() == typeof(ErrorActivity<>));
        } 

        // Node loop that strips out ErrorActivities on Save. Assumes that ErrorActivities are never
        // nested, and that XamlObjectReader doesn't have line info.
        internal static void TransformAndStripErrors(XamlObjectReader objectReader, XamlWriter writer) 
        {
            // Every ErrorActivity is prefixed with all the NamespaceDeclarations that were in scope 
            // in the original document. We track the current namespaces in scope on Save, so that we 
            // can strip out any redundant declarations.
            NamespaceStackNode currentNamespaces = null; 
            NamespaceStackNode pendingNamespaces = null;

            while (objectReader.Read())
            { 
                // Update the namespace stack
                switch (objectReader.NodeType) 
                { 
                    case XamlNodeType.NamespaceDeclaration:
                        if (pendingNamespaces == null) 
                        {
                            pendingNamespaces = new NamespaceStackNode() { PreviousNode = currentNamespaces };
                        }
                        pendingNamespaces.Add(objectReader.Namespace.Prefix, objectReader.Namespace.Namespace); 
                        break;
                    case XamlNodeType.StartObject: 
                    case XamlNodeType.GetObject: 
                        if (pendingNamespaces != null)
                        { 
                            currentNamespaces = pendingNamespaces;
                            pendingNamespaces = null;
                        }
                        currentNamespaces.ObjectDepth++; 
                        break;
                } 
 
                if (objectReader.NodeType == XamlNodeType.StartObject && IsErrorActivity(objectReader.Type.UnderlyingType))
                { 
                    ActivityFragment.TransformErrorActivityContents(objectReader, writer, currentNamespaces);
                }
                else
                { 
                    writer.WriteNode(objectReader);
                } 
 
                if (objectReader.NodeType == XamlNodeType.EndObject)
                { 
                    currentNamespaces.ObjectDepth--;
                    if (currentNamespaces.ObjectDepth == 0)
                    {
                        currentNamespaces = currentNamespaces.PreviousNode; 
                    }
                } 
            } 
        }
 
        XamlFragment CurrentFragment
        {
            get { return this.fragmentStack.Peek(); }
        } 

        NameScopeFrame CurrentNameScope 
        { 
            get { return this.nameScopeStack.Peek(); }
        } 

        XamlWriter CurrentWriter
        {
            get { return CurrentFragment.NodeQueue.Writer; } 
        }
 
        static void AppendShortName(StringBuilder result, XamlType type) 
        {
            result.Append(type.Name); 
            if (type.IsGeneric)
            {
                result.Append("(");
                bool isFirst = true; 
                foreach (XamlType typeArg in type.TypeArguments)
                { 
                    if (isFirst) 
                    {
                        isFirst = false; 
                    }
                    else
                    {
                        result.Append(","); 
                    }
                    AppendShortName(result, typeArg); 
                } 
                result.Append(")");
            } 
        }

        // If a generic type is unknown, we don't know whether the open generic couldn't be resolved,
        // or just its children. So we only want to surface errors for types that don't have unknown 
        // children.
        static void GetLeafUnresolvedTypeArgs(XamlType type, HashSet unresolvedTypeArgs) 
        { 
            Fx.Assert(type.IsUnknown, "Method should only be called for unknown types");
            bool hasUnknownChildren = false; 
            if (type.IsGeneric)
            {
                foreach (XamlType typeArg in type.TypeArguments)
                { 
                    if (typeArg.IsUnknown)
                    { 
                        GetLeafUnresolvedTypeArgs(typeArg, unresolvedTypeArgs); 
                        hasUnknownChildren = true;
                    } 
                }
            }
            if (!hasUnknownChildren)
            { 
                unresolvedTypeArgs.Add(type);
            } 
        } 

        static string GetXamlMemberName(XamlMember member) 
        {
            if (member.IsDirective)
            {
                return "{" + member.PreferredXamlNamespace + "}" + member.Name; 
            }
            else 
            { 
                return GetXamlTypeName(member.DeclaringType) + "." + member.Name;
            } 
        }

        static string GetXamlTypeName(XamlType type)
        { 
            StringBuilder result = new StringBuilder();
            if (type.UnderlyingType == null) 
            { 
                result.Append("{");
                result.Append(type.PreferredXamlNamespace); 
                result.Append("}");
            }
            else
            { 
                result.Append(type.UnderlyingType.Namespace);
                result.Append("."); 
            } 
            AppendShortName(result, type);
            return result.ToString(); 
        }

        static bool IsWhitespace(string value)
        { 
            foreach (char c in value)
            { 
                if (c != '\r' && c != '\n' && c != ' ' && c != '\t') 
                {
                    return false; 
                }
            }
            return true;
        } 

        // Validate named references and write out the complete nodestream to the ObjectWriter 
        void CompleteLoad() 
        {
            CompleteNameReferences(); 
            XamlFragment.FindBrokenReferences(this.rootFragment);

            if (this.rootFragment.HasError)
            { 
                this.Result = null;
            } 
            else 
            {
                this.rootFragment.WriteTo(this.objectWriter, false); 
                this.Result = this.objectWriter.Result;
            }
        }
 
        // Gets the property type of the containing member, or its item type if it's a collection.
        XamlType GetParentPropertyType(out bool parentIsDictionary) 
        { 
            XamlMember parentMember;
            XamlType collectionType; 
            XamlType result = GetParentPropertyType(out parentMember, out collectionType);
            parentIsDictionary = collectionType != null && collectionType.IsDictionary;
            return result;
        } 

        XamlType GetParentPropertyType(out XamlMember parentMember, out XamlType collectionType) 
        { 
            parentMember = this.xamlStack.Previous(1).Member;
            Fx.Assert(parentMember != null, "StartObject or Value without preceding StartMember"); 
            if (parentMember.IsDirective &&
                (parentMember.Type.IsCollection || parentMember.Type.IsDictionary))
            {
                if (parentMember == XamlLanguage.Items) 
                {
                    collectionType = this.xamlStack.Previous(1).Type; 
                    if (collectionType == null) 
                    {
                        // This is a GetObject, need to look at the containing member 
                        collectionType = this.xamlStack.Previous(2).Member.Type;
                    }
                }
                else 
                {
                    collectionType = parentMember.Type; 
                } 
                return collectionType.ItemType;
            } 
            collectionType = null;
            return parentMember.Type;
        }
 
        // Checks whether to push a new ActivityFrame for a new StartObject (i.e. whether the object
        // is an activity and is replaceable in case of error). 
        void PushNewActivityFrameIfNeeded() 
        {
            Fx.Assert(this.xamlStack.Count > 0, "PushNewActivityFrameIfNeeded called without a StartObject"); 
            if (this.xamlStack.Count == 1)
            {
                // This is the root of the document
                return; 
            }
            if (CurrentFragment.HasError) 
            { 
                // We're already inside an error frame, no point pushing any more frames
                return; 
            }
            // Check the parent property type (not the object type) because that's what determines
            // whether we can inject an ErrorActivity.
            bool parentIsDictionary; 
            XamlType parentType = GetParentPropertyType(out parentIsDictionary);
            if (parentType != null && parentType.UnderlyingType != null && !parentIsDictionary && 
                ActivityFragment.IsActivityType(parentType.UnderlyingType)) 
            {
                this.fragmentStack.Push(new ActivityFragment(SchemaContext) { Type = parentType.UnderlyingType }); 
                CurrentFragment.Namespaces = this.xamlStack.Current.Namespaces;
            }
        }
 
        void PushNameScope()
        { 
            this.nameScopeStack.Push(new NameScopeFrame(this.nameScopeStack.Peek())); 
        }
 
        void PushXamlFrame(XamlType type)
        {
            NamespaceStackNode currentNamespaces = this.xamlStack.Count > 0 ? this.xamlStack.Current.Namespaces : null;
            this.xamlStack.Push(new XamlFrame { Type = type }); 
            if (this.pendingNamespaces != null)
            { 
                this.pendingNamespaces.PreviousNode = currentNamespaces; 
                this.xamlStack.Current.Namespaces = this.pendingNamespaces;
                this.pendingNamespaces = null; 
            }
            else
            {
                this.xamlStack.Current.Namespaces = currentNamespaces; 
            }
        } 
 
        void ValidateStartObject()
        { 
            // Check if type is known
            XamlType type = this.xamlStack.Current.Type;
            if (type.IsUnknown)
            { 
                HashSet unresolvedTypes = null;
                if (type.IsGeneric) 
                { 
                    unresolvedTypes = new HashSet();
                    GetLeafUnresolvedTypeArgs(type, unresolvedTypes); 
                }
                if (unresolvedTypes != null &&
                    (unresolvedTypes.Count > 1 || !unresolvedTypes.Contains(type)))
                { 
                    ValidationError(SR.UnresolvedGenericType, GetXamlTypeName(type));
                    foreach (XamlType unresolvedTypeArg in unresolvedTypes) 
                    { 
                        ValidationErrorUnknownType(unresolvedTypeArg);
                    } 
                }
                else
                {
                    ValidationErrorUnknownType(type); 
                }
            } 
            else if (this.xamlStack.Count > 1) 
            {
                // Check assignability to parent member 
                if (!type.IsMarkupExtension)
                {
                    XamlMember parentMember;
                    XamlType collectionType; 
                    XamlType expectedType = GetParentPropertyType(out parentMember, out collectionType);
                    if (collectionType != null) 
                    { 
                        if (!CollectionAcceptsType(collectionType, type))
                        { 
                            ValidationError(SR.UnassignableCollection, type, collectionType.ItemType, collectionType);
                        }
                    }
                    else if (parentMember != null && !parentMember.IsUnknown && 
                        !type.CanAssignTo(parentMember.Type) && parentMember.DeferringLoader == null)
                    { 
                        ValidationError(SR.UnassignableObject, type, parentMember.Type, parentMember); 
                    }
                } 
            }

            // Update the NameScope stack
            if (type.IsNameScope && this.xamlStack.Count > 1) 
            {
                PushNameScope(); 
            } 
            CurrentNameScope.Depth++;
        } 

        void ValidateGetObject()
        {
            XamlType type = this.xamlStack.Previous(1).Member.Type; 
            if (type.IsNameScope)
            { 
                PushNameScope(); 
            }
            CurrentNameScope.Depth++; 
        }

        // Check whether a member is set more than once
        bool ValidateSetMember() 
        {
            XamlFrame frame = this.xamlStack.Current; 
            if (frame != null) 
            {
                if (frame.MemberIsSet && !frame.Member.IsUnknown && !frame.Member.IsDirective) 
                {
                    ValidationError(SR.MemberCanOnlyBeSetOnce, frame.Member);
                    return false;
                } 
                frame.MemberIsSet = true;
            } 
            return true; 
        }
 
        bool CollectionAcceptsType(XamlType collectionType, XamlType type)
        {
            return collectionType.IsUnknown ||
                collectionType.AllowedContentTypes == null || 
                collectionType.AllowedContentTypes.Any(contentType => type.CanAssignTo(contentType));
        } 
 
        void ValidateStartMember()
        { 
            XamlFrame currentFrame = this.xamlStack.Current;
            XamlMember member = currentFrame.Member;

            // Make sure that the member is known. 
            // Don't bother surfacing an error for unknown instance properties or unknown content on
            // unknown types. It's redundant, since we'll already surface an error for the unknown type. 
            if (member == XamlLanguage.UnknownContent) 
            {
                if (!currentFrame.Type.IsUnknown) 
                {
                    ValidationError(SR.UnknownContent, this.xamlStack.Current.Type);
                }
            } 
            else if (member.IsUnknown && (member.IsAttachable || member.IsDirective || !member.DeclaringType.IsUnknown))
            { 
                ValidationError(SR.UnresolvedMember, member); 
            }
 
            // Check for duplicate members
            if (currentFrame.PastMembers == null)
            {
                currentFrame.PastMembers = new HashSet(); 
            }
            if (currentFrame.PastMembers.Contains(member)) 
            { 
                ValidationError(SR.DuplicateMember, member);
            } 
            else
            {
                currentFrame.PastMembers.Add(member);
            } 

            // Check for misplaced attachable members 
            if (member.IsAttachable && !currentFrame.Type.IsUnknown && !currentFrame.Type.CanAssignTo(member.TargetType)) 
            {
                ValidationError(SR.MemberOnBadTargetType, member, member.TargetType); 
            }

            // Update the NameScope stack
            if (member.DeferringLoader != null) 
            {
                PushNameScope(); 
            } 
            CurrentNameScope.Depth++;
        } 

        void ValidateEndMember()
        {
            DecrementNameScopeDepth(); 
        }
 
        void ValidateEndObject() 
        {
            DecrementNameScopeDepth(); 
        }

        void ValidateValue(object value)
        { 
            XamlType type = this.xamlStack.Current.Type;
            XamlMember member = this.xamlStack.Current.Member; 
            string valueString = value as string; 
            if (valueString == null || member.IsUnknown ||  !ValidateSetMember() || IsWhitespace(valueString))
            { 
                return;
            }

            // Check if this is x:Name or RuntimeNameProperty 
            if (member == XamlLanguage.Name || (type != null && member == type.GetAliasedProperty(XamlLanguage.Name)))
            { 
                if (!CurrentNameScope.RegisterName(valueString, CurrentFragment)) 
                {
                    ValidationError(SR.DuplicateName, valueString); 
                }
                return;
            }
 
            // Check if this is an x:Reference
            if (type == XamlLanguage.Reference && (member == this.nameOfReference || member == XamlLanguage.PositionalParameters)) 
            { 
                CurrentNameScope.AddNeededName(CurrentFragment, valueString, this.lineNumber, this.linePosition);
                return; 
            }
            XamlValueConverter converter =
                (member == XamlLanguage.Initialization) ? type.TypeConverter : member.TypeConverter;
            if (converter != null && converter.ConverterType == typeof(NameReferenceConverter)) 
            {
                CurrentNameScope.AddNeededName(CurrentFragment, valueString, this.lineNumber, this.linePosition); 
            } 

            // Check if text is supported on this member 
            if (member == XamlLanguage.Initialization)
            {
                if (!type.IsUnknown && type.TypeConverter == null && !XamlLanguage.String.CanAssignTo(type))
                { 
                    ValidationError(SR.NoTypeConverter, type);
                } 
            } 
            else if (member.IsDirective)
            { 
                if (member == XamlLanguage.Items)
                {
                    if (type == null)
                    { 
                        // Inside a GetObject - get the type from the parent member
                        type = this.xamlStack.Previous(1).Member.Type; 
                    } 
                    if (!CollectionAcceptsType(type, XamlLanguage.String))
                    { 
                        ValidationError(SR.NoTextInCollection, type);
                    }
                }
            } 
            else if (member.TypeConverter == null && !XamlLanguage.String.CanAssignTo(member.Type) &&
                (member.DeferringLoader == null || member.DeferringLoader == this.activityLoader)) 
            { 
                ValidationError(SR.NoTextInProperty, member);
            } 
        }

        void ValidationError(string message, params object[] arguments)
        { 
            ValidationError(message, this.lineNumber, this.linePosition, arguments);
            CurrentFragment.HasError = true; 
        } 

        void ValidationError(string message, int lineNumber, int linePosition, params object[] arguments) 
        {
            // The default ToString implementations can be very clunky, especially for generics.
            // Use our own friendlier versions instead.
            for (int i = 0; i < arguments.Length; i++) 
            {
                XamlType type = arguments[i] as XamlType; 
                if (type != null) 
                {
                    arguments[i] = GetXamlTypeName(type); 
                }
                else
                {
                    XamlMember member = arguments[i] as XamlMember; 
                    if (member != null)
                    { 
                        arguments[i] = GetXamlMemberName(member); 
                    }
                } 
            }
            string error = string.Format(CultureInfo.CurrentCulture, message, arguments);
            if (LoadErrors == null)
            { 
                LoadErrors = new List();
            } 
            LoadErrors.Add(new XamlLoadErrorInfo(error, lineNumber, linePosition)); 
        }
 
        [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotIgnoreMethodResults, Justification =
            "StringBuilder.Append just returns the same instance that was called")]
        void ValidationErrorUnknownType(XamlType type)
        { 
            StringBuilder result = new StringBuilder();
            string clrns, assembly; 
            if (XamlNamespaceHelper.TryParseClrNsUri(type.PreferredXamlNamespace, out clrns, out assembly)) 
            {
                if (assembly == null) 
                {
                    assembly = this.LocalAssemblyName;
                }
                StringBuilder typeName = new StringBuilder(); 
                typeName.Append(clrns);
                typeName.Append("."); 
                AppendShortName(typeName, type); 
                ValidationError(SR.UnresolvedTypeInAssembly, typeName, assembly);
            } 
            else
            {
                StringBuilder typeName = new StringBuilder();
                AppendShortName(typeName, type); 
                ValidationError(SR.UnresolvedTypeInNamespace, typeName, type.PreferredXamlNamespace);
            } 
        } 

        void DecrementNameScopeDepth() 
        {
            CurrentNameScope.Depth--;
            if (CurrentNameScope.Depth == 0)
            { 
                this.poppedNameScopes.Add(this.nameScopeStack.Pop());
            } 
        } 

        // Resolves all simple name references in the tree, raising validation errors for any that 
        // can't be resolved.
        void CompleteNameReferences()
        {
            foreach (NameScopeFrame nameScope in this.poppedNameScopes) 
            {
                if (nameScope.NeededNames == null) 
                { 
                    continue;
                } 
                foreach (NameReference reference in nameScope.NeededNames)
                {
                    XamlFragment target = nameScope.FindName(reference.Name);
                    if (target == null) 
                    {
                        ValidationError(SR.UnresolvedName, reference.LineNumber, reference.LinePosition, reference.Name); 
                        reference.Fragment.HasError = true; 
                    }
                    else 
                    {
                        if (target.ReferencedBy == null)
                        {
                            target.ReferencedBy = new HashSet(); 
                        }
                        target.ReferencedBy.Add(reference.Fragment); 
                    } 
                }
            } 
        }

        class XamlFrame
        { 
            public XamlType Type { get; set; }
            public XamlMember Member { get; set; } 
            public bool MemberIsSet { get; set; } 
            public NamespaceStackNode Namespaces { get; set; }
            public HashSet PastMembers { get; set; } 
        }

        // A stack that is implemented as a list to allow walking up the stack.
        class WalkableStack : List where T : class 
        {
            public T Pop() 
            { 
                T result = this[Count - 1];
                this.RemoveAt(Count - 1); 
                return result;
            }

            public T Previous(int index) 
            {
                return this[Count - 1 - index]; 
            } 

            public void Push(T frame) 
            {
                Add(frame);
            }
 
            public T Current
            { 
                get { return Count > 0 ? this[Count - 1] : null; } 
            }
        } 

        // Class to buffer a tree of XAML fragments and write them back out in the correct order.
        class XamlFragment
        { 
            private XamlFragment firstChild;
            private XamlFragment nextSibling; 
 
            public XamlFragment(XamlSchemaContext schemaContext)
            { 
                NodeQueue = new XamlNodeQueue(schemaContext);
            }

            public XamlNodeQueue NodeQueue { get; private set; } 
            public int ObjectDepth { get; set; }
            public bool HasError { get; set; } 
            public NamespaceStackNode Namespaces { get; set; } 
            public HashSet ReferencedBy { get; set; }
 
            // Adds a child fragment at the current position of the NodeQueue.
            // We store the fragment as a Value Node, and expand out its contents at Write time.
            // We also store the fragments in a simple tree structure to so we can iterate them quickly.
            public void AddChild(XamlFragment newChild) 
            {
                NodeQueue.Writer.WriteValue(newChild); 
                XamlFragment curChild = this.firstChild; 
                if (curChild == null)
                { 
                    this.firstChild = newChild;
                }
                else
                { 
                    while (curChild.nextSibling != null)
                    { 
                        curChild = curChild.nextSibling; 
                    }
                    curChild.nextSibling = newChild; 
                }
            }

            // Find all references to error fragments and mark the referencing fragments as also errored. 
            public static void FindBrokenReferences(XamlFragment rootFragment)
            { 
                // By starting from the root of the tree and walking its children, we ensure we traverse 
                // each node at least once, and so find every error fragment.
                // Given an error fragment, we want to mark all its children and all its referencing fragments 
                // as errors, and process them recursively.
                Queue queue = new Queue();
                queue.Enqueue(rootFragment);
                while (queue.Count > 0) 
                {
                    if (rootFragment.HasError) 
                    { 
                        // We found an error at the root. We won't be able to load any part of the document,
                        // so skip this redundant processing. 
                        return;
                    }

                    XamlFragment current = queue.Dequeue(); 
                    if (current.HasError)
                    { 
                        // Mark all this fragment's children as errored, and enqueue them for recursive processing. 
                        XamlFragment child = current.firstChild;
                        while (child != null) 
                        {
                            child.HasError = true;
                            queue.Enqueue(child);
                            child = child.nextSibling; 
                        }
 
                        // Mark all fragments that reference this fragment as errored, and enqueue them for recursive processing. 
                        if (current.ReferencedBy != null)
                        { 
                            foreach (XamlFragment referencingFragment in current.ReferencedBy)
                            {
                                referencingFragment.HasError = true;
                                queue.Enqueue(referencingFragment); 
                            }
                        } 
 
                        // Clear the links so that we don't traverse them again if there is a cycle.
                        current.firstChild = null; 
                        current.ReferencedBy = null;
                    }
                    else
                    { 
                        // This fragment is healthy, but we need to check for any errors in its children.
                        // Don't remove the children, we'll need to traverse them if this fragment gets 
                        // marked as errored later. 
                        XamlFragment child = current.firstChild;
                        while (child != null) 
                        {
                            queue.Enqueue(child);
                            child = child.nextSibling;
                        } 
                    }
                } 
            } 

            // Write this fragment and all its children out to the specified writer. 
            public virtual void WriteTo(XamlWriter writer, bool parentHasError)
            {
                // In the constrained designer scenario, we can always assume that there is line info.
                XamlReader nodeReader = NodeQueue.Reader; 
                IXamlLineInfo lineInfo = (IXamlLineInfo)nodeReader;
                IXamlLineInfoConsumer lineInfoConsumer = (IXamlLineInfoConsumer)writer; 
                int lineNumber = 0; 
                int linePosition = 0;
 
                while (nodeReader.Read())
                {
                    if (lineInfo.LineNumber > 0 &&
                            (lineInfo.LineNumber != lineNumber || lineInfo.LinePosition != linePosition)) 
                    {
                        lineNumber = lineInfo.LineNumber; 
                        linePosition = lineInfo.LinePosition; 
                        lineInfoConsumer.SetLineInfo(lineNumber, linePosition);
                    } 
                    XamlFragment child = (nodeReader.NodeType == XamlNodeType.Value) ? nodeReader.Value as XamlFragment : null;
                    if (child != null)
                    {
                        child.WriteTo(writer, parentHasError || HasError); 
                    }
                    else 
                    { 
                        writer.WriteNode(nodeReader);
                    } 
                }
            }
        }
 
        class ActivityFragment : XamlFragment
        { 
            public ActivityFragment(XamlSchemaContext schemaContext) 
                :base(schemaContext)
            { 
            }

            public Type Type { get; set; }
 
            // We can only construct an ErrorActivity that is assignable to properties of type
            // Activity or Activity, not any of their descendants. 
            public static bool IsActivityType(Type type) 
            {
                return type == typeof(Activity) || 
                    (type.IsGenericType &&
                    type.GetGenericTypeDefinition() == typeof(Activity<>));
            }
 
            public override void WriteTo(XamlWriter writer, bool parentHasError)
            { 
                if (HasError && !parentHasError) 
                {
                    Fx.Assert(this.Type != null && IsActivityType(this.Type), "Cannot create ErrorActivity for non-Activity property"); 
                    Type errorType;
                    if (this.Type == typeof(Activity))
                    {
                        errorType = typeof(ErrorActivity); 
                    }
                    else 
                    { 
                        errorType = typeof(ErrorActivity<>).MakeGenericType(this.Type.GetGenericArguments()[0]);
                    } 
                    XamlType errorXamlType = writer.SchemaContext.GetXamlType(errorType);
                    writer.WriteStartObject(errorXamlType);
                    writer.WriteStartMember(errorXamlType.GetMember(ErrorActivity.ErrorNodesProperty));
 
                    XamlNodeList errorNodes = GetErrorNodes();
                    ErrorActivity.WriteNodeList(writer, errorNodes); 
 
                    writer.WriteEndMember(); // 
                    writer.WriteEndObject(); //  
                }
                else
                {
                    base.WriteTo(writer, parentHasError); 
                }
            } 
 
            // Extracts the Error Nodes contents out of the ErrorActivity and writes them to the
            // specified writer. 
            // Expects reader to be positioned on SO ErrorActivity and leaves it on corresponding EO.
            public static void TransformErrorActivityContents(XamlObjectReader objectReader, XamlWriter writer,
                NamespaceStackNode currentNamespaces)
            { 
                XamlMember errorNodesMember = objectReader.Type.GetMember(ErrorActivity.ErrorNodesProperty);
                // Skip past off  
                objectReader.Read(); 

                do 
                {
                    Fx.Assert(objectReader.NodeType == XamlNodeType.StartMember, "Expected StartMember");
                    if (objectReader.Member == errorNodesMember)
                    { 
                        // Skip past 
                        objectReader.Read(); 
 
                        // Skip past the dummy StartObject & StartMember
                        Fx.Assert(objectReader.NodeType == XamlNodeType.StartObject, "Expected StartObject"); 
                        objectReader.Read();
                        Fx.Assert(objectReader.NodeType == XamlNodeType.StartMember, "Expected StartMember");
                        objectReader.Read();
 
                        // Strip redundant namespaces
                        while (objectReader.NodeType == XamlNodeType.NamespaceDeclaration) 
                        { 
                            string ns = currentNamespaces.LookupNamespace(objectReader.Namespace.Prefix);
                            if (ns != objectReader.Namespace.Namespace && 
                                !IsIgnorableCompatNamespace(objectReader.Namespace, currentNamespaces))
                            {
                                writer.WriteNamespace(objectReader.Namespace);
                            } 
                            objectReader.Read();
                        } 
 
                        // Pass through the original contents, stripping out any hidden APs added by
                        // the XamlDebuggerXmlReader, since XOR wouldn't write them out. 
                        XamlType debuggerReaderType = objectReader.SchemaContext.GetXamlType(typeof(XamlDebuggerXmlReader));
                        Fx.Assert(objectReader.NodeType == XamlNodeType.StartObject, "Expected StartObject");
                        XamlReader subReader = objectReader.ReadSubtree();
                        subReader.Read(); 
                        while (!subReader.IsEof)
                        { 
                            if (subReader.NodeType == XamlNodeType.StartMember && 
                                subReader.Member.DeclaringType == debuggerReaderType &&
                                subReader.Member.SerializationVisibility == DesignerSerializationVisibility.Hidden) 
                            {
                                subReader.Skip();
                            }
                            else 
                            {
                                writer.WriteNode(subReader); 
                                subReader.Read(); 
                            }
                        } 

                        // Close out the dummy StartObject & StartMember
                        Fx.Assert(objectReader.NodeType == XamlNodeType.EndMember, "Expected EndMember");
                        objectReader.Read(); 
                        Fx.Assert(objectReader.NodeType == XamlNodeType.EndObject, "Expected EndObject");
                        objectReader.Read(); 
 
                        // Skip past 
                        Fx.Assert(objectReader.NodeType == XamlNodeType.EndMember, "Expected EndMember"); 
                        objectReader.Read();
                    }
                    else
                    { 
                        // Skip any APs added by the designer
                        Fx.Assert(objectReader.Member.IsAttachable, "Unexpected member on ErrorActivity"); 
                        objectReader.Skip(); 
                    }
                } 
                while (objectReader.NodeType != XamlNodeType.EndObject); // 
            }

            // If the namespace is the markup-compat namespace, we skip writing it out as long as there 
            // is an ignorable namespace at the root of the doc; DesignTimeXamlWriter will add it to the
            // root later. We assume that the exact prefix for markup-compat doesn't matter, just whether the 
            // namespace is defined. 
            static bool IsIgnorableCompatNamespace(NamespaceDeclaration ns, NamespaceStackNode currentNamespaces)
            { 
                if (ns.Namespace == NameSpaces.Mc)
                {
                    NamespaceStackNode rootNamespaces = currentNamespaces;
                    while (rootNamespaces.PreviousNode != null) 
                    {
                        rootNamespaces = rootNamespaces.PreviousNode; 
                    } 
                    foreach (string rootNs in rootNamespaces.Values)
                    { 
                        if (NameSpaces.ShouldIgnore(rootNs))
                        {
                            return true;
                        } 
                    }
                } 
                return false; 
            }
 
            XamlNodeList GetErrorNodes()
            {
                XamlNodeList result = new XamlNodeList(NodeQueue.Writer.SchemaContext);
 
                // Dummy StartObject & StartMember. This is here so that ObjectReader doesn't try
                // to hoist all the namespaces on save, which would cause them to be added as 
                // Imports by the VBExpression converter. 
                result.Writer.WriteStartObject(XamlLanguage.Object);
                result.Writer.WriteStartMember(XamlLanguage.UnknownContent); 

                // Write out all namespaces in scope to the NodeList, to ensure that the any type
                // converters that use namespaces/prefixes still work on round trip.
                // (We can strip out the redundant ones on Save.) 
                foreach (KeyValuePair ns in Namespaces.FlattenNamespaces())
                { 
                    result.Writer.WriteNamespace(new NamespaceDeclaration(ns.Value, ns.Key)); 
                }
 
                // Write out the original contents of this fragment, expanding our children if any.
                base.WriteTo(result.Writer, true);

                // Close the dummy object 
                result.Writer.WriteEndMember();
                result.Writer.WriteEndObject(); 
 
                result.Writer.Close();
                return result; 
            }
        }

        class NameScopeFrame 
        {
            private Dictionary declaredNames; 
            private List neededNames; 

            public NameScopeFrame Parent { get; private set; } 
            public int Depth { get; set; }
            public List NeededNames { get { return this.neededNames; } }

            public NameScopeFrame(NameScopeFrame parent) 
            {
                Parent = parent; 
            } 

            public void AddNeededName(XamlFragment fragment, string name, int lineNumber, int linePosition) 
            {
                if (this.neededNames == null)
                {
                    this.neededNames = new List(); 
                }
                this.neededNames.Add(new NameReference 
                { 
                    Fragment = fragment,
                    Name = name, 
                    LineNumber = lineNumber,
                    LinePosition = linePosition
                });
            } 

            public XamlFragment FindName(string name) 
            { 
                NameScopeFrame current = this;
                do 
                {
                    XamlFragment result = null;
                    if (current.declaredNames != null && current.declaredNames.TryGetValue(name, out result))
                    { 
                        return result;
                    } 
                    current = current.Parent; 
                }
                while (current != null); 
                return null;
            }

            public bool RegisterName(string name, XamlFragment containingFragment) 
            {
                if (this.declaredNames == null) 
                { 
                    this.declaredNames = new Dictionary();
                } 
                if (this.declaredNames.ContainsKey(name))
                {
                    return false;
                } 
                this.declaredNames.Add(name, containingFragment);
                return true; 
            } 
        }
 
        class NameReference
        {
            public XamlFragment Fragment { get; set; }
            public string Name { get; set; } 
            public int LineNumber { get; set; }
            public int LinePosition { get; set; } 
        } 

        class NamespaceStackNode : Dictionary 
        {
            public NamespaceStackNode PreviousNode { get; set; }

            public int ObjectDepth { get; set; } 

            public IEnumerable> FlattenNamespaces() 
            { 
                // We need to hide not only any shadowed prefixes, but any shadowed namespaces, since
                // XamlXmlWriter doesn't allow declaration of multiple prefixes for the same namespaace 
                // at the same scope.
                HashSet prefixes = new HashSet();
                HashSet namespaces = new HashSet();
                NamespaceStackNode current = this; 
                do
                { 
                    foreach (KeyValuePair pair in current) 
                    {
                        if (!prefixes.Contains(pair.Key) && !namespaces.Contains(pair.Value)) 
                        {
                            yield return pair;
                            prefixes.Add(pair.Key);
                            namespaces.Add(pair.Value); 
                        }
                    } 
                    current = current.PreviousNode; 
                }
                while (current != null); 
            }

            public string LookupNamespace(string prefix)
            { 
                NamespaceStackNode current = this;
                do 
                { 
                    string ns;
                    if (current.TryGetValue(prefix, out ns)) 
                    {
                        return ns;
                    }
                    current = current.PreviousNode; 
                }
                while (current != null); 
                return null; 
            }
        } 
    }
}

// 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