JD2022-TU1/main/tools/framework/JD.Serialization/Serializers/SerializationExtensions.cs

123 lines
No EOL
5 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using JD.Core;
namespace JD.Serialization
{
/// <summary>
/// Helper methods for the serializers.
/// </summary>
public static class SerializationExtensions
{
/// <summary>
/// Contains a cached default instantiation of each Type. It allows to test for default values.
/// </summary>
private static readonly Cache<Type, object> _defaultInstances = new Cache<Type, object>();
/// <summary>
/// Caches the serializable fields of given type.
/// </summary>
private static readonly Cache<object, IEnumerable<PropertyInfo>> _serializableFields = new Cache<object, IEnumerable<PropertyInfo>>();
/// <summary>
/// Caches the attributes of a property.
/// </summary>
private static readonly Cache<object, System.Attribute> _propertyAttributes = new Cache<object, System.Attribute>();
public static TAttribtue GetAttribute<TAttribtue>(this PropertyInfo property)
where TAttribtue : System.Attribute
{
return (TAttribtue) _propertyAttributes.GetOrCompute(new {property, attributeType = typeof (TAttribtue)}, toCompute =>
{
return property.GetCustomAttributes(typeof (TAttribtue), false).OfType<TAttribtue>().FirstOrDefault();
});
}
public static bool IsOptional(this PropertyInfo property)
{
return property.GetAttribute<UAFOptionnalAttribute>() != null;
}
public static bool IsFactoryType(this PropertyInfo property, Type type)
{
return ObjectFactory.isTypeInAnyFactory(type) || (property != null && property.GetAttribute<UAFFactoryAttribute>() != null);
}
public static IEnumerable<PropertyInfo> SerializableFields(this Type type, SerializationFlags flags = SerializationFlags.Default, bool includeSpecialFields = false)
{
// Manipulation reflection can reduce perf and we are sure this info never changes, this caches the result.
return _serializableFields.GetOrCompute(new { type, flags, includeSpecialFields }, arguments => ComputeSerializableFields(type, flags, includeSpecialFields));
}
private static IEnumerable<PropertyInfo> ComputeSerializableFields(Type type, SerializationFlags flags, bool includeSpecialFields = false)
{
// We reverse the member to try to reproduce the engine order (base classes first) so that the files does not change a lot.
// when they are saved by the tool or by the engine.
var result = new List<PropertyInfo>();
while (type != null)
{
PropertyInfo[] properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
result.InsertRange(0, properties.Where(field => field.IsSerializable(flags, includeSpecialFields)));
type = type.BaseType;
}
return result;
}
public static bool IsSerializable(this PropertyInfo property, SerializationFlags flags = SerializationFlags.Default, bool includeSpecialFields = false)
{
var attribute = property.GetAttribute<UAFSerializableAttribute>();
bool isSerializable = attribute != null && (attribute.Flags & flags) != 0;
if (!isSerializable && includeSpecialFields)
{
return property.GetAttribute<UafSerializableSpecialAttribute>() != null;
}
return isSerializable;
}
#region Default Values
/// <summary>
/// True if the property value is the default value of the serializable field.
/// </summary>
public static bool HasDefaultValue(this object instance, PropertyInfo property)
{
object propertyValue = property.GetValue(instance, null);
object defaultValue = property.GetValue(DefaultInstance(instance.GetType()), null);
if (propertyValue is IUAFCollection)
{
return ((IUAFCollection)propertyValue).Count == 0;
}
if (defaultValue != null && propertyValue != null &&
defaultValue.GetType() == propertyValue.GetType() &&
!BasicTypeList.IsBasicType(propertyValue.GetType()))
{
return IsDefaultClass(propertyValue);
}
return Equals(propertyValue, defaultValue);
}
private static object DefaultInstance(this Type type)
{
return _defaultInstances.GetOrCompute(type, Activator.CreateInstance);
}
/// <summary>
/// True if the class is not modified.
/// </summary>
/// <remarks>This allows to skip non modified fields when writing a lua file.</remarks>
private static bool IsDefaultClass(this object instance)
{
return instance.GetType().SerializableFields().All(instance.HasDefaultValue);
}
#endregion
}
}