#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 strings; 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(); strings = 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; } #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.Strings.AddRange(mapper.strings.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.strings = new Index(bundle.Strings); return mapper.GetObject(bundle.RootBoxId); } } }