Index: /branches/PersistenceOverhaul/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableTypeAttribute.cs
===================================================================
--- /branches/PersistenceOverhaul/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableTypeAttribute.cs	(revision 14770)
+++ /branches/PersistenceOverhaul/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/StorableTypeAttribute.cs	(revision 14771)
@@ -37,5 +37,5 @@
 
     public Guid Guid { get; private set; }
-    public bool Released { get; set; }
+    public uint Version { get; set; }
 
     /// <summary>
@@ -43,13 +43,13 @@
     /// </summary>
     /// <param name="memberSelection">The storable class memberSelection.</param>
-    public StorableTypeAttribute(StorableMemberSelection memberSelection, string guid) {
+    public StorableTypeAttribute(StorableMemberSelection memberSelection, string guid, uint version = 1) {
       MemberSelection = memberSelection;
       Guid = new Guid(guid);
-      Released = false;
+      Version = version;
     }
 
-    public StorableTypeAttribute(string guid) {
+    public StorableTypeAttribute(string guid, uint version = 1) {
       Guid = new Guid(guid);
-      Released = false;
+      Version = version;
     }
 
Index: /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/Core/Mapper.cs
===================================================================
--- /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/Core/Mapper.cs	(revision 14770)
+++ /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/Core/Mapper.cs	(revision 14771)
@@ -95,4 +95,7 @@
     }
 
+    public Box GetBox(uint boxId) {
+      return boxId2Box[boxId];
+    }
     public uint GetBoxId(object o) {
       uint boxId;
Index: /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/Core/StorableConversionAttribute.cs
===================================================================
--- /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/Core/StorableConversionAttribute.cs	(revision 14771)
+++ /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/Core/StorableConversionAttribute.cs	(revision 14771)
@@ -0,0 +1,45 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2015 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 <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Reflection;
+
+namespace HeuristicLab.Persistence {
+  [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+  public sealed class StorableConversionAttribute : Attribute {
+    public uint SrcVersion { get; set; }
+
+    public StorableConversionAttribute(uint srcVersion)
+    {
+      this.SrcVersion = srcVersion;
+    }
+
+    public static bool IsStorableConversionMethod(MethodInfo mi) {
+      return Attribute.IsDefined(mi, typeof(StorableConversionAttribute), false);
+    }
+    public static StorableConversionAttribute GetTransformerAttribute(MethodInfo mi) {
+      return (StorableConversionAttribute)Attribute.GetCustomAttribute(mi, typeof(StorableConversionAttribute), false);
+    }
+    public static uint GetVersion(MethodInfo mi) {
+      return GetTransformerAttribute(mi).SrcVersion;
+    }
+  }
+}
Index: /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/HeuristicLab.Persistence-4.0.csproj
===================================================================
--- /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/HeuristicLab.Persistence-4.0.csproj	(revision 14770)
+++ /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/HeuristicLab.Persistence-4.0.csproj	(revision 14771)
@@ -55,4 +55,5 @@
     <Compile Include="Core\Serializer.cs" />
     <Compile Include="Core\Transformer.cs" />
+    <Compile Include="Core\StorableConversionAttribute.cs" />
     <Compile Include="Core\TransformerAttribute.cs" />
     <Compile Include="Core\TypeInfo.cs" />
Index: /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/Protos/PersistenceMessages.proto
===================================================================
--- /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/Protos/PersistenceMessages.proto	(revision 14770)
+++ /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/Protos/PersistenceMessages.proto	(revision 14771)
@@ -11,5 +11,5 @@
 message Box {
   optional uint32 transformer_id = 1;
-  optional uint32 type_id = 2;
+  optional uint32 type_id = 2; 
 
   extensions 100 to max;
@@ -177,5 +177,6 @@
     required TypeBox type = 250;
   }
-  repeated uint32 generic_type_ids = 1 [packed = true];
+  optional uint32 version = 1;
+  repeated uint32 generic_type_ids = 2 [packed = true];
 }
 
Index: /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/Transformers/StorableClassTransformer.cs
===================================================================
--- /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/Transformers/StorableClassTransformer.cs	(revision 14770)
+++ /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/Transformers/StorableClassTransformer.cs	(revision 14771)
@@ -70,4 +70,6 @@
       var type = obj.GetType();
       var typeInfo = Mapper.StaticCache.GetTypeInfo(type);
+      var typeBox = mapper.GetBox(box.TypeId).GetExtension(TypeBox.Type);
+      var version = typeBox.HasVersion ? typeBox.Version : 1;
 
       var components = new Dictionary<uint, uint>();
@@ -76,10 +78,27 @@
       }
 
+      var conversionMethods =
+        type.GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
+          .Where(StorableConversionAttribute.IsStorableConversionMethod)
+          .Where(mi => StorableConversionAttribute.GetVersion(mi) >= version)
+          .OrderBy(StorableConversionAttribute.GetVersion);
+
+      // put all objects into dictionary for optional conversion
+      var dict = new Dictionary<string, object>();
+      foreach (var component in components) {
+        dict.Add(mapper.GetString(component.Key), mapper.GetObject(component.Value));
+      }
+      
+      // TODO: check that all entries in the dictionary can be mapped to a field or property
+      foreach (var convMeth in conversionMethods) {
+        dict = (Dictionary<string, object>)convMeth.Invoke(null, new object[] { dict });
+      }
+
       foreach (var componentInfo in typeInfo.Fields) {
         var field = (FieldInfo)componentInfo.MemberInfo;
-        uint componentId;
-        bool found = components.TryGetValue(mapper.GetStringId(componentInfo.Name), out componentId);
+        object val = null;
+        bool found = dict.TryGetValue(componentInfo.Name, out val);
         if (found)
-          field.SetValue(obj, mapper.GetObject(componentId));
+          field.SetValue(obj, val);
         else if (componentInfo.StorableAttribute.DefaultValue != null)
           field.SetValue(obj, componentInfo.StorableAttribute.DefaultValue);
@@ -88,8 +107,8 @@
       foreach (var componentInfo in typeInfo.Properties.Where(x => x.Writeable)) {
         var property = (PropertyInfo)componentInfo.MemberInfo;
-        uint componentId;
-        bool found = components.TryGetValue(mapper.GetStringId(componentInfo.Name), out componentId);
+        object val = null;
+        bool found = dict.TryGetValue(componentInfo.Name, out val);
         if (found)
-          property.SetValue(obj, mapper.GetObject(componentId), null);
+          property.SetValue(obj, val, null);
         else if (componentInfo.StorableAttribute.DefaultValue != null)
           property.SetValue(obj, componentInfo.StorableAttribute.DefaultValue, null);
Index: /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/Transformers/Transformers.cs
===================================================================
--- /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/Transformers/Transformers.cs	(revision 14770)
+++ /branches/PersistenceOverhaul/HeuristicLab.Persistence/4.0/Transformers/Transformers.cs	(revision 14771)
@@ -299,4 +299,7 @@
         box.TypeId = mapper.GetTypeId(type);
       }
+
+      if (StorableTypeAttribute.IsStorableType(type))
+        typeBox.Version = StorableTypeAttribute.GetStorableTypeAttribute(type).Version;
       box.SetExtension(TypeBox.Type, typeBox.Build());
     }
Index: /branches/PersistenceOverhaul/HeuristicLab.Tests/HeuristicLab.Persistence-3.3/UseCasesPersistenceNew.cs
===================================================================
--- /branches/PersistenceOverhaul/HeuristicLab.Tests/HeuristicLab.Persistence-3.3/UseCasesPersistenceNew.cs	(revision 14770)
+++ /branches/PersistenceOverhaul/HeuristicLab.Tests/HeuristicLab.Persistence-3.3/UseCasesPersistenceNew.cs	(revision 14771)
@@ -2108,11 +2108,11 @@
     #region conversion
 
-    [StorableType("F9F51075-490C-48E3-BF64-14514A210149")]
+    [StorableType("F9F51075-490C-48E3-BF64-14514A210149", 1)]
     private class OldBaseType {
       [Storable]
       public ItemCollection<IItem> items;
     }
-    [StorableType("D211A828-6440-4E72-A8C7-AA4F9B4FFA75")]
-    private class OldType : OldBaseType {
+    [StorableType("D211A828-6440-4E72-A8C7-AA4F9B4FFA75", 1)]
+    private class V1 : OldBaseType {
       [Storable]
       public IntValue a;
@@ -2122,5 +2122,5 @@
 
       [Storable]
-      public OldType mySelf;
+      public V1 mySelf;
 
       [Storable]
@@ -2134,11 +2134,11 @@
 
 
-    [StorableType("B72C58C8-3321-4706-AA94-578F57337070")]
+    [StorableType("B72C58C8-3321-4706-AA94-578F57337070", 2)]
     private class NewBaseType {
       [Storable]
       public DoubleValue[] items;
     }
-    [StorableType("00000000-0000-0000-0000-BADCAFFEE000")] // for testing
-    private partial class NewType : NewBaseType {
+    [StorableType("00000000-0000-0000-0000-BADCAFFEE000", 2)] // for testing (version 2)
+    private partial class V2 : NewBaseType {
       [Storable]
       public int a;
@@ -2148,5 +2148,5 @@
 
       [Storable]
-      public NewType mySelf;
+      public V2 mySelf;
 
       [Storable]
@@ -2161,40 +2161,81 @@
 
     // conversion part
-    private partial class NewType : NewBaseType {
-      //[Storable(AllowOneWay = true, Name = "items")]
-      //private ItemCollection<IItem> itemsCompat {
-      //  set { items = value.Select(iv => new DoubleValue((double)(((dynamic)iv).Value))).ToArray(); }
-      //}
-
-      //[Storable(AllowOneWay = true, Name = "a")]
-      //private IntValue aBackwardsCompat { set { a = value.Value; } }
-
-      //[StorableConversion(Name = "a", Version=1)]
-      //private int ConvertA(IntValue oldValue) { return oldValue.Value; }
-      //[StorableConversion(Name = "newA", Version=2)]
-      //private double ConvertA(int oldValue) { return (double)oldValue; }
-
-      //[StorableConversion(OldNames= new [] {"x", "y"}, Name = "point", Version=2)]
-      //public Point ConvertXY(int x, int y) { return new Point(x, y); }
-
-      //[StorableConversion(OldNames= new [] {tup}, Names = [] {"TupItem1", "TupItem2"}, Version=2)]
-      //public IEnumerable<object> ConvertTup(Tuple<int, int> tup) {
-      //  yield return tup.Item1;
-      //  yield return tup.Item2;
-      //}
-      //
-      //[Storable(AllowOneWay = true, Name = "val")]
-      //private ItemList<IntValue> valCompat { set { val = value.Select(iv => iv.Value).ToArray(); } }
-      //
-      //[Storable(AllowOneWay = true, Name = "mySelf")]
-      //private NewType mySelfCompat { set { mySelf = value; } }
-
-
-
-      //[StorableConversion(SrcVersion = 1)]
-      //private Dictionary<string, object> ConvertAll(Dictionary<string, object> values)
-      //{
-      //  
-      //}
+    private partial class V2 : NewBaseType {
+      [StorableConversion(srcVersion: 1)]
+      private static Dictionary<string, object> ConvertV1(Dictionary<string, object> values) {
+        var newValues = new Dictionary<string, object>();
+        var items = (ItemCollection<IItem>)values["OldBaseType.items"];
+        newValues["NewBaseType.items"] = items.Select(iv => new DoubleValue((double)(((dynamic)iv).Value))).ToArray();
+        newValues["V2.a"] = ((IntValue)values["V1.a"]).Value;
+        newValues["V2.val"] = ((ItemList<IntValue>)values["V1.vals"]).Select(iv => iv.Value).ToArray();
+
+        newValues["V2.mySelf"] = values["V1.mySelf"]; // myself type will be mapped correctly
+
+        var tup = (Tuple<int, int>)values["V1.tup"];
+        if (tup != null) {
+          newValues["V2.TupItem1"] = tup.Item1;
+          newValues["V2.TupItem2"] = tup.Item2;
+        }
+
+        newValues["V2.coords"] = new Point((int)values["V1.x"], (int)values["V1.y"]);
+
+        return newValues;
+      }
+    }
+
+    [StorableType("00000000-0000-0000-0000-BADCAFFEE200", 3)] // for testing (version 3)
+    private partial class V3 : NewBaseType {
+      [Storable]
+      public int a;
+
+      [Storable]
+      public int[] val;
+
+      [Storable]
+      public V3 mySelf;
+
+      [Storable]
+      public Tuple<int, int> tup;
+
+      [Storable]
+      public Point coords;
+    }
+
+    // conversion part
+    private partial class V3 {
+      [StorableConversion(srcVersion: 1)]
+      private static Dictionary<string, object> ConvertV1(Dictionary<string, object> values) {
+        var newValues = new Dictionary<string, object>();
+        var items = (ItemCollection<IItem>)values["OldBaseType.items"];
+        newValues["NewBaseType.items"] = items.Select(iv => new DoubleValue((double)(((dynamic)iv).Value))).ToArray();
+        newValues["V2.a"] = ((IntValue)values["V1.a"]).Value;
+        newValues["V2.val"] = ((ItemList<IntValue>)values["V1.vals"]).Select(iv => iv.Value).ToArray();
+
+        newValues["V2.mySelf"] = values["V1.mySelf"]; // myself type will be mapped correctly
+
+        var tup = (Tuple<int, int>)values["V1.tup"];
+        if (tup != null) {
+          newValues["V2.TupItem1"] = tup.Item1;
+          newValues["V2.TupItem2"] = tup.Item2;
+        }
+
+        newValues["V2.coords"] = new Point((int)values["V1.x"], (int)values["V1.y"]);
+
+        return newValues;
+      }
+
+      [StorableConversion(srcVersion: 2)]
+      private static Dictionary<string, object> ConvertV2(Dictionary<string, object> values) {
+        var newValues = new Dictionary<string, object>();
+        newValues["NewBaseType.items"] = values["NewBaseType.items"];
+        newValues["V3.a"] = values["V2.a"];
+        newValues["V3.val"] = values["V2.val"];
+        newValues["V3.mySelf"] = values["V2.mySelf"];
+
+        newValues["V3.tup"] = Tuple.Create((int)values["V2.TupItem1"], (int)values["V2.TupItem2"]);
+        newValues["V3.coords"] = values["V2.coords"];
+
+        return newValues;
+      }
     }
 
@@ -2203,9 +2244,10 @@
     [TestProperty("Time", "short")]
     public void TestConversion() {
-      var test = new Func<OldType>(() => {
-        var p = new OldType();
+      var test = new Func<V1>(() => {
+        var p = new V1();
         p.a = new IntValue(1);
         p.mySelf = p;
         p.vals = new ItemList<IntValue>(new IntValue[] { p.a, new IntValue(2), new IntValue(3) });
+        p.tup = Tuple.Create(17, 4);
         var dv = new DoubleValue(1.0);
         p.items = new ItemCollection<IItem>(new IItem[] { dv, dv, p.a });
@@ -2216,20 +2258,58 @@
       var old = test();
       serializer.Serialize(old, tempFile);
-      Mapper.StaticCache.DeregisterType(StorableTypeAttribute.GetStorableTypeAttribute(typeof(OldType)).Guid);
-      Mapper.StaticCache.RegisterType(StorableTypeAttribute.GetStorableTypeAttribute(typeof(OldType)).Guid, typeof(NewType));
-
-      object o = serializer.Deserialize(tempFile);
-      var restored = (NewType)o;
+      Mapper.StaticCache.DeregisterType(StorableTypeAttribute.GetStorableTypeAttribute(typeof(V1)).Guid);
+      Mapper.StaticCache.DeregisterType(StorableTypeAttribute.GetStorableTypeAttribute(typeof(V2)).Guid);
+
+      Mapper.StaticCache.RegisterType(StorableTypeAttribute.GetStorableTypeAttribute(typeof(V1)).Guid, typeof(V2));
+
+      object o = serializer.Deserialize(tempFile);
+      var restored = (V2)o;
       Assert.AreEqual(restored.a, old.a.Value);
       Assert.IsTrue(restored.val.SequenceEqual(old.vals.Select(iv => iv.Value)));
       Assert.IsTrue(restored.items.Select(item => item.Value).SequenceEqual(old.items.Select(iv => (double)((dynamic)iv).Value)));
-      Assert.AreSame(restored.items[0], restored.items[1]);
+      // Assert.AreSame(restored.items[0], restored.items[1]);
       Assert.AreSame(restored, restored.mySelf);
 
-      string msg = Profile(test);
-      Console.WriteLine(msg);
-    }
-
-#endregion
+      //string msg = Profile(test);
+      //Console.WriteLine(msg);
+    }
+
+    [TestMethod]
+    [TestCategory("Persistence4")]
+    [TestProperty("Time", "short")]
+    public void TestConversion2() {
+      var test = new Func<V2>(() => {
+        var p = new V2();
+        p.a = 1;
+        p.mySelf = p;
+        p.val = new int[] {2, 3, 4};
+        p.TupItem1 = 17;
+        p.TupItem2 = 4;
+        p.items = new DoubleValue[] {new DoubleValue(1.0), new DoubleValue(2.0) };
+        return p;
+      });
+
+      ProtoBufSerializer serializer = new ProtoBufSerializer();
+      var old = test();
+      serializer.Serialize(old, tempFile);
+      Mapper.StaticCache.DeregisterType(StorableTypeAttribute.GetStorableTypeAttribute(typeof(V1)).Guid);
+      Mapper.StaticCache.DeregisterType(StorableTypeAttribute.GetStorableTypeAttribute(typeof(V2)).Guid);
+      Mapper.StaticCache.DeregisterType(StorableTypeAttribute.GetStorableTypeAttribute(typeof(V3)).Guid);
+
+      Mapper.StaticCache.RegisterType(StorableTypeAttribute.GetStorableTypeAttribute(typeof(V2)).Guid, typeof(V3));
+
+      object o = serializer.Deserialize(tempFile);
+      var restored = (V3)o;
+      Assert.AreEqual(restored.a, old.a);
+      Assert.IsTrue(restored.val.SequenceEqual(old.val));
+      Assert.IsTrue(restored.items.Select(item => item.Value).SequenceEqual(old.items.Select(iv => (double)((dynamic)iv).Value)));
+      // Assert.AreSame(restored.items[0], restored.items[1]);
+      Assert.AreSame(restored, restored.mySelf);
+
+      //string msg = Profile(test);
+      //Console.WriteLine(msg);
+    }
+
+    #endregion
 
     [TestMethod]
