Free cookie consent management tool by TermsFeed Policy Generator

Changeset 5324


Ignore:
Timestamp:
01/18/11 15:50:23 (13 years ago)
Author:
epitzer
Message:

Implement one-way serialization that allows either only loading or only saving of storable members by setting a new property called AllowOneWay on the [Storable] attribute (#1385)

Location:
trunk/sources/HeuristicLab.Persistence/3.3
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.Persistence/3.3/Core/DataMemberAccessor.cs

    r4068 r5324  
    150150        return GenerateFieldGetter(fieldInfo);
    151151      } else if (memberInfo.MemberType == MemberTypes.Property) {
    152         PropertyInfo propertyInfo = (PropertyInfo)memberInfo;
    153         if (!propertyInfo.CanRead || !propertyInfo.CanWrite) {
    154           throw new PersistenceException(
    155             "Storable properties must implement both a Get and a Set Accessor. ");
    156         }
    157         return GeneratePropertyGetter(propertyInfo);
     152        return GeneratePropertyGetter((PropertyInfo)memberInfo);
    158153      } else {
    159154        throw new PersistenceException(
     
    172167        return GenerateFieldSetter(fieldInfo);
    173168      } else if (memberInfo.MemberType == MemberTypes.Property) {
    174         PropertyInfo propertyInfo = (PropertyInfo)memberInfo;
    175         if (!propertyInfo.CanRead || !propertyInfo.CanWrite) {
    176           throw new PersistenceException(
    177             "Storable properties must implement both a Get and a Set Accessor. ");
    178         }
    179         return GeneratePropertySetter(propertyInfo);
     169        return GeneratePropertySetter((PropertyInfo)memberInfo);
    180170      } else {
    181171        throw new PersistenceException(
     
    201191
    202192    /// <summary>
    203     /// Generates a dynamically compiled sett to access fields (even private ones).
     193    /// Generates a dynamically compiled setter to access fields (even private ones).
    204194    /// </summary>
    205195    /// <param name="fieldInfo">The field info.</param>
     
    223213    /// <returns>A Func&lt;object, object&gt;</returns>
    224214    public static Func<object, object> GeneratePropertyGetter(PropertyInfo propertyInfo) {
     215      MethodInfo getter = propertyInfo.GetGetMethod(true);
     216      if (getter == null)
     217        return null;
    225218      DynamicMethod dm = new DynamicMethod("", typeof(object), new Type[] { typeof(object) }, propertyInfo.DeclaringType, true);
    226219      ILGenerator ilgen = dm.GetILGenerator();
    227220      ilgen.Emit(OpCodes.Ldarg_0);
    228221      ilgen.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
    229       ilgen.Emit(OpCodes.Callvirt, propertyInfo.GetGetMethod(true));
     222      ilgen.Emit(OpCodes.Callvirt, getter);
    230223      ilgen.Emit(OpCodes.Box, propertyInfo.PropertyType);
    231224      ilgen.Emit(OpCodes.Ret);
     
    239232    /// <returns>An Action&lt;object, object%gt;</returns>
    240233    public static Action<object, object> GeneratePropertySetter(PropertyInfo propertyInfo) {
     234      MethodInfo setter = propertyInfo.GetSetMethod(true);
     235      if (setter == null)
     236        return null;
    241237      DynamicMethod dm = new DynamicMethod("", null, new Type[] { typeof(object), typeof(object) }, propertyInfo.DeclaringType, true);
    242238      ILGenerator ilgen = dm.GetILGenerator();
     
    245241      ilgen.Emit(OpCodes.Ldarg_1);
    246242      ilgen.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
    247       ilgen.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod(true));
     243      ilgen.Emit(OpCodes.Callvirt, setter);
    248244      ilgen.Emit(OpCodes.Ret);
    249245      return (Action<object, object>)dm.CreateDelegate(typeof(Action<object, object>));
  • trunk/sources/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableAttribute.cs

    r4068 r5324  
    5656
    5757    /// <summary>
     58    /// Allow storable attribute on properties with only a getter or a setter. These
     59    /// properties will then by either only serialized but not deserialized or only
     60    /// deserialized (if stored) but not serialized again.
     61    /// </summary>
     62    public bool AllowOneWay { get; set; }
     63
     64    /// <summary>
    5865    /// Returns a <see cref="System.String"/> that represents this instance.
    5966    /// </summary>
  • trunk/sources/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableMemberInfo.cs

    r4068 r5324  
    2323using System.Reflection;
    2424using System.Text;
     25using HeuristicLab.Persistence.Core;
    2526
    2627namespace HeuristicLab.Persistence.Default.CompositeSerializers.Storable {
     
    4344      DefaultValue = attribute.DefaultValue;
    4445      MemberInfo = memberInfo;
     46      if (!attribute.AllowOneWay)
     47        CheckPropertyAccess(memberInfo as PropertyInfo);
    4548    }
    46     public StorableMemberInfo(MemberInfo memberInfo) {
     49    public StorableMemberInfo(MemberInfo memberInfo, bool allowOneWay) {
    4750      MemberInfo = memberInfo;
     51      if (!allowOneWay)
     52        CheckPropertyAccess(memberInfo as PropertyInfo);
     53    }
     54    private static void CheckPropertyAccess(PropertyInfo propertyInfo) {
     55      if (propertyInfo == null)
     56        return;
     57      if (!propertyInfo.CanRead || !propertyInfo.CanWrite)
     58        throw new PersistenceException("Properties must be readable and writable or explicity enable one way serialization.");
    4859    }
    4960    public void SetDisentangledName(string name) {
     
    5162        DisentangledName = name;
    5263    }
     64    /// <summary>
     65    /// Gets the type who first defined this property in the class hierarchy when the
     66    /// property has subsequently been overridden but not shadowed with <code>new</code>.
     67    /// </summary>
     68    /// <returns>The properties base type.</returns>
    5369    public Type GetPropertyDeclaringBaseType() {
    54       return ((PropertyInfo)MemberInfo).GetGetMethod(true).GetBaseDefinition().DeclaringType;
     70      PropertyInfo pi = MemberInfo as PropertyInfo;
     71      if (pi == null)
     72        throw new PersistenceException("fields don't have a declaring base type, directly use FullyQualifiedMemberName instead");
     73      if (pi.CanRead)
     74        return pi.GetGetMethod(true).GetBaseDefinition().DeclaringType;
     75      if (pi.CanWrite)
     76        return pi.GetSetMethod(true).GetBaseDefinition().DeclaringType;
     77      throw new InvalidOperationException("property has neigher a getter nor a setter.");
    5578    }
    5679  }
  • trunk/sources/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableReflection.cs

    r4068 r5324  
    5858        }
    5959      }
    60 
    6160      return DisentangleNameMapping(storableMembers);
    6261    }
     
    108107            !memberInfo.Name.StartsWith("<") &&
    109108            !memberInfo.Name.EndsWith("k__BackingField"))
    110           storableMembers.Add(new StorableMemberInfo(memberInfo));
    111       }
    112     }
    113 
    114 
     109          storableMembers.Add(new StorableMemberInfo(memberInfo, false));
     110      }
     111    }
     112
     113    /// <summary>
     114    /// Ascertain distinct names for all fields and properties. This method takes care
     115    /// of disentangling equal names from different class hiarachy levels.
     116    ///
     117    /// Field names are replaced with their fully qualified name which includes
     118    /// the class names where they were declared.
     119    ///
     120    /// Property names are first reduced to unqiue accessors that are not overrides of
     121    /// each other and the replaced with their fully qualified name if more than one
     122    /// accessor remains.
     123    /// </summary>
     124    /// <param name="storableMemberInfos"></param>
     125    /// <returns></returns>
    115126    private static IEnumerable<StorableMemberInfo> DisentangleNameMapping(
    116127        IEnumerable<StorableMemberInfo> storableMemberInfos) {
     
    138149    }
    139150
     151    /// <summary>
     152    /// Merges property accessors that are overrides of each other but differentiates if a new
     153    /// property that shadows older implementations has been introduced with <code>new</code>.
     154    /// </summary>
     155    /// <param name="members">A list of <code>StorableMemberInfo</code>s for properties of the same type.</param>
     156    /// <returns>A fieltered <code>IEnumerable</code> of propery infos.</returns>
    140157    private static IEnumerable<StorableMemberInfo> MergePropertyAccessors(List<StorableMemberInfo> members) {
    141158      var uniqueAccessors = new Dictionary<Type, StorableMemberInfo>();
  • trunk/sources/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableSerializer.cs

    r5292 r5324  
    118118    public IEnumerable<Tag> Decompose(object obj) {
    119119      foreach (var accessor in GetStorableAccessors(obj.GetType())) {
    120         yield return new Tag(accessor.Name, accessor.Get(obj));
     120        if (accessor.Get != null)
     121          yield return new Tag(accessor.Name, accessor.Get(obj));
    121122      }
    122123    }
     
    151152      }
    152153      foreach (var accessor in GetStorableAccessors(instance.GetType())) {
    153         if (memberDict.ContainsKey(accessor.Name)) {
    154           accessor.Set(instance, memberDict[accessor.Name].Value);
    155         } else if (accessor.DefaultValue != null) {
    156           accessor.Set(instance, accessor.DefaultValue);
     154        if (accessor.Set != null) {
     155          if (memberDict.ContainsKey(accessor.Name)) {
     156            accessor.Set(instance, memberDict[accessor.Name].Value);
     157          } else if (accessor.DefaultValue != null) {
     158            accessor.Set(instance, accessor.DefaultValue);
     159          }
    157160        }
    158161      }
  • trunk/sources/HeuristicLab.Persistence/3.3/Tests/UseCases.cs

    r5290 r5324  
    3131using HeuristicLab.Persistence.Auxiliary;
    3232using HeuristicLab.Persistence.Core;
     33using HeuristicLab.Persistence.Core.Tokens;
    3334using HeuristicLab.Persistence.Default.CompositeSerializers;
    3435using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
     
    606607        XmlGenerator.Serialize(c, tempFile);
    607608        Assert.Fail("Exception not thrown");
    608       }
    609       catch (PersistenceException) {
     609      } catch (PersistenceException) {
    610610      }
    611611    }
     
    619619        XmlGenerator.Serialize(s, tempFile);
    620620        Assert.Fail("Exception expected");
    621       }
    622       catch (PersistenceException) { }
     621      } catch (PersistenceException) { }
    623622      List<int> newList = (List<int>)XmlParser.Deserialize(tempFile);
    624623      Assert.AreEqual(list[0], newList[0]);
     
    659658        }
    660659        Assert.Fail("Exception expected");
    661       }
    662       catch (PersistenceException px) {
     660      } catch (PersistenceException px) {
    663661        Assert.AreEqual(3, px.Data.Count);
    664662      }
     
    688686        d = new Deserializer(XmlParser.ParseTypeCache(new StringReader(newTypeString)));
    689687        Assert.Fail("Exception expected");
    690       }
    691       catch (PersistenceException x) {
     688      } catch (PersistenceException x) {
    692689        Assert.IsTrue(x.Message.Contains("incompatible"));
    693690      }
     
    698695        d = new Deserializer(XmlParser.ParseTypeCache(new StringReader(newTypeString)));
    699696        Assert.Fail("Exception expected");
    700       }
    701       catch (PersistenceException x) {
     697      } catch (PersistenceException x) {
    702698        Assert.IsTrue(x.Message.Contains("newer"));
    703699      }
     
    866862        ExplodingDefaultConstructor newX = (ExplodingDefaultConstructor)XmlParser.Deserialize(tempFile);
    867863        Assert.Fail("Exception expected");
    868       }
    869       catch (PersistenceException pe) {
     864      } catch (PersistenceException pe) {
    870865        Assert.AreEqual(pe.InnerException.Message, "this constructor will always fail");
    871866      }
     
    878873        XmlGenerator.Serialize(ns, tempFile);
    879874        Assert.Fail("PersistenceException expected");
    880       }
    881       catch (PersistenceException x) {
     875      } catch (PersistenceException x) {
    882876        Assert.IsTrue(x.Message.Contains(new StorableSerializer().JustifyRejection(typeof(NonSerializable))));
    883877      }
     
    11611155    }
    11621156
     1157    [StorableClass]
     1158    public class ReadOnlyFail {
     1159      [Storable]
     1160      public string ReadOnly {
     1161        get { return "fail"; }
     1162      }
     1163    }
     1164
     1165    [TestMethod]
     1166    public void TestReadOnlyFail() {
     1167      try {
     1168        XmlGenerator.Serialize(new ReadOnlyFail(), tempFile);
     1169        Assert.Fail("Exception expected");
     1170      } catch (PersistenceException) {
     1171      } catch {
     1172        Assert.Fail("PersistenceException expected");
     1173      }
     1174    }
     1175
     1176
     1177    [StorableClass]
     1178    public class WriteOnlyFail {
     1179      [Storable]
     1180      public string WriteOnly {
     1181        set { throw new InvalidOperationException("this property should never be set."); }
     1182      }
     1183    }
     1184
     1185    [TestMethod]
     1186    public void TestWriteOnlyFail() {
     1187      try {
     1188        XmlGenerator.Serialize(new WriteOnlyFail(), tempFile);
     1189        Assert.Fail("Exception expected");
     1190      } catch (PersistenceException) {
     1191      } catch {
     1192        Assert.Fail("PersistenceException expected.");
     1193      }
     1194    }
     1195
     1196    [StorableClass]
     1197    public class OneWayTest {
     1198      public OneWayTest() { this.value = "default"; }
     1199      public string value;
     1200      [Storable(AllowOneWay=true)]
     1201      public string ReadOnly {
     1202        get { return "ReadOnly"; }
     1203      }
     1204      [Storable(AllowOneWay=true)]
     1205      public string WriteOnly {
     1206        set { this.value = value; }
     1207      }
     1208    }
     1209
     1210    [TestMethod]
     1211    public void TestOneWaySerialization() {
     1212      var test = new OneWayTest();
     1213      var serializer = new Serializer(test, ConfigurationService.Instance.GetDefaultConfig(new XmlFormat()));
     1214      var it = serializer.GetEnumerator();
     1215      it.MoveNext();
     1216      Assert.AreEqual("ROOT", ((BeginToken)it.Current).Name); it.MoveNext();
     1217      Assert.AreEqual("ReadOnly", ((PrimitiveToken)it.Current).Name); it.MoveNext();
     1218      Assert.AreEqual("ROOT", ((EndToken)it.Current).Name); it.MoveNext();
     1219      var deserializer = new Deserializer(new[] {
     1220        new TypeMapping(0, typeof(OneWayTest).AssemblyQualifiedName, typeof(StorableSerializer).AssemblyQualifiedName),
     1221        new TypeMapping(1, typeof(string).AssemblyQualifiedName, typeof(String2XmlSerializer).AssemblyQualifiedName) });
     1222      var newTest = (OneWayTest)deserializer.Deserialize(new ISerializationToken[] {
     1223        new BeginToken("ROOT", 0, 0),
     1224        new PrimitiveToken("WriteOnly", 1, 1, new XmlString("<![CDATA[serial data]]>")),
     1225        new EndToken("ROOT", 0, 0)
     1226      });
     1227      Assert.AreEqual("serial data", newTest.value);
     1228    }
     1229
     1230
     1231
    11631232    [ClassInitialize]
    11641233    public static void Initialize(TestContext testContext) {
Note: See TracChangeset for help on using the changeset viewer.