Show More
Commit Description:
Various UI improvements.
Commit Description:
Various UI improvements.
File last commit:
Show/Diff file:
Action:
FNA/src/Content/ContentReaders/ReflectiveReader.cs
300 lines | 6.5 KiB | text/x-csharp | CSharpLexer
#region License
/* FNA - XNA4 Reimplementation for Desktop Platforms
* Copyright 2009-2020 Ethan Lee and the MonoGame Team
*
* Released under the Microsoft Public License.
* See LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using System.Reflection;
#endregion
namespace Microsoft.Xna.Framework.Content
{
internal class ReflectiveReader<T> : ContentTypeReader
{
#region Reader Delegates
delegate void ReadElement(ContentReader input, object parent);
private List<ReadElement> readers;
#endregion
#region Public Properties
public override bool CanDeserializeIntoExistingObject
{
get
{
return TargetType.IsClass;
}
}
#endregion
#region Private Variables
private ConstructorInfo constructor;
private ContentTypeReader baseTypeReader;
#endregion
#region Internal Constructor
internal ReflectiveReader() : base(typeof(T))
{
}
#endregion
#region Protected ContentTypeReader Methods
protected internal override void Initialize(ContentTypeReaderManager manager)
{
base.Initialize(manager);
Type baseType = TargetType.BaseType;
if (baseType != null && baseType != typeof(object))
{
baseTypeReader = manager.GetTypeReader(baseType);
}
constructor = TargetType.GetDefaultConstructor();
const BindingFlags attrs = (
BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.DeclaredOnly
);
/* Sometimes, overridden properties of abstract classes can show up even with
* BindingFlags.DeclaredOnly is passed to GetProperties. Make sure that
* all properties in this list are defined in this class by comparing
* its get method with that of its base class. If they're the same
* Then it's an overridden property.
*/
PropertyInfo[] properties = TargetType.GetProperties(attrs);
FieldInfo[] fields = TargetType.GetFields(attrs);
readers = new List<ReadElement>(fields.Length + properties.Length);
// Gather the properties.
foreach (PropertyInfo property in properties)
{
MethodInfo pm = property.GetGetMethod(true);
if (pm == null || pm != pm.GetBaseDefinition())
{
continue;
}
ReadElement read = GetElementReader(manager, property);
if (read != null)
{
readers.Add(read);
}
}
// Gather the fields.
foreach (FieldInfo field in fields)
{
ReadElement read = GetElementReader(manager, field);
if (read != null)
{
readers.Add(read);
}
}
}
protected internal override object Read(ContentReader input, object existingInstance)
{
T obj;
if (existingInstance != null)
{
obj = (T) existingInstance;
}
else
{
if (constructor == null)
{
obj = (T) Activator.CreateInstance(typeof(T));
}
else
{
obj = (T) constructor.Invoke(null);
}
}
if (baseTypeReader != null)
{
baseTypeReader.Read(input, obj);
}
// Box the type.
object boxed = (object) obj;
foreach (ReadElement reader in readers)
{
reader(input, boxed);
}
// Unbox it... required for value types.
obj = (T) boxed;
return obj;
}
#endregion
#region Private Static Methods
private static ReadElement GetElementReader(
ContentTypeReaderManager manager,
MemberInfo member
) {
PropertyInfo property = member as PropertyInfo;
FieldInfo field = member as FieldInfo;
if (property != null)
{
// Properties must have at least a getter.
if (property.CanRead == false)
{
return null;
}
// Skip over indexer properties
if (property.GetIndexParameters().Length > 0)
{
return null;
}
}
// Are we explicitly asked to ignore this item?
Attribute attr = Attribute.GetCustomAttribute(
member,
typeof(ContentSerializerIgnoreAttribute)
);
if (attr != null)
{
return null;
}
ContentSerializerAttribute contentSerializerAttribute = Attribute.GetCustomAttribute(
member,
typeof(ContentSerializerAttribute)
) as ContentSerializerAttribute;
if (contentSerializerAttribute == null)
{
if (property != null)
{
/* There is no ContentSerializerAttribute, so non-public
* properties cannot be deserialized.
*/
MethodInfo getMethod = property.GetGetMethod(true);
if (getMethod != null && !getMethod.IsPublic)
{
return null;
}
MethodInfo setMethod = property.GetSetMethod(true);
if (setMethod != null && !setMethod.IsPublic)
{
return null;
}
/* If the read-only property has a type reader,
* and CanDeserializeIntoExistingObject is true,
* then it is safe to deserialize into the existing object.
*/
if (!property.CanWrite)
{
ContentTypeReader typeReader = manager.GetTypeReader(property.PropertyType);
if (typeReader == null || !typeReader.CanDeserializeIntoExistingObject)
{
return null;
}
}
}
else
{
/* There is no ContentSerializerAttribute, so non-public
* fields cannot be deserialized.
*/
if (!field.IsPublic)
{
return null;
}
// evolutional: Added check to skip initialise only fields
if (field.IsInitOnly)
{
return null;
}
}
}
Action<object, object> setter;
Type elementType;
if (property != null)
{
elementType = property.PropertyType;
if (property.CanWrite)
{
setter = (o, v) => property.SetValue(o, v, null);
}
else
{
setter = (o, v) => { };
}
}
else
{
elementType = field.FieldType;
setter = field.SetValue;
}
if ( contentSerializerAttribute != null &&
contentSerializerAttribute.SharedResource )
{
return (input, parent) =>
{
Action<object> action = value => setter(parent, value);
input.ReadSharedResource(action);
};
}
// We need to have a reader at this point.
ContentTypeReader reader = manager.GetTypeReader(elementType);
if (reader == null)
{
throw new ContentLoadException(string.Format(
"Content reader could not be found for {0} type.",
elementType.FullName
));
}
/* We use the construct delegate to pick the correct existing
* object to be the target of deserialization.
*/
Func<object, object> construct = parent => null;
if (property != null && !property.CanWrite)
{
construct = parent => property.GetValue(parent, null);
}
return (input, parent) =>
{
object existing = construct(parent);
object obj2 = input.ReadObject(reader, existing);
setter(parent, obj2);
};
}
#endregion
}
}