19dd93267764a99cda675deb586878df6ba5896f
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Xaml / ErrorTolerantObjectWriter.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4
5
6 namespace System.Activities.Presentation.Xaml
7 {
8     using System;
9     using System.Activities;
10     using System.Activities.Debugger;
11     using System.Collections;
12     using System.Collections.Generic;
13     using System.ComponentModel;
14     using System.Diagnostics.CodeAnalysis;
15     using System.Globalization;
16     using System.Linq;
17     using System.Runtime;
18     using System.Text;
19     using System.Xaml;
20     using System.Xaml.Schema;
21     using Microsoft.Activities.Presentation.Xaml;
22     using NameReferenceConverter = System.Windows.Markup.NameReferenceConverter;
23
24     // This XamlWriter wraps an ObjectWriter to provide limited error tolerance, as follows:
25     // - Buffer the node stream as a tree of fragments (one for each activity) and statically validate it.
26     // - Write the node stream out to an ObjectWriter, wrapping any subtrees that failed validation 
27     //   in an ErrorActivity.
28     // - If validation fails at the root level, we don't load any object, we only provide validation errors.
29     // We are only tolerant of errors that can be detected statically; i.e. we are not resillient
30     // to XamlObjectWriter exceptions.
31
32     internal class ErrorTolerantObjectWriter : XamlWriter, IXamlLineInfoConsumer
33     {
34         // We store three main types of state:
35         // - The current state of the nodestream (XamlFrames), for validation purposes.
36         // - Buffered XamlFragments.
37         // - NameScopes, which have 
38
39         // Current state of the nodestream, for performing validation
40         WalkableStack<XamlFrame> xamlStack;
41
42         // Root of the tree of completed fragments (except for the root, which may be in progress)
43         XamlFragment rootFragment;
44
45         // Stack of in-progress fragments
46         Stack<XamlFragment> fragmentStack;
47
48         // Stack of in-progress namescopes; fragments can overlap multiple namescopes, and vice versa
49         Stack<NameScopeFrame> nameScopeStack;
50
51         // Completed namescopes, saved so we can resolve all references at end of parse
52         List<NameScopeFrame> poppedNameScopes;
53
54         // Pending NS declarations whose corresponding StartObject hasn't been written yet
55         NamespaceStackNode pendingNamespaces;
56
57         XamlObjectWriter objectWriter;
58         XamlType typeOfActivity;
59         XamlMember nameOfReference;
60         XamlValueConverter<XamlDeferringLoader> activityLoader;
61         int lineNumber, linePosition;
62
63         public string LocalAssemblyName { get; set; }
64
65         public IList<XamlLoadErrorInfo> LoadErrors { get; private set; }
66
67         public ErrorTolerantObjectWriter(XamlSchemaContext schemaContext)
68         {
69             this.xamlStack = new WalkableStack<XamlFrame>();
70             this.rootFragment = new XamlFragment(schemaContext);
71             this.fragmentStack = new Stack<XamlFragment>();
72             this.fragmentStack.Push(this.rootFragment);
73             this.nameScopeStack = new Stack<NameScopeFrame>();
74             this.nameScopeStack.Push(new NameScopeFrame(null));
75             this.poppedNameScopes = new List<NameScopeFrame>();
76             this.objectWriter = new XamlObjectWriter(schemaContext);
77             this.typeOfActivity = objectWriter.SchemaContext.GetXamlType(typeof(Activity));
78             this.nameOfReference = XamlLanguage.Reference.GetMember("Name");
79             this.activityLoader = typeOfActivity.GetMember("Implementation").DeferringLoader;
80         }
81
82         public object Result { get; private set; }
83
84         public override XamlSchemaContext SchemaContext { get { return this.objectWriter.SchemaContext; } }
85
86         public override void WriteNamespace(NamespaceDeclaration namespaceDeclaration)
87         {
88             if (this.rootFragment.HasError)
89             {
90                 return;
91             }
92             if (this.pendingNamespaces == null)
93             {
94                 this.pendingNamespaces = new NamespaceStackNode();
95             }
96             this.pendingNamespaces.Add(namespaceDeclaration.Prefix, namespaceDeclaration.Namespace);
97             CurrentWriter.WriteNamespace(namespaceDeclaration);
98         }
99
100         public override void WriteStartObject(XamlType type)
101         {
102             // This validation must be done before pushing the object frame, so that if there is an error,
103             // we treat the containing type as an error and not just the subtree.
104             ValidateSetMember();
105             PushXamlFrame(type);
106
107             // Pushing the activity frame must be done before the rest of validation, because if this
108             // is an unknown Activity, we want to treat just this subtree as an error, not its parent.
109             PushNewActivityFrameIfNeeded();
110             ValidateStartObject();
111             if (this.rootFragment.HasError)
112             {
113                 return;
114             }
115             CurrentWriter.WriteStartObject(type);
116             CurrentFragment.ObjectDepth++;
117         }
118
119         public override void WriteGetObject()
120         {
121             PushXamlFrame(null);
122             ValidateGetObject();
123             if (this.rootFragment.HasError)
124             {
125                 return;
126             }
127             CurrentWriter.WriteGetObject();
128             CurrentFragment.ObjectDepth++;
129         }
130
131         public override void WriteEndObject()
132         {
133             this.xamlStack.Pop();
134             ValidateEndObject();
135             if (this.rootFragment.HasError)
136             {
137                 return;
138             }
139             CurrentWriter.WriteEndObject();
140             CurrentFragment.ObjectDepth--;
141             if (CurrentFragment.ObjectDepth == 0)
142             {
143                 XamlFragment completedFragment = CurrentFragment;
144                 this.fragmentStack.Pop();
145                 if (this.fragmentStack.Count == 0)
146                 {
147                     Fx.Assert(completedFragment == this.rootFragment, "Base of stack should be root fragment");
148                     CompleteLoad();
149                 }
150                 else
151                 {
152                     CurrentFragment.AddChild(completedFragment);
153                 }
154             }
155         }
156
157         public override void WriteStartMember(XamlMember member)
158         {
159             Fx.Assert(this.xamlStack.Count > 0 && this.xamlStack.Current.Member == null, "Unexpected StartMember");
160             this.xamlStack.Current.Member = member;
161             ValidateStartMember();
162             if (this.rootFragment.HasError)
163             {
164                 return;
165             }
166             CurrentWriter.WriteStartMember(member);
167         }
168
169         public override void WriteEndMember()
170         {
171             Fx.Assert(this.xamlStack.Count > 0 && this.xamlStack.Current.Member != null, "Unexpected EndMember");
172             this.xamlStack.Current.Member = null;
173             this.xamlStack.Current.MemberIsSet = false;
174             ValidateEndMember();
175             if (this.rootFragment.HasError)
176             {
177                 return;
178             }
179             CurrentWriter.WriteEndMember();
180         }
181
182         public override void WriteValue(object value)
183         {
184             ValidateValue(value);
185             if (this.rootFragment.HasError)
186             {
187                 return;
188             }
189             CurrentWriter.WriteValue(value);
190         }
191
192         public void SetLineInfo(int lineNumber, int linePosition)
193         {
194             // We need to save the line info statically, for validation errors
195             this.lineNumber = lineNumber;
196             this.linePosition = linePosition;
197
198             // But we also need to keep it in [....] with the nodestream, for XOW errors
199             // XOW and XamlNodeQueue.Writer both implement IXamlLineInfoConsumer, so we can do a straight cast
200             if (this.rootFragment.HasError)
201             {
202                 return;
203             }
204             ((IXamlLineInfoConsumer)CurrentWriter).SetLineInfo(lineNumber, linePosition);
205         }
206
207         // ObjectWriter always wants LineInfo
208         public bool ShouldProvideLineInfo { get { return true; } }
209
210         internal static bool IsErrorActivity(Type objectType)
211         {
212             return objectType == typeof(ErrorActivity) ||
213                 (objectType != null && objectType.IsGenericType &&
214                  objectType.GetGenericTypeDefinition() == typeof(ErrorActivity<>));
215         }
216
217         // Node loop that strips out ErrorActivities on Save. Assumes that ErrorActivities are never
218         // nested, and that XamlObjectReader doesn't have line info.
219         internal static void TransformAndStripErrors(System.Xaml.XamlReader objectReader, XamlWriter writer)
220         {
221             // Every ErrorActivity is prefixed with all the NamespaceDeclarations that were in scope
222             // in the original document. We track the current namespaces in scope on Save, so that we
223             // can strip out any redundant declarations.
224             NamespaceStackNode currentNamespaces = null;
225             NamespaceStackNode pendingNamespaces = null;
226
227             while (objectReader.Read())
228             {
229                 // Update the namespace stack
230                 switch (objectReader.NodeType)
231                 {
232                     case XamlNodeType.NamespaceDeclaration:
233                         if (pendingNamespaces == null)
234                         {
235                             pendingNamespaces = new NamespaceStackNode() { PreviousNode = currentNamespaces };
236                         }
237                         pendingNamespaces.Add(objectReader.Namespace.Prefix, objectReader.Namespace.Namespace);
238                         break;
239                     case XamlNodeType.StartObject:
240                     case XamlNodeType.GetObject:
241                         if (pendingNamespaces != null)
242                         {
243                             currentNamespaces = pendingNamespaces;
244                             pendingNamespaces = null;
245                         }
246                         currentNamespaces.ObjectDepth++;
247                         break;
248                 }
249
250                 if (objectReader.NodeType == XamlNodeType.StartObject && IsErrorActivity(objectReader.Type.UnderlyingType))
251                 {
252                     ActivityFragment.TransformErrorActivityContents(objectReader, writer, currentNamespaces);
253                 }
254                 else
255                 {
256                     writer.WriteNode(objectReader);
257                 }
258
259                 if (objectReader.NodeType == XamlNodeType.EndObject)
260                 {
261                     currentNamespaces.ObjectDepth--;
262                     if (currentNamespaces.ObjectDepth == 0)
263                     {
264                         currentNamespaces = currentNamespaces.PreviousNode;
265                     }
266                 }
267             }
268         }
269
270         XamlFragment CurrentFragment
271         {
272             get { return this.fragmentStack.Peek(); }
273         }
274
275         NameScopeFrame CurrentNameScope
276         {
277             get { return this.nameScopeStack.Peek(); }
278         }
279
280         XamlWriter CurrentWriter
281         {
282             get { return CurrentFragment.NodeQueue.Writer; }
283         }
284
285         static void AppendShortName(StringBuilder result, XamlType type)
286         {
287             result.Append(type.Name);
288             if (type.IsGeneric)
289             {
290                 result.Append("(");
291                 bool isFirst = true;
292                 foreach (XamlType typeArg in type.TypeArguments)
293                 {
294                     if (isFirst)
295                     {
296                         isFirst = false;
297                     }
298                     else
299                     {
300                         result.Append(",");
301                     }
302                     AppendShortName(result, typeArg);
303                 }
304                 result.Append(")");
305             }
306         }
307
308         // If a generic type is unknown, we don't know whether the open generic couldn't be resolved,
309         // or just its children. So we only want to surface errors for types that don't have unknown
310         // children.
311         static void GetLeafUnresolvedTypeArgs(XamlType type, HashSet<XamlType> unresolvedTypeArgs)
312         {
313             Fx.Assert(type.IsUnknown, "Method should only be called for unknown types");
314             bool hasUnknownChildren = false;
315             if (type.IsGeneric)
316             {
317                 foreach (XamlType typeArg in type.TypeArguments)
318                 {
319                     if (typeArg.IsUnknown)
320                     {
321                         GetLeafUnresolvedTypeArgs(typeArg, unresolvedTypeArgs);
322                         hasUnknownChildren = true;
323                     }
324                 }
325             }
326             if (!hasUnknownChildren)
327             {
328                 unresolvedTypeArgs.Add(type);
329             }
330         }
331
332         internal static string GetXamlMemberName(XamlMember member)
333         {
334             if (member.IsDirective)
335             {
336                 return "{" + member.PreferredXamlNamespace + "}" + member.Name;
337             }
338             else
339             {
340                 return GetXamlTypeName(member.DeclaringType) + "." + member.Name;
341             }
342         }
343
344         internal static string GetXamlTypeName(XamlType type)
345         {
346             string typeNs = type.PreferredXamlNamespace;
347             string typeName = GetFullTypeNameWithoutNamespace(type);
348             string clrns, assembly;
349             if (XamlNamespaceHelper.TryParseClrNsUri(typeNs, out clrns, out assembly))
350             {
351                 return clrns + "." + typeName;
352             }
353             else
354             {
355                 return typeNs + ":" + typeName;
356             }
357         }
358
359         static bool IsWhitespace(string value)
360         {
361             foreach (char c in value)
362             {
363                 if (c != '\r' && c != '\n' && c != ' ' && c != '\t')
364                 {
365                     return false;
366                 }
367             }
368             return true;
369         }
370
371         // Validate named references and write out the complete nodestream to the ObjectWriter
372         void CompleteLoad()
373         {
374             CompleteNameReferences();
375             XamlFragment.FindBrokenReferences(this.rootFragment);
376
377             if (this.rootFragment.HasError)
378             {
379                 this.Result = null;
380             }
381             else
382             {
383                 this.rootFragment.WriteTo(this.objectWriter, false);
384                 this.Result = this.objectWriter.Result;
385             }
386         }
387
388         // Gets the property type of the containing member, or its item type if it's a collection.
389         XamlType GetParentPropertyType(out bool parentIsDictionary)
390         {
391             XamlMember parentMember;
392             XamlType collectionType;
393             XamlType result = GetParentPropertyType(out parentMember, out collectionType);
394             parentIsDictionary = collectionType != null && collectionType.IsDictionary;
395             return result;
396         }
397
398         XamlType GetParentPropertyType(out XamlMember parentMember, out XamlType collectionType)
399         {
400             parentMember = this.xamlStack.Previous(1).Member;
401             Fx.Assert(parentMember != null, "StartObject or Value without preceding StartMember");
402             if (parentMember.IsDirective &&
403                 (parentMember.Type.IsCollection || parentMember.Type.IsDictionary))
404             {
405                 if (parentMember == XamlLanguage.Items)
406                 {
407                     collectionType = this.xamlStack.Previous(1).Type;
408                     if (collectionType == null)
409                     {
410                         // This is a GetObject, need to look at the containing member
411                         collectionType = this.xamlStack.Previous(2).Member.Type;
412                     }
413                 }
414                 else
415                 {
416                     collectionType = parentMember.Type;
417                 }
418                 return collectionType.ItemType;
419             }
420             collectionType = null;
421             return parentMember.Type;
422         }
423
424         // Checks whether to push a new ActivityFrame for a new StartObject (i.e. whether the object
425         // is an activity and is replaceable in case of error).
426         void PushNewActivityFrameIfNeeded()
427         {
428             Fx.Assert(this.xamlStack.Count > 0, "PushNewActivityFrameIfNeeded called without a StartObject");
429             if (this.xamlStack.Count == 1)
430             {
431                 // This is the root of the document
432                 return;
433             }
434             if (CurrentFragment.HasError)
435             {
436                 // We're already inside an error frame, no point pushing any more frames
437                 return;
438             }
439             // Check the parent property type (not the object type) because that's what determines
440             // whether we can inject an ErrorActivity.
441             bool parentIsDictionary;
442             XamlType parentType = GetParentPropertyType(out parentIsDictionary);
443             if (parentType != null && parentType.UnderlyingType != null && !parentIsDictionary &&
444                 ActivityFragment.IsActivityType(parentType.UnderlyingType))
445             {
446                 this.fragmentStack.Push(new ActivityFragment(SchemaContext) { Type = parentType.UnderlyingType });
447                 CurrentFragment.Namespaces = this.xamlStack.Current.Namespaces;
448             }
449         }
450
451         void PushNameScope()
452         {
453             this.nameScopeStack.Push(new NameScopeFrame(this.nameScopeStack.Peek()));
454         }
455
456         void PushXamlFrame(XamlType type)
457         {
458             NamespaceStackNode currentNamespaces = this.xamlStack.Count > 0 ? this.xamlStack.Current.Namespaces : null;
459             this.xamlStack.Push(new XamlFrame { Type = type });
460             if (this.pendingNamespaces != null)
461             {
462                 this.pendingNamespaces.PreviousNode = currentNamespaces;
463                 this.xamlStack.Current.Namespaces = this.pendingNamespaces;
464                 this.pendingNamespaces = null;
465             }
466             else
467             {
468                 this.xamlStack.Current.Namespaces = currentNamespaces;
469             }
470         }
471
472         void ValidateStartObject()
473         {
474             // Check if type is known
475             XamlType type = this.xamlStack.Current.Type;
476             if (type.IsUnknown)
477             {
478                 HashSet<XamlType> unresolvedTypes = null;
479                 if (type.IsGeneric)
480                 {
481                     unresolvedTypes = new HashSet<XamlType>();
482                     GetLeafUnresolvedTypeArgs(type, unresolvedTypes);
483                 }
484                 if (unresolvedTypes != null &&
485                     (unresolvedTypes.Count > 1 || !unresolvedTypes.Contains(type)))
486                 {
487                     ValidationError(SR.UnresolvedGenericType, GetXamlTypeName(type));
488                     foreach (XamlType unresolvedTypeArg in unresolvedTypes)
489                     {
490                         ValidationErrorUnknownType(unresolvedTypeArg);
491                     }
492                 }
493                 else
494                 {
495                     ValidationErrorUnknownType(type);
496                 }
497             }
498             else if (this.xamlStack.Count > 1)
499             {
500                 // Check assignability to parent member
501                 if (!type.IsMarkupExtension)
502                 {
503                     XamlMember parentMember;
504                     XamlType collectionType;
505                     XamlType expectedType = GetParentPropertyType(out parentMember, out collectionType);
506                     if (collectionType != null)
507                     {
508                         if (!CollectionAcceptsType(collectionType, type))
509                         {
510                             ValidationError(SR.UnassignableCollection, type, collectionType.ItemType, collectionType);
511                         }
512                     }
513                     else if (parentMember != null && !parentMember.IsUnknown &&
514                         !type.CanAssignTo(parentMember.Type) && parentMember.DeferringLoader == null)
515                     {
516                         ValidationError(SR.UnassignableObject, type, parentMember.Type, parentMember.Name);
517                     }
518                 }
519             }
520
521             // Update the NameScope stack
522             if (type.IsNameScope && this.xamlStack.Count > 1)
523             {
524                 PushNameScope();
525             }
526             CurrentNameScope.Depth++;
527         }
528
529         void ValidateGetObject()
530         {
531             XamlType type = this.xamlStack.Previous(1).Member.Type;
532             if (type.IsNameScope)
533             {
534                 PushNameScope();
535             }
536             CurrentNameScope.Depth++;
537         }
538
539         // Check whether a member is set more than once
540         bool ValidateSetMember()
541         {
542             XamlFrame frame = this.xamlStack.Current;
543             if (frame != null)
544             {
545                 if (frame.MemberIsSet && !frame.Member.IsUnknown && !frame.Member.IsDirective)
546                 {
547                     ValidationError(SR.MemberCanOnlyBeSetOnce, frame.Member);
548                     return false;
549                 }
550                 frame.MemberIsSet = true;
551             }
552             return true;
553         }
554
555         bool CollectionAcceptsType(XamlType collectionType, XamlType type)
556         {
557             return collectionType.IsUnknown ||
558                 collectionType.AllowedContentTypes == null ||
559                 collectionType.AllowedContentTypes.Any(contentType => type.CanAssignTo(contentType));
560         }
561
562         void ValidateStartMember()
563         {
564             XamlFrame currentFrame = this.xamlStack.Current;
565             XamlMember member = currentFrame.Member;
566
567             // Make sure that the member is known.
568             // Don't bother surfacing an error for unknown instance properties or unknown content on 
569             // unknown types. It's redundant, since we'll already surface an error for the unknown type.
570             if (member == XamlLanguage.UnknownContent)
571             {
572                 if (!currentFrame.Type.IsUnknown)
573                 {
574                     ValidationError(SR.UnknownContent, this.xamlStack.Current.Type);
575                 }
576             }
577             else if (member.IsUnknown && (member.IsAttachable || member.IsDirective || !member.DeclaringType.IsUnknown))
578             {
579                 ValidationError(SR.UnresolvedMember, member.Name, member.DeclaringType);
580             }
581
582             // Check for duplicate members
583             if (currentFrame.PastMembers == null)
584             {
585                 currentFrame.PastMembers = new HashSet<XamlMember>();
586             }
587             if (currentFrame.PastMembers.Contains(member))
588             {
589                 ValidationError(SR.DuplicateMember, member);
590             }
591             else
592             {
593                 currentFrame.PastMembers.Add(member);
594             }
595
596             // Check for misplaced attachable members
597             if (member.IsAttachable && !currentFrame.Type.IsUnknown && !currentFrame.Type.CanAssignTo(member.TargetType))
598             {
599                 ValidationError(SR.MemberOnBadTargetType, member.Name, member.TargetType);
600             }
601
602             // Update the NameScope stack
603             if (member.DeferringLoader != null)
604             {
605                 PushNameScope();
606             }
607             CurrentNameScope.Depth++;
608         }
609
610         void ValidateEndMember()
611         {
612             DecrementNameScopeDepth();
613         }
614
615         void ValidateEndObject()
616         {
617             DecrementNameScopeDepth();
618         }
619
620         void ValidateValue(object value)
621         {
622             XamlType type = this.xamlStack.Current.Type;
623             XamlMember member = this.xamlStack.Current.Member;
624             string valueString = value as string;
625             if (valueString == null || member.IsUnknown || !ValidateSetMember() || IsWhitespace(valueString))
626             {
627                 return;
628             }
629
630             // Check if this is x:Name or RuntimeNameProperty
631             if (member == XamlLanguage.Name || (type != null && member == type.GetAliasedProperty(XamlLanguage.Name)))
632             {
633                 if (!CurrentNameScope.RegisterName(valueString, CurrentFragment))
634                 {
635                     ValidationError(SR.DuplicateName, valueString);
636                 }
637                 return;
638             }
639
640             // Check if this is an x:Reference
641             if (type == XamlLanguage.Reference && (member == this.nameOfReference || member == XamlLanguage.PositionalParameters))
642             {
643                 CurrentNameScope.AddNeededName(CurrentFragment, valueString, this.lineNumber, this.linePosition);
644                 return;
645             }
646             XamlValueConverter<TypeConverter> converter =
647                 (member == XamlLanguage.Initialization) ? type.TypeConverter : member.TypeConverter;
648             if (converter != null && converter.ConverterType == typeof(NameReferenceConverter))
649             {
650                 CurrentNameScope.AddNeededName(CurrentFragment, valueString, this.lineNumber, this.linePosition);
651             }
652
653             // Check if text is supported on this member
654             if (member == XamlLanguage.Initialization)
655             {
656                 if (!type.IsUnknown && type.TypeConverter == null && !XamlLanguage.String.CanAssignTo(type))
657                 {
658                     ValidationError(SR.NoTypeConverter, type);
659                 }
660             }
661             else if (member.IsDirective)
662             {
663                 if (member == XamlLanguage.Items)
664                 {
665                     if (type == null)
666                     {
667                         // Inside a GetObject - get the type from the parent member
668                         type = this.xamlStack.Previous(1).Member.Type;
669                     }
670                     if (!CollectionAcceptsType(type, XamlLanguage.String))
671                     {
672                         ValidationError(SR.NoTextInCollection, type);
673                     }
674                 }
675             }
676             else if (member.TypeConverter == null && !XamlLanguage.String.CanAssignTo(member.Type) &&
677                 (member.DeferringLoader == null || member.DeferringLoader == this.activityLoader))
678             {
679                 ValidationError(SR.NoTextInProperty, XamlLanguage.String, member.Type, member.Name);
680             }
681         }
682
683         void ValidationError(string message, params object[] arguments)
684         {
685             ValidationError(message, this.lineNumber, this.linePosition, arguments);
686             CurrentFragment.HasError = true;
687         }
688
689         void ValidationError(string message, int lineNumber, int linePosition, params object[] arguments)
690         {
691             // The default ToString implementations can be very clunky, especially for generics.
692             // Use our own friendlier versions instead.
693             for (int i = 0; i < arguments.Length; i++)
694             {
695                 XamlType type = arguments[i] as XamlType;
696                 if (type != null)
697                 {
698                     arguments[i] = GetXamlTypeName(type);
699                 }
700                 else
701                 {
702                     XamlMember member = arguments[i] as XamlMember;
703                     if (member != null)
704                     {
705                         arguments[i] = GetXamlMemberName(member);
706                     }
707                 }
708             }
709             string error = string.Format(CultureInfo.CurrentCulture, message, arguments);
710             if (LoadErrors == null)
711             {
712                 LoadErrors = new List<XamlLoadErrorInfo>();
713             }
714             LoadErrors.Add(new XamlLoadErrorInfo(error, lineNumber, linePosition));
715         }
716
717         [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotIgnoreMethodResults, Justification =
718             "StringBuilder.Append just returns the same instance that was called")]
719         void ValidationErrorUnknownType(XamlType type)
720         {
721             StringBuilder result = new StringBuilder();
722             string clrns, assembly;
723             if (XamlNamespaceHelper.TryParseClrNsUri(type.PreferredXamlNamespace, out clrns, out assembly))
724             {
725                 if (assembly == null)
726                 {
727                     assembly = this.LocalAssemblyName;
728                 }
729                 StringBuilder typeName = new StringBuilder();
730                 typeName.Append(clrns);
731                 typeName.Append(".");
732                 AppendShortName(typeName, type);
733                 ValidationError(SR.UnresolvedTypeInAssembly, typeName, assembly);
734             }
735             else
736             {
737                 StringBuilder typeName = new StringBuilder();
738                 AppendShortName(typeName, type);
739                 ValidationError(SR.UnresolvedTypeInNamespace, typeName, type.PreferredXamlNamespace);
740             }
741         }
742
743         void DecrementNameScopeDepth()
744         {
745             CurrentNameScope.Depth--;
746             if (CurrentNameScope.Depth == 0)
747             {
748                 this.poppedNameScopes.Add(this.nameScopeStack.Pop());
749             }
750         }
751
752         // Resolves all simple name references in the tree, raising validation errors for any that
753         // can't be resolved.
754         void CompleteNameReferences()
755         {
756             foreach (NameScopeFrame nameScope in this.poppedNameScopes)
757             {
758                 if (nameScope.NeededNames == null)
759                 {
760                     continue;
761                 }
762                 foreach (NameReference reference in nameScope.NeededNames)
763                 {
764                     XamlFragment target = nameScope.FindName(reference.Name);
765                     if (target == null)
766                     {
767                         ValidationError(SR.UnresolvedName, reference.LineNumber, reference.LinePosition, reference.Name);
768                         reference.Fragment.HasError = true;
769                     }
770                     else
771                     {
772                         if (target.ReferencedBy == null)
773                         {
774                             target.ReferencedBy = new HashSet<XamlFragment>();
775                         }
776                         target.ReferencedBy.Add(reference.Fragment);
777                     }
778                 }
779             }
780         }
781
782         private static string GetFullTypeNameWithoutNamespace(XamlType xamlType)
783         {
784             string typeName = string.Empty;
785             if (xamlType != null)
786             {
787                 typeName = xamlType.Name;
788                 bool firstTypeArg = true;
789                 if (xamlType.TypeArguments != null && xamlType.TypeArguments.Count > 0)
790                 {
791                     typeName += "(";
792                     foreach (XamlType typeArg in xamlType.TypeArguments)
793                     {
794                         if (!firstTypeArg)
795                         {
796                             typeName += ",";
797                         }
798                         else
799                         {
800                             firstTypeArg = false;
801                         }
802                         typeName += typeArg.Name;
803                     }
804                     typeName += ")";
805                 }
806             }
807             return typeName;
808         }
809
810         class XamlFrame
811         {
812             public XamlType Type { get; set; }
813             public XamlMember Member { get; set; }
814             public bool MemberIsSet { get; set; }
815             public NamespaceStackNode Namespaces { get; set; }
816             public HashSet<XamlMember> PastMembers { get; set; }
817         }
818
819         // A stack that is implemented as a list to allow walking up the stack.
820         class WalkableStack<T> : List<T> where T : class
821         {
822             public T Pop()
823             {
824                 T result = this[Count - 1];
825                 this.RemoveAt(Count - 1);
826                 return result;
827             }
828
829             public T Previous(int index)
830             {
831                 return this[Count - 1 - index];
832             }
833
834             public void Push(T frame)
835             {
836                 Add(frame);
837             }
838
839             public T Current
840             {
841                 get { return Count > 0 ? this[Count - 1] : null; }
842             }
843         }
844
845         // Class to buffer a tree of XAML fragments and write them back out in the correct order.
846         class XamlFragment
847         {
848             private XamlFragment firstChild;
849             private XamlFragment nextSibling;
850
851             public XamlFragment(XamlSchemaContext schemaContext)
852             {
853                 NodeQueue = new XamlNodeQueue(schemaContext);
854             }
855
856             public XamlNodeQueue NodeQueue { get; private set; }
857             public int ObjectDepth { get; set; }
858             public bool HasError { get; set; }
859             public NamespaceStackNode Namespaces { get; set; }
860             public HashSet<XamlFragment> ReferencedBy { get; set; }
861
862             // Adds a child fragment at the current position of the NodeQueue.
863             // We store the fragment as a Value Node, and expand out its contents at Write time.
864             // We also store the fragments in a simple tree structure to so we can iterate them quickly.
865             public void AddChild(XamlFragment newChild)
866             {
867                 NodeQueue.Writer.WriteValue(newChild);
868                 XamlFragment curChild = this.firstChild;
869                 if (curChild == null)
870                 {
871                     this.firstChild = newChild;
872                 }
873                 else
874                 {
875                     while (curChild.nextSibling != null)
876                     {
877                         curChild = curChild.nextSibling;
878                     }
879                     curChild.nextSibling = newChild;
880                 }
881             }
882
883             // Find all references to error fragments and mark the referencing fragments as also errored.
884             public static void FindBrokenReferences(XamlFragment rootFragment)
885             {
886                 // By starting from the root of the tree and walking its children, we ensure we traverse
887                 // each node at least once, and so find every error fragment.
888                 // Given an error fragment, we want to mark all its children and all its referencing fragments
889                 // as errors, and process them recursively.
890                 Queue<XamlFragment> queue = new Queue<XamlFragment>();
891                 queue.Enqueue(rootFragment);
892                 while (queue.Count > 0)
893                 {
894                     if (rootFragment.HasError)
895                     {
896                         // We found an error at the root. We won't be able to load any part of the document,
897                         // so skip this redundant processing.
898                         return;
899                     }
900
901                     XamlFragment current = queue.Dequeue();
902                     if (current.HasError)
903                     {
904                         // Mark all this fragment's children as errored, and enqueue them for recursive processing.
905                         XamlFragment child = current.firstChild;
906                         while (child != null)
907                         {
908                             child.HasError = true;
909                             queue.Enqueue(child);
910                             child = child.nextSibling;
911                         }
912
913                         // Mark all fragments that reference this fragment as errored, and enqueue them for recursive processing.
914                         if (current.ReferencedBy != null)
915                         {
916                             foreach (XamlFragment referencingFragment in current.ReferencedBy)
917                             {
918                                 referencingFragment.HasError = true;
919                                 queue.Enqueue(referencingFragment);
920                             }
921                         }
922
923                         // Clear the links so that we don't traverse them again if there is a cycle.
924                         current.firstChild = null;
925                         current.ReferencedBy = null;
926                     }
927                     else
928                     {
929                         // This fragment is healthy, but we need to check for any errors in its children.
930                         // Don't remove the children, we'll need to traverse them if this fragment gets
931                         // marked as errored later.
932                         XamlFragment child = current.firstChild;
933                         while (child != null)
934                         {
935                             queue.Enqueue(child);
936                             child = child.nextSibling;
937                         }
938                     }
939                 }
940             }
941
942             // Write this fragment and all its children out to the specified writer.
943             public virtual void WriteTo(XamlWriter writer, bool parentHasError)
944             {
945                 // In the constrained designer scenario, we can always assume that there is line info.
946                 XamlReader nodeReader = NodeQueue.Reader;
947                 IXamlLineInfo lineInfo = (IXamlLineInfo)nodeReader;
948                 IXamlLineInfoConsumer lineInfoConsumer = (IXamlLineInfoConsumer)writer;
949                 int lineNumber = 0;
950                 int linePosition = 0;
951
952                 while (nodeReader.Read())
953                 {
954                     if (lineInfo.LineNumber > 0 &&
955                             (lineInfo.LineNumber != lineNumber || lineInfo.LinePosition != linePosition))
956                     {
957                         lineNumber = lineInfo.LineNumber;
958                         linePosition = lineInfo.LinePosition;
959                         lineInfoConsumer.SetLineInfo(lineNumber, linePosition);
960                     }
961                     XamlFragment child = (nodeReader.NodeType == XamlNodeType.Value) ? nodeReader.Value as XamlFragment : null;
962                     if (child != null)
963                     {
964                         child.WriteTo(writer, parentHasError || HasError);
965                     }
966                     else
967                     {
968                         writer.WriteNode(nodeReader);
969                     }
970                 }
971             }
972         }
973
974         class ActivityFragment : XamlFragment
975         {
976             public ActivityFragment(XamlSchemaContext schemaContext)
977                 : base(schemaContext)
978             {
979             }
980
981             public Type Type { get; set; }
982
983             // We can only construct an ErrorActivity that is assignable to properties of type
984             // Activity or Activity<T>, not any of their descendants.
985             public static bool IsActivityType(Type type)
986             {
987                 return type == typeof(Activity) ||
988                     (type.IsGenericType &&
989                     type.GetGenericTypeDefinition() == typeof(Activity<>));
990             }
991
992             public override void WriteTo(XamlWriter writer, bool parentHasError)
993             {
994                 if (HasError && !parentHasError)
995                 {
996                     Fx.Assert(this.Type != null && IsActivityType(this.Type), "Cannot create ErrorActivity for non-Activity property");
997                     Type errorType;
998                     if (this.Type == typeof(Activity))
999                     {
1000                         errorType = typeof(ErrorActivity);
1001                     }
1002                     else
1003                     {
1004                         errorType = typeof(ErrorActivity<>).MakeGenericType(this.Type.GetGenericArguments()[0]);
1005                     }
1006                     XamlType errorXamlType = writer.SchemaContext.GetXamlType(errorType);
1007                     writer.WriteStartObject(errorXamlType);
1008                     writer.WriteStartMember(errorXamlType.GetMember(ErrorActivity.ErrorNodesProperty));
1009
1010                     XamlNodeList errorNodes = GetErrorNodes();
1011                     ErrorActivity.WriteNodeList(writer, errorNodes);
1012
1013                     writer.WriteEndMember(); // </ErrorActivity.ErrorNodeList>
1014                     writer.WriteEndObject(); // </ErrorActivity>
1015                 }
1016                 else
1017                 {
1018                     base.WriteTo(writer, parentHasError);
1019                 }
1020             }
1021
1022             // Extracts the Error Nodes contents out of the ErrorActivity and writes them to the 
1023             // specified writer.
1024             // Expects reader to be positioned on SO ErrorActivity and leaves it on corresponding EO.
1025             public static void TransformErrorActivityContents(System.Xaml.XamlReader objectReader, XamlWriter writer,
1026                 NamespaceStackNode currentNamespaces)
1027             {
1028                 XamlMember errorNodesMember = objectReader.Type.GetMember(ErrorActivity.ErrorNodesProperty);
1029                 // Skip past off <ErrorActivity>
1030                 objectReader.Read();
1031
1032                 do
1033                 {
1034                     Fx.Assert(objectReader.NodeType == XamlNodeType.StartMember, "Expected StartMember");
1035                     if (objectReader.Member == errorNodesMember)
1036                     {
1037                         // Skip past <ErrorActivity.ErrorNodes>
1038                         objectReader.Read();
1039
1040                         // Skip past the dummy StartObject & StartMember
1041                         Fx.Assert(objectReader.NodeType == XamlNodeType.StartObject, "Expected StartObject");
1042                         objectReader.Read();
1043                         Fx.Assert(objectReader.NodeType == XamlNodeType.StartMember, "Expected StartMember");
1044                         objectReader.Read();
1045
1046                         // Strip redundant namespaces
1047                         while (objectReader.NodeType == XamlNodeType.NamespaceDeclaration)
1048                         {
1049                             string ns = currentNamespaces.LookupNamespace(objectReader.Namespace.Prefix);
1050                             if (ns != objectReader.Namespace.Namespace &&
1051                                 !IsIgnorableCompatNamespace(objectReader.Namespace, currentNamespaces))
1052                             {
1053                                 writer.WriteNamespace(objectReader.Namespace);
1054                             }
1055                             objectReader.Read();
1056                         }
1057
1058                         // Pass through the original contents, stripping out any hidden APs added by
1059                         // the XamlDebuggerXmlReader, since XOR wouldn't write them out.
1060                         XamlType debuggerReaderType = objectReader.SchemaContext.GetXamlType(typeof(XamlDebuggerXmlReader));
1061                         Fx.Assert(objectReader.NodeType == XamlNodeType.StartObject, "Expected StartObject");
1062                         XamlReader subReader = objectReader.ReadSubtree();
1063                         subReader.Read();
1064                         while (!subReader.IsEof)
1065                         {
1066                             if (subReader.NodeType == XamlNodeType.StartMember &&
1067                                 subReader.Member.DeclaringType == debuggerReaderType &&
1068                                 subReader.Member.SerializationVisibility == DesignerSerializationVisibility.Hidden)
1069                             {
1070                                 subReader.Skip();
1071                             }
1072                             else
1073                             {
1074                                 writer.WriteNode(subReader);
1075                                 subReader.Read();
1076                             }
1077                         }
1078
1079                         // Close out the dummy StartObject & StartMember
1080                         Fx.Assert(objectReader.NodeType == XamlNodeType.EndMember, "Expected EndMember");
1081                         objectReader.Read();
1082                         Fx.Assert(objectReader.NodeType == XamlNodeType.EndObject, "Expected EndObject");
1083                         objectReader.Read();
1084
1085                         // Skip past </ErrorActivity.ErrorNodes>
1086                         Fx.Assert(objectReader.NodeType == XamlNodeType.EndMember, "Expected EndMember");
1087                         objectReader.Read();
1088                     }
1089                     else
1090                     {
1091                         // Skip any APs added by the designer
1092                         Fx.Assert(objectReader.Member.IsAttachable, "Unexpected member on ErrorActivity");
1093                         objectReader.Skip();
1094                     }
1095                 }
1096                 while (objectReader.NodeType != XamlNodeType.EndObject); // </ErrorActivity>
1097             }
1098
1099             // If the namespace is the markup-compat namespace, we skip writing it out as long as there
1100             // is an ignorable namespace at the root of the doc; DesignTimeXamlWriter will add it to the
1101             // root later. We assume that the exact prefix for markup-compat doesn't matter, just whether the
1102             // namespace is defined.
1103             static bool IsIgnorableCompatNamespace(NamespaceDeclaration ns, NamespaceStackNode currentNamespaces)
1104             {
1105                 if (ns.Namespace == NameSpaces.Mc)
1106                 {
1107                     NamespaceStackNode rootNamespaces = currentNamespaces;
1108                     while (rootNamespaces.PreviousNode != null)
1109                     {
1110                         rootNamespaces = rootNamespaces.PreviousNode;
1111                     }
1112                     foreach (string rootNs in rootNamespaces.Values)
1113                     {
1114                         if (NameSpaces.ShouldIgnore(rootNs))
1115                         {
1116                             return true;
1117                         }
1118                     }
1119                 }
1120                 return false;
1121             }
1122
1123             XamlNodeList GetErrorNodes()
1124             {
1125                 XamlNodeList result = new XamlNodeList(NodeQueue.Writer.SchemaContext);
1126
1127                 // Dummy StartObject & StartMember. This is here so that ObjectReader doesn't try
1128                 // to hoist all the namespaces on save, which would cause them to be added as
1129                 // Imports by the VBExpression converter.
1130                 result.Writer.WriteStartObject(XamlLanguage.Object);
1131                 result.Writer.WriteStartMember(XamlLanguage.UnknownContent);
1132
1133                 // Write out all namespaces in scope to the NodeList, to ensure that the any type
1134                 // converters that use namespaces/prefixes still work on round trip.
1135                 // (We can strip out the redundant ones on Save.)
1136                 foreach (KeyValuePair<string, string> ns in Namespaces.FlattenNamespaces())
1137                 {
1138                     result.Writer.WriteNamespace(new NamespaceDeclaration(ns.Value, ns.Key));
1139                 }
1140
1141                 // Write out the original contents of this fragment, expanding our children if any.
1142                 base.WriteTo(result.Writer, true);
1143
1144                 // Close the dummy object
1145                 result.Writer.WriteEndMember();
1146                 result.Writer.WriteEndObject();
1147
1148                 result.Writer.Close();
1149                 return result;
1150             }
1151         }
1152
1153         class NameScopeFrame
1154         {
1155             private Dictionary<string, XamlFragment> declaredNames;
1156             private List<NameReference> neededNames;
1157
1158             public NameScopeFrame Parent { get; private set; }
1159             public int Depth { get; set; }
1160             public List<NameReference> NeededNames { get { return this.neededNames; } }
1161
1162             public NameScopeFrame(NameScopeFrame parent)
1163             {
1164                 Parent = parent;
1165             }
1166
1167             public void AddNeededName(XamlFragment fragment, string name, int lineNumber, int linePosition)
1168             {
1169                 if (this.neededNames == null)
1170                 {
1171                     this.neededNames = new List<NameReference>();
1172                 }
1173                 this.neededNames.Add(new NameReference
1174                 {
1175                     Fragment = fragment,
1176                     Name = name,
1177                     LineNumber = lineNumber,
1178                     LinePosition = linePosition
1179                 });
1180             }
1181
1182             public XamlFragment FindName(string name)
1183             {
1184                 NameScopeFrame current = this;
1185                 do
1186                 {
1187                     XamlFragment result = null;
1188                     if (current.declaredNames != null && current.declaredNames.TryGetValue(name, out result))
1189                     {
1190                         return result;
1191                     }
1192                     current = current.Parent;
1193                 }
1194                 while (current != null);
1195                 return null;
1196             }
1197
1198             public bool RegisterName(string name, XamlFragment containingFragment)
1199             {
1200                 if (this.declaredNames == null)
1201                 {
1202                     this.declaredNames = new Dictionary<string, XamlFragment>();
1203                 }
1204                 if (this.declaredNames.ContainsKey(name))
1205                 {
1206                     return false;
1207                 }
1208                 this.declaredNames.Add(name, containingFragment);
1209                 return true;
1210             }
1211         }
1212
1213         class NameReference
1214         {
1215             public XamlFragment Fragment { get; set; }
1216             public string Name { get; set; }
1217             public int LineNumber { get; set; }
1218             public int LinePosition { get; set; }
1219         }
1220
1221         class NamespaceStackNode : Dictionary<string, string>
1222         {
1223             public NamespaceStackNode PreviousNode { get; set; }
1224
1225             public int ObjectDepth { get; set; }
1226
1227             public IEnumerable<KeyValuePair<string, string>> FlattenNamespaces()
1228             {
1229                 // We need to hide not only any shadowed prefixes, but any shadowed namespaces, since
1230                 // XamlXmlWriter doesn't allow declaration of multiple prefixes for the same namespaace
1231                 // at the same scope.
1232                 HashSet<string> prefixes = new HashSet<string>();
1233                 HashSet<string> namespaces = new HashSet<string>();
1234                 NamespaceStackNode current = this;
1235                 do
1236                 {
1237                     foreach (KeyValuePair<string, string> pair in current)
1238                     {
1239                         if (!prefixes.Contains(pair.Key) && !namespaces.Contains(pair.Value))
1240                         {
1241                             yield return pair;
1242                             prefixes.Add(pair.Key);
1243                             namespaces.Add(pair.Value);
1244                         }
1245                     }
1246                     current = current.PreviousNode;
1247                 }
1248                 while (current != null);
1249             }
1250
1251             public string LookupNamespace(string prefix)
1252             {
1253                 NamespaceStackNode current = this;
1254                 do
1255                 {
1256                     string ns;
1257                     if (current.TryGetValue(prefix, out ns))
1258                     {
1259                         return ns;
1260                     }
1261                     current = current.PreviousNode;
1262                 }
1263                 while (current != null);
1264                 return null;
1265             }
1266         }
1267     }
1268 }