using System.Collections.Generic; using System.Collections; using System; using System.Linq; using HeuristicLab.Persistence.Auxiliary; using HeuristicLab.Persistence.Interfaces; using HeuristicLab.Persistence.Core.Tokens; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; using System.Text; using System.Reflection; using System.IO; namespace HeuristicLab.Persistence.Core { public class Serializer : IEnumerable { class ReferenceEqualityComparer : IEqualityComparer { public new bool Equals(object a, object b) { return Object.ReferenceEquals(a, b); } public int GetHashCode(object obj) { if (obj == null) return 0; return obj.GetHashCode(); } } private readonly object obj; private readonly string rootName; private readonly Dictionary obj2id; private readonly Dictionary typeCache; private readonly Configuration configuration; private readonly bool isTestRun; private readonly List exceptions; public List TypeCache { get { BuildTypeCache(); return externalTypeCache; } } public List RequiredFiles { get { BuildTypeCache(); return requiredFiles; } } private List externalTypeCache; private List requiredFiles; private void BuildTypeCache() { externalTypeCache = new List(); Dictionary assemblies = new Dictionary(); foreach (var pair in typeCache) { string serializer = null; IPrimitiveSerializer f = configuration.GetPrimitiveSerializer(pair.Key); if (f != null) { serializer = f.GetType().AssemblyQualifiedName; assemblies[f.GetType().Assembly] = true; } else { ICompositeSerializer d = configuration.GetCompositeSerializer(pair.Key); serializer = d.GetType().AssemblyQualifiedName; assemblies[d.GetType().Assembly] = true; } externalTypeCache.Add(new TypeMapping(pair.Value, pair.Key.AssemblyQualifiedName, serializer)); assemblies[pair.Key.Assembly] = true; } Dictionary files = new Dictionary(); foreach (Assembly a in assemblies.Keys) { files[a.CodeBase] = true; } requiredFiles = new List(files.Keys); } public Serializer(object obj, Configuration configuration) : this(obj, configuration, "ROOT") { } public Serializer(object obj, Configuration configuration, string rootName) : this(obj, configuration, rootName, false) { } public Serializer(object obj, Configuration configuration, string rootName, bool isTestRun) { this.obj = obj; this.rootName = rootName; this.configuration = configuration; obj2id = new Dictionary(new ReferenceEqualityComparer()) { { new object(), 0 } }; typeCache = new Dictionary(); this.isTestRun = isTestRun; this.exceptions = new List(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public IEnumerator GetEnumerator() { var enumerator = Serialize(new DataMemberAccessor(rootName, null, () => obj, null)); if (isTestRun) { return AddExceptionCompiler(enumerator); } else { return enumerator; } } public IEnumerator AddExceptionCompiler(IEnumerator enumerator) { while (enumerator.MoveNext()) yield return enumerator.Current; if (exceptions.Count == 1) throw exceptions[0]; if (exceptions.Count > 1) throw new PersistenceException("Multiple exceptions during serialization", exceptions); } private IEnumerator Serialize(DataMemberAccessor accessor) { object value = accessor.Get(); if (value == null) return NullReferenceEnumerator(accessor.Name); Type type = value.GetType(); if (obj2id.ContainsKey(value)) return ReferenceEnumerator(accessor.Name, obj2id[value]); if (!typeCache.ContainsKey(type)) typeCache.Add(type, typeCache.Count); int typeId = typeCache[type]; int? id = null; if (!type.IsValueType) { id = obj2id.Count; obj2id.Add(value, (int)id); } try { IPrimitiveSerializer primitiveSerializer = configuration.GetPrimitiveSerializer(type); if (primitiveSerializer != null) return PrimitiveEnumerator(accessor.Name, typeId, primitiveSerializer.Format(value), id); ICompositeSerializer compositeSerializer = configuration.GetCompositeSerializer(type); if (compositeSerializer != null) return CompositeEnumerator(accessor.Name, compositeSerializer.Decompose(value), id, typeId, compositeSerializer.CreateMetaInfo(value)); throw new PersistenceException( String.Format( "No suitable method for serializing values of type \"{0}\" found\r\n" + "primitive serializers:\r\n{1}\r\n" + "composite serializers:\r\n{2}", value.GetType().VersionInvariantName(), string.Join("\r\n", configuration.PrimitiveSerializers.Select(f => f.GetType().VersionInvariantName()).ToArray()), string.Join("\r\n", configuration.CompositeSerializers.Select(d => d.GetType().VersionInvariantName()).ToArray()) )); } catch (Exception x) { if (isTestRun) { exceptions.Add(x); return new List().GetEnumerator(); } else { throw x; } } } private IEnumerator NullReferenceEnumerator(string name) { yield return new NullReferenceToken(name); } private IEnumerator ReferenceEnumerator(string name, int id) { yield return new ReferenceToken(name, id); } private IEnumerator PrimitiveEnumerator(string name, int typeId, ISerialData serializedValue, int? id) { yield return new PrimitiveToken(name, typeId, id, serializedValue); } private IEnumerator CompositeEnumerator( string name, IEnumerable tags, int? id, int typeId, IEnumerable metaInfo) { yield return new BeginToken(name, typeId, id); bool first = true; foreach (var tag in metaInfo) { IEnumerator metaIt = Serialize(new DataMemberAccessor(tag.Value, tag.Name)); while (metaIt.MoveNext()) { if (first) { yield return new MetaInfoBeginToken(); first = false; } yield return metaIt.Current; } } if (!first) { yield return new MetaInfoEndToken(); } foreach (var tag in tags) { IEnumerator it = Serialize(new DataMemberAccessor(tag.Value, tag.Name)); while (it.MoveNext()) yield return it.Current; } yield return new EndToken(name, typeId, id); } } }