jsdk-offical
Version:
JSDK is the most comprehensive TypeScript framework, like JDK.
491 lines (436 loc) • 18.6 kB
text/typescript
/**
* @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);