All files / src standard-evaluation-context.ts

100% Statements 95/95
100% Branches 23/23
83.33% Functions 20/24
100% Lines 95/95

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 1231x           1x 1x 1x 1x 1x 1x   1x 5286x 5286x 5286x 5286x 5286x   5286x 5286x 5286x   5286x 5286x   5286x 5286x 5286x 5286x   5286x     5286x 5286x 1x 1x     5286x 5286x 5286x   5286x 5286x 1x 1x     5286x 5286x 5286x   5286x 5286x   5286x 157x 157x 5286x 4x 4x   5286x 65x 65x 5286x 58x 58x   5286x 14x 14x 5286x 3x 3x   5286x 100x 100x 5286x 2x 2x   5286x 33x 33x 5286x 1x 1x   5286x 25x 25x 5286x 22x 22x   5286x 13x 13x 5286x 12x 12x   5286x 3x 3x   5286x 64x 64x 64x 64x 64x 1x 1x 64x 64x 5286x  
import { TypedValue } from './typed-value.js';
import type { EvaluationContext } from './evaluation-context/evaluation-context.js';
import type { PropertyAccessor } from './evaluation-context/property-accessor.js';
import type { MethodResolver } from './evaluation-context/method-resolver.js';
import type { TypeLocator } from './type/type-locator.js';
import type { BeanResolver } from './bean/bean-resolver.js';
import { SpelTypeConverter } from './bridge/type-coercion.js';
import { MapAccessor } from './evaluation-context/map-accessor.js';
import { ArrayAccessor } from './evaluation-context/array-accessor.js';
import { ReflectivePropertyAccessor } from './evaluation-context/reflective-property-accessor.js';
import { TypeDescriptorAccessor } from './evaluation-context/type-descriptor-accessor.js';
import { ReflectiveMethodResolver } from './evaluation-context/reflective-method-resolver.js';
 
export class StandardEvaluationContext implements EvaluationContext {
  private rootObject: TypedValue;
  private readonly variables = new Map<string, TypedValue>();
  private readonly functions = new Map<string, (...args: unknown[]) => unknown>();
  private readonly propertyAccessors: PropertyAccessor[] = [];
  private readonly methodResolvers: MethodResolver[] = [];
 
  private typeLocator: TypeLocator;
  private beanResolver: BeanResolver;
  private typeConverter: SpelTypeConverter;
 
  constructor(rootObject?: unknown) {
    this.rootObject = new TypedValue(rootObject);
 
    this.propertyAccessors.push(new MapAccessor());
    this.propertyAccessors.push(new ArrayAccessor());
    this.propertyAccessors.push(new TypeDescriptorAccessor());
    this.propertyAccessors.push(new ReflectivePropertyAccessor());
 
    this.methodResolvers.push(new ReflectiveMethodResolver());
 
    // These will be set to actual implementations later; for now use placeholder stubs
    this.typeLocator = {
      findType: (name: string) => {
        throw new Error(`Type "${name}" not found. TypeLocator not configured.`);
      },
      // Stub: no-op until a real TypeLocator is installed via setTypeLocator()
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      registerType: () => {},
      hasType: () => false,
    };
 
    this.beanResolver = {
      resolve: (name: string) => {
        throw new Error(`Bean "${name}" not found. BeanResolver not configured.`);
      },
      // Stub: no-op until a real BeanResolver is installed via setBeanResolver()
      // eslint-disable-next-line @typescript-eslint/no-empty-function
      register: () => {},
      has: () => false,
    };
 
    this.typeConverter = new SpelTypeConverter();
  }
 
  public getRootObject(): TypedValue {
    return this.rootObject;
  }
  public setRootObject(obj: unknown): void {
    this.rootObject = new TypedValue(obj);
  }
 
  public lookupVariable(name: string): TypedValue | null {
    return this.variables.get(name) ?? null;
  }
  public setVariable(name: string, value: unknown): void {
    this.variables.set(name, new TypedValue(value));
  }
 
  public lookupFunction(name: string): ((...args: unknown[]) => unknown) | null {
    return this.functions.get(name) ?? null;
  }
  public registerFunction(name: string, fn: (...args: unknown[]) => unknown): void {
    this.functions.set(name, fn);
  }
 
  public getPropertyAccessors(): PropertyAccessor[] {
    return this.propertyAccessors;
  }
  public addPropertyAccessor(accessor: PropertyAccessor): void {
    this.propertyAccessors.push(accessor);
  }
 
  public getMethodResolvers(): MethodResolver[] {
    return this.methodResolvers;
  }
  public addMethodResolver(resolver: MethodResolver): void {
    this.methodResolvers.push(resolver);
  }
 
  public getTypeLocator(): TypeLocator {
    return this.typeLocator;
  }
  public setTypeLocator(typeLocator: TypeLocator): void {
    this.typeLocator = typeLocator;
  }
 
  public getBeanResolver(): BeanResolver {
    return this.beanResolver;
  }
  public setBeanResolver(beanResolver: BeanResolver): void {
    this.beanResolver = beanResolver;
  }
 
  public getTypeConverter(): SpelTypeConverter {
    return this.typeConverter;
  }
 
  public createChildContext(rootObject: unknown): EvaluationContext {
    const child = new StandardEvaluationContext(rootObject);
    child.typeLocator = this.typeLocator;
    child.beanResolver = this.beanResolver;
    child.typeConverter = this.typeConverter;
    for (const [name, fn] of this.functions) {
      child.functions.set(name, fn);
    }
    return child;
  }
}