|
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace Microsoft.Build.Tasks.Xaml
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Xaml;
using System.Xaml.Schema;
using System.Reflection;
using System.Runtime;
using System.Globalization;
using System.Diagnostics.CodeAnalysis;
using System.IO;
internal class XamlValidatingReader : XamlWrappingReader
{
XamlStackWriter _stack = new XamlStackWriter();
Assembly assembly;
Type definedType;
string rootNamespace;
string localAssemblyName;
string realAssemblyName;
// We use this instead of XamlLanguage.Null, because XamlLanguage uses live types
// where we use ROL
XamlType xNull;
public event EventHandler<ValidationEventArgs> OnValidationError;
public XamlValidatingReader(XamlReader underlyingReader, Assembly assembly, string rootNamespace, string realAssemblyName)
: base(underlyingReader)
{
this.assembly = assembly;
this.definedType = null;
this.rootNamespace = rootNamespace;
this.localAssemblyName = assembly != null ? assembly.GetName().Name : null;
this.realAssemblyName = realAssemblyName;
this.xNull = underlyingReader.SchemaContext.GetXamlType(new XamlTypeName(XamlLanguage.Null));
}
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.DoNotCatchGeneralExceptionTypes,
Justification = "Need to catch and log the exception here so that all the errors, including the exception thrown, are surfaced.")]
public override bool Read()
{
if (!base.Read())
{
return false;
}
try
{
if (_stack.Depth == 0)
{
State_AtRoot();
}
else if (_stack.TopFrame.FrameType == XamlStackFrameType.Member)
{
if (_stack.TopFrame.IsSet() && !AllowsMultiple(_stack.TopFrame.Member))
{
State_ExpectEndMember();
}
else
{
State_InsideMember();
}
}
else
{
if (_stack.TopFrame.FrameType != XamlStackFrameType.Object && _stack.TopFrame.FrameType != XamlStackFrameType.GetObject)
{
ValidationError(SR.UnexpectedXaml);
}
State_InsideObject();
}
}
catch (FileLoadException e)
{
if (Fx.IsFatal(e))
{
throw;
}
ValidationError(SR.AssemblyCannotBeResolved(XamlBuildTaskServices.FileNotLoaded));
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
ValidationError(e.Message);
}
return true;
}
protected virtual void ValidationError(string message, params object[] args)
{
EventHandler<ValidationEventArgs> handler = OnValidationError;
if (handler != null)
{
string formattedMessage =
(args == null || args.Length == 0) ?
message : string.Format(CultureInfo.InvariantCulture, message, args);
handler(this, new ValidationEventArgs(formattedMessage, LineNumber, LinePosition));
}
}
private void State_AtRoot()
{
switch (NodeType)
{
case XamlNodeType.NamespaceDeclaration:
return;
case XamlNodeType.StartObject:
ValidateUnknown(Type);
break;
default:
ValidationError(SR.UnexpectedXaml);
break;
}
_stack.WriteNode(this);
}
private void State_InsideObject()
{
switch (NodeType)
{
case XamlNodeType.NamespaceDeclaration:
return;
case XamlNodeType.StartMember:
if (_stack.TopFrame.IsSet(Member))
{
ValidationError(SR.UnexpectedXaml);
}
if (_stack.TopFrame.FrameType == XamlStackFrameType.GetObject)
{
ValidateMemberOnGetObject(Member);
}
else
{
ValidateUnknown(Member);
ValidateMemberOnType(Member, _stack.TopFrame.Type);
}
break;
case XamlNodeType.EndObject:
break;
default:
ValidationError(SR.UnexpectedXamlDupMember);
break;
}
_stack.WriteNode(this);
}
private void State_InsideMember()
{
switch (NodeType)
{
case XamlNodeType.NamespaceDeclaration:
return;
case XamlNodeType.StartObject:
ValidateUnknown(Type);
ValidateTypeToMemberOnStack(Type);
break;
case XamlNodeType.GetObject:
ValidateGetObjectOnMember(_stack.TopFrame.Member);
break;
case XamlNodeType.Value:
ValidateValueToMemberOnStack(Value);
break;
case XamlNodeType.EndMember:
break;
default:
ValidationError(SR.UnexpectedXaml);
break;
}
_stack.WriteNode(this);
}
private void State_ExpectEndMember()
{
if (NodeType != XamlNodeType.EndMember)
{
ValidationError(SR.UnexpectedXaml);
}
_stack.WriteNode(this);
}
private void ValidateGetObjectOnMember(XamlMember member)
{
if (member == XamlLanguage.Items || member == XamlLanguage.PositionalParameters)
{
ValidationError(SR.UnexpectedXaml);
}
else if (!member.IsUnknown && member != XamlLanguage.UnknownContent &&
!member.Type.IsCollection && !member.Type.IsDictionary)
{
ValidationError(SR.UnexpectedXaml);
}
}
private void ValidateMemberOnGetObject(XamlMember member)
{
if (member != XamlLanguage.Items)
{
ValidationError(SR.UnexpectedXaml);
}
}
private void ValidateMemberOnType(XamlMember member, XamlType type)
{
if (member.IsUnknown || type.IsUnknown)
{
return;
}
if (member.IsDirective)
{
if (member == XamlLanguage.Items)
{
if (!type.IsCollection && !type.IsDictionary)
{
ValidationError(SR.UnexpectedXamlDictionary(member.Name, GetXamlTypeName(_stack.TopFrame.Type)));
}
}
if (member == XamlLanguage.Class && _stack.Depth > 1)
{
ValidationError(SR.UnexpectedXamlClass);
}
}
else if (member.IsAttachable)
{
if (!type.CanAssignTo(member.TargetType))
{
ValidationError(SR.UnexpectedXamlAttachableMember(member.Name, GetXamlTypeName(member.TargetType)));
}
}
else if (!member.IsDirective && !type.CanAssignTo(member.DeclaringType))
{
ValidationError(SR.UnexpectedXamlMemberNotAssignable(member.Name, GetXamlTypeName(type)));
}
}
private void ValidateTypeToMemberOnStack(XamlType type)
{
if (type.IsUnknown)
{
return;
}
if (type == this.xNull)
{
ValidateValueToMemberOnStack(null);
}
XamlMember member = _stack.TopFrame.Member;
if (member == XamlLanguage.PositionalParameters || type.IsMarkupExtension || member.IsUnknown)
{
return;
}
if (member == XamlLanguage.Items)
{
XamlType collectionType = GetCollectionTypeOnStack();
if (collectionType == null || collectionType.IsUnknown || collectionType.AllowedContentTypes == null)
{
return;
}
if (!collectionType.AllowedContentTypes.Any(contentType => type.CanAssignTo(contentType)))
{
ValidationError(SR.UnassignableCollection(GetXamlTypeName(type), GetXamlTypeName(collectionType.ItemType), GetXamlTypeName(collectionType)));
}
}
else if (member.IsDirective && (member.Type.IsCollection || member.Type.IsDictionary))
{
XamlType collectionType = member.Type;
if (collectionType == null || collectionType.IsUnknown || collectionType.AllowedContentTypes == null)
{
return;
}
if (!collectionType.AllowedContentTypes.Any(contentType => type.CanAssignTo(contentType)))
{
ValidationError(SR.UnassignableCollection(GetXamlTypeName(type), GetXamlTypeName(collectionType.ItemType), GetXamlTypeName(collectionType)));
}
}
else if (!type.CanAssignTo(member.Type))
{
if (member.DeferringLoader != null)
{
return;
}
if (NodeType == XamlNodeType.Value)
{
ValidationError(SR.UnassignableTypes(GetXamlTypeName(type), GetXamlTypeName(member.Type), member.Name));
}
else
{
ValidationError(SR.UnassignableTypesObject(GetXamlTypeName(type), GetXamlTypeName(member.Type), member.Name));
}
}
}
private void ValidateValueToMemberOnStack(object value)
{
XamlMember member = _stack.TopFrame.Member;
if (member.IsUnknown)
{
return;
}
if (value != null)
{
if (member.IsEvent)
{
if (this.definedType != null && this.definedType.GetMethod(value as string,
BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) == null)
{
ValidationError(SR.UnexpectedXamlEventHandlerNotFound(value, definedType.FullName));
}
return;
}
else if (member == XamlLanguage.Class)
{
string className = value as string;
Fx.Assert(!string.IsNullOrEmpty(className), "ClassName cannot be null");
if (!string.IsNullOrEmpty(this.rootNamespace))
{
className = this.rootNamespace + "." + className;
}
if (this.assembly != null)
{
this.definedType = this.assembly.GetType(className);
}
return;
}
else if (member.TypeConverter != null)
{
return;
}
XamlType typeOfValue = SchemaContext.GetXamlType(value.GetType());
ValidateTypeToMemberOnStack(typeOfValue);
}
else
{
if (member == XamlLanguage.PositionalParameters)
{
return;
}
if (member == XamlLanguage.Items)
{
XamlType collectionType = GetCollectionTypeOnStack();
if (collectionType == null || collectionType.IsUnknown || collectionType.AllowedContentTypes == null)
{
return;
}
if (!collectionType.AllowedContentTypes.Any(contentType => contentType.IsNullable))
{
ValidationError(SR.UnassignableCollection("(null)", GetXamlTypeName(collectionType.ItemType), GetXamlTypeName(collectionType)));
}
}
else
{
if (!member.Type.IsNullable)
{
ValidationError(SR.UnassignableTypes("(null)", GetXamlTypeName(member.Type), member.Name));
}
}
}
}
private bool AllowsMultiple(XamlMember member)
{
return
member == XamlLanguage.Items ||
member == XamlLanguage.PositionalParameters ||
member == XamlLanguage.UnknownContent;
}
private XamlType GetCollectionTypeOnStack()
{
Fx.Assert(_stack.TopFrame.Member == XamlLanguage.Items, "CollectionType should have _Items member");
XamlType result;
if (_stack.FrameAtDepth(_stack.Depth - 1).FrameType == XamlStackFrameType.GetObject)
{
XamlMember member = _stack.FrameAtDepth(_stack.Depth - 2).Member;
if (member.IsUnknown)
{
return null;
}
result = member.Type;
}
else
{
result = _stack.FrameAtDepth(_stack.Depth - 1).Type;
}
Fx.Assert(result.IsUnknown || result.IsCollection || result.IsDictionary,
"Incorrect Collection Type Encountered");
return result;
}
private void ValidateUnknown(XamlMember member)
{
if (member == XamlLanguage.UnknownContent)
{
ValidationError(SR.MemberUnknownContect(GetXamlTypeName(_stack.TopFrame.Type)));
}
else if (member.IsUnknown)
{
bool retryAttachable = false;
XamlType declaringType = member.DeclaringType;
if (_stack.Depth == 1 && declaringType.IsUnknown &&
!string.IsNullOrEmpty(this.rootNamespace) &&
this.definedType != null && declaringType.Name == this.definedType.Name)
{
// Need to handle the case where the namespace of a member on the document root
// is missing the project root namespace
string clrNs;
if (XamlBuildTaskServices.TryExtractClrNs(declaringType.PreferredXamlNamespace, out clrNs))
{
clrNs = string.IsNullOrEmpty(clrNs) ? this.rootNamespace : this.rootNamespace + "." + clrNs;
if (clrNs == this.definedType.Namespace)
{
declaringType = SchemaContext.GetXamlType(this.definedType);
retryAttachable = true;
}
}
}
XamlMember typeMember = declaringType.GetMember(member.Name);
if (typeMember == null && retryAttachable)
{
typeMember = declaringType.GetAttachableMember(member.Name);
}
if (typeMember == null || typeMember.IsUnknown)
{
if (member.IsAttachable)
{
ValidationError(SR.UnresolvedAttachableMember(GetXamlTypeName(member.DeclaringType) + "." + member.Name));
}
else if (member.IsDirective)
{
ValidationError(SR.UnresolvedDirective(member.PreferredXamlNamespace + ":" + member.Name));
}
else
{
// Skip if declaring type is unknown as the member unknown error messages become redundant.
if (declaringType != null && !declaringType.IsUnknown)
{
ValidationError(SR.UnresolvedMember(member.Name, GetXamlTypeName(declaringType)));
}
}
}
}
}
private void ValidateUnknown(XamlType type)
{
if (type.IsUnknown)
{
if (type.IsGeneric)
{
ThrowGenericTypeValidationError(type);
}
else
{
ThrowTypeValidationError(type);
}
}
}
private void ThrowGenericTypeValidationError(XamlType type)
{
IList<XamlType> unresolvedLeafTypeList = new List<XamlType>();
XamlBuildTaskServices.GetUnresolvedLeafTypeArg(type, ref unresolvedLeafTypeList);
if (unresolvedLeafTypeList.Count > 1 || !unresolvedLeafTypeList.Contains(type))
{
string fullTypeName = GetXamlTypeName(type);
ValidationError(SR.UnresolvedGenericType(fullTypeName));
foreach (XamlType xamlType in unresolvedLeafTypeList)
{
ThrowTypeValidationError(xamlType);
}
}
else
{
ThrowTypeValidationError(type);
}
}
private void ThrowTypeValidationError(XamlType type)
{
string typeName, assemblyName, ns;
if (XamlBuildTaskServices.GetTypeNameInAssemblyOrNamespace(type, this.localAssemblyName, this.realAssemblyName, out typeName, out assemblyName, out ns))
{
ValidationError(SR.UnresolvedTypeWithAssemblyName(ns + "." + typeName, assemblyName));
}
else
{
ValidationError(SR.UnresolvedTypeWithNamespace(typeName, ns));
}
}
private string GetXamlTypeName(XamlType type)
{
return XamlBuildTaskServices.GetTypeName(type, this.localAssemblyName, this.realAssemblyName);
}
}
}
|