Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / NetFx40 / Tools / System.Activities.Presentation / System / Activities / Presentation / Xaml / ErrorTolerantObjectWriter.cs / 1407647 / ErrorTolerantObjectWriter.cs
//---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Activities.Presentation.Xaml { using System; using System.Activities; using System.Activities.Debugger; using System.Activities.XamlIntegration; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime; using System.Text; using System.Xaml; using System.Xaml.Schema; using System.Xml; using System.Linq; using NameReferenceConverter = System.Windows.Markup.NameReferenceConverter; using XamlDeferLoad = System.Windows.Markup.XamlDeferLoadAttribute; // This XamlWriter wraps an ObjectWriter to provide limited error tolerance, as follows: // - Buffer the node stream as a tree of fragments (one for each activity) and statically validate it. // - Write the node stream out to an ObjectWriter, wrapping any subtrees that failed validation // in an ErrorActivity. // - If validation fails at the root level, we don't load any object, we only provide validation errors. // We are only tolerant of errors that can be detected statically; i.e. we are not resillient // to XamlObjectWriter exceptions. internal class ErrorTolerantObjectWriter : XamlWriter, IXamlLineInfoConsumer { // We store three main types of state: // - The current state of the nodestream (XamlFrames), for validation purposes. // - Buffered XamlFragments. // - NameScopes, which have // Current state of the nodestream, for performing validation WalkableStackxamlStack; // Root of the tree of completed fragments (except for the root, which may be in progress) XamlFragment rootFragment; // Stack of in-progress fragments Stack fragmentStack; // Stack of in-progress namescopes; fragments can overlap multiple namescopes, and vice versa Stack nameScopeStack; // Completed namescopes, saved so we can resolve all references at end of parse List poppedNameScopes; // Pending NS declarations whose corresponding StartObject hasn't been written yet NamespaceStackNode pendingNamespaces; XamlObjectWriter objectWriter; XamlType typeOfActivity; XamlMember nameOfReference; XamlValueConverter activityLoader; int lineNumber, linePosition; public string LocalAssemblyName { get; set; } public IList LoadErrors { get; private set; } public ErrorTolerantObjectWriter(XamlSchemaContext schemaContext) { this.xamlStack = new WalkableStack (); this.rootFragment = new XamlFragment(schemaContext); this.fragmentStack = new Stack (); this.fragmentStack.Push(this.rootFragment); this.nameScopeStack = new Stack (); this.nameScopeStack.Push(new NameScopeFrame(null)); this.poppedNameScopes = new List (); this.objectWriter = new XamlObjectWriter(schemaContext); this.typeOfActivity = objectWriter.SchemaContext.GetXamlType(typeof(Activity)); this.nameOfReference = XamlLanguage.Reference.GetMember("Name"); this.activityLoader = typeOfActivity.GetMember("Implementation").DeferringLoader; } public object Result { get; private set; } public override XamlSchemaContext SchemaContext { get { return this.objectWriter.SchemaContext; } } public override void WriteNamespace(NamespaceDeclaration namespaceDeclaration) { if (this.rootFragment.HasError) { return; } if (this.pendingNamespaces == null) { this.pendingNamespaces = new NamespaceStackNode(); } this.pendingNamespaces.Add(namespaceDeclaration.Prefix, namespaceDeclaration.Namespace); CurrentWriter.WriteNamespace(namespaceDeclaration); } public override void WriteStartObject(XamlType type) { // This validation must be done before pushing the object frame, so that if there is an error, // we treat the containing type as an error and not just the subtree. ValidateSetMember(); PushXamlFrame(type); // Pushing the activity frame must be done before the rest of validation, because if this // is an unknown Activity, we want to treat just this subtree as an error, not its parent. PushNewActivityFrameIfNeeded(); ValidateStartObject(); if (this.rootFragment.HasError) { return; } CurrentWriter.WriteStartObject(type); CurrentFragment.ObjectDepth++; } public override void WriteGetObject() { PushXamlFrame(null); ValidateGetObject(); if (this.rootFragment.HasError) { return; } CurrentWriter.WriteGetObject(); CurrentFragment.ObjectDepth++; } public override void WriteEndObject() { this.xamlStack.Pop(); ValidateEndObject(); if (this.rootFragment.HasError) { return; } CurrentWriter.WriteEndObject(); CurrentFragment.ObjectDepth--; if (CurrentFragment.ObjectDepth == 0) { XamlFragment completedFragment = CurrentFragment; this.fragmentStack.Pop(); if (this.fragmentStack.Count == 0) { Fx.Assert(completedFragment == this.rootFragment, "Base of stack should be root fragment"); CompleteLoad(); } else { CurrentFragment.AddChild(completedFragment); } } } public override void WriteStartMember(XamlMember member) { Fx.Assert(this.xamlStack.Count > 0 && this.xamlStack.Current.Member == null, "Unexpected StartMember"); this.xamlStack.Current.Member = member; ValidateStartMember(); if (this.rootFragment.HasError) { return; } CurrentWriter.WriteStartMember(member); } public override void WriteEndMember() { Fx.Assert(this.xamlStack.Count > 0 && this.xamlStack.Current.Member != null, "Unexpected EndMember"); this.xamlStack.Current.Member = null; this.xamlStack.Current.MemberIsSet = false; ValidateEndMember(); if (this.rootFragment.HasError) { return; } CurrentWriter.WriteEndMember(); } public override void WriteValue(object value) { ValidateValue(value); if (this.rootFragment.HasError) { return; } CurrentWriter.WriteValue(value); } public void SetLineInfo(int lineNumber, int linePosition) { // We need to save the line info statically, for validation errors this.lineNumber = lineNumber; this.linePosition = linePosition; // But we also need to keep it in [....] with the nodestream, for XOW errors // XOW and XamlNodeQueue.Writer both implement IXamlLineInfoConsumer, so we can do a straight cast if (this.rootFragment.HasError) { return; } ((IXamlLineInfoConsumer)CurrentWriter).SetLineInfo(lineNumber, linePosition); } // ObjectWriter always wants LineInfo public bool ShouldProvideLineInfo { get { return true; } } internal static bool IsErrorActivity(Type objectType) { return objectType == typeof(ErrorActivity) || (objectType != null && objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(ErrorActivity<>)); } // Node loop that strips out ErrorActivities on Save. Assumes that ErrorActivities are never // nested, and that XamlObjectReader doesn't have line info. internal static void TransformAndStripErrors(XamlObjectReader objectReader, XamlWriter writer) { // Every ErrorActivity is prefixed with all the NamespaceDeclarations that were in scope // in the original document. We track the current namespaces in scope on Save, so that we // can strip out any redundant declarations. NamespaceStackNode currentNamespaces = null; NamespaceStackNode pendingNamespaces = null; while (objectReader.Read()) { // Update the namespace stack switch (objectReader.NodeType) { case XamlNodeType.NamespaceDeclaration: if (pendingNamespaces == null) { pendingNamespaces = new NamespaceStackNode() { PreviousNode = currentNamespaces }; } pendingNamespaces.Add(objectReader.Namespace.Prefix, objectReader.Namespace.Namespace); break; case XamlNodeType.StartObject: case XamlNodeType.GetObject: if (pendingNamespaces != null) { currentNamespaces = pendingNamespaces; pendingNamespaces = null; } currentNamespaces.ObjectDepth++; break; } if (objectReader.NodeType == XamlNodeType.StartObject && IsErrorActivity(objectReader.Type.UnderlyingType)) { ActivityFragment.TransformErrorActivityContents(objectReader, writer, currentNamespaces); } else { writer.WriteNode(objectReader); } if (objectReader.NodeType == XamlNodeType.EndObject) { currentNamespaces.ObjectDepth--; if (currentNamespaces.ObjectDepth == 0) { currentNamespaces = currentNamespaces.PreviousNode; } } } } XamlFragment CurrentFragment { get { return this.fragmentStack.Peek(); } } NameScopeFrame CurrentNameScope { get { return this.nameScopeStack.Peek(); } } XamlWriter CurrentWriter { get { return CurrentFragment.NodeQueue.Writer; } } static void AppendShortName(StringBuilder result, XamlType type) { result.Append(type.Name); if (type.IsGeneric) { result.Append("("); bool isFirst = true; foreach (XamlType typeArg in type.TypeArguments) { if (isFirst) { isFirst = false; } else { result.Append(","); } AppendShortName(result, typeArg); } result.Append(")"); } } // If a generic type is unknown, we don't know whether the open generic couldn't be resolved, // or just its children. So we only want to surface errors for types that don't have unknown // children. static void GetLeafUnresolvedTypeArgs(XamlType type, HashSet unresolvedTypeArgs) { Fx.Assert(type.IsUnknown, "Method should only be called for unknown types"); bool hasUnknownChildren = false; if (type.IsGeneric) { foreach (XamlType typeArg in type.TypeArguments) { if (typeArg.IsUnknown) { GetLeafUnresolvedTypeArgs(typeArg, unresolvedTypeArgs); hasUnknownChildren = true; } } } if (!hasUnknownChildren) { unresolvedTypeArgs.Add(type); } } static string GetXamlMemberName(XamlMember member) { if (member.IsDirective) { return "{" + member.PreferredXamlNamespace + "}" + member.Name; } else { return GetXamlTypeName(member.DeclaringType) + "." + member.Name; } } static string GetXamlTypeName(XamlType type) { StringBuilder result = new StringBuilder(); if (type.UnderlyingType == null) { result.Append("{"); result.Append(type.PreferredXamlNamespace); result.Append("}"); } else { result.Append(type.UnderlyingType.Namespace); result.Append("."); } AppendShortName(result, type); return result.ToString(); } static bool IsWhitespace(string value) { foreach (char c in value) { if (c != '\r' && c != '\n' && c != ' ' && c != '\t') { return false; } } return true; } // Validate named references and write out the complete nodestream to the ObjectWriter void CompleteLoad() { CompleteNameReferences(); XamlFragment.FindBrokenReferences(this.rootFragment); if (this.rootFragment.HasError) { this.Result = null; } else { this.rootFragment.WriteTo(this.objectWriter, false); this.Result = this.objectWriter.Result; } } // Gets the property type of the containing member, or its item type if it's a collection. XamlType GetParentPropertyType(out bool parentIsDictionary) { XamlMember parentMember; XamlType collectionType; XamlType result = GetParentPropertyType(out parentMember, out collectionType); parentIsDictionary = collectionType != null && collectionType.IsDictionary; return result; } XamlType GetParentPropertyType(out XamlMember parentMember, out XamlType collectionType) { parentMember = this.xamlStack.Previous(1).Member; Fx.Assert(parentMember != null, "StartObject or Value without preceding StartMember"); if (parentMember.IsDirective && (parentMember.Type.IsCollection || parentMember.Type.IsDictionary)) { if (parentMember == XamlLanguage.Items) { collectionType = this.xamlStack.Previous(1).Type; if (collectionType == null) { // This is a GetObject, need to look at the containing member collectionType = this.xamlStack.Previous(2).Member.Type; } } else { collectionType = parentMember.Type; } return collectionType.ItemType; } collectionType = null; return parentMember.Type; } // Checks whether to push a new ActivityFrame for a new StartObject (i.e. whether the object // is an activity and is replaceable in case of error). void PushNewActivityFrameIfNeeded() { Fx.Assert(this.xamlStack.Count > 0, "PushNewActivityFrameIfNeeded called without a StartObject"); if (this.xamlStack.Count == 1) { // This is the root of the document return; } if (CurrentFragment.HasError) { // We're already inside an error frame, no point pushing any more frames return; } // Check the parent property type (not the object type) because that's what determines // whether we can inject an ErrorActivity. bool parentIsDictionary; XamlType parentType = GetParentPropertyType(out parentIsDictionary); if (parentType != null && parentType.UnderlyingType != null && !parentIsDictionary && ActivityFragment.IsActivityType(parentType.UnderlyingType)) { this.fragmentStack.Push(new ActivityFragment(SchemaContext) { Type = parentType.UnderlyingType }); CurrentFragment.Namespaces = this.xamlStack.Current.Namespaces; } } void PushNameScope() { this.nameScopeStack.Push(new NameScopeFrame(this.nameScopeStack.Peek())); } void PushXamlFrame(XamlType type) { NamespaceStackNode currentNamespaces = this.xamlStack.Count > 0 ? this.xamlStack.Current.Namespaces : null; this.xamlStack.Push(new XamlFrame { Type = type }); if (this.pendingNamespaces != null) { this.pendingNamespaces.PreviousNode = currentNamespaces; this.xamlStack.Current.Namespaces = this.pendingNamespaces; this.pendingNamespaces = null; } else { this.xamlStack.Current.Namespaces = currentNamespaces; } } void ValidateStartObject() { // Check if type is known XamlType type = this.xamlStack.Current.Type; if (type.IsUnknown) { HashSet unresolvedTypes = null; if (type.IsGeneric) { unresolvedTypes = new HashSet (); GetLeafUnresolvedTypeArgs(type, unresolvedTypes); } if (unresolvedTypes != null && (unresolvedTypes.Count > 1 || !unresolvedTypes.Contains(type))) { ValidationError(SR.UnresolvedGenericType, GetXamlTypeName(type)); foreach (XamlType unresolvedTypeArg in unresolvedTypes) { ValidationErrorUnknownType(unresolvedTypeArg); } } else { ValidationErrorUnknownType(type); } } else if (this.xamlStack.Count > 1) { // Check assignability to parent member if (!type.IsMarkupExtension) { XamlMember parentMember; XamlType collectionType; XamlType expectedType = GetParentPropertyType(out parentMember, out collectionType); if (collectionType != null) { if (!CollectionAcceptsType(collectionType, type)) { ValidationError(SR.UnassignableCollection, type, collectionType.ItemType, collectionType); } } else if (parentMember != null && !parentMember.IsUnknown && !type.CanAssignTo(parentMember.Type) && parentMember.DeferringLoader == null) { ValidationError(SR.UnassignableObject, type, parentMember.Type, parentMember); } } } // Update the NameScope stack if (type.IsNameScope && this.xamlStack.Count > 1) { PushNameScope(); } CurrentNameScope.Depth++; } void ValidateGetObject() { XamlType type = this.xamlStack.Previous(1).Member.Type; if (type.IsNameScope) { PushNameScope(); } CurrentNameScope.Depth++; } // Check whether a member is set more than once bool ValidateSetMember() { XamlFrame frame = this.xamlStack.Current; if (frame != null) { if (frame.MemberIsSet && !frame.Member.IsUnknown && !frame.Member.IsDirective) { ValidationError(SR.MemberCanOnlyBeSetOnce, frame.Member); return false; } frame.MemberIsSet = true; } return true; } bool CollectionAcceptsType(XamlType collectionType, XamlType type) { return collectionType.IsUnknown || collectionType.AllowedContentTypes == null || collectionType.AllowedContentTypes.Any(contentType => type.CanAssignTo(contentType)); } void ValidateStartMember() { XamlFrame currentFrame = this.xamlStack.Current; XamlMember member = currentFrame.Member; // Make sure that the member is known. // Don't bother surfacing an error for unknown instance properties or unknown content on // unknown types. It's redundant, since we'll already surface an error for the unknown type. if (member == XamlLanguage.UnknownContent) { if (!currentFrame.Type.IsUnknown) { ValidationError(SR.UnknownContent, this.xamlStack.Current.Type); } } else if (member.IsUnknown && (member.IsAttachable || member.IsDirective || !member.DeclaringType.IsUnknown)) { ValidationError(SR.UnresolvedMember, member); } // Check for duplicate members if (currentFrame.PastMembers == null) { currentFrame.PastMembers = new HashSet (); } if (currentFrame.PastMembers.Contains(member)) { ValidationError(SR.DuplicateMember, member); } else { currentFrame.PastMembers.Add(member); } // Check for misplaced attachable members if (member.IsAttachable && !currentFrame.Type.IsUnknown && !currentFrame.Type.CanAssignTo(member.TargetType)) { ValidationError(SR.MemberOnBadTargetType, member, member.TargetType); } // Update the NameScope stack if (member.DeferringLoader != null) { PushNameScope(); } CurrentNameScope.Depth++; } void ValidateEndMember() { DecrementNameScopeDepth(); } void ValidateEndObject() { DecrementNameScopeDepth(); } void ValidateValue(object value) { XamlType type = this.xamlStack.Current.Type; XamlMember member = this.xamlStack.Current.Member; string valueString = value as string; if (valueString == null || member.IsUnknown || !ValidateSetMember() || IsWhitespace(valueString)) { return; } // Check if this is x:Name or RuntimeNameProperty if (member == XamlLanguage.Name || (type != null && member == type.GetAliasedProperty(XamlLanguage.Name))) { if (!CurrentNameScope.RegisterName(valueString, CurrentFragment)) { ValidationError(SR.DuplicateName, valueString); } return; } // Check if this is an x:Reference if (type == XamlLanguage.Reference && (member == this.nameOfReference || member == XamlLanguage.PositionalParameters)) { CurrentNameScope.AddNeededName(CurrentFragment, valueString, this.lineNumber, this.linePosition); return; } XamlValueConverter converter = (member == XamlLanguage.Initialization) ? type.TypeConverter : member.TypeConverter; if (converter != null && converter.ConverterType == typeof(NameReferenceConverter)) { CurrentNameScope.AddNeededName(CurrentFragment, valueString, this.lineNumber, this.linePosition); } // Check if text is supported on this member if (member == XamlLanguage.Initialization) { if (!type.IsUnknown && type.TypeConverter == null && !XamlLanguage.String.CanAssignTo(type)) { ValidationError(SR.NoTypeConverter, type); } } else if (member.IsDirective) { if (member == XamlLanguage.Items) { if (type == null) { // Inside a GetObject - get the type from the parent member type = this.xamlStack.Previous(1).Member.Type; } if (!CollectionAcceptsType(type, XamlLanguage.String)) { ValidationError(SR.NoTextInCollection, type); } } } else if (member.TypeConverter == null && !XamlLanguage.String.CanAssignTo(member.Type) && (member.DeferringLoader == null || member.DeferringLoader == this.activityLoader)) { ValidationError(SR.NoTextInProperty, member); } } void ValidationError(string message, params object[] arguments) { ValidationError(message, this.lineNumber, this.linePosition, arguments); CurrentFragment.HasError = true; } void ValidationError(string message, int lineNumber, int linePosition, params object[] arguments) { // The default ToString implementations can be very clunky, especially for generics. // Use our own friendlier versions instead. for (int i = 0; i < arguments.Length; i++) { XamlType type = arguments[i] as XamlType; if (type != null) { arguments[i] = GetXamlTypeName(type); } else { XamlMember member = arguments[i] as XamlMember; if (member != null) { arguments[i] = GetXamlMemberName(member); } } } string error = string.Format(CultureInfo.CurrentCulture, message, arguments); if (LoadErrors == null) { LoadErrors = new List (); } LoadErrors.Add(new XamlLoadErrorInfo(error, lineNumber, linePosition)); } [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotIgnoreMethodResults, Justification = "StringBuilder.Append just returns the same instance that was called")] void ValidationErrorUnknownType(XamlType type) { StringBuilder result = new StringBuilder(); string clrns, assembly; if (XamlNamespaceHelper.TryParseClrNsUri(type.PreferredXamlNamespace, out clrns, out assembly)) { if (assembly == null) { assembly = this.LocalAssemblyName; } StringBuilder typeName = new StringBuilder(); typeName.Append(clrns); typeName.Append("."); AppendShortName(typeName, type); ValidationError(SR.UnresolvedTypeInAssembly, typeName, assembly); } else { StringBuilder typeName = new StringBuilder(); AppendShortName(typeName, type); ValidationError(SR.UnresolvedTypeInNamespace, typeName, type.PreferredXamlNamespace); } } void DecrementNameScopeDepth() { CurrentNameScope.Depth--; if (CurrentNameScope.Depth == 0) { this.poppedNameScopes.Add(this.nameScopeStack.Pop()); } } // Resolves all simple name references in the tree, raising validation errors for any that // can't be resolved. void CompleteNameReferences() { foreach (NameScopeFrame nameScope in this.poppedNameScopes) { if (nameScope.NeededNames == null) { continue; } foreach (NameReference reference in nameScope.NeededNames) { XamlFragment target = nameScope.FindName(reference.Name); if (target == null) { ValidationError(SR.UnresolvedName, reference.LineNumber, reference.LinePosition, reference.Name); reference.Fragment.HasError = true; } else { if (target.ReferencedBy == null) { target.ReferencedBy = new HashSet (); } target.ReferencedBy.Add(reference.Fragment); } } } } class XamlFrame { public XamlType Type { get; set; } public XamlMember Member { get; set; } public bool MemberIsSet { get; set; } public NamespaceStackNode Namespaces { get; set; } public HashSet PastMembers { get; set; } } // A stack that is implemented as a list to allow walking up the stack. class WalkableStack : List where T : class { public T Pop() { T result = this[Count - 1]; this.RemoveAt(Count - 1); return result; } public T Previous(int index) { return this[Count - 1 - index]; } public void Push(T frame) { Add(frame); } public T Current { get { return Count > 0 ? this[Count - 1] : null; } } } // Class to buffer a tree of XAML fragments and write them back out in the correct order. class XamlFragment { private XamlFragment firstChild; private XamlFragment nextSibling; public XamlFragment(XamlSchemaContext schemaContext) { NodeQueue = new XamlNodeQueue(schemaContext); } public XamlNodeQueue NodeQueue { get; private set; } public int ObjectDepth { get; set; } public bool HasError { get; set; } public NamespaceStackNode Namespaces { get; set; } public HashSet ReferencedBy { get; set; } // Adds a child fragment at the current position of the NodeQueue. // We store the fragment as a Value Node, and expand out its contents at Write time. // We also store the fragments in a simple tree structure to so we can iterate them quickly. public void AddChild(XamlFragment newChild) { NodeQueue.Writer.WriteValue(newChild); XamlFragment curChild = this.firstChild; if (curChild == null) { this.firstChild = newChild; } else { while (curChild.nextSibling != null) { curChild = curChild.nextSibling; } curChild.nextSibling = newChild; } } // Find all references to error fragments and mark the referencing fragments as also errored. public static void FindBrokenReferences(XamlFragment rootFragment) { // By starting from the root of the tree and walking its children, we ensure we traverse // each node at least once, and so find every error fragment. // Given an error fragment, we want to mark all its children and all its referencing fragments // as errors, and process them recursively. Queue queue = new Queue (); queue.Enqueue(rootFragment); while (queue.Count > 0) { if (rootFragment.HasError) { // We found an error at the root. We won't be able to load any part of the document, // so skip this redundant processing. return; } XamlFragment current = queue.Dequeue(); if (current.HasError) { // Mark all this fragment's children as errored, and enqueue them for recursive processing. XamlFragment child = current.firstChild; while (child != null) { child.HasError = true; queue.Enqueue(child); child = child.nextSibling; } // Mark all fragments that reference this fragment as errored, and enqueue them for recursive processing. if (current.ReferencedBy != null) { foreach (XamlFragment referencingFragment in current.ReferencedBy) { referencingFragment.HasError = true; queue.Enqueue(referencingFragment); } } // Clear the links so that we don't traverse them again if there is a cycle. current.firstChild = null; current.ReferencedBy = null; } else { // This fragment is healthy, but we need to check for any errors in its children. // Don't remove the children, we'll need to traverse them if this fragment gets // marked as errored later. XamlFragment child = current.firstChild; while (child != null) { queue.Enqueue(child); child = child.nextSibling; } } } } // Write this fragment and all its children out to the specified writer. public virtual void WriteTo(XamlWriter writer, bool parentHasError) { // In the constrained designer scenario, we can always assume that there is line info. XamlReader nodeReader = NodeQueue.Reader; IXamlLineInfo lineInfo = (IXamlLineInfo)nodeReader; IXamlLineInfoConsumer lineInfoConsumer = (IXamlLineInfoConsumer)writer; int lineNumber = 0; int linePosition = 0; while (nodeReader.Read()) { if (lineInfo.LineNumber > 0 && (lineInfo.LineNumber != lineNumber || lineInfo.LinePosition != linePosition)) { lineNumber = lineInfo.LineNumber; linePosition = lineInfo.LinePosition; lineInfoConsumer.SetLineInfo(lineNumber, linePosition); } XamlFragment child = (nodeReader.NodeType == XamlNodeType.Value) ? nodeReader.Value as XamlFragment : null; if (child != null) { child.WriteTo(writer, parentHasError || HasError); } else { writer.WriteNode(nodeReader); } } } } class ActivityFragment : XamlFragment { public ActivityFragment(XamlSchemaContext schemaContext) :base(schemaContext) { } public Type Type { get; set; } // We can only construct an ErrorActivity that is assignable to properties of type // Activity or Activity , not any of their descendants. public static bool IsActivityType(Type type) { return type == typeof(Activity) || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Activity<>)); } public override void WriteTo(XamlWriter writer, bool parentHasError) { if (HasError && !parentHasError) { Fx.Assert(this.Type != null && IsActivityType(this.Type), "Cannot create ErrorActivity for non-Activity property"); Type errorType; if (this.Type == typeof(Activity)) { errorType = typeof(ErrorActivity); } else { errorType = typeof(ErrorActivity<>).MakeGenericType(this.Type.GetGenericArguments()[0]); } XamlType errorXamlType = writer.SchemaContext.GetXamlType(errorType); writer.WriteStartObject(errorXamlType); writer.WriteStartMember(errorXamlType.GetMember(ErrorActivity.ErrorNodesProperty)); XamlNodeList errorNodes = GetErrorNodes(); ErrorActivity.WriteNodeList(writer, errorNodes); writer.WriteEndMember(); // writer.WriteEndObject(); // } else { base.WriteTo(writer, parentHasError); } } // Extracts the Error Nodes contents out of the ErrorActivity and writes them to the // specified writer. // Expects reader to be positioned on SO ErrorActivity and leaves it on corresponding EO. public static void TransformErrorActivityContents(XamlObjectReader objectReader, XamlWriter writer, NamespaceStackNode currentNamespaces) { XamlMember errorNodesMember = objectReader.Type.GetMember(ErrorActivity.ErrorNodesProperty); // Skip past off objectReader.Read(); do { Fx.Assert(objectReader.NodeType == XamlNodeType.StartMember, "Expected StartMember"); if (objectReader.Member == errorNodesMember) { // Skip past } // If the namespace is the markup-compat namespace, we skip writing it out as long as there // is an ignorable namespace at the root of the doc; DesignTimeXamlWriter will add it to the // root later. We assume that the exact prefix for markup-compat doesn't matter, just whether the // namespace is defined. static bool IsIgnorableCompatNamespace(NamespaceDeclaration ns, NamespaceStackNode currentNamespaces) { if (ns.Namespace == NameSpaces.Mc) { NamespaceStackNode rootNamespaces = currentNamespaces; while (rootNamespaces.PreviousNode != null) { rootNamespaces = rootNamespaces.PreviousNode; } foreach (string rootNs in rootNamespaces.Values) { if (NameSpaces.ShouldIgnore(rootNs)) { return true; } } } return false; } XamlNodeList GetErrorNodes() { XamlNodeList result = new XamlNodeList(NodeQueue.Writer.SchemaContext); // Dummy StartObject & StartMember. This is here so that ObjectReader doesn't try // to hoist all the namespaces on save, which would cause them to be added as // Imports by the VBExpression converter. result.Writer.WriteStartObject(XamlLanguage.Object); result.Writer.WriteStartMember(XamlLanguage.UnknownContent); // Write out all namespaces in scope to the NodeList, to ensure that the any type // converters that use namespaces/prefixes still work on round trip. // (We can strip out the redundant ones on Save.) foreach (KeyValuePairobjectReader.Read(); // Skip past the dummy StartObject & StartMember Fx.Assert(objectReader.NodeType == XamlNodeType.StartObject, "Expected StartObject"); objectReader.Read(); Fx.Assert(objectReader.NodeType == XamlNodeType.StartMember, "Expected StartMember"); objectReader.Read(); // Strip redundant namespaces while (objectReader.NodeType == XamlNodeType.NamespaceDeclaration) { string ns = currentNamespaces.LookupNamespace(objectReader.Namespace.Prefix); if (ns != objectReader.Namespace.Namespace && !IsIgnorableCompatNamespace(objectReader.Namespace, currentNamespaces)) { writer.WriteNamespace(objectReader.Namespace); } objectReader.Read(); } // Pass through the original contents, stripping out any hidden APs added by // the XamlDebuggerXmlReader, since XOR wouldn't write them out. XamlType debuggerReaderType = objectReader.SchemaContext.GetXamlType(typeof(XamlDebuggerXmlReader)); Fx.Assert(objectReader.NodeType == XamlNodeType.StartObject, "Expected StartObject"); XamlReader subReader = objectReader.ReadSubtree(); subReader.Read(); while (!subReader.IsEof) { if (subReader.NodeType == XamlNodeType.StartMember && subReader.Member.DeclaringType == debuggerReaderType && subReader.Member.SerializationVisibility == DesignerSerializationVisibility.Hidden) { subReader.Skip(); } else { writer.WriteNode(subReader); subReader.Read(); } } // Close out the dummy StartObject & StartMember Fx.Assert(objectReader.NodeType == XamlNodeType.EndMember, "Expected EndMember"); objectReader.Read(); Fx.Assert(objectReader.NodeType == XamlNodeType.EndObject, "Expected EndObject"); objectReader.Read(); // Skip past Fx.Assert(objectReader.NodeType == XamlNodeType.EndMember, "Expected EndMember"); objectReader.Read(); } else { // Skip any APs added by the designer Fx.Assert(objectReader.Member.IsAttachable, "Unexpected member on ErrorActivity"); objectReader.Skip(); } } while (objectReader.NodeType != XamlNodeType.EndObject); //ns in Namespaces.FlattenNamespaces()) { result.Writer.WriteNamespace(new NamespaceDeclaration(ns.Value, ns.Key)); } // Write out the original contents of this fragment, expanding our children if any. base.WriteTo(result.Writer, true); // Close the dummy object result.Writer.WriteEndMember(); result.Writer.WriteEndObject(); result.Writer.Close(); return result; } } class NameScopeFrame { private Dictionary declaredNames; private List neededNames; public NameScopeFrame Parent { get; private set; } public int Depth { get; set; } public List NeededNames { get { return this.neededNames; } } public NameScopeFrame(NameScopeFrame parent) { Parent = parent; } public void AddNeededName(XamlFragment fragment, string name, int lineNumber, int linePosition) { if (this.neededNames == null) { this.neededNames = new List (); } this.neededNames.Add(new NameReference { Fragment = fragment, Name = name, LineNumber = lineNumber, LinePosition = linePosition }); } public XamlFragment FindName(string name) { NameScopeFrame current = this; do { XamlFragment result = null; if (current.declaredNames != null && current.declaredNames.TryGetValue(name, out result)) { return result; } current = current.Parent; } while (current != null); return null; } public bool RegisterName(string name, XamlFragment containingFragment) { if (this.declaredNames == null) { this.declaredNames = new Dictionary (); } if (this.declaredNames.ContainsKey(name)) { return false; } this.declaredNames.Add(name, containingFragment); return true; } } class NameReference { public XamlFragment Fragment { get; set; } public string Name { get; set; } public int LineNumber { get; set; } public int LinePosition { get; set; } } class NamespaceStackNode : Dictionary { public NamespaceStackNode PreviousNode { get; set; } public int ObjectDepth { get; set; } public IEnumerable > FlattenNamespaces() { // We need to hide not only any shadowed prefixes, but any shadowed namespaces, since // XamlXmlWriter doesn't allow declaration of multiple prefixes for the same namespaace // at the same scope. HashSet prefixes = new HashSet (); HashSet namespaces = new HashSet (); NamespaceStackNode current = this; do { foreach (KeyValuePair pair in current) { if (!prefixes.Contains(pair.Key) && !namespaces.Contains(pair.Value)) { yield return pair; prefixes.Add(pair.Key); namespaces.Add(pair.Value); } } current = current.PreviousNode; } while (current != null); } public string LookupNamespace(string prefix) { NamespaceStackNode current = this; do { string ns; if (current.TryGetValue(prefix, out ns)) { return ns; } current = current.PreviousNode; } while (current != null); return null; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- dataprotectionpermission.cs
- GradientStop.cs
- ExceptionUtil.cs
- AutoFocusStyle.xaml.cs
- XPathDocumentBuilder.cs
- _Win32.cs
- SizeKeyFrameCollection.cs
- SelectionEditingBehavior.cs
- ReceiveMessageContent.cs
- Utils.cs
- XmlJsonWriter.cs
- AdvancedBindingEditor.cs
- ClientConfigurationHost.cs
- AsmxEndpointPickerExtension.cs
- MultiBindingExpression.cs
- ConfigXmlWhitespace.cs
- CompareValidator.cs
- SubMenuStyle.cs
- ConfigDefinitionUpdates.cs
- X509CertificateCollection.cs
- DataSourceConverter.cs
- ImagingCache.cs
- DocumentReference.cs
- CompensateDesigner.cs
- ColumnMapCopier.cs
- X509RecipientCertificateServiceElement.cs
- DateTimeConstantAttribute.cs
- CompositeDesignerAccessibleObject.cs
- ReflectTypeDescriptionProvider.cs
- UnmanagedMarshal.cs
- CategoryGridEntry.cs
- UserMapPath.cs
- ContextStaticAttribute.cs
- MSG.cs
- ClockGroup.cs
- PrivateUnsafeNativeCompoundFileMethods.cs
- CheckedListBox.cs
- ParentQuery.cs
- HtmlGenericControl.cs
- ReservationCollection.cs
- UpDownEvent.cs
- ListChangedEventArgs.cs
- XmlWriterSettings.cs
- SafeViewOfFileHandle.cs
- DataGridViewLayoutData.cs
- Preprocessor.cs
- EncodingNLS.cs
- XmlDownloadManager.cs
- SmtpDigestAuthenticationModule.cs
- XComponentModel.cs
- MenuItem.cs
- AttributeAction.cs
- XmlEventCache.cs
- KeyNotFoundException.cs
- CompilationRelaxations.cs
- SQLInt64.cs
- WorkflowServiceHostFactory.cs
- BufferBuilder.cs
- AmbientProperties.cs
- MenuAutoFormat.cs
- ReferenceList.cs
- XmlSignificantWhitespace.cs
- PagePropertiesChangingEventArgs.cs
- EncryptedPackageFilter.cs
- Window.cs
- ServicePointManagerElement.cs
- ProjectionAnalyzer.cs
- KoreanLunisolarCalendar.cs
- DataGridTableCollection.cs
- SspiSecurityTokenParameters.cs
- ProxyWebPartManager.cs
- BinaryConverter.cs
- MenuItemAutomationPeer.cs
- SignedPkcs7.cs
- MessageFormatterConverter.cs
- DispatcherExceptionEventArgs.cs
- BamlRecordWriter.cs
- ReflectPropertyDescriptor.cs
- MultiTargetingUtil.cs
- DataGridComponentEditor.cs
- UserControlParser.cs
- DataGridViewColumnEventArgs.cs
- IsolationInterop.cs
- AvTraceDetails.cs
- EdmComplexPropertyAttribute.cs
- PerformanceCounterPermission.cs
- TabControl.cs
- OneOf.cs
- EpmCustomContentWriterNodeData.cs
- FontCollection.cs
- SafeArrayRankMismatchException.cs
- ResXResourceReader.cs
- CacheDependency.cs
- AnnotationResource.cs
- XsltArgumentList.cs
- FormViewCommandEventArgs.cs
- WhitespaceReader.cs
- XmlSchemaExporter.cs
- KeyFrames.cs
- LogExtentCollection.cs