File: Microsoft.Tools.Common\Microsoft\Activities\Presentation\Xaml\XamlObjectReaderWithSequence.cs
Project: ndp\cdf\src\NetFx40\Tools\System.Activities.Presentation.csproj (System.Activities.Presentation)
//----------------------------------------------------------------
// <copyright company="Microsoft Corporation">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//----------------------------------------------------------------
 
namespace Microsoft.Activities.Presentation.Xaml
{
    using System.Activities;
    using System.Activities.XamlIntegration;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Xaml;
 
    internal sealed class XamlObjectReaderWithSequence : XamlObjectReader
    {
        private int sequenceNumber;
        private Dictionary<int, object> sequenceNumberToObjectMap;
        private HashSet<object> visitedObjects;
 
        private Stack<object> objects = new Stack<object>();
        private XamlMember xamlMember = null;
 
        public XamlObjectReaderWithSequence(object instance, XamlSchemaContext schemaContext)
            : base(instance, schemaContext)
        {
        }
 
        public Dictionary<int, object> SequenceNumberToObjectMap
        {
            get
            {
                if (this.sequenceNumberToObjectMap == null)
                {
                    this.sequenceNumberToObjectMap = new Dictionary<int, object>();
                }
 
                return this.sequenceNumberToObjectMap;
            }
        }
 
        private HashSet<object> VisitedObjects
        {
            get
            {
                if (this.visitedObjects == null)
                {
                    this.visitedObjects = new HashSet<object>();
                }
 
                return this.visitedObjects;
            }
        }
 
        public override bool Read()
        {
            bool readResult = base.Read();
 
            if (readResult)
            {
                switch (this.NodeType)
                {
                    case XamlNodeType.StartObject:
                        this.objects.Push(this.Instance);
                        this.MapObjectWithSequenceNumber(this.Instance);
                        break;
                    case XamlNodeType.GetObject:
                        this.objects.Push(this.Instance);
                        break;
                    case XamlNodeType.EndObject:
                        this.objects.Pop();
                        break;
                    case XamlNodeType.StartMember:
                        this.xamlMember = this.Member;
                        break;
                    case XamlNodeType.EndMember:
                        this.xamlMember = null;
                        break;
                    case XamlNodeType.Value:
                        this.MapObjectWithSequenceNumber(this.GetRealObject());
                        break;
                }
            }
 
            return readResult;
        }
 
        // Current Node contains the value after original object is serialized to a ValueNode, this method
        // try to get the original object before it is serialized.
        private object GetRealObject()
        {
            if (this.Value is string)
            {
                object parent = this.objects.Peek();
 
                if (parent == null)
                {
                    return null;
                }
 
                // handle <InArgument x:TypeArguments="...">[expression]</InArgument>
                if (this.xamlMember == XamlLanguage.Initialization)
                {
                    Argument argument = parent as Argument;
                    if (argument != null)
                    {
                        return argument.Expression;
                    }
 
                    return null;
                }
 
                if (this.xamlMember == null || !this.xamlMember.IsNameValid || this.xamlMember.IsAttachable || this.xamlMember.IsDirective)
                {
                    return null;
                }
 
                // handle <x:Array Type="x:Int32"><x:Int32>1</x:Int32>...</x:Array>, type is not limited to x:Int32
                // Here property.DeclaringType would be ArrayExtension, while parent would be a real array (System.Int32[]), 
                // calling property.GetValue(parent, null) would cause a TargetException since the type and object doesn't match.
                // So stop further processing for ArrayExtension.
                if (this.xamlMember.DeclaringType == XamlLanguage.Array)
                {
                    // We can get the element type by parent.GetType().GetElementType(),
                    // but we really don't care about that, so just return null.
                    return null;
                }
 
                PropertyInfo property = this.xamlMember.UnderlyingMember as PropertyInfo;
 
                if (property == null || !property.CanRead)
                {
                    return null;
                }
 
                object realObject = property.GetValue(parent, null);
 
                // NOTE this is to handle argument containing an IValueSerializable expression
                // this logic should be kept the same as that in System.Activities.Debugger.XamlDebuggerXmlReader.NotifySourceLocationFound.
                Argument argumentObject = realObject as Argument;
                if (argumentObject != null && argumentObject.Expression is IValueSerializableExpression)
                {
                    realObject = argumentObject.Expression;
                }
 
                return realObject;
            }
 
            return this.Value;
        }
 
        private void MapObjectWithSequenceNumber(object mappedObject)
        {
            if (mappedObject == null)
            {
                return;
            }
 
            if (!this.VisitedObjects.Contains(mappedObject) && !(mappedObject is string))
            {
                this.VisitedObjects.Add(mappedObject);
                this.SequenceNumberToObjectMap.Add(this.sequenceNumber, mappedObject);
                this.sequenceNumber++;
            }
        }
    }
}