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;
}
}
}
}