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

org.leialearns.utilities.Setting Maven / Gradle / Ivy

package org.leialearns.utilities;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.leialearns.utilities.Display.display;
import static org.leialearns.utilities.Display.displayParts;
import static org.leialearns.utilities.Static.getLoggingClass;

/**
 * Encapsulates a reference to a value that can be set only once. Declare a final Setting<T> instead
 * of a final T property, if the value is not known when the object is constructed, but otherwise behaves
 * like a final property. A setting reference is thread-safe.
 * @param  The type of the value
 */
public class Setting {
    private final Logger logger = getLogger();
    private final String name;
    private final Expression defaultExpression;
    private final Object lock = new Object();
    private volatile T value = null;

    /**
     * Creates a new Setting instance. The default value is used in case the setting is not set before the
     * getter is called.
     * @param name A label for the setting
     * @param defaultExpression An expression that lazily returns a default value
     */
    public Setting(String name, Expression defaultExpression) {
        this.name = name;
        this.defaultExpression = defaultExpression;
    }

    /**
     * Creates a new Setting instance. The default value is used in case the setting is not set before the
     * getter is called.
     * @param name A label for the setting
     * @param defaultValue The default value
     */
    public Setting(String name, final T defaultValue) {
        this(name, new Expression() {
            @Override
            public T get() {
                return defaultValue;
            }
        });
    }

    /**
     * Creates a new Setting instance.
     * @param name A label for the setting
     */
    public Setting(String name) {
        this(name, (T) null);
    }

    /**
     * Sets the value, if possible.
     * @param value The value to set
     * @throws java.lang.IllegalStateException If the reference was already set to another value
     */
    public void set(T value) {
        if (logger.isTraceEnabled()) {
            logger.trace("Name: " + display(name) + ": Value: " + display(value), new Throwable("Stack trace"));
        }
        T thisValue = offerInternal(value);
        if (thisValue != null) {
            throw new IllegalStateException("Value was already set to another value: " + name + " == [" + display(thisValue) + "], not: [" + display(value) + "]");
        }
    }

    /**
     * Returns the value of this setting. If the values was not set, it is set to the (lazily evaluated) default value
     * first. If the expression that calculates the default value returns null, an IllegalStateException
     * is thrown.
     * @return The value of this setting
     * @throws java.lang.IllegalStateException
     */
    public T get() {
        return getInternal(
            new Function() {
                @Override
                public T get(T x) {
                    return x;
                }
            },
            new Expression() {
                @Override
                public T get() {
                    T defaultValue = defaultExpression.get();
                    if (defaultValue == null) {
                        throw new IllegalStateException("Value is not set: " + name);
                    }
                    value = defaultValue;
                    return defaultValue;
                }
            }
        );
    }

    /**
     * Returns a flag that indicates whether the value of this setting is already fixated.
     * @return true if the value is fixated; false otherwise
     */
    public boolean isFixated() {
        return getInternal(
                new Function() {
                    @Override
                    public Boolean get(T x) {
                        return true;
                    }
                },
                new Expression() {
                    @Override
                    public Boolean get() {
                        return false;
                    }
                }
        );
    }

    protected  Q getInternal(Function successCase, Expression failureCase) {
        Q result = null;
        T thisValue = this.value;
        if (thisValue == null) {
            synchronized (lock) {
                // Double checked locking: needs J2SE5
                thisValue = this.value;
                if (thisValue == null) {
                    result = failureCase.get();
                    if (logger.isDebugEnabled()) {
                        logger.debug("Use default value for setting: " + name + ": " + display(result));
                    }
                    if (logger.isTraceEnabled()) {
                        logger.trace("Stack trace" ,new Throwable());
                    }
                }
            }
        }
        if (thisValue != null) {
            result = successCase.get(thisValue);
        }
        return result;
    }

    /**
     * Offers the value as a candidate for the reference.
     * @param value The candidate value for this setting
     * @return true if the given value is equal to the value of the setting; false otherwise
     */
    public boolean offer(T value) {
        return offerInternal(value) == null;
    }

    protected T offerInternal(T value) {
        if (value == null) {
            throw new IllegalArgumentException("Value should not be null: " + name);
        }
        T thisValue = this.value;
        if (thisValue == null) {
            synchronized (lock) {
                // Double checked locking: needs J2SE5
                thisValue = this.value;
                if (thisValue == null) {
                    this.value = value;
                    logger.trace("Set [" + name + "] to: [" + value + "]");
                }
            }
        }
        logger.trace("Compare [" + thisValue + "] to: [" + value + "]");
        if (thisValue == null) {
            logger.trace("Null"); // Okay
        } else if (thisValue == value) {
            thisValue = null;
        } else if (value.equals(thisValue)) {
            logger.warn("Weak identity: [" + thisValue + "] equals [" + value + "]");
            thisValue = null;
        } else {
            logger.debug("Mismatch: [" + thisValue + "] differs from [" + value + "]");
        }
        return thisValue;
    }

    protected Logger getLogger() {
        return LoggerFactory.getLogger(getLoggingClass(this));
    }

    public String toString() {
        return displayParts("Setting", value);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy