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

ca.odell.glazedlists.GlazedLists Maven / Gradle / Ivy

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

import ca.odell.glazedlists.FunctionList.Function;
import ca.odell.glazedlists.event.ListEventListener;
import ca.odell.glazedlists.event.ListEventPublisher;
import ca.odell.glazedlists.gui.AdvancedTableFormat;
import ca.odell.glazedlists.gui.TableFormat;
import ca.odell.glazedlists.gui.WritableTableFormat;
import ca.odell.glazedlists.impl.*;
import ca.odell.glazedlists.impl.beans.*;
import ca.odell.glazedlists.impl.filter.StringTextFilterator;
import ca.odell.glazedlists.impl.functions.ConstantFunction;
import ca.odell.glazedlists.impl.matchers.FixedMatcherEditor;
import ca.odell.glazedlists.impl.sort.*;
import ca.odell.glazedlists.matchers.Matcher;
import ca.odell.glazedlists.matchers.MatcherEditor;
import ca.odell.glazedlists.matchers.Matchers;
import ca.odell.glazedlists.util.concurrent.ReadWriteLock;

import java.beans.PropertyChangeEvent;
import java.util.*;

/**
 * A factory for creating all sorts of objects to be used with Glazed Lists.
 *
 * @author Jesse Wilson
 */
public final class GlazedLists {

    /**
     * A dummy constructor to prevent instantiation of this class
     */
    private GlazedLists() {
        throw new UnsupportedOperationException();
    }

    // Utility Methods // // // // // // // // // // // // // // // // // // //

    /**
     * Replace the complete contents of the target {@link EventList} with the complete
     * contents of the source {@link EventList} while making as few list changes
     * as possible.
     *
     * 

In a multi-threaded environment, it is necessary that the caller obtain * the write lock for the target list before this method is invoked. If the * source list is an {@link EventList}, its read lock must also be acquired. * *

This method shall be used when it is necessary to update an EventList * to a newer state while minimizing the number of change events fired. It * is desirable over {@link List#clear() clear()}; {@link List#addAll(Collection) addAll()} * because it will not cause selection to be lost if unnecessary. It is also * useful where firing changes may be expensive, such as when they will cause * writes to disk or the network. * *

This is implemented using Eugene W. Myer's paper, "An O(ND) Difference * Algorithm and Its Variations", the same algorithm found in GNU diff. * *

Note that the runtime of this method is significantly less efficient * in both time and memory than the {@link #replaceAllSorted sorted} version * of replaceAll. * * @param updates whether to fire update events for Objects that are equal in * both {@link List}s. */ public static void replaceAll(EventList target, List source, boolean updates) { Diff.replaceAll(target, source, updates); } /** * Overloaded version of {@link #replaceAll(EventList,List,boolean)} that uses * a {@link Comparator} to determine equality rather than * {@link Object#equals(Object) equals()}. * * @param comparator the {@link Comparator} to determine equality between * elements. This {@link Comparator} must return 0 for * elements that are equal and nonzero for elements that are not equal. * Sort order is not used. */ public static void replaceAll(EventList target, List source, boolean updates, Comparator comparator) { Diff.replaceAll(target, source, updates, comparator); } /** * Replace the complete contents of the target {@link EventList} with the complete * contents of the source {@link Collection} while making as few list changes * as possible. * *

Unlike the {@link #replaceAll general} versions of this method, the * sorted version requires that both the input and the output * are sorted collections, and that they're sorted with the * {@link Comparator} specified. If they're sorted in {@link Comparable natural} * order, use {@link #comparableComparator()}. * *

In a multi-threaded environment, it is necessary that the caller obtain * the write lock for the target list before this method is invoked. If the * source list is an {@link EventList}, its read lock must also be acquired. * *

This method shall be used when it is necessary to update an EventList * to a newer state while minimizing the number of change events fired. It * is desirable over {@link List#clear() clear()}; {@link List#addAll(Collection) addAll()} * because it will not cause selection to be lost if unnecessary. It is also * useful where firing changes may be expensive, such as when they will cause * writes to disk or the network. * *

Note that this method is significantly more efficient in both * time and memory than the {@link #replaceAll general} version of replaceAll. * * @see Collections#sort * @see SortedSet * * @param target an EventList sorted with the {@link Comparator} specified. * Its contents will be replaced with those in source. * @param source a collection sorted with the {@link Comparator} specified. * @param comparator defines the sort order for both target and source. It * should also define identity. Ie, elements that compare to 0 by * this comparator represent the same logical element in the list. If * null, the {@link #comparableComparator} will be used, * which means that all elements must implement {@link Comparable}. * @param updates whether to fire update events for Objects that are equal in * both {@link List}s. */ public static void replaceAllSorted(EventList target, Collection source, boolean updates, Comparator comparator) { GlazedListsImpl.replaceAll(target, source, updates, comparator); } // Comparators // // // // // // // // // // // // // // // // // // // // /** Provide Singleton access for all Comparators with no internal state */ private static Comparator booleanComparator = null; private static Comparator comparableComparator = null; private static Comparator reversedComparable = null; /** * Creates a {@link Comparator} that uses Reflection to compare two * instances of the specified {@link Class} by the given JavaBean * properties. The JavaBean property and any extra * properties must implement {@link Comparable}. * *

The following example code sorts a List of Customers by first name, * with ties broken by last name. * *

