UNPKG

jsdk-offical

Version:

JSDK is the most comprehensive TypeScript framework, like JDK.

491 lines (436 loc) 18.6 kB
/** * @project JSDK * @license MIT * @website https://github.com/fengboyue/jsdk * * @version 2.5.0 * @update Move to sugar package * * @version 2.0.0 * @author Frank.Feng */ /// <reference path="../util/Check.ts"/> /// <reference path="../util/Jsons.ts"/> /// <reference path="../util/Arrays.ts"/> /// <reference path="../util/Functions.ts"/> /// <reference path="../util/Strings.ts"/> /// <reference path="../util/Log.ts"/> /// <reference path="Annotation.ts"/> /// <reference path="ReflectInterface.ts"/> module JS { export namespace sugar { let Y = Types, J = Jsons; /** * The @klass annotation. */ export function klass(fullName: string): any { return Annotations.define({ name: 'klass', handler: (anno: string, values: Array<any>, obj: Function | object) => { Class.reflect(<Function>obj, values[0]); }, target: AnnotationTarget.CLASS }, [fullName]); } /** * The reflected class of a method. * @final */ export class Method { public readonly ownerClass: Class<any>; public readonly name: string; public readonly paramTypes: Array<Type>; public readonly returnType: Type; public readonly fn: Function; public readonly isStatic: boolean = false; public readonly annotations: string[] = []; public readonly parameterAnnotations: string[] = []; constructor(clazz: Class<any>, name: string, isStatic: boolean, fn: Function, paramTypes: Array<Type>, returnType: Type) { this.ownerClass = clazz; this.name = name; this.paramTypes = paramTypes; this.returnType = returnType; this.fn = fn; this.isStatic = isStatic; } public invoke(obj: object, ...args: Array<any>): any { let fn = this.isStatic ? this.ownerClass.getKlass() : this.fn, context = this.isStatic ? this.ownerClass.getKlass() : obj; return Reflect.apply(fn, context, args); } } /** * The reflected class of a field. * @final */ export class Field { public readonly ownerClass: Class<any>; public readonly name: string; public readonly type: Type; public readonly isStatic: boolean = false; public readonly annotations: string[] = []; constructor(clazz: Class<any>, name: string, isStatic: boolean, type: Type) { this.ownerClass = clazz; this.name = name; this.type = type; this.isStatic = isStatic; } public set(value: any, obj?: object): void { let target: any = this.isStatic ? this.ownerClass.getKlass() : obj; target[this.name] = value; } public get(obj?: object): any { let target: any = this.isStatic ? this.ownerClass.getKlass() : obj; return target[this.name]; } } /** * The reflected class for a class. * @final */ export class Class<T> { /** * Full name */ public readonly name: string; public readonly shortName: string; private _klass: T; private _superklass: T; private _methods: JsonObject<Method> = {}; private _fields: JsonObject<Field> = {}; /** * @constructor * @param name full name * @param klass class constructor */ constructor(name: string, klass: T) { this.name = name; (<any>klass).class = this; this._klass = klass; this.shortName = (<any>this._klass).name; this._superklass = <any>Class.getSuperklass(<any>this._klass); this._init(); } public static getSuperklass(klass: Klass<any>): Klass<any> { if (Object === klass) return null; let sup = Object.getPrototypeOf(klass); return <Klass<any>>Object.getPrototypeOf(Object) === sup ? Object : sup; } private static _reflectable(obj: Object, className: string): void { obj.className = className; if (!obj.getClass) {//如果没注解过,则动态创建此函数 obj.getClass = function (): Class<any> { return Class.forName(this.className); } } } /** * Converts class full name to class constructor */ public static byName(name: string): Klass<any> { if (!name) return null; var p = name.split('.'), len = p.length, p0 = p[0], b = window[p0] || eval(p0); if (!b) throw new TypeError('Can\'t found class:' + name); for (var i = 1; i < len; i++) { var pi = p[i]; if (!pi) break; b[pi] = b[pi] || {}; b = b[pi]; } return b; } /** * Returns a new instance of a class constructor or its fullname. */ public static newInstance<T>(ctor: string | Klass<T>, ...args): T { let tar = Y.isString(ctor) ? Class.byName(<string>ctor) : <Function>ctor; if (!tar) throw new NotFoundError(`The class<${ctor}> is not found!`); return <T>Reflect.construct(tar, J.clone(args)) } /** * Returns a new instance of a class constructor or its alias. */ public static aliasInstance<T>(alias: string | Klass<T>, ...args): T { let cls = Class.forName(alias, true); if (!cls) throw new NotFoundError(`The class<${alias}> is not found!`); return cls.newInstance.apply(cls, args) } /** * Aspect a method of a class. * @param klass class constructor * @param method method name * @param advisor */ public static aop<T>(klass: Klass<any>, method: string, advisor: AopAdvisor<T>) { let isStatic = klass.hasOwnProperty(method), m: Function = isStatic ? klass[method] : klass.prototype[method]; if (!Y.isFunction(m)) return; let obj = isStatic ? klass : klass.prototype; if (!obj.hasOwnProperty('__' + method)) obj['__' + method] = m; Object.defineProperty(obj, method, { value: m.aop(advisor), writable: true }) } /** * Cancel all advisors of a class method. * @param klass class constructor * @param method method name */ public static cancelAop(klass: Klass<any>, method: string) { let isStatic = klass.hasOwnProperty(method), m: Function = isStatic ? klass[method] : klass.prototype[method]; if (!Y.isFunction(m)) return; let obj = isStatic ? klass : klass.prototype; obj[method] = obj['__' + method]; } /** * Aspect a method of this class. * @param method method name * @param advisor */ public aop<T>(method: string, advisor: AopAdvisor<T>) { let m = this.method(method); if (!m) return; let pro = m.isStatic ? this._klass : (<any>this._klass).prototype; pro[method] = m.fn.aop(advisor); } private _cancelAop(m: Method) { let pro = m.isStatic ? this._klass : (<any>this._klass).prototype; pro[m.name] = m.fn; } /** * Cancel all advisors of this class method. * @param method method name */ public cancelAop(method?: string) { let ms = method ? [this.method(method)] : this.methods(); ms.forEach(m => { this._cancelAop(m); }) } /** * Equals a reflected Class. */ public equals(cls: Class<any>|Klass<any>): boolean { if (!cls) return false; return cls instanceof Class?this.getKlass() === cls.getKlass():this.getKlass() === cls; } /** * Is subclass of a class. */ public subclassOf(cls: Class<any> | Klass<any>): boolean { let klass: Klass<any> = (cls.constructor && cls.constructor === <any>Class) ? (<Class<any>>cls).getKlass() : <any>cls; return Y.subklassOf(this.getKlass(), klass); } /** * Returns a new instance of this class. */ public newInstance<T>(...args): T { let obj = Reflect.construct(<any>this._klass, Arrays.newArray(arguments)); Class._reflectable(obj, this.name); return <T>obj; } /** * Returns the super class. */ public getSuperclass(): Class<T> { if (this === Object.class) return null; return this._superklass ? (<any>this._superklass).class : Object.class; } /** * Returns the constructor of this class. */ public getKlass<T>(): Klass<T> { return (<Function><any>this._klass).prototype.constructor; } private _parseStaticMembers(ctor: Function) { let mKeys = ctor === Object ? ['class'] : Reflect.ownKeys(ctor); for (let i = 0, len = mKeys.length; i < len; i++) { const key = mKeys[i].toString(); if (!this._isValidStatic(key)) continue; const obj = ctor[key]; if (Y.isFunction(obj)) { this._methods[key] = new Method(this, key, true, <Function>obj, null, null); } else { this._fields[key] = new Field(this, key, true, Y.type(obj)); } } } private _parseInstanceMembers(proto: Function) { let protoKeys = proto === Object.prototype ? ['toString'] : Reflect.ownKeys(proto); for (let i = 0, len = protoKeys.length; i < len; i++) { const key = protoKeys[i].toString(); if (!this._isValidInstance(key)) continue; const obj = this._forceProto(proto, key); if (Y.isFunction(obj)) { this._methods[key] = new Method(this, key, false, <Function>obj, null, null); } else { this._fields[key] = new Field(this, key, false, Y.type(obj)); } } } //BUGFIX: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Called_on_incompatible_type private _forceProto(proto: any, key: string) { let rst: object; try { rst = proto[key]; } catch (e) { if (<any>this._klass === File) { if (key == 'lastModified') return 0; if (key == 'lastModifiedDate') return new Date(); } try { let obj = this.newInstance(); return obj[key]; } catch (e1) { return ''; } } return rst } private _isValidStatic(mName) { return ['prototype', 'name', 'length'].findIndex(v=>{ return v==mName }) < 0; } private _isValidInstance(mName) { return !mName.startsWith('__') && mName != 'constructor'; } private _init() {//只能得到静态字段、静态方法、实例方法,无法得到实例字段 this._parseStaticMembers(<any>this._klass); this._parseInstanceMembers((<any>this._klass).prototype); } private _toArray(json: JsonObject): any[] { let arr = []; J.forEach(json, v => { arr[arr.length] = v; }); return arr; } /** * Returns a reflected Method. */ public method(name: string): Method { return this.methodsMap()[name]; } /** * Returns a json-map of all reflected Methods. */ public methodsMap(): JsonObject<Method> { return this._methods; } /** * Returns an array of all reflected Methods. */ public methods(): Method[] { return this._toArray(this.methodsMap()); } /** * Returns a reflected Field. * @param name field name * @param instance Need a class instance when the field is an instance-field */ public field(name: string, instance?: object): Field { return this.fieldsMap(instance)[name]; } private _instanceFields(instance: object) { let fs = {}, keys = Reflect.ownKeys(instance); //获取所有实例属性 for (let i = 0; i < keys.length; i++) { const key = keys[i].toString(); if (this._isValidInstance(key)) { const obj = instance[key]; if (!Y.isFunction(obj)) fs[key] = new Field(this, key, false, Y.type(obj)); } } this._fields = J.union(fs, this._fields); } /** * Returns a json-map of all static-fields and instance-fields. * @param instance Need a class instance when the field is an instance-field * @param anno An annotation on the instance field */ public fieldsMap(instance?: object, anno?: Annotation): JsonObject<Field> { if (instance) this._instanceFields(instance); let fs = {}; if (anno && instance) { J.forEach(this._fields, (field: Field, key: string) => { if (Annotations.hasAnnotation(anno, instance, key)) fs[key] = field; }) } else { fs = this._fields; } return fs; } /** * Returns an array of all static-fields and instance-fields. * @param instance Need a class instance when the field is an instance-field * @param anno An annotation on the instance field */ public fields(instance?: object, anno?: Annotation): Field[] { return this._toArray(this.fieldsMap(instance, anno)); } //所有反射类的全名注册表 private static _MAP: JsonObject<Class<any>> = {}; //所有反射类的别名注册表 private static _ALIAS_MAP: JsonObject<Class<any>> = {}; /** * Returns a reflect Class for name. * @param name full name or alias name * @param isAlias */ public static forName<T>(name: string | Klass<T>, isAlias?: boolean): Class<T> { if (!name) return null; let isStr = Y.isString(name) if (!isStr && (<Klass<T>>name).class) return (<Klass<T>>name).class; let classname: string = isStr ? <string>name : (<Klass<T>>name).name; return isAlias ? this._ALIAS_MAP[classname] : this._MAP[classname]; } /** * Returns all reflected Classes by full class-name. */ public static all(): JsonObject<Class<any>> { return this._MAP; } /** * Registers a class will be reflectable. * @param klass class constructor * @param className class full name * @param alias class alias */ public static reflect<T>(klass: Klass<T>, className?: string, alias?: string) { let name = className || klass.name, cls = this.forName(name); if (cls) return; //BUGFIX: 不可以修改Object的原型,否则引发:1、Object静态成员同步增加;2、jquery选择器异常 if (klass !== Object) { var $P = (<any>klass).prototype; $P.className = name; $P.getClass = function () { return Class.forName(name); }; } let cs = new Class(name, klass); this._MAP[name] = cs; if (alias) this._ALIAS_MAP[alias] = cs; } /** * Find all classes of a namespace string. * @param ns */ public static classesOf(ns:string): Class<any>[]{ if(!ns) return null; if(ns.endsWith('.*')) ns = ns.slice(0, ns.length-2); let a = []; J.forEach(this._MAP, (cls, name)=>{ if(name.startsWith(ns)) a.push(cls) }) return a; } } } } //预定义短类名 import Method = JS.sugar.Method; import Field = JS.sugar.Field; import Class = JS.sugar.Class; import klass = JS.sugar.klass; //反射化重要且基础的对象 Class.reflect(Object);