18. N4JS Objects

18.1. Reflection Model

N4JS provided metadata for reflection (and introspection). This information is available via instances of some reflection model classes (described below) attached to types and (in some cases) functions.

The following class diagrams shows the defined classes. Note that for performance reasons, the actual structure may vary from the model.

cd reflectionModel
Figure 17. N4JS Reflection Classes

Remark: This section is work in progress. The final goal is to provide a metamodel similar to Ecore, but we will only add new features if needed.

/**
 * Base class for all N4 reflective elements.
 */
export public abstract class N4Element extends Object {
    /**
     * Annotations attached to this element.
     */
    public Array<N4Annotation> annotations = [];

    /**
     * The origin string formed as <ID-VERSION>, where ID is containing project artefact id, and VERSION is its version
     */
    public String origin;

    /**
     * Returns true if an annotation with the given name is attached to the element.
     */
    public boolean hasAnnotation(string name) {
        return !!this.anyAnnotation(name)
    }

    /**
     * Returns any annotation with the given name attached to the element, or null if no such annotation is found.
     */
    public N4Annotation anyAnnotation(string name) {
        for (var i=this.annotations.length-1; i>=0; i--) {
            var a = this.annotations[i];
            if (a) {
                return a;
            }
        }
        return null;
    }
    /**
     * Returns all annotations with the given name attached to the element, or an empty array if no such annotations are found.
     */
    public Array<N4Annotation> allAnnotations(string name) {
        return this.annotations.filter(function(a){return a.name==name});
    }
}

/**
 * Base class for all reflective classes with a name.
 */
export public abstract class N4NamedElement extends N4Element {
    /**
     * The simple name of a named element.
     */
    public string name;
}

/**
 * Base class for all reflective classes describing a type (declaration).
 */
export public abstract class N4Type extends N4NamedElement {
    /**
     * The FQN of the type.
     */
    public string fqn;

    /**
     * Returns true if this N4Class describes an N4-class declaration.
     */
    public boolean get isClass() { return false; }

    /**
     * Returns true if this N4Class describes an N4-interface declaration.
     */
    public boolean get isInterface() { return false; }

    /**
     * Returns true if this N4Class describes an N4-enumeration declaration.
     */
    public boolean get isEnum() { return false; }
}

/**
 * Base class for meta types of classes or interfaces.
 */
export public abstract class N4Classifier extends N4Type {

    /**
     * The N4Class of the super type, may be null if super type is a not an N4Class.
     */
    public N4Class n4superType;

    /**
     * Array of the FQN of all (transitively) implemented interfaces, i.e. interfaces directly implemented by this class, its super
     * class or interfaces extended by directly implemented interfaces.
     */
    public Array<string> allImplementedInterfaces;

    /**
     * Array of all owned members, that is members defined in the class directly.
     * This field is private as it is an internal detail, members are accessed via methods defined in this class.
     */
    private Array<N4Member> ownedMembers;

    /**
     * Array of all consumed members, that is members mixed into the classifier via interface implementation and default methods.
     * This field is private as it is an internal detail, members are accessed via methods defined in this class.
     */
    private Array<N4Member> consumedMembers;

    /**
     * Only used internally, must not be called by client.
     */
    protected constructor(@Spec ~~this spec) {}

    /**
     * Returns all members defined by this class directly, consumed, and inherited. The boolean flags control which members are returned.
     *
     * @param consumed if set, consumed members are returned as well (false by default)
     * @param inherited if set, inherited members are returned as well (false by default)
     * @param _static if set, static members are returned, otherwise instance members (false by default).
     * @return array of members, may be empty but never null
     */
    public Array<? extends N4Member> members(boolean? consumed, boolean? inherited, boolean? _static) {
        return null; // TODO
    }

    /**
     * Returns all members defined in this classifier (or inherited) with an annotation
     * of given name attached to it. The boolean flags control which methods are returned.
     *
     * @param name name of annotation to be used as filter
     * @param consumed if set, consumed members are returned as well (false by default)
     * @param inherited if set, inherited members are returned as well (false by default)
     * @param _static if set, static members are returned, otherwise instance members (false by default).
     * @return array of members, may be empty but never null
     */
    public Array<? extends N4Member> membersWithAnnotation(string name, boolean? consumed, boolean? inherited, boolean? _static) {
        return null; // TODO
    }

    /**
     * Returns all data fields defined by this class directly, consumed, and inherited. The boolean flags control which data fields are returned.
     *
     * @param consumed if set, consumed data fields are returned as well (false by default)
     * @param inherited if set, inherited data fields are returned as well (false by default)
     * @param _static if set, static data fields are returned, otherwise instance members (false by default).
     * @return array of data fields, may be empty but never null
     */
    public Array<? extends N4DataField> dataFields(boolean? consumed, boolean? inherited, boolean? _static) {
        return null; // TODO
    }

    /**
     * Returns all data fields defined in this classifier (or inherited) with an annotation
     * of given name attached to it. The boolean flags control which data fields are returned.
     *
     * @param name name of annotation to be used as filter
     * @param consumed if set, consumed data fields are returned as well (false by default)
     * @param inherited if set, inherited data fields are returned as well (false by default)
     * @param _static if set, static data fields are returned, otherwise instance members (false by default).
     * @return array of data fields, may be empty but never null
     */
    public Array<? extends N4DataField> dataFieldsWithAnnotation(string name, boolean? consumed, boolean? inherited, boolean? _static) {
        return null; // TODO
    }

