All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eolang.core.EOObject Maven / Gradle / Ivy

The newest version!
package org.eolang.core;

import org.eolang.core.data.EOData;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;


/**
 * Declares the base EO object.
 * All the objects in the target Java platform environment are constructed
 * based on this abstract class (both standard and user-defined).
 */
public abstract class EOObject implements Cloneable {

    /**
     * User-defined class (i.e., one generated by transpiler) overrides this method
     * to declare its decoratee (i.e., an object it decorates).
     * By default, this method returns null, which means that objects that do not override it have no decoratee objects.
     *
     * @return The decoratee of this object, or null if it is not present.
     */
    protected EOObject _decoratee() {
        return null;
    }

    /**
     * User-defined class (i.e., one generated by transpiler) overrides this method
     * to declare its parent (i.e., an object it belongs to).
     * By default, this method returns null, which means that objects that do not override it
     * have no parent objects (i.e., they are not nested, but package-scoped (global) objects).
     *
     * @return The parent of this object, or null if it is not present.
     */
    protected EOObject _parent() {
        return null;
    }

    /**
     * Provides checked (safe) access to the decoratee of this object.
     * This method is used when the decoratee object is explicitly accessed in the user-defined code through the '@' symbol.
     *
     * @return The decoratee of this object if it is present.
     * @throws RuntimeException Thrown when the decoratee object is not declared for this object.
     */
    public EOObject _getDecoratedObject() {
        EOObject decoratee = _decoratee();
        if (decoratee == null) {
            throw new RuntimeException(String.format("Can't access the decoratee object of the %s object: the @ attribute is not bound.", getClass().getTypeName()));
        } else {
            return decoratee;
        }
    }

    /**
     * Provides checked (safe) access to the parent of this object.
     * This method is used when the parent object is explicitly accessed in the user-defined code through the '^' symbol.
     *
     * @return The parent of this object if it is present.
     * @throws RuntimeException Thrown when the parent object is not declared for this object.
     */
    public EOObject _getParentObject() {
        EOObject parent = _parent();
        if (parent == null) {
            throw new RuntimeException(String.format("Can't access the parent object of the %s object.", getClass().getTypeName()));
        } else {
            return parent;
        }
    }

    /**
     * Retrieves data behind this object (i.e., performs dataization operation over the object).
     *
     * @return Data behind this object.
     * @throws RuntimeException Thrown when this object cannot be dataized since it has nor data behind it, neither a decoratee to rely on.
     */
    public EOData _getData() {
        final EOObject decoratedObject = _decoratee();
        if (decoratedObject == null) {
            throw new RuntimeException(String.format("Object %s cannot be dataized: it has nor data behind it, neither a decoratee to rely on.", getClass().getTypeName()));
        }
        return decoratedObject._getData();
    }

    /**
     * Instantiates the attribute object {@code name} of this object.
     * Performs lookup of the attribute in the class that declares this object + in the decoration hierarchy.
     *
     * @param name      The name of the attribute being accessed.
     * @param arguments The arguments that are passed to the attribute object application/instantiation method.
     * @return The attribute object instantiated with the provided arguments.
     * @throws RuntimeException Thrown when the attribute is not present in this object.
     */
    public EOObject _getAttribute(String name, EOObject... arguments) {
        try {
            Method method = Arrays.stream(getClass().getMethods()).filter(mthd -> mthd.getName().equals(name)).findFirst().get();
            Parameter[] methodParams = method.getParameters();
            method.setAccessible(true);
            return (EOObject) method.invoke(this, _prepareFreeAtt(methodParams, arguments));
        } catch (Exception e) {
            if (_decoratee() != null && _decoratee() != this) {
                return _decoratee()._getAttribute(name, arguments);
            } else {
                e.printStackTrace();
                throw new RuntimeException(String.format("Can't access the %s attribute of the %s object", name, getClass().getTypeName()));
            }
        }
    }

    private Object[] _prepareFreeAtt(Parameter[] methodParams, EOObject... arguments) {
        List methodValues = new ArrayList<>();
        for (int i = 0; i < methodParams.length; i++) {
            if (methodParams[i].getType().getCanonicalName().endsWith("[]")) {
                List objs = Arrays.stream(arguments).skip(i).collect(Collectors.toList());
                methodValues.add(objs.toArray(new EOObject[0]));
                break;
            } else {
                methodValues.add(arguments[i]);
            }
        }
        return methodValues.toArray();
    }
}