File: XamlDebuggerXmlReader.cs

package info (click to toggle)
mono 6.8.0.105%2Bdfsg-3.3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,284,512 kB
  • sloc: cs: 11,172,132; xml: 2,850,069; ansic: 671,653; cpp: 122,091; perl: 59,366; javascript: 30,841; asm: 22,168; makefile: 20,093; sh: 15,020; python: 4,827; pascal: 925; sql: 859; sed: 16; php: 1
file content (839 lines) | stat: -rw-r--r-- 38,450 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
// <copyright>
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>

namespace System.Activities.Debugger
{
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.IO;
    using System.Reflection;
    using System.Runtime;
    using System.Xaml;
    using System.Xaml.Schema;
    using System.ComponentModel;
    using System.Windows.Markup;
    using System.Activities.XamlIntegration;

    public class XamlDebuggerXmlReader : XamlReader, IXamlLineInfo
    {
        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
        public static readonly AttachableMemberIdentifier StartLineName = new AttachableMemberIdentifier(typeof(XamlDebuggerXmlReader), StartLineMemberName);

        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
        public static readonly AttachableMemberIdentifier StartColumnName = new AttachableMemberIdentifier(typeof(XamlDebuggerXmlReader), StartColumnMemberName);

        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
        public static readonly AttachableMemberIdentifier EndLineName = new AttachableMemberIdentifier(typeof(XamlDebuggerXmlReader), EndLineMemberName);

        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
        public static readonly AttachableMemberIdentifier EndColumnName = new AttachableMemberIdentifier(typeof(XamlDebuggerXmlReader), EndColumnMemberName);

        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
        public static readonly AttachableMemberIdentifier FileNameName = new AttachableMemberIdentifier(typeof(XamlDebuggerXmlReader), FileNameMemberName);

        private const string StartLineMemberName = "StartLine";
        private const string StartColumnMemberName = "StartColumn";
        private const string EndLineMemberName = "EndLine";
        private const string EndColumnMemberName = "EndColumn";
        private const string FileNameMemberName = "FileName";

        private static readonly Type attachingType = typeof(XamlDebuggerXmlReader);
        private static readonly MethodInfo startLineGetterMethodInfo = attachingType.GetMethod("GetStartLine", BindingFlags.Public | BindingFlags.Static);
        private static readonly MethodInfo startLineSetterMethodInfo = attachingType.GetMethod("SetStartLine", BindingFlags.Public | BindingFlags.Static);
        private static readonly MethodInfo startColumnGetterMethodInfo = attachingType.GetMethod("GetStartColumn", BindingFlags.Public | BindingFlags.Static);
        private static readonly MethodInfo startColumnSetterMethodInfo = attachingType.GetMethod("SetStartColumn", BindingFlags.Public | BindingFlags.Static);
        private static readonly MethodInfo endLineGetterMethodInfo = attachingType.GetMethod("GetEndLine", BindingFlags.Public | BindingFlags.Static);
        private static readonly MethodInfo endLineSetterMethodInfo = attachingType.GetMethod("SetEndLine", BindingFlags.Public | BindingFlags.Static);
        private static readonly MethodInfo endColumnGetterMethodInfo = attachingType.GetMethod("GetEndColumn", BindingFlags.Public | BindingFlags.Static);
        private static readonly MethodInfo endColumnSetterMethodInfo = attachingType.GetMethod("SetEndColumn", BindingFlags.Public | BindingFlags.Static);

        private XamlMember startLineMember;
        private XamlMember startColumnMember;
        private XamlMember endLineMember;
        private XamlMember endColumnMember;
        private XamlSchemaContext schemaContext;
        private IXamlLineInfo xamlLineInfo;
        private XmlReaderWithSourceLocation xmlReaderWithSourceLocation;
        private XamlReader underlyingReader;
        private Stack<XamlNode> objectDeclarationRecords;
        private Dictionary<XamlNode, DocumentRange> initializationValueRanges;
        private Queue<XamlNode> bufferedXamlNodes;
        private XamlSourceLocationCollector sourceLocationCollector;
        private XamlNode current;
        private bool collectNonActivitySourceLocation;
        private int suppressMarkupExtensionLevel;

