package de.uni_hamburg.fs;

import de.renew.util.Value;


/**
 * Abstract class representing a feature of a Java object.
 * Provides common functionality for accessing and modifying object properties.
 */
abstract class JavaFeature {
    /**
     * Gets the Java class of this feature's return type.
     *
     * @return the Java class representing the feature's type
     */
    abstract Class<?> getJavaClass();

    /**
     * Gets the value of this feature from the specified Java object.
     * Handles primitive values by wrapping them in Value objects.
     *
     * @param javaObject the object from which to get the feature value
     * @return the feature value, potentially wrapped in a Value object
     */
    Object getValue(Object javaObject) {
        Object value = getObjectValue(javaObject);
        if (getJavaClass().isPrimitive() && value != null && !(value instanceof Exception)) {
            value = new Value(value); // wrap as Value object
        }
        return value;
    }

    /**
     * Gets the raw object value of this feature from the specified Java object.
     *
     * @param javaObject the object from which to get the feature value
     * @return the raw object value of the feature
     */
    abstract Object getObjectValue(Object javaObject);

    /**
     * Checks if this feature can be set (is writable).
     *
     * @return true if the feature can be set, false otherwise
     */
    abstract boolean canSet();

    /**
     * Sets the value of this feature on the specified Java object.
     * Handles unwrapping Value objects for primitive types.
     *
     * @param javaObject the object on which to set the feature
     * @param value the value to set, potentially a Value object for primitives
     */
    void setValue(Object javaObject, Object value) {
        if (getJavaClass().isPrimitive()) {
            if (value == null) {
                return; // normally, primitives can't be null, but it may
            }


            // happen when a get method failed.
            value = ((Value) value).value;
        }
        setObjectValue(javaObject, value);
    }

    /**
     * Sets the raw object value of this feature on the specified Java object.
     *
     * @param javaObject the object on which to set the feature
     * @param value the raw value to set
     */
    abstract void setObjectValue(Object javaObject, Object value);
}