public TypeInfo(Type type) { Type = type; StorableTypeAttribute = StorableTypeAttribute.GetStorableTypeAttribute(type); StorableTypeAttributeGuid = string.Empty; Fields = Enumerable.Empty <ComponentInfo <FieldInfo> >(); WriteableProperties = Enumerable.Empty <ComponentInfo <PropertyInfo> >(); ReadableProperties = Enumerable.Empty <ComponentInfo <PropertyInfo> >(); BeforeSerializationHooks = Enumerable.Empty <MethodInfo>(); AfterDeserializationHooks = Enumerable.Empty <MethodInfo>(); Used = 0; Reflect(); }
public void UpdateRegisteredTypes() { foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) { if (asm.IsDynamic) { continue; } if (cachedAssemblies.Contains(asm.FullName)) { continue; } cachedAssemblies.Add(asm.FullName); foreach (var t in asm.GetTypes()) { if (typeof(ITransformer).IsAssignableFrom(t) && !t.IsAbstract) { var transformer = (ITransformer)Activator.CreateInstance(t); RegisterTransformer(transformer); } } foreach (var t in asm.GetTypes()) { if (StorableTypeAttribute.IsStorableType(t)) { type2Guid.Add(t, StorableTypeAttribute.GetStorableTypeAttribute(t).Guid); foreach (var guid in StorableTypeAttribute.GetStorableTypeAttribute(t).Guids) { if (guid2Type.ContainsKey(guid)) { throw new PersistenceException($"The GUID {guid} is already used by type {guid2Type[guid]}.", t); } guid2Type.Add(guid, t); } } else if (typeof(IStorableTypeMap).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract) { var knownTypeMap = (IStorableTypeMap)Activator.CreateInstance(t); foreach (var tup in knownTypeMap.KnownStorableTypes) { RegisterType(tup.Item1, tup.Item2); } } } } }
public static StorableTypeAttribute GetStorableTypeAttribute(Type type) { StorableTypeAttribute attrib = null; if (!nonStorableTypes.Contains(type) && !attributeCache.TryGetValue(type, out attrib)) { attrib = (StorableTypeAttribute)GetCustomAttribute(type, typeof(StorableTypeAttribute), false); if (attrib != null) { attributeCache[type] = attrib; } else { nonStorableTypes.Add(type); } } return(attrib); }
protected override void Populate(Box box, object value, Mapper mapper) { var kvpBox = new RepeatedValueBox(); kvpBox.Kvps = new RepeatedKeyValuePairsBox(); box.Values = kvpBox; var keys = kvpBox.Kvps.Keys; var values = kvpBox.Kvps.Values; foreach (DictionaryEntry item in (IDictionary)value) { if (mapper.CancellationToken.IsCancellationRequested) { return; } keys.Add(mapper.GetBoxId(item.Key)); values.Add(mapper.GetBoxId(item.Value)); } var type = value.GetType(); var propertyInfo = type.GetProperty("Comparer"); var comparer = propertyInfo.GetValue(value); var comparerType = comparer.GetType(); if (StorableTypeAttribute.IsStorableType(comparerType)) { kvpBox.ComparerId = mapper.GetBoxId(comparer); } else if (comparerType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).Any()) { throw new NotSupportedException("Cannot serialize non-storable equality comparers with fields"); } else { kvpBox.ComparerTypeId = mapper.GetTypeMetadataId(comparerType, transformer: null); // there is no transformer for the comparer type } }
private static void ExecuteAfterDeserializationHooks(IEnumerator <object> e) { var emptyArgs = new object[0]; while (e.MoveNext()) { var obj = e.Current; if (obj == null || !StorableTypeAttribute.IsStorableType(obj.GetType())) { continue; } var typeList = new LinkedList <Type>(); for (var type = obj.GetType(); type != null; type = type.BaseType) { typeList.AddFirst(type); } foreach (var type in typeList) { if (!StorableTypeAttribute.IsStorableType(type)) { continue; } var typeInfo = StaticCache.GetTypeInfo(type); foreach (var hook in typeInfo.AfterDeserializationHooks) { try { hook.Invoke(obj, emptyArgs); } catch (TargetInvocationException t) { throw t.InnerException; } } } } }
protected override void Populate(Box box, object value, Mapper mapper) { box.Values = new RepeatedValueBox(); var type = value.GetType(); var propertyInfo = type.GetProperty("Comparer"); var comparer = propertyInfo.GetValue(value); var comparerType = comparer.GetType(); if (StorableTypeAttribute.IsStorableType(comparerType)) { box.Values.ComparerId = mapper.GetBoxId(comparer); } else if (comparerType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).Any()) { throw new NotSupportedException("Cannot serialize non-storable equality comparers with fields"); } else { box.Values.ComparerTypeId = mapper.GetTypeMetadataId(comparerType, transformer: null); // there is no transformer for the comparer type } AddRange((IEnumerable)value, box.Values, mapper); }
private void Reflect() { var type = Type; if (StorableTypeAttribute != null) { StorableTypeAttributeGuid = StorableTypeAttribute.Guid.ToString().ToUpperInvariant(); string guidPrefix = StorableTypeAttributeGuid; // check constructors if (!type.IsValueType && !type.IsEnum && !type.IsInterface && GetStorableConstructor() == null && GetDefaultConstructor() == null) { throw new PersistenceException("No storable constructor or parameterless constructor found.", type); } var fields = new List <ComponentInfo <FieldInfo> >(); var properties = new List <ComponentInfo <PropertyInfo> >(); var beforeSerializationHooks = new List <MethodInfo>(); var afterDeserializationHooks = new List <MethodInfo>(); if (StorableTypeAttribute.MemberSelection != StorableMemberSelection.AllProperties) { // TODO: improved performance for static fields var fieldInfos = type.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic) .Where(x => !x.Name.StartsWith("<") && !x.Name.EndsWith("k__BackingField")); // exclude backing fields if (StorableTypeAttribute.MemberSelection == StorableMemberSelection.MarkedOnly) { fieldInfos = fieldInfos.Where(StorableAttribute.IsStorable).ToArray(); } foreach (var field in fieldInfos) { var name = field.Name; var attrib = StorableAttribute.GetStorableAttribute(field); if (attrib != null) { if (!string.IsNullOrEmpty(attrib.Name) && !string.IsNullOrEmpty(attrib.OldName)) { throw new PersistenceException($"Field {field.Name} cannot use Name and OldName at the same time.", type); } if (!string.IsNullOrEmpty(attrib.Name)) { name = attrib.Name; } else if (!string.IsNullOrEmpty(attrib.OldName)) { name = attrib.OldName; } } var nameParts = name.Split('.').ToArray(); var sourceType = type; var tmpGuid = Guid.Empty; for (int i = 0; i < nameParts.Length; i++) { var part = nameParts[i]; if (part == "base") { sourceType = sourceType.BaseType; } else if (Guid.TryParse(part, out tmpGuid)) { if (i != 0 || nameParts.Length != 2) { throw new PersistenceException($"Field {field.Name} has an invalid path.", type); } guidPrefix = tmpGuid.ToString().ToUpper(); break; } else if (i != nameParts.Length - 1) { throw new PersistenceException($"Field {field.Name} has an invalid path.", type); } else { break; } } if (sourceType != type) { name = nameParts[nameParts.Length - 1]; guidPrefix = StorableTypeAttribute.GetStorableTypeAttribute(sourceType).Guid.ToString().ToUpperInvariant(); } else if (tmpGuid != Guid.Empty) { name = nameParts[nameParts.Length - 1]; } fields.Add(new ComponentInfo <FieldInfo>(name, guidPrefix + "." + name, field, type, attrib, true, true)); } } if (StorableTypeAttribute.MemberSelection != StorableMemberSelection.AllFields) { // TODO: improved performance for static properties var propertyInfos = type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic) .Where(x => !x.GetIndexParameters().Any()); // exclude indexed properties if (StorableTypeAttribute.MemberSelection == StorableMemberSelection.MarkedOnly) { propertyInfos = propertyInfos.Where(StorableAttribute.IsStorable).ToArray(); } foreach (var property in propertyInfos) { var name = property.Name; var attrib = StorableAttribute.GetStorableAttribute(property); if (attrib != null) { if (!string.IsNullOrEmpty(attrib.Name) && !string.IsNullOrEmpty(attrib.OldName)) { throw new PersistenceException($"Property {property.Name} cannot use Name and OldName at the same time.", type); } if (attrib.AllowOneWay && !string.IsNullOrEmpty(attrib.OldName)) { throw new PersistenceException($"Property {property.Name} cannot use AllowOneWay and OldName at the same time.", type); } if (!string.IsNullOrEmpty(attrib.Name)) { name = attrib.Name; } else if (!string.IsNullOrEmpty(attrib.OldName)) { name = attrib.OldName; } } if ((!property.CanRead || !property.CanWrite) && (attrib == null || !attrib.AllowOneWay && string.IsNullOrEmpty(attrib.OldName))) { throw new PersistenceException($"Property {property.Name} must be readable and writable or have one way serialization explicitly enabled or use OldName.", type); } var nameParts = name.Split('.').ToArray(); var sourceType = type; var tmpGuid = Guid.Empty; for (int i = 0; i < nameParts.Length; i++) { var part = nameParts[i]; if (part == "base") { sourceType = sourceType.BaseType; } else if (Guid.TryParse(part, out tmpGuid)) { if (i != 0 || nameParts.Length != 2) { throw new PersistenceException($"Property {property.Name} has an invalid path.", type); } guidPrefix = tmpGuid.ToString().ToUpper(); break; } else if (i != nameParts.Length - 1) { throw new PersistenceException($"Property {property.Name} has an invalid path.", type); } else { break; } } if (sourceType != type) { name = nameParts[nameParts.Length - 1]; guidPrefix = StorableTypeAttribute.GetStorableTypeAttribute(sourceType).Guid.ToString().ToUpper(); } else if (tmpGuid != Guid.Empty) { name = nameParts[nameParts.Length - 1]; } var declaringType = GetPropertyDeclaringBaseType(property); properties.Add(new ComponentInfo <PropertyInfo>(name, guidPrefix + "." + name, property, declaringType, attrib, property.CanRead, property.CanWrite)); } } var methodInfos = type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic) .Where(StorableHookAttribute.IsStorableHook) .Where(x => x.ReturnType == typeof(void) && !x.GetParameters().Any()); foreach (var method in methodInfos) { foreach (var attrib in StorableHookAttribute.GetStorableHookAttributes(method)) { if (attrib.HookType == HookType.BeforeSerialization) { beforeSerializationHooks.Add(method); } if (attrib.HookType == HookType.AfterDeserialization) { afterDeserializationHooks.Add(method); } } } Fields = fields; WriteableProperties = properties.Where(p => p.Writeable).ToArray(); ReadableProperties = properties.Where(p => p.Readable).ToArray(); BeforeSerializationHooks = beforeSerializationHooks; AfterDeserializationHooks = afterDeserializationHooks; } }
protected override void Populate(Box box, object value, Mapper mapper) { if (mapper.CancellationToken.IsCancellationRequested) { return; } var emptyArgs = new object[0]; var type = value.GetType(); // traverse type hierarchy var typeInfo = Mapper.StaticCache.GetTypeInfo(type); do { foreach (var hook in typeInfo.BeforeSerializationHooks) { try { hook.Invoke(value, emptyArgs); } catch (TargetInvocationException t) { throw t.InnerException; } } type = type.BaseType; typeInfo = Mapper.StaticCache.GetTypeInfo(type); } while (StorableTypeAttribute.IsStorableType(type) && !mapper.CancellationToken.IsCancellationRequested); var set = new HashSet <Tuple <Type, string> >(); // traverse type hierarchy var membersBox = new StorableTypeMembersBox(); box.Members = membersBox; type = value.GetType(); typeInfo = Mapper.StaticCache.GetTypeInfo(type); membersBox.StorableTypeMetadataId = mapper.GetStorableTypeMetadata(typeInfo.StorableTypeAttributeGuid); var layout = mapper.GetStorableTypeLayout(membersBox.StorableTypeMetadataId); while (StorableTypeAttribute.IsStorableType(type) && !mapper.CancellationToken.IsCancellationRequested) { foreach (var componentInfo in typeInfo.Fields) { if (!layout.IsPopulated) { layout.MemberNames.Add(componentInfo.Name); } membersBox.ValueBoxId.Add(mapper.GetBoxId(componentInfo.MemberInfo.GetValue(value))); } foreach (var componentInfo in typeInfo.ReadableProperties) { var declaringType = componentInfo.DeclaringType; if (!set.Add(Tuple.Create(declaringType, componentInfo.Name))) { continue; } if (!layout.IsPopulated) { layout.MemberNames.Add(componentInfo.Name); } membersBox.ValueBoxId.Add(mapper.GetBoxId(componentInfo.MemberInfo.GetValue(value, null))); } layout.IsPopulated = true; // prepare for next iteration type = type.BaseType; if (StorableTypeAttribute.IsStorableType(type)) { typeInfo = Mapper.StaticCache.GetTypeInfo(type); layout.ParentLayoutId = mapper.GetStorableTypeMetadata(typeInfo.StorableTypeAttributeGuid); layout = mapper.GetStorableTypeLayout(layout.ParentLayoutId); } } }
public override bool CanTransformType(Type type) { return(StorableTypeAttribute.IsStorableType(type) && !type.IsValueType && !type.IsEnum || // don't transform structs or enums type.BaseType != null && CanTransformType(type.BaseType)); }
public override void FillFromBox(object obj, Box box, Mapper mapper) { if (mapper.CancellationToken.IsCancellationRequested) { return; } var dict = new Dictionary <string, object>(); var members = box.Members; var layout = mapper.GetStorableTypeLayout(members.StorableTypeMetadataId); var valueIdx = 0; while (layout != null && !mapper.CancellationToken.IsCancellationRequested) { for (int j = 0; j < layout.MemberNames.Count; j++) { string key = mapper.GetComponentInfoKey(layout.TypeGuid, layout.MemberNames[j]); object value = mapper.GetObject(members.ValueBoxId[valueIdx++]); dict.Add(key, value); } layout = mapper.GetStorableTypeLayout(layout.ParentLayoutId); } var type = mapper.StorableTypeMetadataToType(mapper.GetTypeMetadata(box.TypeMetadataId)); var typeInfo = Mapper.StaticCache.GetTypeInfo(type); var typeStack = new Stack <Tuple <Type, TypeInfo> >(); do { typeInfo = Mapper.StaticCache.GetTypeInfo(type); typeStack.Push(Tuple.Create(type, typeInfo)); type = type.BaseType; } while (StorableTypeAttribute.IsStorableType(type) && !mapper.CancellationToken.IsCancellationRequested); foreach (var frame in typeStack) { type = frame.Item1; typeInfo = frame.Item2; // set stored or default values for all fields and properties foreach (var componentInfo in typeInfo.Fields) { var attrib = componentInfo.StorableAttribute; var fieldInfo = componentInfo.MemberInfo; if (dict.TryGetValue(componentInfo.FullName, out object value)) { fieldInfo.SetValue(obj, value); } else if (attrib != null && attrib.DefaultValue != null) { fieldInfo.SetValue(obj, attrib.DefaultValue); } } foreach (var componentInfo in typeInfo.WriteableProperties) { var attrib = componentInfo.StorableAttribute; var propertyInfo = componentInfo.MemberInfo; if (dict.TryGetValue(componentInfo.FullName, out object value)) { propertyInfo.SetValue(obj, value, null); } else if (attrib != null && attrib.DefaultValue != null) { propertyInfo.SetValue(obj, attrib.DefaultValue, null); } } } }