        public XamlDebuggerXmlReader(TextReader underlyingTextReader)
            : this(underlyingTextReader, new XamlSchemaContext())
        {
        }

        public XamlDebuggerXmlReader(TextReader underlyingTextReader, XamlSchemaContext schemaContext)
            : this(underlyingTextReader, schemaContext, localAssembly: null)
        {
        }

        internal XamlDebuggerXmlReader(TextReader underlyingTextReader, XamlSchemaContext schemaContext, Assembly localAssembly)
        {
            UnitTestUtility.Assert(underlyingTextReader != null, "underlyingTextReader should not be null and is ensured by caller.");
            this.xmlReaderWithSourceLocation = new XmlReaderWithSourceLocation(underlyingTextReader);
            this.underlyingReader = new XamlXmlReader(this.xmlReaderWithSourceLocation, schemaContext, new XamlXmlReaderSettings { ProvideLineInfo = true, LocalAssembly = localAssembly });
            this.xamlLineInfo = (IXamlLineInfo)this.underlyingReader;
            UnitTestUtility.Assert(this.xamlLineInfo.HasLineInfo, "underlyingReader is constructed with the ProvideLineInfo option above.");
            this.schemaContext = schemaContext;
            this.objectDeclarationRecords = new Stack<XamlNode>();
            this.initializationValueRanges = new Dictionary<XamlNode, DocumentRange>();
            this.bufferedXamlNodes = new Queue<XamlNode>();
            this.current = this.CreateCurrentNode();
            this.SourceLocationFound += XamlDebuggerXmlReader.SetSourceLocation;
        }

        // A XamlReader that need to collect source level information is necessary
        // the one that is closest to the source document.
        // This constructor is fundamentally flawed because it allows any XAML reader
        // Which could output some XAML node that does not correspond to source.
        [Obsolete("Don't use this constructor. Use \"public XamlDebuggerXmlReader(TextReader underlyingTextReader)\" or \"public XamlDebuggerXmlReader(TextReader underlyingTextReader, XamlSchemaContext schemaContext)\" instead.")]
        public XamlDebuggerXmlReader(XamlReader underlyingReader, TextReader textReader)
            : this(underlyingReader, underlyingReader as IXamlLineInfo, textReader)
        {
        }

        // This one is worse because in implementation we expect the same object instance through two parameters.
        [Obsolete("Don't use this constructor. Use \"public XamlDebuggerXmlReader(TextReader underlyingTextReader)\" or \"public XamlDebuggerXmlReader(TextReader underlyingTextReader, XamlSchemaContext schemaContext)\" instead.")]
        public XamlDebuggerXmlReader(XamlReader underlyingReader, IXamlLineInfo xamlLineInfo, TextReader textReader)
        {
            this.underlyingReader = underlyingReader;
            this.xamlLineInfo = xamlLineInfo;
            this.xmlReaderWithSourceLocation = new XmlReaderWithSourceLocation(textReader);
            this.initializationValueRanges = new Dictionary<XamlNode, DocumentRange>();
            // Parse the XML at once to get all the locations we wanted.
            while (this.xmlReaderWithSourceLocation.Read())
            {
            }
            this.schemaContext = underlyingReader.SchemaContext;
            this.objectDeclarationRecords = new Stack<XamlNode>();
            this.bufferedXamlNodes = new Queue<XamlNode>();
            this.current = this.CreateCurrentNode();
            this.SourceLocationFound += XamlDebuggerXmlReader.SetSourceLocation;
        }

        public event EventHandler<SourceLocationFoundEventArgs> SourceLocationFound
        {
            add { this._sourceLocationFound += value; }
            remove { this._sourceLocationFound -= value; }
        }

        private event EventHandler<SourceLocationFoundEventArgs> _sourceLocationFound;

        public bool CollectNonActivitySourceLocation
        {
            get
            {
                return this.collectNonActivitySourceLocation;
            }

            set
            {
                this.collectNonActivitySourceLocation = value;
            }
        }

