[corlib] Update ValueTuple implementation
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / Microsoft.Tools.Common / Microsoft / Activities / Presentation / Xaml / XamlObjectReaderWithSequence.cs
1 //----------------------------------------------------------------
2 // <copyright company="Microsoft Corporation">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //----------------------------------------------------------------
6
7 namespace Microsoft.Activities.Presentation.Xaml
8 {
9     using System.Activities;
10     using System.Activities.XamlIntegration;
11     using System.Collections.Generic;
12     using System.Reflection;
13     using System.Xaml;
14
15     internal sealed class XamlObjectReaderWithSequence : XamlObjectReader
16     {
17         private int sequenceNumber;
18         private Dictionary<int, object> sequenceNumberToObjectMap;
19         private HashSet<object> visitedObjects;
20
21         private Stack<object> objects = new Stack<object>();
22         private XamlMember xamlMember = null;
23
24         public XamlObjectReaderWithSequence(object instance, XamlSchemaContext schemaContext)
25             : base(instance, schemaContext)
26         {
27         }
28
29         public Dictionary<int, object> SequenceNumberToObjectMap
30         {
31             get
32             {
33                 if (this.sequenceNumberToObjectMap == null)
34                 {
35                     this.sequenceNumberToObjectMap = new Dictionary<int, object>();
36                 }
37
38                 return this.sequenceNumberToObjectMap;
39             }
40         }
41
42         private HashSet<object> VisitedObjects
43         {
44             get
45             {
46                 if (this.visitedObjects == null)
47                 {
48                     this.visitedObjects = new HashSet<object>();
49                 }
50
51                 return this.visitedObjects;
52             }
53         }
54
55         public override bool Read()
56         {
57             bool readResult = base.Read();
58
59             if (readResult)
60             {
61                 switch (this.NodeType)
62                 {
63                     case XamlNodeType.StartObject:
64                         this.objects.Push(this.Instance);
65                         this.MapObjectWithSequenceNumber(this.Instance);
66                         break;
67                     case XamlNodeType.GetObject:
68                         this.objects.Push(this.Instance);
69                         break;
70                     case XamlNodeType.EndObject:
71                         this.objects.Pop();
72                         break;
73                     case XamlNodeType.StartMember:
74                         this.xamlMember = this.Member;
75                         break;
76                     case XamlNodeType.EndMember:
77                         this.xamlMember = null;
78                         break;
79                     case XamlNodeType.Value:
80                         this.MapObjectWithSequenceNumber(this.GetRealObject());
81                         break;
82                 }
83             }
84
85             return readResult;
86         }
87
88         // Current Node contains the value after original object is serialized to a ValueNode, this method
89         // try to get the original object before it is serialized.
90         private object GetRealObject()
91         {
92             if (this.Value is string)
93             {
94                 object parent = this.objects.Peek();
95
96                 if (parent == null)
97                 {
98                     return null;
99                 }
100
101                 // handle <InArgument x:TypeArguments="...">[expression]</InArgument>
102                 if (this.xamlMember == XamlLanguage.Initialization)
103                 {
104                     Argument argument = parent as Argument;
105                     if (argument != null)
106                     {
107                         return argument.Expression;
108                     }
109
110                     return null;
111                 }
112
113                 if (this.xamlMember == null || !this.xamlMember.IsNameValid || this.xamlMember.IsAttachable || this.xamlMember.IsDirective)
114                 {
115                     return null;
116                 }
117
118                 // handle <x:Array Type="x:Int32"><x:Int32>1</x:Int32>...</x:Array>, type is not limited to x:Int32
119                 // Here property.DeclaringType would be ArrayExtension, while parent would be a real array (System.Int32[]), 
120                 // calling property.GetValue(parent, null) would cause a TargetException since the type and object doesn't match.
121                 // So stop further processing for ArrayExtension.
122                 if (this.xamlMember.DeclaringType == XamlLanguage.Array)
123                 {
124                     // We can get the element type by parent.GetType().GetElementType(),
125                     // but we really don't care about that, so just return null.
126                     return null;
127                 }
128
129                 PropertyInfo property = this.xamlMember.UnderlyingMember as PropertyInfo;
130
131                 if (property == null || !property.CanRead)
132                 {
133                     return null;
134                 }
135
136                 object realObject = property.GetValue(parent, null);
137
138                 // NOTE this is to handle argument containing an IValueSerializable expression
139                 // this logic should be kept the same as that in System.Activities.Debugger.XamlDebuggerXmlReader.NotifySourceLocationFound.
140                 Argument argumentObject = realObject as Argument;
141                 if (argumentObject != null && argumentObject.Expression is IValueSerializableExpression)
142                 {
143                     realObject = argumentObject.Expression;
144                 }
145
146                 return realObject;
147             }
148
149             return this.Value;
150         }
151
152         private void MapObjectWithSequenceNumber(object mappedObject)
153         {
154             if (mappedObject == null)
155             {
156                 return;
157             }
158
159             if (!this.VisitedObjects.Contains(mappedObject) && !(mappedObject is string))
160             {
161                 this.VisitedObjects.Add(mappedObject);
162                 this.SequenceNumberToObjectMap.Add(this.sequenceNumber, mappedObject);
163                 this.sequenceNumber++;
164             }
165         }
166     }
167 }