private TypeDesc ImportTypeDesc(Type type, MemberInfo memberInfo, bool directReference)
{
TypeDesc typeDesc = null;
TypeKind kind;
Type arrayElementType = null;
Type baseType = null;
TypeFlags flags = 0;
Exception exception = null;
if (!type.GetTypeInfo().IsVisible)
{
flags |= TypeFlags.Unsupported;
exception = new InvalidOperationException(SR.Format(SR.XmlTypeInaccessible, type.FullName));
}
else if (directReference && (type.GetTypeInfo().IsAbstract && type.GetTypeInfo().IsSealed))
{
flags |= TypeFlags.Unsupported;
exception = new InvalidOperationException(SR.Format(SR.XmlTypeStatic, type.FullName));
}
if (!type.GetTypeInfo().IsValueType)
flags |= TypeFlags.Reference;
if (type == typeof(object))
{
kind = TypeKind.Root;
flags |= TypeFlags.HasDefaultConstructor;
}
else if (type == typeof(ValueType))
{
kind = TypeKind.Enum;
flags |= TypeFlags.Unsupported;
if (exception == null)
{
exception = new NotSupportedException(SR.Format(SR.XmlSerializerUnsupportedType, type.FullName));
}
}
else if (type == typeof(void))
{
kind = TypeKind.Void;
}
else if (typeof(IXmlSerializable).IsAssignableFrom(type))
{
kind = TypeKind.Serializable;
flags |= TypeFlags.Special | TypeFlags.CanBeElementValue;
flags |= GetConstructorFlags(type, ref exception);
}
else if (type.IsArray)
{
kind = TypeKind.Array;
if (type.GetArrayRank() > 1)
{
flags |= TypeFlags.Unsupported;
if (exception == null)
{
exception = new NotSupportedException(SR.Format(SR.XmlUnsupportedRank, type.FullName));
}
}
arrayElementType = type.GetElementType();
flags |= TypeFlags.HasDefaultConstructor;
}
else if (typeof(ICollection).IsAssignableFrom(type))
{
kind = TypeKind.Collection;
arrayElementType = GetCollectionElementType(type, memberInfo == null ? null : memberInfo.DeclaringType.FullName + "." + memberInfo.Name);
flags |= GetConstructorFlags(type, ref exception);
}
else if (type == typeof(XmlQualifiedName))
{
kind = TypeKind.Primitive;
}
else if (type.GetTypeInfo().IsPrimitive)
{
kind = TypeKind.Primitive;
flags |= TypeFlags.Unsupported;
if (exception == null)
{
exception = new NotSupportedException(SR.Format(SR.XmlSerializerUnsupportedType, type.FullName));
}
}
else if (type.GetTypeInfo().IsEnum)
{
kind = TypeKind.Enum;
}
else if (type.GetTypeInfo().IsValueType)
{
kind = TypeKind.Struct;
if (IsOptionalValue(type))
{
baseType = type.GetGenericArguments()[0];
flags |= TypeFlags.OptionalValue;
}
else
{
baseType = type.GetTypeInfo().BaseType;
}
if (type.GetTypeInfo().IsAbstract) flags |= TypeFlags.Abstract;
}
else if (type.GetTypeInfo().IsClass)
{
if (type == typeof(XmlAttribute))
{
kind = TypeKind.Attribute;
flags |= TypeFlags.Special | TypeFlags.CanBeAttributeValue;
}
else if (typeof(XmlNode).IsAssignableFrom(type))
{
kind = TypeKind.Node;
baseType = type.GetTypeInfo().BaseType;
flags |= TypeFlags.Special | TypeFlags.CanBeElementValue | TypeFlags.CanBeTextValue;
if (typeof(XmlText).IsAssignableFrom(type))
flags &= ~TypeFlags.CanBeElementValue;
else if (typeof(XmlElement).IsAssignableFrom(type))
flags &= ~TypeFlags.CanBeTextValue;
else if (type.IsAssignableFrom(typeof(XmlAttribute)))
flags |= TypeFlags.CanBeAttributeValue;
}
else
{
kind = TypeKind.Class;
baseType = type.GetTypeInfo().BaseType;
if (type.GetTypeInfo().IsAbstract)
flags |= TypeFlags.Abstract;
}
}
else if (type.GetTypeInfo().IsInterface)
{
kind = TypeKind.Void;
flags |= TypeFlags.Unsupported;
if (exception == null)
{
if (memberInfo == null)
{
exception = new NotSupportedException(SR.Format(SR.XmlUnsupportedInterface, type.FullName));
}
else
{
exception = new NotSupportedException(SR.Format(SR.XmlUnsupportedInterfaceDetails, memberInfo.DeclaringType.FullName + "." + memberInfo.Name, type.FullName));
}
}
}
else
{
kind = TypeKind.Void;
flags |= TypeFlags.Unsupported;
if (exception == null)
{
exception = new NotSupportedException(SR.Format(SR.XmlSerializerUnsupportedType, type.FullName));
}
}
// check to see if the type has public default constructor for classes
if (kind == TypeKind.Class && !type.GetTypeInfo().IsAbstract)
{
flags |= GetConstructorFlags(type, ref exception);
}
// check if a struct-like type is enumerable
if (kind == TypeKind.Struct || kind == TypeKind.Class)
{
if (typeof(IEnumerable).IsAssignableFrom(type))
{
arrayElementType = GetEnumeratorElementType(type, ref flags);
kind = TypeKind.Enumerable;
// GetEnumeratorElementType checks for the security attributes on the GetEnumerator(), Add() methods and Current property,
// we need to check the MoveNext() and ctor methods for the security attribues
flags |= GetConstructorFlags(type, ref exception);
}
}
typeDesc = new TypeDesc(type, CodeIdentifier.MakeValid(TypeName(type)), type.ToString(), kind, null, flags, null);
typeDesc.Exception = exception;
if (directReference && (typeDesc.IsClass || kind == TypeKind.Serializable))
typeDesc.CheckNeedConstructor();
if (typeDesc.IsUnsupported)
{
// return right away, do not check anything else
return typeDesc;
}
_typeDescs.Add(type, typeDesc);
if (arrayElementType != null)
{
TypeDesc td = GetTypeDesc(arrayElementType, memberInfo, true, false);
// explicitly disallow read-only elements, even if they are collections
if (directReference && (td.IsCollection || td.IsEnumerable) && !td.IsPrimitive)
{
td.CheckNeedConstructor();
}
typeDesc.ArrayElementTypeDesc = td;
}
if (baseType != null && baseType != typeof(object) && baseType != typeof(ValueType))
{
typeDesc.BaseTypeDesc = GetTypeDesc(baseType, memberInfo, false, false);
}
return typeDesc;
}