208 lines
7.4 KiB
TypeScript
208 lines
7.4 KiB
TypeScript
/// <reference types="@javatypes/jdk" />
|
|
|
|
const JavaClass = Java.type('java.lang.Class')
|
|
const JavaObject = Java.type('java.lang.Object')
|
|
const NoSuchFieldException = Java.type('java.lang.NoSuchFieldException')
|
|
const fieldCache = new Map<string, java.lang.reflect.Field>()
|
|
const methodCache = new Map<string, java.lang.reflect.Method>()
|
|
|
|
/**
|
|
* 反射工具类
|
|
* Created by MiaoWoo on 2017/2/9 0009.
|
|
*/
|
|
class Reflect {
|
|
private obj: java.lang.Object
|
|
private class: java.lang.Class<any>
|
|
|
|
constructor(obj: any) {
|
|
// if (obj === undefined || obj === null) { throw Error(`reflect object can't be ${obj}!`) }
|
|
if (obj instanceof JavaClass) {
|
|
this.obj = null
|
|
this.class = obj
|
|
} else {
|
|
this.obj = obj
|
|
if (obj !== null && obj !== undefined && obj.class) {
|
|
this.class = obj.class
|
|
}
|
|
}
|
|
}
|
|
|
|
method(name: string, ...args: java.lang.Class<any>[]) {
|
|
return declaredMethod(this.class, name, ...args)
|
|
}
|
|
|
|
methods() {
|
|
return Java.from(declaredMethods(this.class))
|
|
}
|
|
|
|
field(nameOrIndex: string | java.lang.String | number, declared = false): java.lang.reflect.Field {
|
|
if (nameOrIndex == undefined || nameOrIndex == null) throw new Error(`reflect field name can't be ${nameOrIndex} from ${this.class.getName()}!`)
|
|
let key = this.class.getName() + ':' + nameOrIndex + ':' + declared
|
|
if (fieldCache.has(key)) {
|
|
return fieldCache.get(key)
|
|
}
|
|
let field = null
|
|
if (typeof nameOrIndex == "number") {
|
|
field = this.fields(declared)[nameOrIndex]
|
|
} else {
|
|
try {
|
|
// Try getting a public field
|
|
field = this.class.getField(nameOrIndex)
|
|
} catch (ex: any) {
|
|
// Try again, getting a non-public field
|
|
field = declaredField(this.class, nameOrIndex)
|
|
}
|
|
}
|
|
if (!field) throw new Error(`can't reflect field ${typeof nameOrIndex == "number" ? 'index' : 'name'} ${nameOrIndex} from ${this.class.getName()}!`)
|
|
fieldCache.set(key, field)
|
|
return accessible(field)
|
|
}
|
|
|
|
fields(declared = false): java.lang.reflect.Field[] {
|
|
return Java.from(declared ? this.class.getDeclaredFields() : this.class.getFields())
|
|
}
|
|
|
|
values(declared = false) {
|
|
return this.fields(declared).reduce((cache, field) => { return cache[field.getName()] = this.get(field.getName()).get() }, {}) as any
|
|
}
|
|
|
|
call(...args: any[]): Reflect {
|
|
let params = args.slice(1)
|
|
let method = accessible(declaredMethod(this.class, args[0], ...types(params)))
|
|
let result = method.invoke(this.get(), params)
|
|
return result && on(result)
|
|
}
|
|
|
|
get(): any
|
|
get(index: number, declared?: boolean): Reflect
|
|
get(prop: string): Reflect
|
|
get(param?: string | number, declared: boolean = true): Reflect | any {
|
|
if (arguments.length === 0) return this.obj
|
|
return on(this.field(param, declared).get(this.obj))
|
|
}
|
|
|
|
set(param: string | number, value: any, declared: boolean = true): Reflect {
|
|
this.field(param, declared).set(this.obj, value)
|
|
return this
|
|
}
|
|
|
|
create(...args): Reflect {
|
|
return on(declaredConstructor(this.class, args).newInstance(args))
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get an array of types for an array of objects
|
|
*/
|
|
function types(values: any[], def?: any) {
|
|
if (values === null) {
|
|
return []
|
|
}
|
|
let result: java.lang.Class<any>[] = []
|
|
values.forEach(t => result.push((t || def) ? JavaObject.class : t instanceof JavaClass ? t : t.class))
|
|
return result
|
|
}
|
|
|
|
function accessible<T extends java.lang.reflect.AccessibleObject>(accessible: T): T {
|
|
if (!accessible?.isAccessible()) {
|
|
accessible.setAccessible(true)
|
|
}
|
|
return accessible
|
|
}
|
|
|
|
function declaredConstructor(clazz, param) {
|
|
let constructor
|
|
try {
|
|
constructor = clazz.getDeclaredConstructor(types(param))
|
|
} catch (ex: any) {
|
|
try {
|
|
constructor = clazz.getDeclaredConstructor(types(param, true))
|
|
} catch (ex: any) {
|
|
constructor = clazz.getDeclaredConstructors()[0]
|
|
}
|
|
}
|
|
return accessible(constructor)
|
|
}
|
|
|
|
function declaredField(clazz: java.lang.Class<any>, name: string | java.lang.String) {
|
|
if (!clazz) { throw Error(`target class can't be ${clazz}!`) }
|
|
let target = clazz
|
|
let field = null
|
|
// noinspection JSUnresolvedVariable
|
|
while (target !== JavaObject.class) {
|
|
console.debug(`reflect field ${name} from ${target.getName()}`)
|
|
try {
|
|
field = target.getDeclaredField(name)
|
|
if (field !== null) { break }
|
|
} catch (error: any) {
|
|
if (target === undefined) { break }
|
|
target = target.getSuperclass()
|
|
console.debug(`切换到超类: ${target.getName()}`)
|
|
}
|
|
}
|
|
if (field === null) {
|
|
throw new NoSuchFieldException(name + " is not found in " + clazz.getName())
|
|
}
|
|
return field
|
|
}
|
|
|
|
function declaredMethod(clazz: java.lang.Class<any>, nameOrIndex: string | number, ...clazzs: java.lang.Class<any>[]): java.lang.reflect.Method {
|
|
let key = clazz.getName() + '.' + nameOrIndex + ':' + (clazzs || []).map(c => c.getName()).join(':')
|
|
if (methodCache.has(key)) { return methodCache.get(key) }
|
|
let target = clazz
|
|
if (typeof nameOrIndex === "number") {
|
|
methodCache.set(key, declaredMethods(clazz)[nameOrIndex])
|
|
} else {
|
|
while (target !== JavaObject.class && !methodCache.has(key)) {
|
|
try {
|
|
console.debug(`reflect method ${typeof nameOrIndex == "number" ? 'index' : 'name'} ${nameOrIndex} from ${target.getName()}`)
|
|
try {
|
|
methodCache.set(key, target.getMethod(nameOrIndex, clazzs as any))
|
|
} catch (ex: any) {
|
|
try {
|
|
methodCache.set(key, target.getDeclaredMethod(nameOrIndex, clazzs as any))
|
|
} catch (ex: any) {
|
|
for (const m of Java.from(declaredMethods(target))) {
|
|
if (m.getName() == nameOrIndex) {
|
|
methodCache.set(key, m)
|
|
break
|
|
}
|
|
}
|
|
throw new Error(`method ${typeof nameOrIndex == "number" ? 'index' : 'name'} ${nameOrIndex} not found.`)
|
|
}
|
|
}
|
|
} catch (error) {
|
|
if (target === undefined) { break }
|
|
target = target.getSuperclass()
|
|
console.debug(`切换到超类: ${target.getName()}`)
|
|
}
|
|
}
|
|
}
|
|
if (!methodCache.has(key)) throw new Error(`can't reflect method ${typeof nameOrIndex == "number" ? 'index' : 'name'} ${nameOrIndex} from ${clazz.getName()}!`)
|
|
return methodCache.get(key)
|
|
}
|
|
|
|
function declaredMethods(clazz: java.lang.Class<any>) {
|
|
return clazz.getDeclaredMethods()
|
|
}
|
|
|
|
function mapToObject(javaObj) {
|
|
if (!Java.isJavaObject(javaObj)) { throw new TypeError(`参数 ${javaObj} 不是一个Java对象!`) }
|
|
let target = Proxy.newProxy(javaObj, {
|
|
apply: (target, name, args) => { return args ? javaObj[name](args) : javaObj[name]() }
|
|
})
|
|
return target
|
|
}
|
|
|
|
function on(obj) {
|
|
// if (!obj || !obj.class) { throw new TypeError(`参数 ${obj} 不是一个Java对象!`) }
|
|
return new Reflect(obj)
|
|
}
|
|
|
|
export = {
|
|
on,
|
|
accessible,
|
|
declaredMethods,
|
|
mapToObject
|
|
}
|