You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
280 lines
9.9 KiB
280 lines
9.9 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
|
|
namespace Apewer.Web
|
|
{
|
|
|
|
/// <summary>入口集合。</summary>
|
|
public sealed class ApiEntries
|
|
{
|
|
|
|
#region 实例。
|
|
|
|
object locker = new object();
|
|
|
|
Dictionary<string, ApiApplication> Applications = null;
|
|
List<ApiApplication> Items = null;
|
|
|
|
internal ApiApplication Get(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(name)) return null;
|
|
if (Applications == null) return null;
|
|
var lower = name.ToLower();
|
|
ApiApplication app;
|
|
var found = false;
|
|
lock (locker) { found = Applications.TryGetValue(lower, out app); }
|
|
return found ? app : null;
|
|
}
|
|
|
|
internal List<ApiApplication> Enumerate() => Items;
|
|
|
|
/// <summary>清空当前实例。</summary>
|
|
public void Clear()
|
|
{
|
|
lock (locker)
|
|
{
|
|
Applications = new Dictionary<string, ApiApplication>();
|
|
Items = new List<ApiApplication>();
|
|
}
|
|
}
|
|
|
|
/// <summary>追加指定的集合,指定 replace 参数将替换当前实例中的同名的入口。</summary>
|
|
public void Append(ApiEntries entries, bool replace = false)
|
|
{
|
|
if (entries == null) return;
|
|
lock (locker)
|
|
{
|
|
var dict = Applications ?? new Dictionary<string, ApiApplication>();
|
|
foreach (var app in entries.Applications)
|
|
{
|
|
var key = app.Key;
|
|
if (dict.ContainsKey(key))
|
|
{
|
|
if (replace) dict[key] = app.Value;
|
|
continue;
|
|
}
|
|
dict.Add(app.Key, app.Value);
|
|
}
|
|
var list = new List<ApiApplication>(dict.Values);
|
|
list.Sort(new Comparison<ApiApplication>((a, b) => a.Lower.CompareTo(b.Lower)));
|
|
Applications = dict;
|
|
Items = list;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 静态方法。
|
|
|
|
/// <summary>从指定的程序集获取入口。</summary>
|
|
public static ApiEntries From(Assembly assembly)
|
|
{
|
|
if (assembly == null) return null;
|
|
var types = RuntimeUtility.GetTypes(assembly, false);
|
|
var dict = new Dictionary<string, ApiApplication>();
|
|
foreach (var type in types)
|
|
{
|
|
var app = Application(type, true);
|
|
if (app == null) continue;
|
|
var lower = app.Lower;
|
|
if (dict.ContainsKey(lower)) continue;
|
|
dict.Add(lower, app);
|
|
|
|
var funcs = new Dictionary<string, ApiFunction>();
|
|
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public);
|
|
foreach (var method in methods)
|
|
{
|
|
var func = Function(app, method);
|
|
if (func == null) continue;
|
|
if (funcs.ContainsKey(func.Lower)) continue;
|
|
funcs.Add(func.Lower, func);
|
|
}
|
|
app.Functions = funcs;
|
|
app.Items = new List<ApiFunction>(funcs.Values);
|
|
app.Items.Sort(new Comparison<ApiFunction>((a, b) => a.Name.CompareTo(b.Name)));
|
|
}
|
|
var entries = new ApiEntries();
|
|
entries.Applications = dict;
|
|
var list = new List<ApiApplication>(dict.Values);
|
|
list.Sort(new Comparison<ApiApplication>((a, b) => a.Lower.CompareTo(b.Lower)));
|
|
entries.Items = list;
|
|
return entries;
|
|
}
|
|
|
|
/// <summary>从多个程序集中获取入口。</summary>
|
|
public static ApiEntries From(IEnumerable<Assembly> assemblies, bool replace = false)
|
|
{
|
|
if (assemblies == null) return null;
|
|
var entries = new ApiEntries();
|
|
foreach (var assembly in assemblies) entries.Append(From(assembly), replace);
|
|
return entries;
|
|
}
|
|
|
|
/// <summary>从当前程序中获取入口。 </summary>
|
|
public static ApiEntries Calling() => From(Assembly.GetCallingAssembly());
|
|
|
|
/// <summary>从当前 AppDomain 中获取入口。</summary>
|
|
public static ApiEntries AppDomain(bool replace = false) => From(System.AppDomain.CurrentDomain.GetAssemblies(), replace);
|
|
|
|
static ApiApplication Application(Type type, bool requireAttribute)
|
|
{
|
|
if (type == null) return null;
|
|
|
|
// 检查类型的属性。
|
|
if (!type.IsClass) return null;
|
|
if (type.IsAbstract) return null;
|
|
if (type.IsGenericType) return null;
|
|
if (type.GetGenericArguments().NotEmpty()) return null;
|
|
if (!RuntimeUtility.CanNew(type)) return null;
|
|
|
|
// 检查类型的特性。
|
|
var apis = type.GetCustomAttributes(typeof(ApiAttribute), false);
|
|
var api = apis.Length > 0 ? (ApiAttribute)apis[0] : null;
|
|
if (requireAttribute && api == null) return null;
|
|
|
|
// 检查基类。
|
|
if (!RuntimeUtility.IsInherits(type, typeof(ApiController))) return null;
|
|
|
|
// Entry
|
|
var entry = new ApiApplication();
|
|
entry.Type = type;
|
|
entry.Attribute = api;
|
|
|
|
// Attribute
|
|
if (api != null)
|
|
{
|
|
entry.Name = string.IsNullOrEmpty(api.Name) ? type.Name : api.Name;
|
|
entry.Lower = entry.Name.ToLower();
|
|
var name = api.Name;
|
|
if (string.IsNullOrEmpty(name)) name = type.Name;
|
|
|
|
entry.Caption = api.Caption;
|
|
entry.Description = api.Description;
|
|
}
|
|
else
|
|
{
|
|
entry.Name = type.Name;
|
|
entry.Lower = entry.Name.ToLower();
|
|
entry.Caption = null;
|
|
entry.Description = null;
|
|
entry.Hidden = true;
|
|
}
|
|
|
|
// Caption
|
|
if (string.IsNullOrEmpty(entry.Caption))
|
|
{
|
|
var captions = type.GetCustomAttributes(typeof(CaptionAttribute), true);
|
|
if (captions.Length > 0)
|
|
{
|
|
var caption = (CaptionAttribute)captions[0];
|
|
entry.Caption = caption.Title;
|
|
entry.Description = caption.Description;
|
|
}
|
|
}
|
|
|
|
// Hidden
|
|
if (type.Contains<HiddenAttribute>(false)) entry.Hidden = true;
|
|
|
|
// Independent
|
|
if (type.Contains<IndependentAttribute>(false)) entry.Independent = true;
|
|
|
|
// Module
|
|
var assemblyName = type.Assembly.GetName();
|
|
entry.Module = TextUtility.Join("-", assemblyName.Name, assemblyName.Version.ToString());
|
|
|
|
return entry;
|
|
}
|
|
|
|
static ApiFunction Function(ApiApplication application, MethodInfo method)
|
|
{
|
|
if (application == null) return null;
|
|
if (method == null) return null;
|
|
|
|
// 滤除构造函数、抽象方法、泛型和非本类定义方法。
|
|
if (method.IsConstructor) return null;
|
|
if (method.IsAbstract) return null;
|
|
if (method.GetGenericArguments().NotEmpty()) return null;
|
|
|
|
// 滤除 get 和 set 访问器。
|
|
var methodName = method.Name;
|
|
if (methodName.StartsWith("get_") || methodName.StartsWith("set_")) return null;
|
|
|
|
// 定义者。
|
|
var declaring = method.DeclaringType;
|
|
if (declaring.Equals(typeof(object))) return null;
|
|
|
|
// 检查 ApiAttribute 特性。
|
|
var apis = method.GetCustomAttributes(typeof(ApiAttribute), false);
|
|
var api = apis.Length > 0 ? (ApiAttribute)apis[0] : null;
|
|
|
|
// Entry
|
|
var entry = new ApiFunction();
|
|
entry.Application = application;
|
|
entry.Method = method;
|
|
|
|
// 返回值。
|
|
var returnable = method.ReturnType;
|
|
if (returnable.Equals(typeof(void))) returnable = null;
|
|
entry.Returnable = returnable;
|
|
|
|
// 参数。
|
|
var pis = method.GetParameters();
|
|
if (pis != null && pis.Length > 0)
|
|
{
|
|
var pisc = pis.Length;
|
|
for (var i = 0; i < pisc; i++)
|
|
{
|
|
var pi = pis[i];
|
|
if (pi.IsIn) return null;
|
|
if (pi.IsOut) return null;
|
|
}
|
|
entry.Parameters = pis;
|
|
|
|
if (pisc == 1)
|
|
{
|
|
var pi = pis[0];
|
|
var pt = pi.ParameterType;
|
|
if (RuntimeUtility.IsInherits(pt, typeof(Source.Record))) entry.ParamIsRecord = true;
|
|
}
|
|
}
|
|
|
|
if (api != null)
|
|
{
|
|
entry.Name = string.IsNullOrEmpty(api.Name) ? method.Name : api.Name;
|
|
entry.Lower = entry.Name.ToLower();
|
|
entry.Caption = api.Caption;
|
|
entry.Description = api.Description;
|
|
}
|
|
else
|
|
{
|
|
entry.Name = method.Name;
|
|
entry.Lower = entry.Name.ToLower();
|
|
entry.Caption = null;
|
|
entry.Description = null;
|
|
}
|
|
entry.Name = method.Name;
|
|
entry.Lower = entry.Name.ToLower();
|
|
|
|
// Caption
|
|
var captions = method.GetCustomAttributes(typeof(CaptionAttribute), true);
|
|
if (captions.Length > 0)
|
|
{
|
|
var caption = (CaptionAttribute)captions[0];
|
|
entry.Caption = caption.Title;
|
|
entry.Description = caption.Description;
|
|
}
|
|
|
|
// Hidden
|
|
entry.Hidden = application.Hidden;
|
|
if (!entry.Hidden && method.Contains<HiddenAttribute>(false)) entry.Hidden = true;
|
|
|
|
return entry;
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
}
|
|
|