        public bool HasLineInfo
        {
            get { return true; }
        }

        public int LineNumber
        {
            get { return this.Current.LineNumber; }
        }

        public int LinePosition
        {
            get { return this.Current.LinePosition; }
        }

        public override XamlNodeType NodeType
        {
            get { return this.Current.NodeType; }
        }

        public override XamlType Type
        {
            get { return this.Current.Type; }
        }

        public override XamlMember Member
        {
            get { return this.Current.Member; }
        }

        public override object Value
        {
            get { return this.Current.Value; }
        }

        public override bool IsEof
        {
            get { return this.underlyingReader.IsEof; }
        }

        public override NamespaceDeclaration Namespace
        {
            get { return this.Current.Namespace; }
        }

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

        internal XamlMember StartLineMember
        {
            get
            {
                if (this.startLineMember == null)
                {
                    this.startLineMember = this.CreateAttachableMember(startLineGetterMethodInfo, startLineSetterMethodInfo, SourceLocationMemberType.StartLine);
                }

                return this.startLineMember;
            }
        }

        internal XamlMember StartColumnMember
        {
            get
            {
                if (this.startColumnMember == null)
                {
                    this.startColumnMember = this.CreateAttachableMember(startColumnGetterMethodInfo, startColumnSetterMethodInfo, SourceLocationMemberType.StartColumn);
                }

                return this.startColumnMember;
            }
        }

        internal XamlMember EndLineMember
        {
            get
            {
                if (this.endLineMember == null)
                {
                    this.endLineMember = this.CreateAttachableMember(endLineGetterMethodInfo, endLineSetterMethodInfo, SourceLocationMemberType.EndLine);
                }

                return this.endLineMember;
            }
        }

        internal XamlMember EndColumnMember
        {
            get
            {
                if (this.endColumnMember == null)
                {
                    this.endColumnMember = this.CreateAttachableMember(endColumnGetterMethodInfo, endColumnSetterMethodInfo, SourceLocationMemberType.EndColumn);
                }

                return this.endColumnMember;
            }
        }

        private XamlNode Current
        {
            get
            {
                return this.current;
            }

            set
            {
                this.current = value;
            }
        }

        private XamlSourceLocationCollector SourceLocationCollector
        {
            get
            {
                if (this.sourceLocationCollector == null)
                {
                    this.sourceLocationCollector = new XamlSourceLocationCollector(this);
                }

                return this.sourceLocationCollector;
            }
        }

        [Fx.Tag.InheritThrows(From = "TryGetProperty", FromDeclaringType = typeof(AttachablePropertyServices))]
        static int GetIntegerAttachedProperty(object instance, AttachableMemberIdentifier memberIdentifier)
        {
            int value;
            if (AttachablePropertyServices.TryGetProperty(instance, memberIdentifier, out value))
            {
                return value;
            }
            else
            {
                return -1;
            }
        }

