#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.ProtocolBuffers; 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 = null; 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 Index strings; private Dictionary boxId2Box; private Dictionary object2BoxId; private Dictionary boxId2object; public uint BoxCount { get; private set; } public Mapper() { transformers = new Index(); types = new Index(); strings = new Index(); boxId2Box = new Dictionary(); object2BoxId = new Dictionary(new MappingEqualityComparer()); boxId2object = new Dictionary(); BoxCount = 0; } public uint GetTransformerId(ITransformer transformer) { return transformers.GetIndex(transformer); } public ITransformer GetTransformer(uint transformerId) { return transformers.GetValue(transformerId); } public uint GetTypeId(Type type) { return types.GetIndex(type); } public Type GetType(uint typeId) { return types.GetValue(typeId); } public uint GetStringId(string str) { return strings.GetIndex(str); } public string GetString(uint stringId) { return strings.GetValue(stringId); } 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 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; } public object CreateInstance(Type type) { return StaticCache.GetTypeInfo(type).GetConstructor()(); } public static Bundle ToBundle(object o) { var mapper = new Mapper(); var bundle = Bundle.CreateBuilder(); bundle.RootBoxId = mapper.GetBoxId(o); bundle.AddRangeTransformerGuids(mapper.transformers.GetValues().Select(x => x.Guid).Select(x => ByteString.CopyFrom(x.ToByteArray()))); bundle.AddRangeTypeGuids(mapper.types.GetValues().Select(x => ByteString.CopyFrom(StaticCache.GetGuid(x).ToByteArray()))); bundle.AddRangeStrings(mapper.strings.GetValues()); bundle.AddRangeBoxes(mapper.boxId2Box.OrderBy(x => x.Key).Select(x => x.Value)); return bundle.Build(); } public static object ToObject(Bundle bundle) { var mapper = new Mapper(); mapper.types = new Index(bundle.TypeGuidsList.Select(x => StaticCache.GetType(new Guid(x.ToByteArray())))); mapper.strings = new Index(bundle.StringsList); mapper.boxId2Box = bundle.BoxesList.Select((b, i) => new { Box = b, Index = i }).ToDictionary(k => (uint)k.Index + 1, v => v.Box); mapper.transformers = new Index(bundle.TransformerGuidsList.Select(x => new Guid(x.ToByteArray())).Select(x => StaticCache.GetTransformer(x))); return mapper.GetObject(bundle.RootBoxId); } } }