using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.ExceptionServices; namespace Ariadne.Vellum { /// /// Builds an arc through the constructor that loads it from a save. /// public static class ArcLoader { /// /// Returns the constructor loads itself through. /// /// The class has no such constructor. public static ConstructorInfo FindConstructor(Type arcType) { ConstructorInfo returnMe = arcType.GetConstructor( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, binder: null, types: new[] { typeof(IArcReader) }, modifiers: null); if (returnMe == null) { throw new InvalidOperationException(arcType.FullName + " has no constructor taking an IArcReader to " + "load it from a save. Is it partial, and does its assembly run Ariadne.Generator?"); } return returnMe; } /// /// Hands every field and local and its base classes load, /// base classes first, and returns true. Returns false if some class in the chain doesn't list its own. /// public static bool TryListParts(Type arcType, IArcPartLister lister) { var chain = new Stack(); for (Type type = arcType; type != null && !IsArcBase(type); type = type.BaseType) { chain.Push(type); } var listers = new List(); foreach (Type type in chain) { MethodInfo method = type.GetMethod("__ListParts", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly, binder: null, types: new[] { typeof(IArcPartLister) }, modifiers: null); if (method == null) { return false; } listers.Add(method); } foreach (MethodInfo method in listers) { method.Invoke(null, new object[] { lister }); } return true; } private static bool IsArcBase(Type type) { return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Arc<>); } /// /// Returns a new loaded from . /// /// The class has no loading constructor. public static object Create(Type arcType, IArcReader reader) { return Create(FindConstructor(arcType), reader); } /// /// Returns a new arc loaded from by , one /// found. /// public static object Create(ConstructorInfo constructor, IArcReader reader) { try { return constructor.Invoke(new object[] { reader }); } catch (TargetInvocationException exception) { // The caller wants the save's own error, not reflection's wrapper around it. ExceptionDispatchInfo.Capture(exception.InnerException).Throw(); throw; } } } }