/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.migrationkit;

// the core Glazed Lists packages
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.FilterList;
import ca.odell.glazedlists.TransformedList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.matchers.AbstractMatcherEditor;
import ca.odell.glazedlists.matchers.Matcher;
import ca.odell.glazedlists.matchers.MatcherEditor;

/**
 * An {@link EventList} that shows a subset of the elements of a source
 * {@link EventList}. This subset is composed of all elements of the source
 * {@link EventList} that match the filter.
 *
 * <p>The filter can be static or dynamic. Changing the behaviour of the filter
 * will change which elements of the source list are included.
 *
 * <p>Extending classes define the filter by implementing the method
 * {@link #filterMatches(Object)}.
 *
 * <p>Extending classes must call {@link #handleFilterChanged()} when the filter
 * has changed in order to update the subset of included elements. This method
 * must also be called at the end of the extending class's constructor.
 *
 * <p><strong><font color="#FF0000">Warning:</font></strong> This class
 * breaks the contract required by {@link java.util.List}. See {@link EventList}
 * for an example.
 *
 * <p><strong><font color="#FF0000">Warning:</font></strong> This class is
 * thread ready but not thread safe. See {@link EventList} for an example
 * of thread safe code.
 *
 * <p><table border="1" width="100%" cellpadding="3" cellspacing="0">
 * <tr class="TableHeadingColor"><td colspan=2><font size="+2"><b>EventList Overview</b></font></td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Writable:</b></td><td>yes</td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Concurrency:</b></td><td>thread ready, not thread safe</td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Performance:</b></td><td>reads: O(log N), writes O(log N), filter changes O(N)</td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Memory:</b></td><td>0 to 26 bytes per element</td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Unit Tests:</b></td><td>N/A</td></tr>
 * <tr><td class="TableSubHeadingColor"><b>Issues:</b></td><td>N/A</td></tr>
 * </table>
 *
 * @deprecated This class uses inheritance when composition is preferrable. By replacing
 *      the overriding method {@link #filterMatches(Object)} with a {@link Matcher} or
 *      {@link MatcherEditor}, logic can be reused. That approach is far more flexible
 *      and powerful than the static filtering required by AbstractFilterList.
 *
 * @since 2004
 * @author <a href="mailto:jesse@swank.ca">Jesse Wilson</a>
 */
public abstract class AbstractFilterList extends TransformedList {

    /** implement Matcher's requirements in one quick inner class */
    private PrivateMatcherEditor editor = null;
    
    /**
     * Creates a {@link AbstractFilterList} that includes a subset of the specified
     * source {@link EventList}.
     *
     * <p>Extending classes must call handleFilterChanged().
     */
    protected AbstractFilterList(EventList source) {
        super(new FilterList(source));
        
        // listen for changes to the source list
        this.source.addListEventListener(this);
    }

    /**
     * Handles a clearing of the filter. That is, the filter list will act as
     * a passthrough and not discriminate any of the elements of the wrapped
     * source list.
     */
    protected void handleFilterCleared() {
        if(editor == null) {
            editor = new PrivateMatcherEditor();
            FilterList filterList = (FilterList)super.source;
            filterList.setMatcherEditor(editor);
        } else {
            editor.fireCleared();
        }
    }

    /**
     * Handles a relaxing or widening of the filter. This may change the
     * contents of this {@link EventList} as filtered elements are unfiltered
     * due to the relaxation of the filter.
     *
     * <p><strong><font color="#FF0000">Warning:</font></strong> This method is
     * thread ready but not thread safe. See {@link EventList} for an example
     * of thread safe code.
     */
    protected final void handleFilterRelaxed() {
        editor.fireRelaxed();
    }

    /**
     * Handles a constraining or narrowing of the filter. This may change the
     * contents of this {@link EventList} as elements are further filtered due
     * to the constraining of the filter.
     *
     * <p><strong><font color="#FF0000">Warning:</font></strong> This method is
     * thread ready but not thread safe. See {@link EventList} for an example
     * of thread safe code.
     */
    protected final void handleFilterConstrained() {
        editor.fireConstrained();
    }

    /**
     * Handles changes to the behavior of the filter. This may change the contents
     * of this {@link EventList} as elements are filtered and unfiltered.
     *
     * <p><strong><font color="#FF0000">Warning:</font></strong> This method is
     * thread ready but not thread safe. See {@link EventList} for an example
     * of thread safe code.
     */
    protected final void handleFilterChanged() {
        if(editor == null) {
            editor = new PrivateMatcherEditor();
            FilterList filterList = (FilterList)super.source;
            filterList.setMatcherEditor(editor);
        } else {
            editor.fireChanged();
        }
    }

    /**
     * Tests if the specified item from the source {@link EventList} is matched by
     * the current filter.
     *
     * @return <tt>true</tt> for elements that match the filter and shall be
     *      included in this {@link EventList} or <tt>false</tt> for elements that
     *      shall not be included in this {@link EventList}.
     */
    public abstract boolean filterMatches(Object element);

    /** {@inheritDoc} */
    public void listChanged(ListEvent listChanges) {
        // just pass on the changes
        updates.forwardEvent(listChanges);
    }

    /** {@inheritDoc} */
    protected boolean isWritable() {
        return true;
    }
    
    /**
     * The MatcherEditor within the {@link AbstractFilterList} is a simple way for
     * it to fit into the new {@link Matcher}s micro framework.
     */
    private class PrivateMatcherEditor extends AbstractMatcherEditor implements Matcher {
        /** 
         * This MatcherEditor's Matcher is itself.
         */
        public PrivateMatcherEditor() {
            this.currentMatcher = this;
        }
    
        /** {@inheritDoc} */
        public boolean matches(Object item) {
            return filterMatches(item);
        }
        public void fireCleared() { super.fireMatchAll(); }
        public void fireRelaxed() { super.fireRelaxed(this); }
        public void fireConstrained() { super.fireConstrained(this); }
        public void fireChanged() { super.fireChanged(this); }
    }


    /** {@inheritDoc} */
    public void dispose() {
        FilterList filteredSource = (FilterList)source;
        super.dispose();
        filteredSource.dispose();
    }
}
