//----------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//----------------------------------------------------------------
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 sequenceNumberToObjectMap;
private HashSet visitedObjects;
private Stack objects = new Stack();
private XamlMember xamlMember = null;
public XamlObjectReaderWithSequence(object instance, XamlSchemaContext schemaContext)
: base(instance, schemaContext)
{
}
public Dictionary SequenceNumberToObjectMap
{
get
{
if (this.sequenceNumberToObjectMap == null)
{
this.sequenceNumberToObjectMap = new Dictionary();
}
return this.sequenceNumberToObjectMap;
}
}
private HashSet VisitedObjects
{
get
{
if (this.visitedObjects == null)
{
this.visitedObjects = new HashSet();
}
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 [expression]
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 1 ... , 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++;
}
}
}
}