    /**
     * Returns all methods defined by this class directly, consumed, and inherited. The boolean flags control which methods are returned.
     *
     * @param consumed if set, consumed methods are returned as well (false by default)
     * @param inherited if set, inherited methods are returned as well (false by default)
     * @param _static if set, static methods are returned, otherwise instance members (false by default).
     * @return array of methods, may be empty but never null
     */
    public Array<? extends N4Method> methods(boolean? consumed, boolean? inherited, boolean? _static) {
        return null; // TODO
    }

    /**
     * Returns all methods defined in this classifier (or inherited) with an annotation
     * of given name attached to it. The boolean flags control which methods are returned.
     *
     * @param name name of annotation to be used as filter
     * @param consumed if set, consumed methods are returned as well (false by default)
     * @param inherited if set, inherited methods are returned as well (false by default)
     * @param _static if set, static methods are returned, otherwise instance members (false by default).
     * @return array of methods, may be empty but never null
     */
    public Array<? extends N4Method> methodsWithAnnotation(string name, boolean? consumed, boolean? inherited, boolean? _static) {
        return null; // TODO
    }


}

/**
 * Meta information of an n4 class.
 */
export @Final public class N4Class extends N4Classifier {

    /**
     * Returns the N4Class instance for a given n4object. This is similar to
     * {@code n4object.constructor.n4type}, however it can also be used in interfaces
     * to get reflective information of the implementor.
     */
    public static N4Class of(N4Object n4object) {
        return n4object.constructor.n4type
    }

    /**
     * Returns true if this N4Class describes an N4-class declaration.
     */
    @Override
    public boolean get isClass() { return true; }
}


/**
 * Meta information of an n4 interface.
 */
export @Final public class N4Interface extends N4Classifier {
    /**
     * Returns true if this N4Class describes an N4-interface declaration.
     */
    @Override
    public boolean get isInterface() { return true; }
}

/**
 * Description of a member, that is a method or field.
 */
export public abstract class N4Member extends N4Element {
    public string name;
}

/**
 * Description of a method.
 */
export @Final public class N4Method extends N4Member {
    public Function jsFunction;
}

/**
 * Description of a simple data field.
 */
export @Final public class N4DataField extends N4Member {
}

/**
 * Description of an accessor, that is a getter or setter.
 */
export @Final public class N4Accessor extends N4Member {
    /**
     * Flag indicating whether accessor is a getter or setter, internal detail.
     */
    private boolean getter;
    /**
     * Returns true if accessor is a getter.
     */
    public boolean isGetter() { return this.getter; }
    /**
     * Returns true if accessor is a setter.
     */
    public boolean isSetter() { return ! this.getter; }
}

/**
 * Description of an N4Enum
 */
export @Final public class N4EnumType extends N4Type {
    /**
     * Returns true if this N4Clasifier describes an N4-enumeration declaration.
     */
    @Override public boolean get isEnum() { return true; }
    /**
     * Returns the N4EnumType instance for a given enum literal. This is similar to
     * {@code n4enum.constructor.n4type}.
     */
    public static N4EnumType of(N4Enum n4enum) {
        return n4enum.constructor.n4type
    }
}

/**
 * Base class for all enumeration, literals are assumed to be static constant fields of concrete subclasses.
 */
export public abstract class N4Enum  extends Object {

    /**
     * Returns the name of a concrete literal
     */
    public abstract string get name();

    /**
     * Returns the value of a concrete literal. If no value is
     * explicitly set, it is similar to the name.
     */
    public abstract string get value()

    /**
     * Returns a string representation of a concrete literal, it returns
     * the same result as value()
     */
    @Override public string toString() { return this.value }

    /**
     * Returns the enum class object of this enum literal for reflection.
     * The very same meta class object can be retrieved from the enumeration type directly.
     */
    public abstract N4Enum get n4Enum()

    /**
     * Natively overridden by concrete enums.
     */
    public static Array<? extends N4Enum> get values() { return null; }

    /**
     * Natively overridden by concrete enums.
     */
    public static N4Enum valueByName(string name) { return null; }

    /**
     * Returns the meta class object of this class for reflection.
     * The very same meta class object can be retrieved from an instance by calling
     * <code>instance.constructor.n4type</code>
     */
    public static N4EnumType get n4type() { return null; }
}

/**
 * Annotation with value.
 */
export @Final public class N4Annotation extends Object {
    public string name;
    public union{string,number} value;
}

/**
 * The base class for all instances of n4 classes.
 */
export public class N4Object {
    /**
     * Returns the meta class object of this class for reflection.
     * The very same meta class object can be retrieved from an instance by calling
     * <code>instance.constructor.n4type</code>
     */
    // defined in types model, added by $makeClass:
    // public static N4Class get n4type() { return null; }
}

18.2. Error Types

N4JS provides additional Error types as subtypes of Error.

18.2.1. N4ApiNotImplemented

Considering API definitions and concrete implementations of those APIs the error N4-Api-Not-Implemented-Error is introduced to specifically report missing implementations. Instances of this error type are inserted internally during the transpilation of API-implementing projects. Whenever a difference to the API in form of a missing implementation is encountered, the transpiler will insert stub-code throwing an instance of N4-Api-Not-Implemented-Error.

API-testing projects can catch those errors and act accordingly. This enables tracking of completeness of implementations by counting the occasions an N4-Api-NotImplemented-Error was encountered.

/**
 * Error type reporting a not implemented situation.
 */
public class N4ApiNotImplementedError extends Error {  }

Quick Links