#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); } } }