     *    List customers = ...
     *    Comparator comparator = GlazedLists.beanPropertyComparator(Customer.class, "firstName", "lastName");
     *    Collections.sort(customers, comparator);
     * 
* * @param clazz the name of the class which defines the accessor method * for the given property and optionaly properties * @param property the name of the first Comparable property to be extracted * and used to compare instances of the clazz * @param properties the name of optional Comparable properties, each of * which is used to break ties for the prior property. */ public static Comparator beanPropertyComparator(Class clazz, String property, String... properties) { // build the Comparator that must exist Comparator firstComparator = beanPropertyComparator(clazz, property, comparableComparator()); // if only one Comparator is specified, return it immediately if (properties.length == 0) return firstComparator; // build the remaining Comparators final List> comparators = new ArrayList>(properties.length+1); comparators.add(firstComparator); for (int i = 0; i < properties.length; i++) comparators.add(beanPropertyComparator(clazz, properties[i], comparableComparator())); // chain all Comparators together return chainComparators(comparators); } /** * Creates a {@link Comparator} that uses Reflection to compare two instances * of the specified {@link Class} by the given JavaBean property. The JavaBean * property is compared using the provided {@link Comparator}. */ public static Comparator beanPropertyComparator(Class className, String property, Comparator propertyComparator) { return new BeanPropertyComparator(className, property, propertyComparator); } /** * Creates a {@link Comparator} for use with {@link Boolean} objects. */ public static Comparator booleanComparator() { if(booleanComparator == null) booleanComparator = new BooleanComparator(); return booleanComparator; } /** * Creates a {@link Comparator} that compares {@link String} objects in * a case-insensitive way. This {@link Comparator} is equivalent to using * {@link String#CASE_INSENSITIVE_ORDER} and exists here for convenience. */ public static Comparator caseInsensitiveComparator() { return String.CASE_INSENSITIVE_ORDER; } /** * Creates a chain of {@link Comparator}s that applies the provided * {@link Comparator}s in the sequence specified until differences or * absolute equality is determined. */ public static Comparator chainComparators(List> comparators) { return new ComparatorChain(comparators); } /** * Creates a chain of {@link Comparator}s that applies the provided * {@link Comparator}s in the sequence specified until differences or * absolute equality is determined. */ public static Comparator chainComparators(Comparator... comparators) { return chainComparators(Arrays.asList(comparators)); } /** * Creates a {@link Comparator} that compares {@link Comparable} objects. */ @SuppressWarnings("unchecked") public static Comparator comparableComparator() { if(comparableComparator == null) comparableComparator = new ComparableComparator(); return (Comparator)comparableComparator; } /** * Creates a reverse {@link Comparator} that works for {@link Comparable} objects. */ @SuppressWarnings("unchecked") public static Comparator reverseComparator() { if(reversedComparable == null) reversedComparable = reverseComparator(comparableComparator()); return (Comparator)reversedComparable; } /** * Creates a reverse {@link Comparator} that inverts the given {@link Comparator}. */ public static Comparator reverseComparator(Comparator forward) { return new ReverseComparator(forward); } // TableFormats // // // // // // // // // // // // // // // // // // // // /** * Creates a {@link TableFormat} that binds JavaBean properties to * table columns via Reflection. */ public static TableFormat tableFormat(String[] propertyNames, String[] columnLabels) { return new BeanTableFormat(null, propertyNames, columnLabels); } /** * Creates a {@link TableFormat} that binds JavaBean properties to * table columns via Reflection. * * @param baseClass the class of the Object to divide into columns. If specified, * the returned class will provide implementation of * {@link AdvancedTableFormat#getColumnClass(int)} and * {@link AdvancedTableFormat#getColumnComparator(int)} by examining the * classes of the column value. */ public static TableFormat tableFormat(Class baseClass, String[] propertyNames, String[] columnLabels) { return new BeanTableFormat(baseClass, propertyNames, columnLabels); } /** * Creates a {@link TableFormat} that binds JavaBean properties to * table columns via Reflection. The returned {@link TableFormat} implements * {@link WritableTableFormat} and may be used for an editable table. */ public static TableFormat tableFormat(String[] propertyNames, String[] columnLabels, boolean[] editable) { return new BeanTableFormat(null, propertyNames, columnLabels, editable); } /** * Creates a {@link TableFormat} that binds JavaBean properties to * table columns via Reflection. The returned {@link TableFormat} implements * {@link WritableTableFormat} and may be used for an editable table. * * @param baseClass the class of the Object to divide into columns. If specified, * the returned class will provide implementation of * {@link AdvancedTableFormat#getColumnClass(int)} and * {@link AdvancedTableFormat#getColumnComparator(int)} by examining the * classes of the column value. */ public static TableFormat tableFormat(Class baseClass, String[] propertyNames, String[] columnLabels, boolean[] editable) { return new BeanTableFormat(baseClass, propertyNames, columnLabels, editable); } // TextFilterators // // // // // // // // // // // // // // // // // // // private static TextFilterator stringTextFilterator = null; /** * Creates a {@link TextFilterator} that searches the given JavaBean * properties. */ public static TextFilterator textFilterator(String... propertyNames) { return new BeanTextFilterator(propertyNames); } /** * Creates a {@link TextFilterator} that searches the given JavaBean * properties. */ public static TextFilterator textFilterator(Class beanClass, String... propertyNames) { return new BeanTextFilterator(beanClass, propertyNames); } /** * Creates a {@link TextFilterator} that searches the given JavaBean * properties. */ public static Filterator filterator(String... propertyNames) { return new BeanTextFilterator(propertyNames); } /** * Creates a {@link TextFilterator} that searches the given JavaBean * properties of the specified class. */ public static Filterator filterator(Class beanClass, String... propertyNames) { return new BeanTextFilterator(beanClass, propertyNames); } /** * Creates a {@link TextFilterator} that searches against an Object's * {@link Object#toString() toString()} value. */ @SuppressWarnings("unchecked") public static TextFilterator toStringTextFilterator() { if(stringTextFilterator == null) stringTextFilterator = new StringTextFilterator(); return (TextFilterator) stringTextFilterator; } // ThresholdEvaluators // // // // // // // // // // // // // // // // // // /** * Creates a {@link ThresholdList.Evaluator} that uses Reflection to utilize an * integer JavaBean property as the threshold evaluation. */ public static ThresholdList.Evaluator thresholdEvaluator(String propertyName) { return new BeanThresholdEvaluator(propertyName); } // CollectionListModels // // // // // // // // // // // // // // // // // /** * Creates a {@link CollectionList.Model} that where {@link List}s or {@link EventList}s * are the elements of a parent {@link EventList}. This can be used to compose * {@link EventList}s from other {@link EventList}s. */ public static CollectionList.Model,E> listCollectionListModel() { return new ListCollectionListModel(); } // EventLists // // // // // // // // // // // // // // // // // // // // // /** * Creates a new {@link EventList} which contains the given elements. * * @param contents the list elements, if null the result will be an empty list * @return the new {@link EventList} */ public static EventList eventListOf(E... contents) { return eventList(contents == null ? Collections.emptyList() : Arrays.asList(contents)); } /** * Creates a new {@link EventList} which contains the contents of the specified * {@link Collection}. The {@link EventList}'s order will be determined by * {@link Collection#iterator() contents.iterator()}. * * @param contents the collection with list elements, if null the result will be * an empty list */ public static EventList eventList(Collection contents) { final EventList result = new BasicEventList(contents == null ? 0 : contents.size()); if(contents != null) result.addAll(contents); return result; } /** * Creates a new {@link EventList} with the given {@link ListEventPublisher} and * {@link ReadWriteLock} which contains the given elements. * * @param publisher the {@link ListEventPublisher} for the new list, may be null * @param lock the {@link ReadWriteLock} for the new list, may be null * @param contents the list elements, if null the result will be an empty list * @return the new {@link EventList} */ public static EventList eventListOf(ListEventPublisher publisher, ReadWriteLock lock, E... contents) { return eventList(publisher, lock, contents == null ? Collections.emptyList() : Arrays .asList(contents)); } /** * Creates a new {@link EventList} with the given {@link ListEventPublisher} and * {@link ReadWriteLock} which contains the contents of the specified * {@link Collection}. The {@link EventList}'s order will be determined by * {@link Collection#iterator() contents.iterator()}. * * @param publisher the {@link ListEventPublisher} for the new list, may be null * @param lock the {@link ReadWriteLock} for the new list, may be null * @param contents the collection with list elements, if null the result will be * an empty list */ public static EventList eventList(ListEventPublisher publisher, ReadWriteLock lock, Collection contents) { final EventList result = new BasicEventList( contents == null ? 0 : contents.size(), publisher, lock); if (contents != null) result.addAll(contents); return result; } /** * Wraps the source in an {@link EventList} that does not allow writing operations. * *

The returned {@link EventList} is useful for programming defensively. A * {@link EventList} is useful to supply an unknown class read-only access * to your {@link EventList}. * *

The returned {@link EventList} will provides an up-to-date view of its source * {@link EventList} so changes to the source {@link EventList} will still be * reflected. For a static copy of any {@link EventList} it is necessary to copy * the contents of that {@link EventList} into an {@link ArrayList}. * *

Warning: This returned EventList * is thread ready but not thread safe. See {@link EventList} for an example * of thread safe code. */ public static TransformedList readOnlyList(EventList source) { return new ReadOnlyList((EventList) source); } /** * Wraps the source in an {@link EventList} that obtains a * {@link ca.odell.glazedlists.util.concurrent.ReadWriteLock ReadWritLock} for all * operations. * *

This provides some support for sharing {@link EventList}s between multiple * threads. * *

Using a {@link ThreadSafeList} for concurrent access to lists can be expensive * because a {@link ca.odell.glazedlists.util.concurrent.ReadWriteLock ReadWriteLock} * is aquired and released for every operation. * *

Warning: Although this class * provides thread safe access, it does not provide any guarantees that changes * will not happen between method calls. For example, the following code is unsafe * because the source {@link EventList} may change between calls to * {@link TransformedList#size() size()} and {@link TransformedList#get(int) get()}: *

 EventList source = ...
     * ThreadSafeList myList = new ThreadSafeList(source);
     * if(myList.size() > 3) {
     *   System.out.println(myList.get(3));
     * }
* *

Warning: The objects returned * by {@link TransformedList#iterator() iterator()}, * {@link TransformedList#subList(int,int) subList()}, etc. are not thread safe. * * @see ca.odell.glazedlists.util.concurrent */ public static TransformedList threadSafeList(EventList source) { return new ThreadSafeList((EventList) source); } /** * Returns a {@link TransformedList} that maps each element of the source list to a target * element by use of a specified {@link Function}. *

* Warning: The returned {@link EventList} is * thread ready but not thread safe. See {@link EventList} for an example of thread safe * code. *

* Warning:This method will * not return a full-featured {@link FunctionList}, but a much simpler * list implementation, that is not writable. * * @param source the source list to transform * @param function the function to tranform each list element * @return a {@link TransformedList} that needs to be disposed after use */ public static TransformedList transformByFunction(EventList source, Function function) { return new SimpleFunctionList(source, function); } /** * Provides a proxy to another ListEventListener that may go out of scope * without explicitly removing itself from the source list's set of * listeners. * *

This exists to solve a garbage collection problem. Suppose I have an * {@link EventList} L and I obtain a {@link ListIterator} for L. * The {@link ListIterator} must listen for change events to L in order * to be consistent. Therefore such an iterator will register itself as a * listener for L. When the iterator goes out of scope (as they usually * do), it will remain as a listener of L. This prevents the iterator * object from ever being garbage collected, though the iterator can never be * never used again! Because iterators can be used very frequently, this will * cause an unacceptable memory leak. * *

Instead of adding the iterator directly as a listener for L, add * a proxy instead. The proxy will retain a WeakReference to the * iterator and forward events to the iterator as long as it is reachable. When * the iterator is no longer reachable, the proxy will remove itself from the * list of listeners for L. All garbage is then available for collection. * * @see java.lang.ref.WeakReference */ public static ListEventListener weakReferenceProxy(EventList source, ListEventListener target) { return new WeakReferenceProxy(source, target); } // ObservableElementList Connectors // // // // // // // // // // // // // /** * Create a new Connector for the {@link ObservableElementList} that works with * JavaBeans' {@link java.beans.PropertyChangeListener}. The methods to add * and remove listeners are detected automatically by examining the bean class * and searching for a method prefixed with "add" or "remove" taking a single * {@link java.beans.PropertyChangeListener} argument. * * * @param beanClass a class with both addPropertyChangeListener(PropertyChangeListener) * and removePropertyChangeListener(PropertyChangeListener), * or similar methods. * @return an ObservableElementList.Connector for the specified class */ public static ObservableElementList.Connector beanConnector(Class beanClass) { return new BeanConnector(beanClass); } /** * Create a new Connector for the {@link ObservableElementList} that works with JavaBeans' * {@link java.beans.PropertyChangeListener}. The methods to add and remove listeners are * detected automatically by examining the bean class and searching for a method prefixed with * "add" or "remove" taking a single {@link java.beans.PropertyChangeListener} argument. *

Use this variant, if you want to control which {@link java.beans.PropertyChangeEvent}s * are delivered to the ObservableElementList. You can match or filter events by name. *

If matchPropertyNames is true, the propertyNames * parameter specifies the set of properties by name whose {@link java.beans.PropertyChangeEvent}s * should be delivered to the ObservableElementList, e.g. property change events for properties * not contained in the specified propertyNames are ignored in this case. * If matchPropertyNames is false, then the specified * propertyNames are filtered, e.g. all but the specified property change events are * delivered to the ObservableElementList. * * @param beanClass a class with both * addPropertyChangeListener(PropertyChangeListener) and * removePropertyChangeListener(PropertyChangeListener), or similar * methods. * @param matchPropertyNames if true, match property change events against the * specified property names, if false filter them * @param propertyNames specifies the properties by name whose {@link java.beans.PropertyChangeEvent}s * should be matched or filtered * @return an ObservableElementList.Connector for the specified class */ public static ObservableElementList.Connector beanConnector(Class beanClass, boolean matchPropertyNames, String... propertyNames) { final Matcher byNameMatcher = Matchers.propertyEventNameMatcher(matchPropertyNames, propertyNames); return beanConnector(beanClass, byNameMatcher); } /** * Create a new Connector for the {@link ObservableElementList} that works with JavaBeans' * {@link java.beans.PropertyChangeListener}. The methods to add and remove listeners are * detected automatically by examining the bean class and searching for a method prefixed with * "add" or "remove" taking a single {@link java.beans.PropertyChangeListener} argument. * *

The event matcher allows filtering of {@link java.beans.PropertyChangeEvent}s. * Only matching events are delivered to the ObservableElementList. * To create a matcher that matches PropertyChangeEvents by property names, you can use * {@link Matchers#propertyEventNameMatcher(boolean, String[])} * * @param beanClass a class with both * addPropertyChangeListener(PropertyChangeListener) and * removePropertyChangeListener(PropertyChangeListener), or similar * methods. * @param eventMatcher for matching PropertyChangeEvents that will be delivered to the * ObservableElementList * @return an ObservableElementList.Connector for the specified class */ public static ObservableElementList.Connector beanConnector(Class beanClass, Matcher eventMatcher) { return new BeanConnector(beanClass, eventMatcher); } /** * Create a new Connector for the {@link ObservableElementList} that works with * JavaBeans' {@link java.beans.PropertyChangeListener}. The methods to add * and remove listeners are specified by name. Such methods must take a single * {@link java.beans.PropertyChangeListener} argument. * * @param beanClass a class with both methods as specified. * @param addListener a method name such as "addPropertyChangeListener" * @param removeListener a method name such as "removePropertyChangeListener" * @return an ObservableElementList.Connector for the specified class */ public static ObservableElementList.Connector beanConnector(Class beanClass, String addListener, String removeListener) { return new BeanConnector(beanClass, addListener, removeListener); } /** * Create a new Connector for the {@link ObservableElementList} that works with * JavaBeans' {@link java.beans.PropertyChangeListener}. The methods to add * and remove listeners are specified by name. Such methods must take a single * {@link java.beans.PropertyChangeListener} argument. * *

The event matcher allows filtering of {@link java.beans.PropertyChangeEvent}s. * Only matching events are delivered to the ObservableElementList. * To create a matcher that matches PropertyChangeEvents by property names, you can use * {@link Matchers#propertyEventNameMatcher(boolean, String[])} * * @param beanClass a class with both methods as specified. * @param addListener a method name such as "addPropertyChangeListener" * @param removeListener a method name such as "removePropertyChangeListener" * @param eventMatcher for matching PropertyChangeEvents that will be delivered to the * ObservableElementList * @return an ObservableElementList.Connector for the specified class */ public static ObservableElementList.Connector beanConnector(Class beanClass, String addListener, String removeListener, Matcher eventMatcher) { return new BeanConnector(beanClass, addListener, removeListener, eventMatcher); } /** * Create a new Connector for the {@link ObservableElementList} that works * with subclasses of the archaic {@link Observable} base class. Each * element of the ObservableElementList must extend the * Observable base class. * * @return an ObservableElementList.Connector for objects that extend {@link Observable} */ public static ObservableElementList.Connector observableConnector() { return new ObservableConnector(); } // Matchers // // // // // // // // // // // // // // // // // // // // // /** * Create a new Matcher which uses reflection to read properties with the * given propertyName from instances of the given * beanClass and compare them with the given value. * * @param beanClass the type of class containing the named bean property * @param propertyName the name of the bean property * @param value the value to compare with the bean property * @return true if the named bean property equals the given value * * @deprecated as of 3/3/2006 - this method has been replaced by * {@link Matchers#beanPropertyMatcher}. {@link Matchers} is now * the permanent factory class which creates all basic Matcher * implementations. */ public static Matcher beanPropertyMatcher(Class beanClass, String propertyName, Object value) { return Matchers.beanPropertyMatcher(beanClass, propertyName, value); } /** * Get a {@link MatcherEditor} that is fixed on the specified {@link Matcher}. */ public static MatcherEditor fixedMatcherEditor(Matcher matcher) { return new FixedMatcherEditor(matcher); } // Functions // // // // // // // // // // // // // // // // // // // // // /** * Get a {@link FunctionList.Function} that always returns the given * value, regardless of its input. */ public static FunctionList.Function constantFunction(V value) { return new ConstantFunction(value); } /** * Get a {@link FunctionList.Function} that extracts the property with the * given propertyName from objects of the given * beanClass and then formats the return value as a String. */ public static FunctionList.Function toStringFunction(Class beanClass, String propertyName) { return new StringBeanFunction(beanClass, propertyName); } /** * Get a {@link FunctionList.Function} that extracts the property with the * given propertyName from objects of the given * beanClass. */ public static FunctionList.Function beanFunction(Class beanClass, String propertyName) { return new BeanFunction(beanClass, propertyName); } // ListEventListeners // // // // // // // // // // // // // // // // // // /** * Synchronize the specified {@link EventList} to the specified {@link List}. * Each time the {@link EventList} is changed, the changes are applied to the * {@link List} as well, so that the two lists are always equal. * *

This is useful when a you need to support a {@link List} datamodel * but would prefer to manipulate that {@link List} with the convenience * of {@link EventList}s: *

List someList = ...
     *
     * // create an EventList with the contents of someList
     * EventList eventList = GlazedLists.eventList(someList);
     *
     * // propagate changes from eventList to someList
     * GlazedLists.syncEventListToList(eventList, someList);
     *
     * // test it out, should print "true, true, true true"
     * eventList.add("boston creme");
     * System.out.println(eventList.equals(someList));
     * eventList.add("crueller");
     * System.out.println(eventList.equals(someList));
     * eventList.remove("bostom creme");
     * System.out.println(eventList.equals(someList));
     * eventList.clear();
     * System.out.println(eventList.equals(someList));
* * @param source the {@link EventList} which provides the master view. * Each change to this {@link EventList} will be applied to the * {@link List}. * @param target the {@link List} to host a copy of the {@link EventList}. * This {@link List} should not be changed after the lists have been * synchronized. Otherwise a {@link RuntimeException} will be thrown * when the drift is detected. This class must support all mutating * {@link List} operations. * @return the {@link ListEventListener} providing the link from the * source {@link EventList} to the target {@link List}. To stop the * synchronization, use * {@link EventList#removeListEventListener(ListEventListener)}. */ public static ListEventListener syncEventListToList(EventList source, List target) { return new SyncListener(source, target); } /** * Check list elements for type safety after they are added to an EventList * using a {@link ListEventListener}. The {@link ListEventListener} which is * installed and returned to the caller (which they may uninstall at their * leisure) will throw an {@link IllegalArgumentException} if it detects the * addition of an element with an unsupported type. * *

* This {@link ListEventListener} is typically used as a tool to check * invariants of the elements of {@link EventList}s during software * development and testing phases. * * @param source * the {@link EventList} on which to provide type safety * @param types * the set of types to which each list element must be assignable * - the set itself must not be null, but null * is an acceptable type within the set and indicates the * {@link EventList} expects to contain null elements * @return the {@link ListEventListener} providing the safety checking on * the given source. To stop the type safety checking, * use {@link EventList#removeListEventListener(ListEventListener)}. */ public static ListEventListener typeSafetyListener(EventList source, Set types) { return new TypeSafetyListener(source, types); } /** * Synchronize the specified {@link EventList} to a MultiMap that is * returned from this method. Each time the {@link EventList} is changed * the MultiMap is updated to reflect the change. * *

This can be useful when it is known that an EventList * will experience very few mutations compared to read operation and wants * to provide a data structure that guarantees fast O(1) reads. * *

The keys of the MultiMap are determined by evaluating each * source element with the keyMaker function. * This form of the MultiMap requires that the keys produced by the * keyMaker are {@link Comparable} and that the natural * ordering of those keys also defines the grouping of values. If either * of those assumptions are false, consider using * {@link #syncEventListToMultiMap(EventList, FunctionList.Function, Comparator)}. * *

If two distinct values, say v1 and v2 each * produce a common key, k, when they are evaluated by the * keyMaker function, then a corresponding entry in the * MultiMap will resemble: * *

k -> {v1, v2} * *

For example, assume the keyMaker function returns the * first letter of a name and the source {@link EventList} * contains the names: * *

{"Andy", "Arthur", "Jesse", "Holger", "James"} * *

The MultiMap returned by this method would thus resemble: * *

* "A" -> {"Andy", "Arthur"}
* "H" -> {"Holger"}
* "J" -> {"Jesse", "James"}
*
* *

It is important to note that all mutating methods on the {@link Map} * interface "write through" to the backing {@link EventList} as expected. * These mutating methods include: * *

    *
  • the mutating methods of {@link Map#keySet()} and its {@link Iterator} *
  • the mutating methods of {@link Map#values()} and its {@link Iterator} *
  • the mutating methods of {@link Map#entrySet()} and its {@link Iterator} *
  • the {@link Map.Entry#setValue} method *
  • the mutating methods of {@link Map} itself, including {@link Map#put}, * {@link Map#putAll}, {@link Map#remove}, and {@link Map#clear} *
* * For information on MultiMaps go here. * * @param source the {@link EventList} which provides the master view. * Each change to this {@link EventList} will be applied to the * MultiMap * @param keyMaker the {@link FunctionList.Function} which produces a key * for each value in the source. It is imperative that the * keyMaker produce immutable objects. * @return a MultiMap which remains in sync with changes that occur to the * underlying source {@link EventList} */ public static DisposableMap> syncEventListToMultiMap(EventList source, FunctionList.Function keyMaker) { return syncEventListToMultiMap(source, keyMaker, comparableComparator()); } /** * Synchronize the specified {@link EventList} to a MultiMap that is * returned from this method. Each time the {@link EventList} is changed * the MultiMap is updated to reflect the change. * *

This can be useful when it is known that an EventList * will experience very few mutations compared to read operation and wants * to provide a data structure that guarantees fast O(1) reads. * *

The keys of the MultiMap are determined by evaluating each * source element with the keyMaker function. * This form of the MultiMap makes no assumptions about the keys of the * MultiMap and relies on the given keyGrouper to define the * grouping of values. * *

If two distinct values, say v1 and v2 each * produce a common key, k, when they are evaluated by the * keyMaker function, then a corresponding entry in the * MultiMap will resemble: * *

k -> {v1, v2} * *

For example, assume the keyMaker function returns the * first letter of a name and the source {@link EventList} * contains the names: * *

{"Andy", "Arthur", "Jesse", "Holger", "James"} * *

The MultiMap returned by this method would thus resemble: * *

* "A" -> {"Andy", "Arthur"}
* "H" -> {"Holger"}
* "J" -> {"Jesse", "James"}
*
* *

It is important to note that all mutating methods on the {@link Map} * interface "write through" to the backing {@link EventList} as expected. * These mutating methods include: * *

    *
  • the mutating methods of {@link Map#keySet()} and its {@link Iterator} *
  • the mutating methods of {@link Map#values()} and its {@link Iterator} *
  • the mutating methods of {@link Map#entrySet()} and its {@link Iterator} *
  • the {@link Map.Entry#setValue} method *
  • the mutating methods of {@link Map} itself, including {@link Map#put}, * {@link Map#putAll}, {@link Map#remove}, and {@link Map#clear} *
* * For information on MultiMaps go here. * * @param source the {@link EventList} which provides the master view. * Each change to this {@link EventList} will be applied to the * MultiMap * @param keyMaker the {@link FunctionList.Function} which produces a key * for each value in the source. It is imperative that the * keyMaker produce immutable objects. * @param keyGrouper the {@link Comparator} which groups together values * that share common keys * @return a MultiMap which remains in sync with changes that occur to the * underlying source {@link EventList} */ public static DisposableMap> syncEventListToMultiMap(EventList source, FunctionList.Function keyMaker, Comparator keyGrouper) { return new GroupingListMultiMap(source, keyMaker, keyGrouper); } /** * Synchronize the specified {@link EventList} to a Map that is returned * from this method. Each time the {@link EventList} is changed the Map is * updated to reflect the change. * *

This can be useful when it is known that an EventList * will experience very few mutations compared to read operation and wants * to provide a data structure that guarantees fast O(1) reads. * *

The keys of the Map are determined by evaluating each * source element with the keyMaker function. * The Map implementation assumes that each value has a unique key, and * verifies this invariant at runtime, throwing a RuntimeException if it * is ever violated. * * For example, if two distinct values, say v1 and * v2 each produce the key k when they are * evaluated by the keyMaker function, an * {@link IllegalStateException} is thrown to proactively indicate the * error. * *

As for example of normal usage, assume the keyMaker * function returns the first letter of a name and the source * {@link EventList} contains the names: * *

{"Kevin", "Jesse", "Holger"} * *

The Map returned by this method would thus resemble: * *

* "K" -> "Kevin"
* "J" -> "Jesse"
* "H" -> "Holger"
*
* *

It is important to note that all mutating methods on the {@link Map} * interface "write through" to the backing {@link EventList} as expected. * These mutating methods include: * *

    *
  • the mutating methods of {@link Map#keySet()} and its {@link Iterator} *
  • the mutating methods of {@link Map#values()} and its {@link Iterator} *
  • the mutating methods of {@link Map#entrySet()} and its {@link Iterator} *
  • the {@link Map.Entry#setValue} method *
* * @param source the {@link EventList} which provides the values of the map. * Each change to this {@link EventList} will be applied to the Map. * @param keyMaker the {@link FunctionList.Function} which produces a key * for each value in the source. It is imperative that the * keyMaker produce immutable objects. * @return a Map which remains in sync with changes that occur to the * underlying source {@link EventList} */ public static DisposableMap syncEventListToMap(EventList source, FunctionList.Function keyMaker) { return new FunctionListMap(source, keyMaker); } }