168 lines
5.9 KiB
C#
168 lines
5.9 KiB
C#
//----------------------------------------------------------------
|
|
// <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++;
|
|
}
|
|
}
|
|
}
|
|
}
|