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 | 1x 1x 1x 1x 1x 60x 60x 60x 60x 60x 60x 2x 2x 58x 58x 60x 39x 39x 39x 39x 1x 1x 1x 1x 1x 1x 1x 39x 57x 8x 8x 8x 57x 3x 3x 3x 15x 60x 1x 8x 8x 1x 8x 2x 8x 8x 8x 1x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 4x 8x 8x 1x 3x 3x 3x 3x 3x 3x 3x 3x 1x | import type { EvaluationContext } from './evaluation-context.js';
import { TypedValue } from '../typed-value.js';
import type { MethodResolver } from './method-resolver.js';
import { SpelEvaluationException } from '../error/spel-evaluation-exception.js';
import { SpelMessage } from '../error/spel-message.js';
export class ReflectiveMethodResolver implements MethodResolver {
public resolve(
_context: EvaluationContext,
target: unknown,
name: string,
args: unknown[],
): TypedValue | null {
if (target === null || target === undefined) {
return null;
}
// 1. Try JS native method
const targetObj = target as Record<string, unknown>;
const fn = targetObj[name];
if (typeof fn === 'function') {
try {
const result = (fn as (...args: unknown[]) => unknown).apply(target, args);
return new TypedValue(result);
} catch (e) {
throw new SpelEvaluationException(
-1,
SpelMessage.EXCEPTION_DURING_METHOD_INVOCATION,
name,
(e as Error).message,
);
}
}
// 2. Special handling: primitive type Java-style methods
if (typeof target === 'string') {
const strResult = this.tryStringMethod(target, name, args);
if (strResult !== null) return strResult;
}
if (typeof target === 'number') {
const numResult = this.tryNumberMethod(target, name);
if (numResult !== null) return numResult;
}
// Not found — return null so accessor chain or other resolvers can try
return null;
}
private tryStringMethod(target: string, name: string, args: unknown[]): TypedValue | null {
switch (name) {
case 'length':
return new TypedValue(target.length);
case 'isEmpty':
return new TypedValue(target.length === 0);
case 'charAt': {
const idx = args[0] as number;
return new TypedValue(idx >= 0 && idx < target.length ? target.charAt(idx) : '');
}
case 'substring':
if (args.length === 1) return new TypedValue(target.substring(args[0] as number));
return new TypedValue(target.substring(args[0] as number, args[1] as number));
case 'contains':
return new TypedValue(target.includes(args[0] as string));
case 'startsWith':
return new TypedValue(target.startsWith(args[0] as string));
case 'endsWith':
return new TypedValue(target.endsWith(args[0] as string));
case 'indexOf':
return new TypedValue(target.indexOf(args[0] as string));
case 'toLowerCase':
return new TypedValue(target.toLowerCase());
case 'toUpperCase':
return new TypedValue(target.toUpperCase());
case 'trim':
return new TypedValue(target.trim());
case 'split':
return new TypedValue(target.split(args[0] as string));
case 'replace':
return new TypedValue(target.split(args[0] as string).join(args[1] as string));
case 'concat':
return new TypedValue(target + String(args[0]));
default:
return null;
}
}
private tryNumberMethod(target: number, name: string): TypedValue | null {
switch (name) {
case 'toString':
return new TypedValue(target.toString());
case 'toFixed':
return new TypedValue(target.toFixed());
case 'toExponential':
return new TypedValue(target.toExponential());
default:
return null;
}
}
}
|