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

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