        [Fx.Tag.InheritThrows(From = "GetIntegerAttachedProperty")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public static object GetStartLine(object instance)
        {
            return GetIntegerAttachedProperty(instance, StartLineName);
        }

        [Fx.Tag.InheritThrows(From = "SetProperty", FromDeclaringType = typeof(AttachablePropertyServices))]
        public static void SetStartLine(object instance, object value)
        {
            AttachablePropertyServices.SetProperty(instance, StartLineName, value);
        }

        [Fx.Tag.InheritThrows(From = "GetIntegerAttachedProperty")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public static object GetStartColumn(object instance)
        {
            return GetIntegerAttachedProperty(instance, StartColumnName);
        }

        [Fx.Tag.InheritThrows(From = "SetProperty", FromDeclaringType = typeof(AttachablePropertyServices))]
        public static void SetStartColumn(object instance, object value)
        {
            AttachablePropertyServices.SetProperty(instance, StartColumnName, value);
        }

        [Fx.Tag.InheritThrows(From = "GetIntegerAttachedProperty")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public static object GetEndLine(object instance)
        {
            return GetIntegerAttachedProperty(instance, EndLineName);
        }

        [Fx.Tag.InheritThrows(From = "SetProperty", FromDeclaringType = typeof(AttachablePropertyServices))]
        public static void SetEndLine(object instance, object value)
        {
            AttachablePropertyServices.SetProperty(instance, EndLineName, value);
        }

        [Fx.Tag.InheritThrows(From = "GetIntegerAttachedProperty")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public static object GetEndColumn(object instance)
        {
            return GetIntegerAttachedProperty(instance, EndColumnName);
        }

        [Fx.Tag.InheritThrows(From = "SetProperty", FromDeclaringType = typeof(AttachablePropertyServices))]
        public static void SetEndColumn(object instance, object value)
        {
            AttachablePropertyServices.SetProperty(instance, EndColumnName, value);
        }

        [Fx.Tag.InheritThrows(From = "SetProperty", FromDeclaringType = typeof(AttachablePropertyServices))]
        public static void SetFileName(object instance, object value)
        {
            AttachablePropertyServices.SetProperty(instance, FileNameName, value);
        }

        [Fx.Tag.InheritThrows(From = "TryGetProperty", FromDeclaringType = typeof(AttachablePropertyServices))]
        public static object GetFileName(object instance)
        {
            string value;
            if (AttachablePropertyServices.TryGetProperty(instance, FileNameName, out value))
            {
                return value;
            }
            else
            {
                return string.Empty;
            }
        }

        // Copy source location information from source to destination (if available)
        public static void CopyAttachedSourceLocation(object source, object destination)
        {
            int startLine, startColumn, endLine, endColumn;

            if (AttachablePropertyServices.TryGetProperty<int>(source, StartLineName, out startLine) &&
                AttachablePropertyServices.TryGetProperty<int>(source, StartColumnName, out startColumn) &&
                AttachablePropertyServices.TryGetProperty<int>(source, EndLineName, out endLine) &&
                AttachablePropertyServices.TryGetProperty<int>(source, EndColumnName, out endColumn))
            {
                SetStartLine(destination, startLine);
                SetStartColumn(destination, startColumn);
                SetEndLine(destination, endLine);
                SetEndColumn(destination, endColumn);
            }
        }

        internal static void SetSourceLocation(object sender, SourceLocationFoundEventArgs args)
        {
            object target = args.Target;
            Type targetType = target.GetType();
            XamlDebuggerXmlReader reader = (XamlDebuggerXmlReader)sender;
            bool shouldStoreAttachedProperty = false;

            if (reader.CollectNonActivitySourceLocation)
            {
                shouldStoreAttachedProperty = !targetType.Equals(typeof(string));
            }
            else 
            {
                if (typeof(Activity).IsAssignableFrom(targetType))
                {
                    if (!typeof(IExpressionContainer).IsAssignableFrom(targetType))
                    {
                        if (!typeof(IValueSerializableExpression).IsAssignableFrom(targetType))
                        {
                            shouldStoreAttachedProperty = true;
                        }
                    }
                }
            }

            shouldStoreAttachedProperty = shouldStoreAttachedProperty && !args.IsValueNode;

            if (shouldStoreAttachedProperty)
            {
                SourceLocation sourceLocation = args.SourceLocation;
                XamlDebuggerXmlReader.SetStartLine(target, sourceLocation.StartLine);
                XamlDebuggerXmlReader.SetStartColumn(target, sourceLocation.StartColumn);
                XamlDebuggerXmlReader.SetEndLine(target, sourceLocation.EndLine);
                XamlDebuggerXmlReader.SetEndColumn(target, sourceLocation.EndColumn);
            }
        }

        public override bool Read()
        {
            bool readSucceed;
            if (this.bufferedXamlNodes.Count > 0)
            {
                this.Current = this.bufferedXamlNodes.Dequeue();
                readSucceed = this.Current != null;
            }
            else
            {
                readSucceed = this.underlyingReader.Read();
                if (readSucceed)
                {
                    this.Current = CreateCurrentNode(this.underlyingReader, this.xamlLineInfo);
                    this.PushObjectDeclarationNodeIfApplicable();
                    switch (this.Current.NodeType)
                    {
                        case XamlNodeType.StartMember:

                            // When we reach a StartMember node, the next node to come might be a Value.
                            // To correctly pass SourceLocation information, we need to rewrite this node to use ValueNodeXamlMemberInvoker.
                            // But we don't know if the next node is a Value node yet, so we are buffering here and look ahead for a single node.
                            UnitTestUtility.Assert(this.bufferedXamlNodes.Count == 0, "this.bufferedXamlNodes should be empty when we reach this code path.");
                            this.bufferedXamlNodes.Enqueue(this.Current);

                            // This directive represents the XAML node or XAML information set 
                            // representation of initialization text, where a string within an 
                            // object element supplies the type construction information for 
                            // the surrounding object element.
                            bool isInitializationValue = this.Current.Member == XamlLanguage.Initialization;

                            bool moreNode = this.underlyingReader.Read();
                            UnitTestUtility.Assert(moreNode, "Start Member must followed by some other nodes.");

                            this.Current = this.CreateCurrentNode();

                            this.bufferedXamlNodes.Enqueue(this.Current);

                            // It is possible that the next node after StartMember is a StartObject/GetObject.
                            // We need to push the object declaration node to the Stack
                            this.PushObjectDeclarationNodeIfApplicable();

                            if (!this.SuppressingMarkupExtension() 
                                && this.Current.NodeType == XamlNodeType.Value)
                            {
                                DocumentRange valueRange;
                                DocumentLocation currentLocation = new DocumentLocation(this.Current.LineNumber, this.Current.LinePosition);
                                bool isInAttribute = this.xmlReaderWithSourceLocation.AttributeValueRanges.TryGetValue(currentLocation, out valueRange);
                                bool isInContent = isInAttribute ? false : this.xmlReaderWithSourceLocation.ContentValueRanges.TryGetValue(currentLocation, out valueRange);

                                if (isInAttribute || (isInContent && !isInitializationValue))
                                {
                                    // For Value Node with known line info, we want to route the value setting process through this Reader.
                                    // Therefore we need to go back to the member node and replace the XamlMemberInvoker.
                                    XamlNode startMemberNodeForValue = this.bufferedXamlNodes.Peek();
                                    XamlMember xamlMemberForValue = startMemberNodeForValue.Member;
                                    XamlMemberInvoker newXamlMemberInvoker = new ValueNodeXamlMemberInvoker(this, xamlMemberForValue.Invoker, valueRange);
                                    startMemberNodeForValue.Member = xamlMemberForValue.ReplaceXamlMemberInvoker(this.schemaContext, newXamlMemberInvoker);
                                }
                                else if (isInContent && isInitializationValue)
                                {
                                    XamlNode currentStartObject = this.objectDeclarationRecords.Peek();
                                    
                                    if (!this.initializationValueRanges.ContainsKey(currentStartObject))
                                    {
                                        this.initializationValueRanges.Add(currentStartObject, valueRange);
                                    }
                                    else
                                    {
                                        UnitTestUtility.Assert(false, 
                                            "I assume it is impossible for an object  to have more than one initialization member");
                                    }
                                }
                            }

                            this.StartAccessingBuffer();
                            break;

                        case XamlNodeType.EndObject:

                            this.InjectLineInfoXamlNodesToBuffer();
                            this.StartAccessingBuffer();
                            break;

                        case XamlNodeType.Value:
                            break;

                        default:
                            break;
                    }
                }
            }

            return readSucceed;
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            if (disposing)
            {
                if (this.underlyingReader != null)
                {
                    ((IDisposable)this.underlyingReader).Dispose();
                }

                this.underlyingReader = null;

                if (this.xmlReaderWithSourceLocation != null)
                {
                    ((IDisposable)this.xmlReaderWithSourceLocation).Dispose();
                }

                this.xmlReaderWithSourceLocation = null;
            }
        }

        private static XamlNode CreateCurrentNode(XamlReader xamlReader, IXamlLineInfo xamlLineInfo)
        {
            XamlNode currentNode = new XamlNode
            {
                Namespace = xamlReader.Namespace,
                NodeType = xamlReader.NodeType,
                Type = xamlReader.Type,
                Member = xamlReader.Member,
                Value = xamlReader.Value,
                LineNumber = xamlLineInfo.LineNumber,
                LinePosition = xamlLineInfo.LinePosition,
            };

            return currentNode;
        }

        private static bool IsMarkupExtension(XamlNode node)
        {
            Fx.Assert(node != null, "node != null");
            return node.Type != null && node.Type.IsMarkupExtension;
        }

        private bool SuppressingMarkupExtension()
        {
            return this.suppressMarkupExtensionLevel != 0;
        }

        private XamlNode CreateCurrentNode()
        {
            return CreateCurrentNode(this.underlyingReader, this.xamlLineInfo);
        }

        private void StartAccessingBuffer()
        {
            this.Current = this.bufferedXamlNodes.Dequeue();
        }

        private void PushObjectDeclarationNodeIfApplicable()
        {
            switch (this.Current.NodeType)
            {
                case XamlNodeType.StartObject:
                case XamlNodeType.GetObject:
                    this.objectDeclarationRecords.Push(this.Current);
                    if (IsMarkupExtension(this.Current))
                    {
                        ++this.suppressMarkupExtensionLevel;
                    }
                    break;
            }
        }

        private void OnValueNodeDeserialized(object value, DocumentRange attributeValueLocation)
        {
            int startLine = attributeValueLocation.Start.LineNumber.Value;
            int startColumn = attributeValueLocation.Start.LinePosition.Value;
            int endLine = attributeValueLocation.End.LineNumber.Value;
            int endColumn = attributeValueLocation.End.LinePosition.Value;
            // XamlDebuggerXmlReader has no idea what the filename is (it only knew a stream of data)
            // So we set FileName = null.

            // To enhance visual selection, endColumn + 1
            SourceLocation valueLocation = new SourceLocation(null, startLine, startColumn, endLine, endColumn + 1);
            this.NotifySourceLocationFound(value, valueLocation, isValueNode: true);
        }

        private void InjectLineInfoXamlNodesToBuffer()
        {
            XamlNode startNode = this.objectDeclarationRecords.Pop();

            if (!this.SuppressingMarkupExtension() 
                && (startNode.Type != null && !startNode.Type.IsUnknown && !startNode.Type.IsMarkupExtension))
            {
                DocumentLocation myStartBracket = null;
                DocumentLocation myEndBracket = null;
                DocumentRange myRange;
                DocumentLocation myStartLocation = new DocumentLocation(startNode.LineNumber, startNode.LinePosition);
                if (this.xmlReaderWithSourceLocation.EmptyElementRanges.TryGetValue(myStartLocation, out myRange))
                {
                    myStartBracket = myRange.Start;
                    myEndBracket = myRange.End;
                }
                else
                {
                    DocumentLocation myEndLocation = new DocumentLocation(this.Current.LineNumber, this.Current.LinePosition);
                    this.xmlReaderWithSourceLocation.StartElementLocations.TryGetValue(myStartLocation, out myStartBracket);
                    this.xmlReaderWithSourceLocation.EndElementLocations.TryGetValue(myEndLocation, out myEndBracket);
                }

                // To enhance visual selection
                DocumentLocation myRealEndBracket = new DocumentLocation(myEndBracket.LineNumber.Value, myEndBracket.LinePosition.Value + 1);

                this.bufferedXamlNodes.Clear();
                this.InjectLineInfoMembersToBuffer(myStartBracket, myRealEndBracket);

                DocumentRange valueRange;
                if (this.initializationValueRanges.TryGetValue(startNode, out valueRange))
                {
                    DocumentRange realValueRange = new DocumentRange(valueRange.Start,
                        new DocumentLocation(valueRange.End.LineNumber.Value, valueRange.End.LinePosition.Value + 1));
                    this.SourceLocationCollector.AddValueRange(new DocumentRange(myStartBracket, myRealEndBracket), realValueRange);
                }
            }

            if (IsMarkupExtension(startNode))
            {
                // Pop a level
                Fx.Assert(this.suppressMarkupExtensionLevel > 0, "this.suppressMarkupExtensionLevel > 0");
                --this.suppressMarkupExtensionLevel;
            }

            // We need to make sure we also buffer the current node so that this is not missed when the buffer exhausts.
            this.bufferedXamlNodes.Enqueue(this.Current);
        }

        private void InjectLineInfoMembersToBuffer(DocumentLocation startPosition, DocumentLocation endPosition)
        {
            this.InjectLineInfoMemberToBuffer(this.StartLineMember, startPosition.LineNumber.Value);
            this.InjectLineInfoMemberToBuffer(this.StartColumnMember, startPosition.LinePosition.Value);
            this.InjectLineInfoMemberToBuffer(this.EndLineMember, endPosition.LineNumber.Value);
            this.InjectLineInfoMemberToBuffer(this.EndColumnMember, endPosition.LinePosition.Value);
        }

        private void InjectLineInfoMemberToBuffer(XamlMember member, int value)
        {
            this.bufferedXamlNodes.Enqueue(new XamlNode { NodeType = XamlNodeType.StartMember, Member = member });
            this.bufferedXamlNodes.Enqueue(new XamlNode { NodeType = XamlNodeType.Value, Value = value });
            this.bufferedXamlNodes.Enqueue(new XamlNode { NodeType = XamlNodeType.EndMember, Member = member });
        }

        private XamlMember CreateAttachableMember(MethodInfo getter, MethodInfo setter, SourceLocationMemberType memberType)
        {
            string memberName = memberType.ToString();
            SourceLocationMemberInvoker invoker = new SourceLocationMemberInvoker(this.SourceLocationCollector, memberType);
            return new XamlMember(memberName, getter, setter, this.schemaContext, invoker);
        }

        private void NotifySourceLocationFound(object instance, SourceLocation currentLocation, bool isValueNode)
        {
            Argument argumentInstance = instance as Argument;

            // For Argument containing an IValueSerializable expression serializing as a ValueNode.
            // We associate the SourceLocation to the expression instead of the Argument.
            // For example, when we have <WriteLine Text="[abc]" />, Then the SourceLocation found for the InArgument object 
            // is associated with the VisualBasicValue object instead.
            if (argumentInstance != null && argumentInstance.Expression is IValueSerializableExpression && isValueNode)
            {
                instance = argumentInstance.Expression;
            }
            if (this._sourceLocationFound != null)
            {
                this._sourceLocationFound(this, new SourceLocationFoundEventArgs(instance, currentLocation, isValueNode));
            }
        }

        private class XamlSourceLocationCollector
        {
            private XamlDebuggerXmlReader parent;
            private object currentObject;
            private int startLine;
            private int startColumn;
            private int endLine;
            private int endColumn;
            private Dictionary<DocumentRange, DocumentRange> objRgnToInitValueRgnMapping;

            internal XamlSourceLocationCollector(XamlDebuggerXmlReader parent)
            {
                this.parent = parent;
                objRgnToInitValueRgnMapping = new Dictionary<DocumentRange, DocumentRange>();
            }

            internal void OnStartLineFound(object instance, int value)
            {
                UnitTestUtility.Assert(this.currentObject == null, "This should be ensured by the XamlSourceLocationObjectReader to emit attachable property in proper order");
                this.currentObject = instance;
                this.startLine = value;
            }

            internal void OnStartColumnFound(object instance, int value)
            {
                UnitTestUtility.Assert(instance == this.currentObject, "This should be ensured by the XamlSourceLocationObjectReader to emit attachable property in proper order");
                this.startColumn = value;
            }

            internal void OnEndLineFound(object instance, int value)
            {
                UnitTestUtility.Assert(instance == this.currentObject, "This should be ensured by the XamlSourceLocationObjectReader to emit attachable property in proper order");
                this.endLine = value;
            }

            internal void OnEndColumnFound(object instance, int value)
            {
                UnitTestUtility.Assert(instance == this.currentObject, "This should be ensured by the XamlSourceLocationObjectReader to emit attachable property in proper order");
                this.endColumn = value;

                // Notify value first to keep the order from "inner to outer".
                this.NotifyValueIfNeeded(instance);

                // XamlDebuggerXmlReader has no idea what the filename is (it only knew a stream of data)
                // So we set FileName = null.
                this.parent.NotifySourceLocationFound(instance, new SourceLocation(/* FileName = */ null, startLine, startColumn, endLine, endColumn), isValueNode: false);
                this.currentObject = null;
            }

            internal void AddValueRange(DocumentRange startNodeRange, DocumentRange valueRange)
            {
                this.objRgnToInitValueRgnMapping.Add(startNodeRange, valueRange);
            }

            private static bool ShouldReportValue(object instance)
            {
                return instance is Argument;
            }

            // in the case:
            // <InArgument x:TypeArguments="x:String">["abc" + ""]</InArgument>
            // instance is a Argument, with a VB Expression.
            // We hope, the VB expression got notified, too.
            private void NotifyValueIfNeeded(object instance)
            {
                if (!ShouldReportValue(instance))
                {
                    return;
                }

                DocumentRange valueRange;
                if (this.objRgnToInitValueRgnMapping.TryGetValue(
                    new DocumentRange(this.startLine, this.startColumn, this.endLine, this.endColumn), out valueRange))
                {
                    this.parent.NotifySourceLocationFound(instance,
                        new SourceLocation(/* FileName = */ null,
                        valueRange.Start.LineNumber.Value,
                        valueRange.Start.LinePosition.Value,
                        valueRange.End.LineNumber.Value,
                        valueRange.End.LinePosition.Value),
                        isValueNode: true);
                }
            }
        }

        private class SourceLocationMemberInvoker : XamlMemberInvoker
        {
            private XamlSourceLocationCollector sourceLocationCollector;
            private SourceLocationMemberType sourceLocationMember;

            public SourceLocationMemberInvoker(XamlSourceLocationCollector sourceLocationCollector, SourceLocationMemberType sourceLocationMember)
            {
                this.sourceLocationCollector = sourceLocationCollector;
                this.sourceLocationMember = sourceLocationMember;
            }

            public override object GetValue(object instance)
            {
                UnitTestUtility.Assert(false, "This method should not be called within framework code.");
                return null;
            }

            public override void SetValue(object instance, object propertyValue)
            {
                UnitTestUtility.Assert(propertyValue is int, "The value for this attachable property should be an integer and is ensured by the emitter.");
                int value = (int)propertyValue;
                switch (this.sourceLocationMember)
                {
                    case SourceLocationMemberType.StartLine:
                        this.sourceLocationCollector.OnStartLineFound(instance, value);
                        break;
                    case SourceLocationMemberType.StartColumn:
                        this.sourceLocationCollector.OnStartColumnFound(instance, value);
                        break;
                    case SourceLocationMemberType.EndLine:
                        this.sourceLocationCollector.OnEndLineFound(instance, value);
                        break;
                    case SourceLocationMemberType.EndColumn:
                        this.sourceLocationCollector.OnEndColumnFound(instance, value);
                        break;
                    default:
                        UnitTestUtility.Assert(false, "All possible SourceLocationMember are exhausted.");
                        break;
                }
            }
        }

        private class ValueNodeXamlMemberInvoker : XamlMemberInvoker
        {
            private XamlDebuggerXmlReader parent;
            private XamlMemberInvoker wrapped;
            private DocumentRange attributeValueRange;

            internal ValueNodeXamlMemberInvoker(XamlDebuggerXmlReader parent, XamlMemberInvoker wrapped, DocumentRange attributeValueRange)
            {
                this.parent = parent;
                this.wrapped = wrapped;
                this.attributeValueRange = attributeValueRange;
            }

            public override ShouldSerializeResult ShouldSerializeValue(object instance)
            {
                return this.wrapped.ShouldSerializeValue(instance);
            }

            public override object GetValue(object instance)
            {
                return this.wrapped.GetValue(instance);
            }

            public override void SetValue(object instance, object value)
            {
                this.parent.OnValueNodeDeserialized(value, this.attributeValueRange);
                this.wrapped.SetValue(instance, value);
            }
        }
    }
}