Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
03/15/10 00:00:12 (14 years ago)
Author:
epitzer
Message:

Move attribute discovery from attribute classes to StorableSerializer (in preparation for unified discovery and handling of AllFields, AllProperties, ...) (#548)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableSerializer.cs

    r3017 r3025  
    66using System.Reflection;
    77using HeuristicLab.Persistence.Auxiliary;
     8using System.Text;
    89
    910namespace HeuristicLab.Persistence.Default.CompositeSerializers.Storable {
     
    1819  public class StorableSerializer : ICompositeSerializer {
    1920
     21
     22    #region ICompositeSerializer implementation
     23
    2024    public int Priority {
    2125      get { return 200; }
     
    2428    public bool CanSerialize(Type type) {
    2529      if (!ReflectionTools.HasDefaultConstructor(type) &&
    26         StorableConstructorAttribute.GetStorableConstructor(type) == null)
     30        GetStorableConstructor(type) == null)
    2731        return false;
    28       return StorableClassAttribute.IsStorableType(type, true);
     32      return IsEmptyOrStorableType(type, true);
    2933    }
    3034
    3135    public string JustifyRejection(Type type) {
    3236      if (!ReflectionTools.HasDefaultConstructor(type) &&
    33         StorableConstructorAttribute.GetStorableConstructor(type) == null)
     37        GetStorableConstructor(type) == null)
    3438        return "no default constructor and no storable constructor";
    35       return "class or one of its base classes is not empty and has no [StorableClass] attribute";
     39      if (!IsEmptyOrStorableType(type, true))
     40        return "class is not marked with the storable class attribute";
     41      return "no reason";
    3642    }
    3743
     
    4248
    4349    public IEnumerable<Tag> Decompose(object obj) {
    44       foreach (var accessor in StorableAttribute.GetStorableAccessors(obj)) {
     50      foreach (var accessor in GetStorableAccessors(obj)) {
    4551        yield return new Tag(accessor.Name, accessor.Get());
    4652      }
     
    5157    public object CreateInstance(Type type, IEnumerable<Tag> metaInfo) {
    5258      try {
    53         ConstructorInfo constructor = StorableConstructorAttribute.GetStorableConstructor(type);
     59        ConstructorInfo constructor = GetStorableConstructor(type);
    5460        return constructor != null ? constructor.Invoke(defaultArgs) : Activator.CreateInstance(type, true);
    5561      } catch (TargetInvocationException x) {
     
    6672        memberDict.Add(iter.Current.Name, iter.Current);
    6773      }     
    68       foreach (var accessor in StorableAttribute.GetStorableAccessors(instance)) {
     74      foreach (var accessor in GetStorableAccessors(instance)) {
    6975        if (memberDict.ContainsKey(accessor.Name)) {
    7076          accessor.Set(memberDict[accessor.Name].Value);
     
    7581      StorableHookAttribute.InvokeHook(HookType.AfterDeserialization, instance);
    7682    }
     83
     84    #endregion
     85
     86    #region constances & private data types
     87
     88    private const BindingFlags ALL_CONSTRUCTORS =
     89      BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
     90
     91    private const BindingFlags DECLARED_INSTANCE_MEMBERS =
     92      BindingFlags.Instance |
     93      BindingFlags.Public |
     94      BindingFlags.NonPublic |
     95      BindingFlags.DeclaredOnly;
     96
     97    private sealed class StorableMemberInfo {
     98      public StorableAttribute Attribute { get; private set; }
     99      public MemberInfo MemberInfo { get; private set; }
     100      public string DisentangledName { get; private set; }
     101      public string FullyQualifiedMemberName {
     102        get {
     103          return new StringBuilder()
     104            .Append(MemberInfo.ReflectedType.FullName)
     105            .Append('.')
     106            .Append(MemberInfo.Name)
     107            .ToString();
     108        }
     109      }
     110      public StorableMemberInfo(StorableAttribute attribute, MemberInfo memberInfo) {
     111        this.Attribute = attribute;
     112        this.MemberInfo = memberInfo;
     113      }
     114      public override string ToString() {
     115        return new StringBuilder()
     116          .Append('[').Append(Attribute).Append(", ")
     117          .Append(MemberInfo).Append('}').ToString();
     118      }
     119      public void SetDisentangledName(string name) {
     120        DisentangledName = Attribute.Name ?? name;
     121      }
     122      public Type GetPropertyDeclaringBaseType() {
     123        return ((PropertyInfo)MemberInfo).GetGetMethod(true).GetBaseDefinition().DeclaringType;
     124      }
     125    }
     126
     127    private sealed class TypeQuery {
     128      public Type Type { get; private set; }
     129      public bool Inherited { get; private set; }
     130      public TypeQuery(Type type, bool inherited) {
     131        this.Type = type;
     132        this.Inherited = inherited;
     133      }
     134    }
     135
     136    private sealed class MemberCache : Dictionary<TypeQuery, IEnumerable<StorableMemberInfo>> { }
     137
     138    #endregion
     139
     140    #region caches
     141
     142    private MemberCache storableMemberCache = new MemberCache();
     143    private Dictionary<Type, ConstructorInfo> constructorCache =
     144      new Dictionary<Type, ConstructorInfo>();
     145
     146    #endregion
     147
     148    #region auxiliary attribute reflection tools
     149
     150    private IEnumerable<StorableMemberInfo> GetStorableMembers(Type type) {
     151      return GetStorableMembers(type, true);
     152    }
     153
     154    private IEnumerable<StorableMemberInfo> GetStorableMembers(Type type, bool inherited) {
     155      lock (storableMemberCache) {
     156        var query = new TypeQuery(type, inherited);
     157        if (storableMemberCache.ContainsKey(query))
     158          return storableMemberCache[query];
     159        var storablesMembers = GenerateStorableMembers(type, inherited);
     160        storableMemberCache[query] = storablesMembers;
     161        return storablesMembers;
     162      }
     163    }
     164
     165    private static IEnumerable<StorableMemberInfo> GenerateStorableMembers(Type type, bool inherited) {
     166      var storableMembers = new List<StorableMemberInfo>();
     167      if (inherited && type.BaseType != null)
     168        storableMembers.AddRange(GenerateStorableMembers(type.BaseType, true));
     169      foreach (MemberInfo memberInfo in type.GetMembers(DECLARED_INSTANCE_MEMBERS)) {
     170        foreach (StorableAttribute attribute in memberInfo.GetCustomAttributes(typeof(StorableAttribute), false)) {
     171          storableMembers.Add(new StorableMemberInfo(attribute, memberInfo));
     172        }
     173      }
     174      return DisentangleNameMapping(storableMembers);
     175    }
     176
     177    private IEnumerable<DataMemberAccessor> GetStorableAccessors(object obj) {
     178      foreach (var memberInfo in GetStorableMembers(obj.GetType()))
     179        yield return new DataMemberAccessor(
     180          memberInfo.MemberInfo,
     181          memberInfo.DisentangledName,
     182          memberInfo.Attribute.DefaultValue,
     183          obj);
     184    }
     185
     186    private static IEnumerable<StorableMemberInfo> DisentangleNameMapping(
     187        IEnumerable<StorableMemberInfo> storableMemberInfos) {
     188      var nameGrouping = new Dictionary<string, List<StorableMemberInfo>>();
     189      foreach (StorableMemberInfo storable in storableMemberInfos) {
     190        if (!nameGrouping.ContainsKey(storable.MemberInfo.Name))
     191          nameGrouping[storable.MemberInfo.Name] = new List<StorableMemberInfo>();
     192        nameGrouping[storable.MemberInfo.Name].Add(storable);
     193      }
     194      var memberInfos = new List<StorableMemberInfo>();
     195      foreach (var storableMemberInfoGroup in nameGrouping.Values) {
     196        if (storableMemberInfoGroup.Count == 1) {
     197          storableMemberInfoGroup[0].SetDisentangledName(storableMemberInfoGroup[0].MemberInfo.Name);
     198          memberInfos.Add(storableMemberInfoGroup[0]);
     199        } else if (storableMemberInfoGroup[0].MemberInfo.MemberType == MemberTypes.Field) {
     200          foreach (var storableMemberInfo in storableMemberInfoGroup) {
     201            storableMemberInfo.SetDisentangledName(storableMemberInfo.FullyQualifiedMemberName);
     202            memberInfos.Add(storableMemberInfo);
     203          }
     204        } else {
     205          memberInfos.AddRange(MergePropertyAccessors(storableMemberInfoGroup));
     206        }
     207      }
     208      return memberInfos;
     209    }
     210
     211    private static IEnumerable<StorableMemberInfo> MergePropertyAccessors(List<StorableMemberInfo> members) {
     212      var uniqueAccessors = new Dictionary<Type, StorableMemberInfo>();
     213      foreach (var member in members)
     214        uniqueAccessors[member.GetPropertyDeclaringBaseType()] = member;
     215      if (uniqueAccessors.Count == 1) {
     216        var storableMemberInfo = uniqueAccessors.Values.First();
     217        storableMemberInfo.SetDisentangledName(storableMemberInfo.MemberInfo.Name);
     218        yield return storableMemberInfo;
     219      } else {
     220        foreach (var attribute in uniqueAccessors.Values) {
     221          attribute.SetDisentangledName(attribute.FullyQualifiedMemberName);
     222          yield return attribute;
     223        }
     224      }
     225    }
     226
     227    private static bool IsEmptyOrStorableType(Type type, bool recusrive) {
     228      if (IsEmptyType(type, recusrive)) return true;
     229      if (!HastStorableClassAttribute(type)) return false;
     230      return !recusrive || type.BaseType == null || IsEmptyOrStorableType(type.BaseType, true);
     231    }
     232
     233    private static bool HastStorableClassAttribute(Type type) {
     234      return type.GetCustomAttributes(typeof(StorableClassAttribute), false).Length > 0;
     235    }
     236
     237    private static bool IsEmptyType(Type type, bool recursive) {
     238      foreach (MemberInfo memberInfo in type.GetMembers(DECLARED_INSTANCE_MEMBERS)) {
     239        if (IsModifiableMember(memberInfo)) return false;
     240      }
     241      return !recursive || type.BaseType == null || IsEmptyType(type.BaseType, true);
     242    }
     243
     244    private static bool IsModifiableMember(MemberInfo memberInfo) {
     245      return memberInfo.MemberType == MemberTypes.Field && IsModifiableField((FieldInfo)memberInfo) ||
     246                memberInfo.MemberType == MemberTypes.Property && IsModifiableProperty((PropertyInfo)memberInfo);
     247    }
     248
     249    private static bool IsModifiableField(FieldInfo fi) {
     250      return !fi.IsLiteral && !fi.IsInitOnly;
     251    }
     252
     253    private static bool IsModifiableProperty(PropertyInfo pi) {
     254      return pi.CanWrite;
     255    }
     256
     257    private ConstructorInfo GetStorableConstructor(Type type) {
     258      lock (constructorCache) {
     259        if (constructorCache.ContainsKey(type))
     260          return constructorCache[type];
     261        foreach (ConstructorInfo ci in type.GetConstructors(ALL_CONSTRUCTORS)) {
     262          if (ci.GetCustomAttributes(typeof(StorableConstructorAttribute), false).Length > 0) {
     263            if (ci.GetParameters().Length != 1 ||
     264                ci.GetParameters()[0].ParameterType != typeof(bool))
     265              throw new PersistenceException("StorableConstructor must have exactly one argument of type bool");
     266            constructorCache[type] = ci;
     267            return ci;
     268          }
     269        }
     270        constructorCache[type] = null;
     271        return null;
     272      }
     273    }
     274
     275    #endregion
    77276  }
    78277}
Note: See TracChangeset for help on using the changeset viewer.