1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
6 namespace System.Activities.Presentation.Xaml
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;
20 using System.Xaml.Schema;
21 using Microsoft.Activities.Presentation.Xaml;
22 using NameReferenceConverter = System.Windows.Markup.NameReferenceConverter;
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.
32 internal class ErrorTolerantObjectWriter : XamlWriter, IXamlLineInfoConsumer
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
39 // Current state of the nodestream, for performing validation
40 WalkableStack<XamlFrame> xamlStack;
42 // Root of the tree of completed fragments (except for the root, which may be in progress)
43 XamlFragment rootFragment;
45 // Stack of in-progress fragments
46 Stack<XamlFragment> fragmentStack;
48 // Stack of in-progress namescopes; fragments can overlap multiple namescopes, and vice versa
49 Stack<NameScopeFrame> nameScopeStack;
51 // Completed namescopes, saved so we can resolve all references at end of parse
52 List<NameScopeFrame> poppedNameScopes;
54 // Pending NS declarations whose corresponding StartObject hasn't been written yet
55 NamespaceStackNode pendingNamespaces;
57 XamlObjectWriter objectWriter;
58 XamlType typeOfActivity;
59 XamlMember nameOfReference;
60 XamlValueConverter<XamlDeferringLoader> activityLoader;
61 int lineNumber, linePosition;
63 public string LocalAssemblyName { get; set; }
65 public IList<XamlLoadErrorInfo> LoadErrors { get; private set; }
67 public ErrorTolerantObjectWriter(XamlSchemaContext schemaContext)
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;
82 public object Result { get; private set; }
84 public override XamlSchemaContext SchemaContext { get { return this.objectWriter.SchemaContext; } }
86 public override void WriteNamespace(NamespaceDeclaration namespaceDeclaration)
88 if (this.rootFragment.HasError)
92 if (this.pendingNamespaces == null)
94 this.pendingNamespaces = new NamespaceStackNode();
96 this.pendingNamespaces.Add(namespaceDeclaration.Prefix, namespaceDeclaration.Namespace);
97 CurrentWriter.WriteNamespace(namespaceDeclaration);
100 public override void WriteStartObject(XamlType type)
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.
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)
115 CurrentWriter.WriteStartObject(type);
116 CurrentFragment.ObjectDepth++;
119 public override void WriteGetObject()
123 if (this.rootFragment.HasError)
127 CurrentWriter.WriteGetObject();
128 CurrentFragment.ObjectDepth++;
131 public override void WriteEndObject()
133 this.xamlStack.Pop();
135 if (this.rootFragment.HasError)
139 CurrentWriter.WriteEndObject();
140 CurrentFragment.ObjectDepth--;
141 if (CurrentFragment.ObjectDepth == 0)
143 XamlFragment completedFragment = CurrentFragment;
144 this.fragmentStack.Pop();
145 if (this.fragmentStack.Count == 0)
147 Fx.Assert(completedFragment == this.rootFragment, "Base of stack should be root fragment");
152 CurrentFragment.AddChild(completedFragment);
157 public override void WriteStartMember(XamlMember member)
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)
166 CurrentWriter.WriteStartMember(member);
169 public override void WriteEndMember()
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;
175 if (this.rootFragment.HasError)
179 CurrentWriter.WriteEndMember();
182 public override void WriteValue(object value)
184 ValidateValue(value);
185 if (this.rootFragment.HasError)
189 CurrentWriter.WriteValue(value);
192 public void SetLineInfo(int lineNumber, int linePosition)
194 // We need to save the line info statically, for validation errors
195 this.lineNumber = lineNumber;
196 this.linePosition = linePosition;
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)
204 ((IXamlLineInfoConsumer)CurrentWriter).SetLineInfo(lineNumber, linePosition);
207 // ObjectWriter always wants LineInfo
208 public bool ShouldProvideLineInfo { get { return true; } }
210 internal static bool IsErrorActivity(Type objectType)
212 return objectType == typeof(ErrorActivity) ||
213 (objectType != null && objectType.IsGenericType &&
214 objectType.GetGenericTypeDefinition() == typeof(ErrorActivity<>));
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)
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;
227 while (objectReader.Read())
229 // Update the namespace stack
230 switch (objectReader.NodeType)
232 case XamlNodeType.NamespaceDeclaration:
233 if (pendingNamespaces == null)
235 pendingNamespaces = new NamespaceStackNode() { PreviousNode = currentNamespaces };
237 pendingNamespaces.Add(objectReader.Namespace.Prefix, objectReader.Namespace.Namespace);
239 case XamlNodeType.StartObject:
240 case XamlNodeType.GetObject:
241 if (pendingNamespaces != null)
243 currentNamespaces = pendingNamespaces;
244 pendingNamespaces = null;
246 currentNamespaces.ObjectDepth++;
250 if (objectReader.NodeType == XamlNodeType.StartObject && IsErrorActivity(objectReader.Type.UnderlyingType))
252 ActivityFragment.TransformErrorActivityContents(objectReader, writer, currentNamespaces);
256 writer.WriteNode(objectReader);
259 if (objectReader.NodeType == XamlNodeType.EndObject)
261 currentNamespaces.ObjectDepth--;
262 if (currentNamespaces.ObjectDepth == 0)
264 currentNamespaces = currentNamespaces.PreviousNode;
270 XamlFragment CurrentFragment
272 get { return this.fragmentStack.Peek(); }
275 NameScopeFrame CurrentNameScope
277 get { return this.nameScopeStack.Peek(); }
280 XamlWriter CurrentWriter
282 get { return CurrentFragment.NodeQueue.Writer; }
285 static void AppendShortName(StringBuilder result, XamlType type)
287 result.Append(type.Name);
292 foreach (XamlType typeArg in type.TypeArguments)
302 AppendShortName(result, typeArg);
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
311 static void GetLeafUnresolvedTypeArgs(XamlType type, HashSet<XamlType> unresolvedTypeArgs)
313 Fx.Assert(type.IsUnknown, "Method should only be called for unknown types");
314 bool hasUnknownChildren = false;
317 foreach (XamlType typeArg in type.TypeArguments)
319 if (typeArg.IsUnknown)
321 GetLeafUnresolvedTypeArgs(typeArg, unresolvedTypeArgs);
322 hasUnknownChildren = true;
326 if (!hasUnknownChildren)
328 unresolvedTypeArgs.Add(type);
332 internal static string GetXamlMemberName(XamlMember member)
334 if (member.IsDirective)
336 return "{" + member.PreferredXamlNamespace + "}" + member.Name;
340 return GetXamlTypeName(member.DeclaringType) + "." + member.Name;
344 internal static string GetXamlTypeName(XamlType type)
346 string typeNs = type.PreferredXamlNamespace;
347 string typeName = GetFullTypeNameWithoutNamespace(type);
348 string clrns, assembly;
349 if (XamlNamespaceHelper.TryParseClrNsUri(typeNs, out clrns, out assembly))
351 return clrns + "." + typeName;
355 return typeNs + ":" + typeName;
359 static bool IsWhitespace(string value)
361 foreach (char c in value)
363 if (c != '\r' && c != '\n' && c != ' ' && c != '\t')
371 // Validate named references and write out the complete nodestream to the ObjectWriter
374 CompleteNameReferences();
375 XamlFragment.FindBrokenReferences(this.rootFragment);
377 if (this.rootFragment.HasError)
383 this.rootFragment.WriteTo(this.objectWriter, false);
384 this.Result = this.objectWriter.Result;
388 // Gets the property type of the containing member, or its item type if it's a collection.
389 XamlType GetParentPropertyType(out bool parentIsDictionary)
391 XamlMember parentMember;
392 XamlType collectionType;
393 XamlType result = GetParentPropertyType(out parentMember, out collectionType);
394 parentIsDictionary = collectionType != null && collectionType.IsDictionary;
398 XamlType GetParentPropertyType(out XamlMember parentMember, out XamlType collectionType)
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))
405 if (parentMember == XamlLanguage.Items)
407 collectionType = this.xamlStack.Previous(1).Type;
408 if (collectionType == null)
410 // This is a GetObject, need to look at the containing member
411 collectionType = this.xamlStack.Previous(2).Member.Type;
416 collectionType = parentMember.Type;
418 return collectionType.ItemType;
420 collectionType = null;
421 return parentMember.Type;
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()
428 Fx.Assert(this.xamlStack.Count > 0, "PushNewActivityFrameIfNeeded called without a StartObject");
429 if (this.xamlStack.Count == 1)
431 // This is the root of the document
434 if (CurrentFragment.HasError)
436 // We're already inside an error frame, no point pushing any more frames
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))
446 this.fragmentStack.Push(new ActivityFragment(SchemaContext) { Type = parentType.UnderlyingType });
447 CurrentFragment.Namespaces = this.xamlStack.Current.Namespaces;
453 this.nameScopeStack.Push(new NameScopeFrame(this.nameScopeStack.Peek()));
456 void PushXamlFrame(XamlType type)
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)
462 this.pendingNamespaces.PreviousNode = currentNamespaces;
463 this.xamlStack.Current.Namespaces = this.pendingNamespaces;
464 this.pendingNamespaces = null;
468 this.xamlStack.Current.Namespaces = currentNamespaces;
472 void ValidateStartObject()
474 // Check if type is known
475 XamlType type = this.xamlStack.Current.Type;
478 HashSet<XamlType> unresolvedTypes = null;
481 unresolvedTypes = new HashSet<XamlType>();
482 GetLeafUnresolvedTypeArgs(type, unresolvedTypes);
484 if (unresolvedTypes != null &&
485 (unresolvedTypes.Count > 1 || !unresolvedTypes.Contains(type)))
487 ValidationError(SR.UnresolvedGenericType, GetXamlTypeName(type));
488 foreach (XamlType unresolvedTypeArg in unresolvedTypes)
490 ValidationErrorUnknownType(unresolvedTypeArg);
495 ValidationErrorUnknownType(type);
498 else if (this.xamlStack.Count > 1)
500 // Check assignability to parent member
501 if (!type.IsMarkupExtension)
503 XamlMember parentMember;
504 XamlType collectionType;
505 XamlType expectedType = GetParentPropertyType(out parentMember, out collectionType);
506 if (collectionType != null)
508 if (!CollectionAcceptsType(collectionType, type))
510 ValidationError(SR.UnassignableCollection, type, collectionType.ItemType, collectionType);
513 else if (parentMember != null && !parentMember.IsUnknown &&
514 !type.CanAssignTo(parentMember.Type) && parentMember.DeferringLoader == null)
516 ValidationError(SR.UnassignableObject, type, parentMember.Type, parentMember.Name);
521 // Update the NameScope stack
522 if (type.IsNameScope && this.xamlStack.Count > 1)
526 CurrentNameScope.Depth++;
529 void ValidateGetObject()
531 XamlType type = this.xamlStack.Previous(1).Member.Type;
532 if (type.IsNameScope)
536 CurrentNameScope.Depth++;
539 // Check whether a member is set more than once
540 bool ValidateSetMember()
542 XamlFrame frame = this.xamlStack.Current;
545 if (frame.MemberIsSet && !frame.Member.IsUnknown && !frame.Member.IsDirective)
547 ValidationError(SR.MemberCanOnlyBeSetOnce, frame.Member);
550 frame.MemberIsSet = true;
555 bool CollectionAcceptsType(XamlType collectionType, XamlType type)
557 return collectionType.IsUnknown ||
558 collectionType.AllowedContentTypes == null ||
559 collectionType.AllowedContentTypes.Any(contentType => type.CanAssignTo(contentType));
562 void ValidateStartMember()
564 XamlFrame currentFrame = this.xamlStack.Current;
565 XamlMember member = currentFrame.Member;
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)
572 if (!currentFrame.Type.IsUnknown)
574 ValidationError(SR.UnknownContent, this.xamlStack.Current.Type);
577 else if (member.IsUnknown && (member.IsAttachable || member.IsDirective || !member.DeclaringType.IsUnknown))
579 ValidationError(SR.UnresolvedMember, member.Name, member.DeclaringType);
582 // Check for duplicate members
583 if (currentFrame.PastMembers == null)
585 currentFrame.PastMembers = new HashSet<XamlMember>();
587 if (currentFrame.PastMembers.Contains(member))
589 ValidationError(SR.DuplicateMember, member);
593 currentFrame.PastMembers.Add(member);
596 // Check for misplaced attachable members
597 if (member.IsAttachable && !currentFrame.Type.IsUnknown && !currentFrame.Type.CanAssignTo(member.TargetType))
599 ValidationError(SR.MemberOnBadTargetType, member.Name, member.TargetType);
602 // Update the NameScope stack
603 if (member.DeferringLoader != null)
607 CurrentNameScope.Depth++;
610 void ValidateEndMember()
612 DecrementNameScopeDepth();
615 void ValidateEndObject()
617 DecrementNameScopeDepth();
620 void ValidateValue(object value)
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))
630 // Check if this is x:Name or RuntimeNameProperty
631 if (member == XamlLanguage.Name || (type != null && member == type.GetAliasedProperty(XamlLanguage.Name)))
633 if (!CurrentNameScope.RegisterName(valueString, CurrentFragment))
635 ValidationError(SR.DuplicateName, valueString);
640 // Check if this is an x:Reference
641 if (type == XamlLanguage.Reference && (member == this.nameOfReference || member == XamlLanguage.PositionalParameters))
643 CurrentNameScope.AddNeededName(CurrentFragment, valueString, this.lineNumber, this.linePosition);
646 XamlValueConverter<TypeConverter> converter =
647 (member == XamlLanguage.Initialization) ? type.TypeConverter : member.TypeConverter;
648 if (converter != null && converter.ConverterType == typeof(NameReferenceConverter))
650 CurrentNameScope.AddNeededName(CurrentFragment, valueString, this.lineNumber, this.linePosition);
653 // Check if text is supported on this member
654 if (member == XamlLanguage.Initialization)
656 if (!type.IsUnknown && type.TypeConverter == null && !XamlLanguage.String.CanAssignTo(type))
658 ValidationError(SR.NoTypeConverter, type);
661 else if (member.IsDirective)
663 if (member == XamlLanguage.Items)
667 // Inside a GetObject - get the type from the parent member
668 type = this.xamlStack.Previous(1).Member.Type;
670 if (!CollectionAcceptsType(type, XamlLanguage.String))
672 ValidationError(SR.NoTextInCollection, type);
676 else if (member.TypeConverter == null && !XamlLanguage.String.CanAssignTo(member.Type) &&
677 (member.DeferringLoader == null || member.DeferringLoader == this.activityLoader))
679 ValidationError(SR.NoTextInProperty, XamlLanguage.String, member.Type, member.Name);
683 void ValidationError(string message, params object[] arguments)
685 ValidationError(message, this.lineNumber, this.linePosition, arguments);
686 CurrentFragment.HasError = true;
689 void ValidationError(string message, int lineNumber, int linePosition, params object[] arguments)
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++)
695 XamlType type = arguments[i] as XamlType;
698 arguments[i] = GetXamlTypeName(type);
702 XamlMember member = arguments[i] as XamlMember;
705 arguments[i] = GetXamlMemberName(member);
709 string error = string.Format(CultureInfo.CurrentCulture, message, arguments);
710 if (LoadErrors == null)
712 LoadErrors = new List<XamlLoadErrorInfo>();
714 LoadErrors.Add(new XamlLoadErrorInfo(error, lineNumber, linePosition));
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)
721 StringBuilder result = new StringBuilder();
722 string clrns, assembly;
723 if (XamlNamespaceHelper.TryParseClrNsUri(type.PreferredXamlNamespace, out clrns, out assembly))
725 if (assembly == null)
727 assembly = this.LocalAssemblyName;
729 StringBuilder typeName = new StringBuilder();
730 typeName.Append(clrns);
731 typeName.Append(".");
732 AppendShortName(typeName, type);
733 ValidationError(SR.UnresolvedTypeInAssembly, typeName, assembly);
737 StringBuilder typeName = new StringBuilder();
738 AppendShortName(typeName, type);
739 ValidationError(SR.UnresolvedTypeInNamespace, typeName, type.PreferredXamlNamespace);
743 void DecrementNameScopeDepth()
745 CurrentNameScope.Depth--;
746 if (CurrentNameScope.Depth == 0)
748 this.poppedNameScopes.Add(this.nameScopeStack.Pop());
752 // Resolves all simple name references in the tree, raising validation errors for any that
753 // can't be resolved.
754 void CompleteNameReferences()
756 foreach (NameScopeFrame nameScope in this.poppedNameScopes)
758 if (nameScope.NeededNames == null)
762 foreach (NameReference reference in nameScope.NeededNames)
764 XamlFragment target = nameScope.FindName(reference.Name);
767 ValidationError(SR.UnresolvedName, reference.LineNumber, reference.LinePosition, reference.Name);
768 reference.Fragment.HasError = true;
772 if (target.ReferencedBy == null)
774 target.ReferencedBy = new HashSet<XamlFragment>();
776 target.ReferencedBy.Add(reference.Fragment);
782 private static string GetFullTypeNameWithoutNamespace(XamlType xamlType)
784 string typeName = string.Empty;
785 if (xamlType != null)
787 typeName = xamlType.Name;
788 bool firstTypeArg = true;
789 if (xamlType.TypeArguments != null && xamlType.TypeArguments.Count > 0)
792 foreach (XamlType typeArg in xamlType.TypeArguments)
800 firstTypeArg = false;
802 typeName += typeArg.Name;
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; }
819 // A stack that is implemented as a list to allow walking up the stack.
820 class WalkableStack<T> : List<T> where T : class
824 T result = this[Count - 1];
825 this.RemoveAt(Count - 1);
829 public T Previous(int index)
831 return this[Count - 1 - index];
834 public void Push(T frame)
841 get { return Count > 0 ? this[Count - 1] : null; }
845 // Class to buffer a tree of XAML fragments and write them back out in the correct order.
848 private XamlFragment firstChild;
849 private XamlFragment nextSibling;
851 public XamlFragment(XamlSchemaContext schemaContext)
853 NodeQueue = new XamlNodeQueue(schemaContext);
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; }
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)
867 NodeQueue.Writer.WriteValue(newChild);
868 XamlFragment curChild = this.firstChild;
869 if (curChild == null)
871 this.firstChild = newChild;
875 while (curChild.nextSibling != null)
877 curChild = curChild.nextSibling;
879 curChild.nextSibling = newChild;
883 // Find all references to error fragments and mark the referencing fragments as also errored.
884 public static void FindBrokenReferences(XamlFragment rootFragment)
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)
894 if (rootFragment.HasError)
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.
901 XamlFragment current = queue.Dequeue();
902 if (current.HasError)
904 // Mark all this fragment's children as errored, and enqueue them for recursive processing.
905 XamlFragment child = current.firstChild;
906 while (child != null)
908 child.HasError = true;
909 queue.Enqueue(child);
910 child = child.nextSibling;
913 // Mark all fragments that reference this fragment as errored, and enqueue them for recursive processing.
914 if (current.ReferencedBy != null)
916 foreach (XamlFragment referencingFragment in current.ReferencedBy)
918 referencingFragment.HasError = true;
919 queue.Enqueue(referencingFragment);
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;
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)
935 queue.Enqueue(child);
936 child = child.nextSibling;
942 // Write this fragment and all its children out to the specified writer.
943 public virtual void WriteTo(XamlWriter writer, bool parentHasError)
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;
950 int linePosition = 0;
952 while (nodeReader.Read())
954 if (lineInfo.LineNumber > 0 &&
955 (lineInfo.LineNumber != lineNumber || lineInfo.LinePosition != linePosition))
957 lineNumber = lineInfo.LineNumber;
958 linePosition = lineInfo.LinePosition;
959 lineInfoConsumer.SetLineInfo(lineNumber, linePosition);
961 XamlFragment child = (nodeReader.NodeType == XamlNodeType.Value) ? nodeReader.Value as XamlFragment : null;
964 child.WriteTo(writer, parentHasError || HasError);
968 writer.WriteNode(nodeReader);
974 class ActivityFragment : XamlFragment
976 public ActivityFragment(XamlSchemaContext schemaContext)
977 : base(schemaContext)
981 public Type Type { get; set; }
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)
987 return type == typeof(Activity) ||
988 (type.IsGenericType &&
989 type.GetGenericTypeDefinition() == typeof(Activity<>));
992 public override void WriteTo(XamlWriter writer, bool parentHasError)
994 if (HasError && !parentHasError)
996 Fx.Assert(this.Type != null && IsActivityType(this.Type), "Cannot create ErrorActivity for non-Activity property");
998 if (this.Type == typeof(Activity))
1000 errorType = typeof(ErrorActivity);
1004 errorType = typeof(ErrorActivity<>).MakeGenericType(this.Type.GetGenericArguments()[0]);
1006 XamlType errorXamlType = writer.SchemaContext.GetXamlType(errorType);
1007 writer.WriteStartObject(errorXamlType);
1008 writer.WriteStartMember(errorXamlType.GetMember(ErrorActivity.ErrorNodesProperty));
1010 XamlNodeList errorNodes = GetErrorNodes();
1011 ErrorActivity.WriteNodeList(writer, errorNodes);
1013 writer.WriteEndMember(); // </ErrorActivity.ErrorNodeList>
1014 writer.WriteEndObject(); // </ErrorActivity>
1018 base.WriteTo(writer, parentHasError);
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)
1028 XamlMember errorNodesMember = objectReader.Type.GetMember(ErrorActivity.ErrorNodesProperty);
1029 // Skip past off <ErrorActivity>
1030 objectReader.Read();
1034 Fx.Assert(objectReader.NodeType == XamlNodeType.StartMember, "Expected StartMember");
1035 if (objectReader.Member == errorNodesMember)
1037 // Skip past <ErrorActivity.ErrorNodes>
1038 objectReader.Read();
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();
1046 // Strip redundant namespaces
1047 while (objectReader.NodeType == XamlNodeType.NamespaceDeclaration)
1049 string ns = currentNamespaces.LookupNamespace(objectReader.Namespace.Prefix);
1050 if (ns != objectReader.Namespace.Namespace &&
1051 !IsIgnorableCompatNamespace(objectReader.Namespace, currentNamespaces))
1053 writer.WriteNamespace(objectReader.Namespace);
1055 objectReader.Read();
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();
1064 while (!subReader.IsEof)
1066 if (subReader.NodeType == XamlNodeType.StartMember &&
1067 subReader.Member.DeclaringType == debuggerReaderType &&
1068 subReader.Member.SerializationVisibility == DesignerSerializationVisibility.Hidden)
1074 writer.WriteNode(subReader);
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();
1085 // Skip past </ErrorActivity.ErrorNodes>
1086 Fx.Assert(objectReader.NodeType == XamlNodeType.EndMember, "Expected EndMember");
1087 objectReader.Read();
1091 // Skip any APs added by the designer
1092 Fx.Assert(objectReader.Member.IsAttachable, "Unexpected member on ErrorActivity");
1093 objectReader.Skip();
1096 while (objectReader.NodeType != XamlNodeType.EndObject); // </ErrorActivity>
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)
1105 if (ns.Namespace == NameSpaces.Mc)
1107 NamespaceStackNode rootNamespaces = currentNamespaces;
1108 while (rootNamespaces.PreviousNode != null)
1110 rootNamespaces = rootNamespaces.PreviousNode;
1112 foreach (string rootNs in rootNamespaces.Values)
1114 if (NameSpaces.ShouldIgnore(rootNs))
1123 XamlNodeList GetErrorNodes()
1125 XamlNodeList result = new XamlNodeList(NodeQueue.Writer.SchemaContext);
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);
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())
1138 result.Writer.WriteNamespace(new NamespaceDeclaration(ns.Value, ns.Key));
1141 // Write out the original contents of this fragment, expanding our children if any.
1142 base.WriteTo(result.Writer, true);
1144 // Close the dummy object
1145 result.Writer.WriteEndMember();
1146 result.Writer.WriteEndObject();
1148 result.Writer.Close();
1153 class NameScopeFrame
1155 private Dictionary<string, XamlFragment> declaredNames;
1156 private List<NameReference> neededNames;
1158 public NameScopeFrame Parent { get; private set; }
1159 public int Depth { get; set; }
1160 public List<NameReference> NeededNames { get { return this.neededNames; } }
1162 public NameScopeFrame(NameScopeFrame parent)
1167 public void AddNeededName(XamlFragment fragment, string name, int lineNumber, int linePosition)
1169 if (this.neededNames == null)
1171 this.neededNames = new List<NameReference>();
1173 this.neededNames.Add(new NameReference
1175 Fragment = fragment,
1177 LineNumber = lineNumber,
1178 LinePosition = linePosition
1182 public XamlFragment FindName(string name)
1184 NameScopeFrame current = this;
1187 XamlFragment result = null;
1188 if (current.declaredNames != null && current.declaredNames.TryGetValue(name, out result))
1192 current = current.Parent;
1194 while (current != null);
1198 public bool RegisterName(string name, XamlFragment containingFragment)
1200 if (this.declaredNames == null)
1202 this.declaredNames = new Dictionary<string, XamlFragment>();
1204 if (this.declaredNames.ContainsKey(name))
1208 this.declaredNames.Add(name, containingFragment);
1215 public XamlFragment Fragment { get; set; }
1216 public string Name { get; set; }
1217 public int LineNumber { get; set; }
1218 public int LinePosition { get; set; }
1221 class NamespaceStackNode : Dictionary<string, string>
1223 public NamespaceStackNode PreviousNode { get; set; }
1225 public int ObjectDepth { get; set; }
1227 public IEnumerable<KeyValuePair<string, string>> FlattenNamespaces()
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;
1237 foreach (KeyValuePair<string, string> pair in current)
1239 if (!prefixes.Contains(pair.Key) && !namespaces.Contains(pair.Value))
1242 prefixes.Add(pair.Key);
1243 namespaces.Add(pair.Value);
1246 current = current.PreviousNode;
1248 while (current != null);
1251 public string LookupNamespace(string prefix)
1253 NamespaceStackNode current = this;
1257 if (current.TryGetValue(prefix, out ns))
1261 current = current.PreviousNode;
1263 while (current != null);