ln.type/rpc/RPCContainer.cs

175 lines
6.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
namespace ln.type.rpc
{
public class PublishAttribute : Attribute
{
}
public class RPCContainer
{
Dictionary<string, RPCModule> modules = new Dictionary<string, RPCModule>();
Func<RPCContainer, RPCModule, MethodInfo, bool> checkAccessHook;
public RPCContainer()
{
}
public RPCContainer(Func<RPCContainer, RPCModule, MethodInfo, bool> checkAccessHook)
{
this.checkAccessHook = checkAccessHook;
}
public void Add(object moduleInstance) => Add(moduleInstance.GetType().Name, moduleInstance);
public void Add(string moduleName,object moduleInstance)
{
if (modules.ContainsKey(moduleName))
throw new ArgumentOutOfRangeException(nameof(moduleName), "Module with same name already added");
RPCModule rpcModule = new RPCModule(this,moduleName,moduleInstance);
modules.Add(moduleName, rpcModule);
}
public void Remove(string moduleName)
{
modules.Remove(moduleName);
}
public void Remove(object moduleInstance)
{
foreach (RPCModule module in modules.Values)
{
if (module.ModuleInstance == moduleInstance)
{
modules.Remove(module.Name);
return;
}
}
}
public RPCResult Invoke(RPCCall call)
{
if ((call.ModuleName != null) && !modules.ContainsKey(call.ModuleName))
throw new KeyNotFoundException(call.ModuleName);
RPCModule rpcModule = call.ModuleName != null ? modules[call.ModuleName] : modules[""];
return rpcModule.Invoke(call);
}
public class RPCModule
{
public RPCContainer Container { get; }
public String Name { get; }
public object ModuleInstance { get; }
public Dictionary<string, MethodInfo> methodInfos = new Dictionary<string, MethodInfo>();
public RPCModule(RPCContainer container,string moduleName,object instance)
{
Container = container;
Name = moduleName;
ModuleInstance = instance;
Initialize(ModuleInstance.GetType());
}
public RPCModule(RPCContainer container,string moduleName,Type type)
{
Container = container;
Name = moduleName;
ModuleInstance = null;
Initialize(type);
}
private void Initialize(Type type)
{
foreach (MethodInfo methodInfo in type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance))
{
if (methodInfo.IsStatic || !Object.ReferenceEquals(ModuleInstance,null) )
{
if (methodInfo.IsPublic || (methodInfo.GetCustomAttribute<PublishAttribute>() != null))
{
methodInfos.Add(methodInfo.Name, methodInfo);
}
}
}
}
public RPCResult Invoke(RPCCall call)
{
if (!Name.Equals(call.ModuleName) && (call.ModuleName != null))
throw new ArgumentOutOfRangeException(nameof(call.ModuleName), "RPC: Invoke called for wrong module");
try
{
if (!methodInfos.ContainsKey(call.MethodName))
throw new KeyNotFoundException(call.MethodName);
MethodInfo methodInfo = methodInfos[call.MethodName];
ParameterInfo[] parameterInfos = methodInfo.GetParameters();
object[] parameters = new object[parameterInfos.Length];
for (int n = 0; n < parameters.Length; n++)
{
Type pType = parameterInfos[n].ParameterType;
if (pType.Equals(typeof(Guid)))
{
parameters[n] = Guid.Parse(call.Parameters[n].ToString());
} else if (pType.IsArray)
{
Type eType = pType.GetElementType();
object[] src = (object[])call.Parameters[n];
Array a = Array.CreateInstance(eType, src.Length);
for (int m=0;m<src.Length;m++)
{
a.SetValue(Convert.ChangeType(src[m], eType), m);
}
parameters[n] = a;
}
else
{
parameters[n] = ImplicitCast(parameterInfos[n].ParameterType, call.Parameters[n]);
if (parameters[n] == null)
parameters[n] = Convert.ChangeType(call.Parameters[n], parameterInfos[n].ParameterType);
}
}
object result = null;
if ((Container.checkAccessHook == null) || Container.checkAccessHook(Container,this,methodInfo))
result = methodInfo.Invoke(ModuleInstance, parameters);
return new RPCResult(call, result);
} catch (Exception e)
{
return new RPCResult(call, e);
}
}
public object ImplicitCast(Type targetType,object value)
{
foreach (MethodInfo methodInfo in targetType.GetMethods(BindingFlags.Public | BindingFlags.Static))
{
if (methodInfo.Name.Equals("op_Implicit") && methodInfo.ReturnType.Equals(targetType))
{
ParameterInfo parameterInfo = methodInfo.GetParameters().FirstOrDefault();
if ((parameterInfo != null)&&(parameterInfo.ParameterType.Equals(value.GetType())))
{
return methodInfo.Invoke(null, new object[] { value });
}
}
}
return null;
}
}
}
}