#region License Information
/* HeuristicLab
* Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using HeuristicLab.Persistence.Core;
namespace HeuristicLab.Persistence.Default.CompositeSerializers.Storable {
internal static class StorableReflection {
private const BindingFlags DECLARED_INSTANCE_MEMBERS =
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.DeclaredOnly;
public delegate void Hook(object o);
public static IEnumerable GenerateStorableMembers(Type type) {
var storableMembers = new List();
if (type.BaseType != null)
storableMembers.AddRange(GenerateStorableMembers(type.BaseType));
var storableClassAttribute = GetStorableClassAttribute(type);
if (storableClassAttribute != null) {
switch (storableClassAttribute.Type) {
case StorableClassType.MarkedOnly:
AddMarkedMembers(type, storableMembers); break;
case StorableClassType.AllFields:
AddAll(type, MemberTypes.Field, storableMembers); break;
case StorableClassType.AllProperties:
AddAll(type, MemberTypes.Property, storableMembers); break;
case StorableClassType.AllFieldsAndAllProperties:
AddAll(type, MemberTypes.Field | MemberTypes.Property, storableMembers); break;
default:
throw new PersistenceException("unsupported [StorableClassType]: " + storableClassAttribute.Type);
}
}
return DisentangleNameMapping(storableMembers);
}
public static bool IsEmptyOrStorableType(Type type, bool recursive) {
if (!HasStorableClassAttribute(type) && !IsEmptyType(type, false)) return false;
return !recursive || type.BaseType == null || IsEmptyOrStorableType(type.BaseType, true);
}
private static object[] emptyArgs = new object[0];
public static IEnumerable CollectHooks(HookType hookType, Type type) {
if (type.BaseType != null)
foreach (var hook in CollectHooks(hookType, type.BaseType))
yield return hook;
if (HasStorableClassAttribute(type)) {
foreach (MethodInfo methodInfo in type.GetMethods(DECLARED_INSTANCE_MEMBERS)) {
if (methodInfo.ReturnType == typeof(void) && methodInfo.GetParameters().Length == 0) {
foreach (StorableHookAttribute hook in methodInfo.GetCustomAttributes(typeof(StorableHookAttribute), false)) {
if (hook != null && hook.HookType == hookType) {
yield return CreateHook(methodInfo);
}
}
}
}
}
}
private static Hook CreateHook(MethodInfo methodInfo) {
return new Hook((o) => methodInfo.Invoke(o, emptyArgs));
}
#region [Storable] helpers
private static void AddMarkedMembers(Type type, List storableMembers) {
foreach (MemberInfo memberInfo in type.GetMembers(DECLARED_INSTANCE_MEMBERS)) {
if (memberInfo.MemberType == MemberTypes.Field ||
memberInfo.MemberType == MemberTypes.Property) {
foreach (StorableAttribute attribute in memberInfo.GetCustomAttributes(typeof(StorableAttribute), false)) {
storableMembers.Add(new StorableMemberInfo(attribute, memberInfo));
}
}
}
}
private static void AddAll(Type type, MemberTypes memberTypes, List storableMembers) {
foreach (MemberInfo memberInfo in type.GetMembers(DECLARED_INSTANCE_MEMBERS)) {
if ((memberInfo.MemberType & memberTypes) == memberInfo.MemberType &&
!memberInfo.Name.StartsWith("<") &&
!memberInfo.Name.EndsWith("k__BackingField"))
storableMembers.Add(new StorableMemberInfo(memberInfo, false));
}
}
///
/// Ascertain distinct names for all fields and properties. This method takes care
/// of disentangling equal names from different class hiarachy levels.
///
/// Field names are replaced with their fully qualified name which includes
/// the class names where they were declared.
///
/// Property names are first reduced to unqiue accessors that are not overrides of
/// each other and the replaced with their fully qualified name if more than one
/// accessor remains.
///
///
///
private static IEnumerable DisentangleNameMapping(IEnumerable storableMemberInfos) {
var nameGrouping = new Dictionary>();
foreach (StorableMemberInfo storable in storableMemberInfos) {
if (!nameGrouping.ContainsKey(storable.MemberInfo.Name))
nameGrouping[storable.MemberInfo.Name] = new List();
nameGrouping[storable.MemberInfo.Name].Add(storable);
}
var memberInfos = new List();
foreach (var storableMemberInfoGroup in nameGrouping.Values) {
if (storableMemberInfoGroup.Count == 1) {
storableMemberInfoGroup[0].SetDisentangledName(storableMemberInfoGroup[0].MemberInfo.Name);
memberInfos.Add(storableMemberInfoGroup[0]);
} else if (storableMemberInfoGroup[0].MemberInfo.MemberType == MemberTypes.Field) {
foreach (var storableMemberInfo in storableMemberInfoGroup) {
storableMemberInfo.SetDisentangledName(storableMemberInfo.FullyQualifiedMemberName);
memberInfos.Add(storableMemberInfo);
}
} else {
memberInfos.AddRange(MergePropertyAccessors(storableMemberInfoGroup));
}
}
return memberInfos;
}
///
/// Merges property accessors that are overrides of each other but differentiates if a new
/// property that shadows older implementations has been introduced with new
.
///
/// A list of StorableMemberInfo
s for properties of the same type.
/// A fieltered IEnumerable
of propery infos.
private static IEnumerable MergePropertyAccessors(List members) {
var uniqueAccessors = new Dictionary();
foreach (var member in members)
uniqueAccessors[member.GetPropertyDeclaringBaseType()] = member;
if (uniqueAccessors.Count == 1) {
var storableMemberInfo = uniqueAccessors.Values.First();
storableMemberInfo.SetDisentangledName(storableMemberInfo.MemberInfo.Name);
yield return storableMemberInfo;
} else {
foreach (var attribute in uniqueAccessors.Values) {
attribute.SetDisentangledName(attribute.FullyQualifiedMemberName);
yield return attribute;
}
}
}
#endregion
#region [StorableClass] helpers
private static StorableClassAttribute GetStorableClassAttribute(Type type) {
lock (storableClassCache) {
if (storableClassCache.ContainsKey(type))
return storableClassCache[type];
StorableClassAttribute attribute = type
.GetCustomAttributes(typeof(StorableClassAttribute), false)
.SingleOrDefault() as StorableClassAttribute;
storableClassCache.Add(type, attribute);
return attribute;
}
}
public static bool HasStorableClassAttribute(Type type) {
return GetStorableClassAttribute(type) != null;
}
private static Dictionary storableClassCache =
new Dictionary();
#endregion
#region other helpers
private static bool IsEmptyType(Type type, bool recursive) {
foreach (MemberInfo memberInfo in type.GetMembers(DECLARED_INSTANCE_MEMBERS)) {
if (IsModifiableMember(memberInfo)) return false;
}
return !recursive || type.BaseType == null || IsEmptyType(type.BaseType, true);
}
private static bool IsModifiableMember(MemberInfo memberInfo) {
return memberInfo.MemberType == MemberTypes.Field && IsModifiableField((FieldInfo)memberInfo) ||
memberInfo.MemberType == MemberTypes.Property && IsModifiableProperty((PropertyInfo)memberInfo);
}
private static bool IsModifiableField(FieldInfo fi) {
return !fi.IsLiteral && !fi.IsInitOnly;
}
private static bool IsModifiableProperty(PropertyInfo pi) {
return pi.CanWrite;
}
#endregion
}
}