All files / src/type standard-type-locator.ts

96.36% Statements 53/55
100% Branches 23/23
88.88% Functions 8/9
96.36% Lines 53/55

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    1x 1x   1x 47x   47x 47x 47x 5x 5x 42x 47x   47x       47x 3x 3x           47x 40x 40x 40x 40x 40x 40x 40x 40x 40x 40x 40x 8x 8x 40x 8x 8x 40x 9x 9x 5x 5x   4x 7x 1x 1x 3x 9x 40x 8x   5x 7x 2x 8x 40x 40x 40x 47x  
import type { TypeDescriptor } from './type-descriptor.js';
import type { TypeLocator } from './type-locator.js';
import { SpelEvaluationException } from '../error/spel-evaluation-exception.js';
import { SpelMessage } from '../error/spel-message.js';
 
export class StandardTypeLocator implements TypeLocator {
  private readonly types = new Map<string, TypeDescriptor>();
 
  public findType(typeName: string): TypeDescriptor {
    const descriptor = this.types.get(typeName);
    if (!descriptor) {
      throw new SpelEvaluationException(-1, SpelMessage.TYPE_NOT_FOUND, typeName);
    }
    return descriptor;
  }
 
  public registerType(name: string, descriptor: TypeDescriptor): void {
    this.types.set(name, descriptor);
  }
 
  public hasType(name: string): boolean {
    return this.types.has(name);
  }
 
  /**
   * Convenience: register a JS 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(name: string, ...args: unknown[]): unknown {
        const method = staticMethods[name];
        if (typeof method === 'function') {
          return method(...args);
        }
        // Try prototype method
        const protoMethod = (constructor.prototype as Record<string, unknown>)[name];
        if (typeof protoMethod === 'function') {
          return (protoMethod as (...a: unknown[]) => unknown)(...args);
        }
        throw new SpelEvaluationException(-1, SpelMessage.METHOD_NOT_FOUND, name, this.name);
      },
      getStaticField(name: string): unknown {
        if (name in staticFields) return staticFields[name];
        // Try constructor property
        const ctor = constructor as unknown as Record<string, unknown>;
        if (name in ctor) return ctor[name];
        throw new SpelEvaluationException(-1, SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE, name);
      },
    };
    this.types.set(name, descriptor);
  }
}