#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 .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using Google.Protobuf;
namespace HeuristicLab.Persistence {
public sealed class Mapper {
internal class MappingEqualityComparer : IEqualityComparer {
bool IEqualityComparer.Equals(object x, object y) {
if (x == null && y == null) return true;
if (x == null ^ y == null) return false;
if (x.GetType() != y.GetType()) return false;
var type = x.GetType();
if (type.IsValueType || type == typeof(string)) return x.Equals(y);
return object.ReferenceEquals(x, y);
}
int IEqualityComparer.GetHashCode(object obj) {
return obj == null ? 0 : obj.GetHashCode();
}
}
private static StaticCache staticCache;
private static object locker = new object();
public static StaticCache StaticCache {
get {
lock (locker) {
if (staticCache == null) staticCache = new StaticCache();
return staticCache;
}
}
}
private Index transformers;
private Index types;
private Dictionary boxId2Box;
private Dictionary object2BoxId;
private Dictionary boxId2Object;
private Index typeBoxes;
private Index strings;
private Index boolArrayBoxes;
private Index intArrayBoxes;
private Index unsignedIntArrayBoxes;
private Index longArrayBoxes;
private Index unsignedLongArrayBoxes;
private Index floatArrayBoxes;
private Index doubleArrayBoxes;
private Index dictionaryBoxes;
private Index storableClassBoxes;
public uint BoxCount { get; private set; }
public Mapper() {
transformers = new Index();
types = new Index();
boxId2Box = new Dictionary();
object2BoxId = new Dictionary(new MappingEqualityComparer());
boxId2Object = new Dictionary();
typeBoxes = new Index();
strings = new Index();
boolArrayBoxes = new Index();
intArrayBoxes = new Index();
unsignedIntArrayBoxes = new Index();
longArrayBoxes = new Index();
unsignedLongArrayBoxes = new Index();
floatArrayBoxes = new Index();
doubleArrayBoxes = new Index();
dictionaryBoxes = new Index();
storableClassBoxes = new Index();
BoxCount = 0;
}
#region Transformers
public uint GetTransformerId(ITransformer transformer) {
return transformers.GetIndex(transformer);
}
public ITransformer GetTransformer(uint transformerId) {
return transformers.GetValue(transformerId);
}
#endregion
#region Types
public uint GetTypeId(Type type) {
return types.GetIndex(type);
}
public Type GetType(uint typeId) {
return types.GetValue(typeId);
}
#endregion
#region Boxes
public uint GetBoxId(object o) {
uint boxId;
if (o == null)
boxId = 0;
else {
if (object2BoxId.TryGetValue(o, out boxId)) return boxId;
var type = o.GetType();
var typeInfo = StaticCache.GetTypeInfo(type);
if (typeInfo.Transformer == null) throw new ArgumentException("Cannot serialize object of type " + o.GetType());
boxId = ++BoxCount;
typeInfo.Used++;
object2BoxId.Add(o, boxId);
boxId2Box.Add(boxId, typeInfo.Transformer.ToBox(o, this));
}
return boxId;
}
public Box GetBox(uint boxId) {
return boxId2Box[boxId];
}
public object GetObject(uint boxId) {
object o;
if (boxId2Object.TryGetValue(boxId, out o)) return o;
Box box;
boxId2Box.TryGetValue(boxId, out box);
if (box == null)
o = null;
else {
var transformer = transformers.GetValue(box.TransformerId);
o = transformer.ToObject(box, this);
boxId2Object.Add(boxId, o);
transformer.FillFromBox(o, box, this);
}
return o;
}
#region Referenced Boxes
#region TypeBoxes
public uint GetTypeBoxId(TypeBox typeBox) { return typeBoxes.GetIndex(typeBox); }
public TypeBox GetTypeBox(uint typeBoxId) { return typeBoxes.GetValue(typeBoxId); }
#endregion
#region BoolArrayBox
public uint GetBoolArrayBoxId(BoolArrayBox boolArrayBox) { return boolArrayBoxes.GetIndex(boolArrayBox); }
public BoolArrayBox GetBoolArrayBox(uint boolArrayBoxId) { return boolArrayBoxes.GetValue(boolArrayBoxId); }
#endregion
#region IntArrayBox
public uint GetIntArrayBoxId(IntArrayBox intArrayBox) { return intArrayBoxes.GetIndex(intArrayBox); }
public IntArrayBox GetIntArrayBox(uint intArrayBoxId) { return intArrayBoxes.GetValue(intArrayBoxId); }
#endregion
#region UnsignedIntArrayBox
public uint GetUnsignedIntArrayBoxId(UnsignedIntArrayBox unsignedIntArrayBox) { return unsignedIntArrayBoxes.GetIndex(unsignedIntArrayBox); }
public UnsignedIntArrayBox GetUnsignedIntArrayBox(uint unsignedIntArrayBoxId) { return unsignedIntArrayBoxes.GetValue(unsignedIntArrayBoxId); }
#endregion
#region LongArrayBox
public uint GetLongArrayBoxId(LongArrayBox longArrayBox) { return longArrayBoxes.GetIndex(longArrayBox); }
public LongArrayBox GetLongArrayBox(uint longArrayBoxId) { return longArrayBoxes.GetValue(longArrayBoxId); }
#endregion
#region UnsignedLongArrayBox
public uint GetUnsignedLongArrayBoxId(UnsignedLongArrayBox unsignedLongArrayBox) { return unsignedLongArrayBoxes.GetIndex(unsignedLongArrayBox); }
public UnsignedLongArrayBox GetUnsignedLongArrayBox(uint unsignedLongArrayBoxId) { return unsignedLongArrayBoxes.GetValue(unsignedLongArrayBoxId); }
#endregion
#region FloatArrayBox
public uint GetFloatArrayBoxId(FloatArrayBox floatArrayBox) { return floatArrayBoxes.GetIndex(floatArrayBox); }
public FloatArrayBox GetFloatArrayBox(uint floatArrayBoxId) { return floatArrayBoxes.GetValue(floatArrayBoxId); }
#endregion
#region DoubleArrayBox
public uint GetDoubleArrayBoxId(DoubleArrayBox doubleArrayBox) { return doubleArrayBoxes.GetIndex(doubleArrayBox); }
public DoubleArrayBox GetDoubleArrayBox(uint doubleArrayBoxId) { return doubleArrayBoxes.GetValue(doubleArrayBoxId); }
#endregion
#region DictionaryBox
public uint GetDictionaryBoxId(DictionaryBox dictionaryBox) { return dictionaryBoxes.GetIndex(dictionaryBox); }
public DictionaryBox GetDictionaryBox(uint dictionaryBoxId) { return dictionaryBoxes.GetValue(dictionaryBoxId); }
#endregion
#region StorableClassBox
public uint GetStorableClassBoxId(StorableClassBox storableClassBox) { return storableClassBoxes.GetIndex(storableClassBox); }
public StorableClassBox GetStorableClassBox(uint storableClassBoxId) { return storableClassBoxes.GetValue(storableClassBoxId); }
#endregion
#endregion
#endregion
#region Strings
public uint GetStringId(string str) {
return strings.GetIndex(str);
}
public string GetString(uint stringId) {
return strings.GetValue(stringId);
}
#endregion
public object CreateInstance(Type type) {
try {
return StaticCache.GetTypeInfo(type).GetConstructor()();
} catch (Exception e) {
throw new PersistenceException("Deserialization failed.", e);
}
}
public static Bundle ToBundle(object o) {
var mapper = new Mapper();
var bundle = new Bundle();
bundle.RootBoxId = mapper.GetBoxId(o);
bundle.TransformerGuids.AddRange(mapper.transformers.GetValues().Select(x => x.Guid).Select(x => ByteString.CopyFrom(x.ToByteArray())));
bundle.TypeGuids.AddRange(mapper.types.GetValues().Select(x => ByteString.CopyFrom(StaticCache.GetGuid(x).ToByteArray())));
bundle.Boxes.AddRange(mapper.boxId2Box.OrderBy(x => x.Key).Select(x => x.Value));
bundle.TypeBoxes.AddRange(mapper.typeBoxes.GetValues());
bundle.Strings.AddRange(mapper.strings.GetValues());
bundle.BoolArrayBoxes.AddRange(mapper.boolArrayBoxes.GetValues());
bundle.IntArrayBoxes.AddRange(mapper.intArrayBoxes.GetValues());
bundle.UnsignedIntArrayBoxes.AddRange(mapper.unsignedIntArrayBoxes.GetValues());
bundle.LongArrayBoxes.AddRange(mapper.longArrayBoxes.GetValues());
bundle.UnsignedLongArrayBoxes.AddRange(mapper.unsignedLongArrayBoxes.GetValues());
bundle.FloatArrayBoxes.AddRange(mapper.floatArrayBoxes.GetValues());
bundle.DoubleArrayBoxes.AddRange(mapper.doubleArrayBoxes.GetValues());
bundle.DictionaryBoxes.AddRange(mapper.dictionaryBoxes.GetValues());
bundle.StorableClassBoxes.AddRange(mapper.storableClassBoxes.GetValues());
return bundle;
}
public static object ToObject(Bundle bundle) {
var mapper = new Mapper();
mapper.transformers = new Index(bundle.TransformerGuids.Select(x => new Guid(x.ToByteArray())).Select(StaticCache.GetTransformer));
mapper.types = new Index(bundle.TypeGuids.Select(x => StaticCache.GetType(new Guid(x.ToByteArray()))));
mapper.boxId2Box = bundle.Boxes.Select((b, i) => new { Box = b, Index = i }).ToDictionary(k => (uint)k.Index + 1, v => v.Box);
mapper.typeBoxes = new Index(bundle.TypeBoxes);
mapper.strings = new Index(bundle.Strings);
mapper.boolArrayBoxes = new Index(bundle.BoolArrayBoxes);
mapper.intArrayBoxes = new Index(bundle.IntArrayBoxes);
mapper.unsignedIntArrayBoxes = new Index(bundle.UnsignedIntArrayBoxes);
mapper.longArrayBoxes = new Index(bundle.LongArrayBoxes);
mapper.unsignedLongArrayBoxes = new Index(bundle.UnsignedLongArrayBoxes);
mapper.floatArrayBoxes = new Index(bundle.FloatArrayBoxes);
mapper.doubleArrayBoxes = new Index(bundle.DoubleArrayBoxes);
mapper.dictionaryBoxes = new Index(bundle.DictionaryBoxes);
mapper.storableClassBoxes = new Index(bundle.StorableClassBoxes);
return mapper.GetObject(bundle.RootBoxId);
}
}
}