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

org.aksw.commons.collection.observable.ObservableValueFromObservableCollection Maven / Gradle / Ivy

package org.aksw.commons.collection.observable;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.VetoableChangeListener;
import java.util.Collection;
import java.util.Iterator;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;


/**
 * Getter/setter view over an observable collection.
 * If that collection has a single item then {@link #get()} method returns it. Otherwise, if there are
 * no or multiple items then the method returns null.
 *
 * @author raven
 *
 * @param 
 */
public class ObservableValueFromObservableCollection
    implements ObservableValue
{
    protected ObservableCollection delegate;
    protected Function, ? extends T> xform;
    protected Function valueToItem;

    public ObservableValueFromObservableCollection(
            ObservableCollection delegate,
            Function, ? extends T> xform,
            Function valueToItem) {
        super();
        this.delegate = delegate;
        this.xform = xform;
        this.valueToItem = valueToItem;
    }

    public static  T getOnlyElementOrNull(Iterable iterable) {
        Iterator it = iterable.iterator();
        T result = it.hasNext() ? it.next() : null;

        if (it.hasNext()) {
            result = null;
            // throw new IllegalStateException();
        }

        return result;
    }

    /**
     * Attempt to get the only element from the underlying collection. If that collection
     * is empty or contains multiple (possible equal) elements then return null.
     */
    @Override
    public T get() {
        T result = xform.apply(delegate);
        return result;
    }

    /* TODO Add a get method that raises an exception:
     *   If there are multiple elements (even if they are equal)
     *   an {@link IllegalStateException} is raised.
     */

    /**
     * First clear the underlying collection.
     * If the given value is non-null then add it to the collection.
     */
    @Override
    public void set(T value) {
        T oldValue = get();
        if (!Objects.equals(oldValue, value)) {
            delegate.clear();
            U item = valueToItem.apply(value);
            if (item != null) {
                delegate.add(item);
            }
        }
    }

    /** Wrap the listener so that the set-based property change event is
     * converted to a single value based on */
    public static  PropertyChangeListener wrapListener(
            Object self, PropertyChangeListener listener,
            Function, ? extends T> xform
            ) {
        return ev -> {
            T oldValue = Optional.ofNullable((Collection)ev.getOldValue())
//                    .map(ObservableValueFromObservableCollection::getOnlyElementOrNull)
                    .map(xform)
                    .orElse(null);

            T newValue = Optional.ofNullable(((Collection)ev.getNewValue()))
                    .map(xform)
                    .orElse(null);

            PropertyChangeEvent newEv = new PropertyChangeEvent(
                    self, "value", oldValue, newValue);
            listener.propertyChange(newEv);
        };
    }

    @Override
    public Registration addPropertyChangeListener(PropertyChangeListener listener) {
        return delegate.addPropertyChangeListener(wrapListener(this, listener, xform));
    }

    @Override
    public Runnable addVetoableChangeListener(VetoableChangeListener listener) {
        throw new UnsupportedOperationException();
    }

    public static  ObservableValue decorate(ObservableCollection delegate) {
        return new ObservableValueFromObservableCollection(delegate, ObservableValueFromObservableCollection::getOnlyElementOrNull, x -> x);
    }

}