using System; using System.Collections.Generic; using System.Reflection; using System.Text; namespace Apewer.Web { /// 入口集合。 public sealed class ApiEntries { #region 实例。 object locker = new object(); Dictionary Applications = null; List 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 Enumerate() => Items; /// 清空当前实例。 public void Clear() { lock (locker) { Applications = new Dictionary(); Items = new List(); } } /// 追加指定的集合,指定 replace 参数将替换当前实例中的同名的入口。 public void Append(ApiEntries entries, bool replace = false) { if (entries == null) return; lock (locker) { var dict = Applications ?? new Dictionary(); 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(dict.Values); list.Sort(new Comparison((a, b) => a.Lower.CompareTo(b.Lower))); Applications = dict; Items = list; } } #endregion #region 静态方法。 /// 从指定的程序集获取入口。 public static ApiEntries From(Assembly assembly) { if (assembly == null) return null; var types = RuntimeUtility.GetTypes(assembly, false); var dict = new Dictionary(); 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(); 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(funcs.Values); app.Items.Sort(new Comparison((a, b) => a.Name.CompareTo(b.Name))); } var entries = new ApiEntries(); entries.Applications = dict; var list = new List(dict.Values); list.Sort(new Comparison((a, b) => a.Lower.CompareTo(b.Lower))); entries.Items = list; return entries; } /// 从多个程序集中获取入口。 public static ApiEntries From(IEnumerable 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; } /// 从当前程序中获取入口。 public static ApiEntries Calling() => From(Assembly.GetCallingAssembly()); /// 从当前 AppDomain 中获取入口。 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(false)) entry.Hidden = true; // Independent if (type.Contains(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(false)) entry.Hidden = true; return entry; } #endregion } }