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

com.zipwhip.binding.MixedCollection Maven / Gradle / Ivy

package com.zipwhip.binding;

import com.zipwhip.events.ObservableHelper;
import com.zipwhip.events.OrderedDataEventObject;
import com.zipwhip.util.KeyValuePair;
import com.zipwhip.util.OrderedMapHelper;

import java.util.Comparator;
import java.util.EventObject;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 * User: Michael
 * Date: 11/26/11
 * Time: 9:19 PM
 */
public class MixedCollection implements Filterable> {

    /**
     * An item has been added
     */
    private ObservableHelper>> onAdd = new ObservableHelper>>();
    /**
     * An item has been removed
     */
    private ObservableHelper>> onRemove = new ObservableHelper>>();
    /**
     * The collection has been loaded with a new set of data. Update your UI
     */
    private ObservableHelper onLoad = new ObservableHelper();

    /**
     * This is where we store our actual data. This is not filtered.
     */
    protected OrderedMapHelper data;

    /**
     * If we have an active filter on the data, this is where it will appear.
     */
    protected OrderedMapHelper query;

    /**
     * This is the active filter.
     */
    protected Filter> filter;
    protected Comparator> sorter;
    // caching
    private EventObject eventObject;

    public MixedCollection(Map data) {
        this.data = new OrderedMapHelper(data);
    }

    protected MixedCollection(OrderedMapHelper data) {
        this.data = new OrderedMapHelper(data);
    }

    public MixedCollection() {
        this((Map) null);
    }

    public int add(K key, V value) {
        KeyValuePair pair = silentPut(key, value);

        int index = getData().indexOf(key);

        if (this.onAdd != null) {
            this.onAdd.notifyObservers(this, new OrderedDataEventObject>(this, pair, index));
        }

        return index;
    }

    public V getAt(int index) {
        return getData().getAt(index);
    }

    public V get(K key) {
        return getData().get(key);
    }

    /**
     * Store the data but dont throw an event. Also tell me if it went into the query map.
     *
     * @param key
     * @param value
     * @return if the value went into the query map or not.
     */
    protected KeyValuePair silentPut(K key, V value) {
        return silentPut(new KeyValuePair(key, value));
    }

    protected KeyValuePair silentPut(KeyValuePair pair) {
        data.put(pair);

        if (query != null) {
            // we have an active query
            try {
                if (filter.call(pair)) {
                    // it's been filtered out.
                    // do nothing
                } else {
                    // it's been included in the data.
                    query.put(pair);
                }
            } catch (Exception e) {
                // we weren't sure what to do.
                // let's just not do anything?
            }
        }

        return pair;
    }


    /**
     * Query this data structure for a subset of the data. If it's filter.filtered() then
     *
     * @param filter
     * @return
     */
    public MixedCollection query(Filter> filter) {
        OrderedMapHelper data = this.data;
        if (query != null) {
            data = query;
        }

        return new MixedCollection(applyFilter(data, filter));
    }

    public synchronized void sort(Comparator> sorter) {
        this.sorter = sorter;

        // we need to sort the current data structure.
        if (isFiltered()) {
            // there's an active query!
            this.query.sort(sorter);
        } else {
            this.data.sort(sorter);
        }

        if (this.onLoad != null) {
            this.onLoad.notifyObservers(this, null);
        }
    }

    public synchronized void setFilter(Filter> filter) {
        if (query != null) {
            // we already have an active query, we need to nuke it.
            // NOTE: is this ok? should we instead instantiate a new one due to someone else using it?
            // I think it's better to just drop the instance of the map entirely.
            // let the garbage collector handle it because "clearing" the map has no real value.
            // we're just abandoning those internal nodes anyway.
        }

        // we need to keep track of it for adds later.
        this.filter = filter;
        this.query = this.applyFilter(this.data, filter);

        // indicate that we've loaded from scratch and the full UI must be redrawn.
        if (this.onLoad != null) {
            this.onLoad.notifyObservers(this, null);
        }
    }

    protected OrderedMapHelper applyFilter(OrderedMapHelper mapToReadFrom, Filter> filter) {
        OrderedMapHelper result = new OrderedMapHelper();

        for (KeyValuePair kvp : mapToReadFrom.getKeys()) {
            try {
                if (filter.call(kvp)) {
                    result.put(kvp);
                }
            } catch (Exception e) {
                // we don't know what to do?
                // just ignore?
            }
        }

        return result;
    }

    public boolean isFiltered() {
        return query != null;
    }

    public synchronized void clearFilter() {
        // wipe references to the query/filter, let it be garbage collected
        this.query = null;
        this.filter = null;
    }

    /**
     * This could be precalculated to be more efficient, but we're on a desktop environment.
     *
     * @return
     */
    private OrderedMapHelper getData() {
        if (isFiltered()) {
            return query;
        }

        return data;
    }

    /**
     * Same as indexOf, but for the value this time.
     *
     * @param record
     * @return
     */
    public int indexOfValue(V record) {
        return getData().findValue(record);
    }

    public void remove(K key) {

        if (query != null) {
            if (onRemove != null) {
                int index = query.indexOf(key);
                KeyValuePair pair = query.getPair(index);

                // kill it
                query.remove(key);
                data.remove(key);

                // announce
                onRemove.notifyObservers(this, new OrderedDataEventObject>(this, pair, index));
            } else {
                // no one cares about the event, just do it quietly.
                query.remove(key);
                data.remove(key);
            }
        } else {
            if (onRemove != null) {
                int index = data.indexOf(key);
                KeyValuePair pair = data.getPair(index);

                // kill it.
                data.remove(key);

                // announce
                onRemove.notifyObservers(this, new OrderedDataEventObject>(this, pair, index));
            } else {
                // no one cares
                data.remove(key);
            }
        }

    }

    public boolean containsKey(K key) {
        return getData().contains(key);
    }

    public void putAll(MixedCollection changes) {
        OrderedMapHelper data = changes.getData();

        for (KeyValuePair pair : data.getKeys()) {
            silentPut(pair);
        }

        // a big huge change (reload your entire UI)
        if (this.onLoad != null) {
            this.onLoad.notifyObservers(this, null);
        }
    }

    /**
     * for a given item, where does it stand in the list.
     *
     * @param key
     * @return
     */
    public int indexOfKey(K key) {
        return getData().indexOf(key);
    }

    public ObservableHelper>> onAdd() {
        return onAdd;
    }

    public ObservableHelper>> onRemove() {
        return onRemove;
    }


    public int size() {
        return getData().size();
    }

    public ObservableHelper>> getOnAdd() {
        return onAdd;
    }

    public ObservableHelper>> getOnRemove() {
        return onRemove;
    }

    public ObservableHelper getOnLoad() {
        return onLoad;
    }

    public void clear() {
        this.data.clear();
        if (this.query != null){
            this.query.clear();
        }

        if (this.onLoad != null){
            this.onLoad.notifyObservers(this, getEventObject());
        }
    }

    private EventObject getEventObject() {
        if (eventObject == null){
            eventObject = new EventObject(this);
        }

        return eventObject;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy