Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | 25x 5840x 5840x 5840x 58400x 236x 236x 56x 180x 179x 181x 181x 171x 9x 18x 13x 6x 3x 3x 2x 42x 8x 8x 12x 12x 8x 4x 4x 1x 3x 8x 5x 5x 2x 42x | import type { TypeDescriptor } from './type-descriptor.js';
import type { TypeLocator } from './type-locator.js';
import { JAVA_TYPE_DEFINITIONS, createTypeDescriptor } from './java-types.js';
import { SpelEvaluationException } from '../error/spel-evaluation-exception.js';
import { SpelMessage } from '../error/spel-message.js';
/** Prefixes tried for an unqualified type name, as Java's implicit imports do. */
const DEFAULT_PREFIXES: readonly string[] = ['java.lang.'];
/**
* Parallels Spring StandardTypeLocator.
*
* Resolves the names used by `T(...)`, `instanceof` and `new ...`.
*
* Two behaviours matter and both mirror Spring:
*
* - `java.lang` is imported implicitly, so `T(String)` and `T(java.lang.String)`
* are the same type. An unqualified name that is not found is retried with
* each registered prefix.
* - The catalogue of available types is explicit (see `java-types.ts`). Where
* Spring consults a class loader, this port has a registry, so a type that is
* not registered raises `TYPE_NOT_FOUND` rather than being loaded on demand.
*/
export class StandardTypeLocator implements TypeLocator {
private readonly types = new Map<string, TypeDescriptor>();
private readonly prefixes: string[];
/**
* @param prefixes Package prefixes tried for an unqualified name. Defaults to
* `java.lang`, matching Java's implicit import.
*/
constructor(prefixes: readonly string[] = DEFAULT_PREFIXES) {
this.prefixes = prefixes.map((prefix) => (prefix.endsWith('.') ? prefix : `${prefix}.`));
for (const definition of JAVA_TYPE_DEFINITIONS) {
this.types.set(definition.name, createTypeDescriptor(definition));
}
}
public findType(typeName: string): TypeDescriptor {
const exact = this.types.get(typeName);
if (exact !== undefined) {
return exact;
}
// An unqualified name is resolved against the implicit imports, so
// `T(String)` finds `java.lang.String` just as it does in Java.
if (!typeName.includes('.')) {
for (const prefix of this.prefixes) {
const imported = this.types.get(prefix + typeName);
if (imported !== undefined) {
return imported;
}
}
}
throw new SpelEvaluationException(-1, SpelMessage.TYPE_NOT_FOUND, typeName);
}
public registerType(name: string, descriptor: TypeDescriptor): void {
this.types.set(name, descriptor);
}
public hasType(name: string): boolean {
if (this.types.has(name)) {
return true;
}
return this.prefixes.some((prefix) => this.types.has(prefix + name));
}
/**
* Add a package prefix for unqualified names, mirroring Spring's
* `registerImport`.
*/
public registerImport(prefix: string): void {
const normalised = prefix.endsWith('.') ? prefix : `${prefix}.`;
if (!this.prefixes.includes(normalised)) {
this.prefixes.push(normalised);
}
}
/**
* Convenience: register a JavaScript constructor as a type.
* Automatically creates a TypeDescriptor wrapping the constructor.
*/
public register(
name: string,
constructor: new (...args: unknown[]) => unknown,
staticMethods: Record<string, (...args: unknown[]) => unknown> = {},
staticFields: Record<string, unknown> = {},
): void {
const descriptor: TypeDescriptor = {
name,
constructor,
staticMethods,
staticFields,
isInstance(value: unknown): boolean {
return value instanceof constructor;
},
newInstance(...args: unknown[]): unknown {
return new constructor(...args);
},
callStaticMethod(methodName: string, ...args: unknown[]): unknown {
const method = staticMethods[methodName];
if (typeof method === 'function') {
return method(...args);
}
// Try prototype method
const protoMethod = (constructor.prototype as Record<string, unknown>)[methodName];
if (typeof protoMethod === 'function') {
return (protoMethod as (...a: unknown[]) => unknown)(...args);
}
throw new SpelEvaluationException(-1, SpelMessage.METHOD_NOT_FOUND, methodName, this.name);
},
getStaticField(fieldName: string): unknown {
if (fieldName in staticFields) return staticFields[fieldName];
// Try constructor property
const ctor = constructor as unknown as Record<string, unknown>;
if (fieldName in ctor) return ctor[fieldName];
throw new SpelEvaluationException(
-1,
SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE,
fieldName,
);
},
};
this.types.set(name, descriptor);
}
}
|