Free cookie consent management tool by TermsFeed Policy Generator

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

add support for automatic serialization of fields and properties (#548)

Location:
trunk/sources/HeuristicLab.Persistence/3.3
Files:
3 added
4 edited

Legend:

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

    r3025 r3029  
    44using System.Linq;
    55
    6 namespace HeuristicLab.Persistence.Default.CompositeSerializers.Storable {
    7 
    8 
    9   /// <summary>
    10   /// Specifies which memebrs are selected for serialization by the StorableSerializer
    11   /// </summary>
    12   public enum StorableClassType {
    13 
    14     /// <summary>
    15     /// Serialize only fields and properties that have been marked
    16     /// with the [Storable] attribute. This is the default value.
    17     /// </summary>
    18     MarkedOnly,
    19 
    20     /// <summary>
    21     /// Serialize all fields but ignore the
    22     /// [Storable] attribute on properties.
    23     /// </summary>
    24     [Obsolete("not implemented yet")]
    25     AllFields,
    26 
    27     /// <summary>
    28     /// Serialize all properties but ignore the
    29     /// [Storable] attirbute on fields.
    30     /// </summary>
    31     [Obsolete("not implemented yet")]
    32     AllProperties,
    33 
    34     /// <summary>
    35     /// Serialize all fields and all properties
    36     /// but ignore the [Storable] on all members.
    37     /// </summary>
    38     [Obsolete("not implemnted yet")]
    39     AllFieldsAndAllProperties
    40   };
    41 
     6namespace HeuristicLab.Persistence.Default.CompositeSerializers.Storable { 
    427
    438  /// <summary>
  • trunk/sources/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableSerializer.cs

    r3025 r3029  
    1212  /// <summary>
    1313  /// Intended for serialization of all custom classes. Classes should have the
    14   /// <c>[StorableClass]</c> attribute set and a serialization mode set.
    15   /// Optionally selected fields and properties can be marked with the
    16   /// <c>[Storable]</c> attribute.
     14  /// <c>[StorableClass]</c> attribute set. The default mode is to serialize
     15  /// members with the <c>[Storable]</c> attribute set. Alternatively the
     16  /// storable mode can be set to <c>AllFields</c>, <c>AllProperties</c>
     17  /// or <c>AllFieldsAndAllProperties</c>.
    1718  /// </summary>
    18   [StorableClass]   
     19  [StorableClass]
    1920  public class StorableSerializer : ICompositeSerializer {
    20 
    2121
    2222    #region ICompositeSerializer implementation
     
    3030        GetStorableConstructor(type) == null)
    3131        return false;
    32       return IsEmptyOrStorableType(type, true);
     32      return StorableReflection.IsEmptyOrStorableType(type, true);
    3333    }
    3434
     
    3737        GetStorableConstructor(type) == null)
    3838        return "no default constructor and no storable constructor";
    39       if (!IsEmptyOrStorableType(type, true))
     39      if (!StorableReflection.IsEmptyOrStorableType(type, true))
    4040        return "class is not marked with the storable class attribute";
    4141      return "no reason";
     
    7171      while (iter.MoveNext()) {
    7272        memberDict.Add(iter.Current.Name, iter.Current);
    73       }     
     73      }
    7474      foreach (var accessor in GetStorableAccessors(instance)) {
    7575        if (memberDict.ContainsKey(accessor.Name)) {
     
    8484    #endregion
    8585
    86     #region constances & private data types
     86    #region constants & private data types
    8787
    8888    private const BindingFlags ALL_CONSTRUCTORS =
    8989      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     }
    12690
    12791    private sealed class TypeQuery {
     
    141105
    142106    private MemberCache storableMemberCache = new MemberCache();
    143     private Dictionary<Type, ConstructorInfo> constructorCache = 
     107    private Dictionary<Type, ConstructorInfo> constructorCache =
    144108      new Dictionary<Type, ConstructorInfo>();
    145109
    146110    #endregion
    147111
    148     #region auxiliary attribute reflection tools
     112    #region attribute access
    149113
    150114    private IEnumerable<StorableMemberInfo> GetStorableMembers(Type type) {
     
    157121        if (storableMemberCache.ContainsKey(query))
    158122          return storableMemberCache[query];
    159         var storablesMembers = GenerateStorableMembers(type, inherited);
     123        var storablesMembers = StorableReflection.GenerateStorableMembers(type, inherited);
    160124        storableMemberCache[query] = storablesMembers;
    161125        return storablesMembers;
    162126      }
    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     }
     127    }   
    256128
    257129    private ConstructorInfo GetStorableConstructor(Type type) {
     
    273145    }
    274146
     147    private IEnumerable<DataMemberAccessor> GetStorableAccessors(object obj) {
     148      return GetStorableMembers(obj.GetType())
     149        .Select(mi => new DataMemberAccessor(mi.MemberInfo, mi.DisentangledName, mi.DefaultValue, obj));
     150    }
     151
    275152    #endregion
     153   
    276154  }
     155 
    277156}
  • trunk/sources/HeuristicLab.Persistence/3.3/HeuristicLab.Persistence-3.3.csproj

    r3005 r3029  
    119119    <Compile Include="Default\CompositeSerializers\NumberEnumerable2StringSerializer.cs" />
    120120    <Compile Include="Default\CompositeSerializers\StackSerializer.cs" />
     121    <Compile Include="Default\CompositeSerializers\Storable\StorableReflection.cs" />
     122    <Compile Include="Default\CompositeSerializers\Storable\StorableMemberInfo.cs" />
     123    <Compile Include="Default\CompositeSerializers\Storable\StorableClassType.cs" />
    121124    <Compile Include="Default\CompositeSerializers\Storable\DataMemberAccessor.cs" />
    122125    <Compile Include="Default\CompositeSerializers\Storable\StorableClassAttribute.cs" />
  • trunk/sources/HeuristicLab.Persistence/3.3/Tests/UseCases.cs

    r3017 r3029  
    815815    }
    816816
     817    [StorableClass(StorableClassType.AllFields)]
     818    public class AllFieldsStorable {
     819      public int Value1 = 1;
     820      [Storable]
     821      public int Value2 = 2;
     822      public int Value3 { get; private set; }
     823      public int Value4 { get; private set; }
     824      [StorableConstructor]
     825      public AllFieldsStorable(bool isDeserializing) {
     826        if (!isDeserializing) {
     827          Value1 = 12;
     828          Value2 = 23;
     829          Value3 = 34;
     830          Value4 = 56;
     831        }
     832      }
     833    }
     834
     835    [TestMethod]
     836    public void TestStorableClassDiscoveryAllFields() {
     837      AllFieldsStorable afs = new AllFieldsStorable(false);
     838      XmlGenerator.Serialize(afs, tempFile);
     839      AllFieldsStorable newAfs = (AllFieldsStorable)XmlParser.Deserialize(tempFile);
     840      Assert.AreEqual(afs.Value1, newAfs.Value1);
     841      Assert.AreEqual(afs.Value2, newAfs.Value2);
     842      Assert.AreEqual(0, newAfs.Value3);
     843      Assert.AreEqual(0, newAfs.Value4);
     844    }
     845
     846    [StorableClass(StorableClassType.AllProperties)]
     847    public class AllPropertiesStorable {
     848      public int Value1 = 1;
     849      [Storable]
     850      public int Value2 = 2;
     851      public int Value3 { get; private set; }
     852      public int Value4 { get; private set; }
     853      [StorableConstructor]
     854      public AllPropertiesStorable(bool isDeserializing) {
     855        if (!isDeserializing) {
     856          Value1 = 12;
     857          Value2 = 23;
     858          Value3 = 34;
     859          Value4 = 56;
     860        }
     861      }
     862    }
     863
     864    [TestMethod]
     865    public void TestStorableClassDiscoveryAllProperties() {
     866      AllPropertiesStorable afs = new AllPropertiesStorable(false);
     867      XmlGenerator.Serialize(afs, tempFile);
     868      AllPropertiesStorable newAfs = (AllPropertiesStorable)XmlParser.Deserialize(tempFile);
     869      Assert.AreEqual(1, newAfs.Value1);
     870      Assert.AreEqual(2, newAfs.Value2);
     871      Assert.AreEqual(afs.Value3, newAfs.Value3);
     872      Assert.AreEqual(afs.Value4, newAfs.Value4);
     873     
     874    }
     875
     876    [StorableClass(StorableClassType.AllFieldsAndAllProperties)]
     877    public class AllFieldsAndAllPropertiesStorable {
     878      public int Value1 = 1;
     879      [Storable]
     880      public int Value2 = 2;
     881      public int Value3 { get; private set; }
     882      public int Value4 { get; private set; }
     883      [StorableConstructor]
     884      public AllFieldsAndAllPropertiesStorable(bool isDeserializing) {
     885        if (!isDeserializing) {
     886          Value1 = 12;
     887          Value2 = 23;
     888          Value3 = 34;
     889          Value4 = 56;
     890        }
     891      }
     892    }
     893
     894    [TestMethod]
     895    public void TestStorableClassDiscoveryAllFieldsAndAllProperties() {
     896      AllFieldsAndAllPropertiesStorable afs = new AllFieldsAndAllPropertiesStorable(false);
     897      XmlGenerator.Serialize(afs, tempFile);
     898      AllFieldsAndAllPropertiesStorable newAfs = (AllFieldsAndAllPropertiesStorable)XmlParser.Deserialize(tempFile);
     899      Assert.AreEqual(afs.Value1, newAfs.Value1);
     900      Assert.AreEqual(afs.Value2, newAfs.Value2);
     901      Assert.AreEqual(afs.Value3, newAfs.Value3);
     902      Assert.AreEqual(afs.Value4, newAfs.Value4);     
     903    }
     904
     905    [StorableClass(StorableClassType.MarkedOnly)]
     906    public class MarkedOnlyStorable {
     907      public int Value1 = 1;
     908      [Storable]
     909      public int Value2 = 2;
     910      public int Value3 { get; private set; }
     911      public int Value4 { get; private set; }
     912      [StorableConstructor]
     913      public MarkedOnlyStorable(bool isDeserializing) {
     914        if (!isDeserializing) {
     915          Value1 = 12;
     916          Value2 = 23;
     917          Value3 = 34;
     918          Value4 = 56;
     919        }
     920      }
     921    }
     922
     923    [TestMethod]
     924    public void TestStorableClassDiscoveryMarkedOnly() {
     925      MarkedOnlyStorable afs = new MarkedOnlyStorable(false);
     926      XmlGenerator.Serialize(afs, tempFile);
     927      MarkedOnlyStorable newAfs = (MarkedOnlyStorable)XmlParser.Deserialize(tempFile);
     928      Assert.AreEqual(1, newAfs.Value1);     
     929      Assert.AreEqual(afs.Value2, newAfs.Value2);
     930      Assert.AreEqual(0, newAfs.Value3);
     931      Assert.AreEqual(0, newAfs.Value4);
     932    }
     933
     934   
     935
    817936    [ClassInitialize]
    818937    public static void Initialize(TestContext testContext) {
Note: See TracChangeset for help on using the changeset viewer.