libglazedlists-java-1.9.0+dfsg.orig/0000775000175000017500000000000012161616112016623 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/0000755000175000017500000000000012106516352020125 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/0000755000175000017500000000000012106516352020510 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/0000755000175000017500000000000012106516352021607 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/0000755000175000017500000000000012106516400024126 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/CompositeList.java0000644000175000017500000001450212106516400027571 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEventPublisher; import ca.odell.glazedlists.util.concurrent.ReadWriteLock; import java.util.Iterator; /** * An {@link EventList} composed of multiple source {@link EventList}s. This list * shows the contents of its source lists. * *

Note that all contained {@link EventList}s must use the same {@link ListEventPublisher} and * {@link ReadWriteLock}, particularly if this {@link EventList} is to be used by multiple threads * concurrently. To construct an {@link EventList} that shares the {@link ListEventPublisher} and * {@link ReadWriteLock} with this {@link CompositeList}, use {@link #createMemberList()}. * *

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

* * * * * * * *
EventList Overview
Writable:only {@link #set(int,Object)} and {@link #remove(int)}
Concurrency:not thread safe
Performance:reads: O(log N), writes O(log N)
Memory:96 bytes per element
Unit Tests:N/A
Issues: * 25 * 93 * 96 * 162 *
* * @author Jesse Wilson */ public class CompositeList extends CollectionList, E> { public CompositeList() { super(new BasicEventList>(), (Model)GlazedLists.listCollectionListModel()); } /** * Create a {@link CompositeList} that uses the given lock. Note that this lock * will also be used when {@link #createMemberList building new member lists}. *

* This can be a convenient constructor to use when the member lists are prebuilt ahead of time * with a common {@link ReadWriteLock} and it is desirable to compose their union with a * {@link CompositeList}. * * @param lock the {@link ReadWriteLock} to use within the {@link CompositeList} * @deprecated replaced by {@link #CompositeList(ListEventPublisher, ReadWriteLock)}, because * prebuilt member lists should share lock and publisher with the * CompositeList. */ public CompositeList(ReadWriteLock lock) { super(new BasicEventList>(lock), (Model)GlazedLists.listCollectionListModel()); } /** * Create a {@link CompositeList} that uses the given publisher and * lock. Note that this publisher and lock will also be used when * {@link #createMemberList building new member lists}. *

* This can be a convenient constructor to use when the member lists are prebuilt ahead of time * with a common {@link ListEventPublisher} and {@link ReadWriteLock} and it is desirable to * compose their union with a {@link CompositeList}. * * @param publisher the {@link ListEventPublisher} to use within the {@link CompositeList} * @param lock the {@link ReadWriteLock} to use within the {@link CompositeList} */ public CompositeList(ListEventPublisher publisher, ReadWriteLock lock) { super(new BasicEventList>(publisher, lock), (Model)GlazedLists.listCollectionListModel()); } /** * Adds the specified {@link EventList} as a source to this {@link CompositeList}. *

* To ensure correct behaviour when this {@link CompositeList} is used by multiple threads, the * specified EventList has to share the same {@link ReadWriteLock} and * {@link ListEventPublisher} with this CompositeList. * * @throws IllegalArgumentException if the specified EventList uses a different * {@link ReadWriteLock} or {@link ListEventPublisher} * @see #createMemberList() */ public void addMemberList(EventList member) { if (!getPublisher().equals(member.getPublisher())) throw new IllegalArgumentException("Member list must share publisher with CompositeList"); if (!getReadWriteLock().equals(member.getReadWriteLock())) throw new IllegalArgumentException("Member list must share lock with CompositeList"); source.add(member); } /** * Creates a new {@link EventList} that shares its {@link ReadWriteLock} and * {@link ListEventPublisher} with this {@link CompositeList}. This is * necessary when this {@link CompositeList} will be used by multiple * threads. * *

Note that the created {@link EventList} must be explicitly added as a member * to this {@link CompositeList} using {@link #addMemberList(EventList)}. * * @return a new EventList appropriate for use as a * {#link #addMemberList member list} of this CompositeList */ public EventList createMemberList() { return new BasicEventList(getPublisher(), getReadWriteLock()); } /** * Removes the specified {@link EventList} as a source {@link EventList} * to this {@link CompositeList}. */ public void removeMemberList(EventList list) { for(Iterator> i = source.iterator(); i.hasNext(); ) { if(i.next() == list) { i.remove(); return; } } throw new IllegalArgumentException("Cannot remove list " + list + " which is not in this CompositeList"); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/TextFilterator.java0000644000175000017500000000231112106516400027746 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import java.util.List; /** * An interface through which a list of Strings for a given object * can be extracted for testing whether a filter matches. * * @see Glazed Lists Tutorial * @see GlazedLists#textFilterator(String[]) * * @author Jesse Wilson */ public interface TextFilterator { /** * Gets the specified object as a list of Strings. These Strings * should contain all object information so that it can be compared * to the filter set. * * @param baseList a list that the implementor shall add their filter * strings to via baseList.add(). This may be a non-empty * List and it is an error to call any method other than add(). * @param element the object to extract the filter strings from. */ public void getFilterStrings(List baseList, E element); }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/FreezableList.java0000644000175000017500000001334012106516400027525 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; // the core Glazed Lists package import ca.odell.glazedlists.event.ListEvent; import java.util.ArrayList; import java.util.List; /** * An {@link EventList} that shows the current contents of its source {@link EventList}. * *

When this {@link EventList} is frozen, changes to its source {@link EventList} * will not be reflected. Instead, the {@link FreezableList} will continue to show * the state of its source {@link EventList} at the time it was frozen. * *

When this {@link EventList} is thawed, changes to its source * {@link EventList} will be reflected. * *

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

* * * * * * * * *
EventList Overview
Writable:writable when thawed (default), not writable when frozen
Concurrency:thread ready, not thread safe
Performance:reads: O(1), writes O(1), freezes O(N)
Memory:frozen: 4 bytes per element, thawed: 0 bytes per element
Unit Tests:N/A
Issues:N/A
Issues: *
* * @author Jesse Wilson */ public final class FreezableList extends TransformedList { /** the state of the freezable list */ private boolean frozen = false; /** the frozen objects */ private List frozenData = new ArrayList(); /** * Creates a {@link FreezableList} that can freeze the view of the specified * source {@link EventList}. */ public FreezableList(EventList source) { super(source); source.addListEventListener(this); } /** {@inheritDoc} */ @Override public E get(int index) { if(frozen) { return frozenData.get(index); } else { return source.get(index); } } /** {@inheritDoc} */ @Override public int size() { if(frozen) { return frozenData.size(); } else { return source.size(); } } /** {@inheritDoc} */ @Override protected boolean isWritable() { return !frozen; } /** * Gets whether this {@link EventList} is showing a previous state of the source * {@link EventList}. * * @return true if this list is showing a previous state of the source * {@link EventList} or false if this is showing the current state * of the source {@link EventList}. */ public boolean isFrozen() { return frozen; } /** * Locks this {@link FreezableList} on the current state of the source * {@link EventList}. While frozen, changes to the source {@link EventList} * will not be reflected by this list. * *

Warning: This method is * thread ready but not thread safe. See {@link EventList} for an example * of thread safe code. */ public void freeze() { if(frozen) throw new IllegalStateException("Cannot freeze a list that is already frozen"); // we are no longer interested in update events source.removeListEventListener(this); // copy the source array into the frozen list frozenData.addAll(source); // mark this list as frozen frozen = true; } /** * Unlocks this {@link FreezableList} to show the same contents of the source * {@link EventList}. When thawed, changes to the source {@link EventList} * will be reflected by this list. * *

Warning: This method is * thread ready but not thread safe. See {@link EventList} for an example * of thread safe code. */ public void thaw() { if(!frozen) throw new IllegalStateException("Cannot thaw a list that is not frozen"); // prep events to listeners of the thaw updates.beginEvent(); for(int i = 0, size = frozenData.size(); i < size; i++) { updates.elementDeleted(0, frozenData.get(i)); } for(int i = 0, size = source.size(); i < size; i++) { updates.elementInserted(0, source.get(i)); } // we don't need our frozen data anymore frozenData.clear(); frozen = false; // being listening to update events source.addListEventListener(this); // fire off the thaw event updates.commitEvent(); } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { if(frozen) { // when a list change event arrives and this list is frozen, // it is possible that the event was queued before this list // was frozen. for this reason we do not throw any exceptions // but instead silently ignore the event } else { // just pass on the changes updates.forwardEvent(listChanges); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/Filterator.java0000644000175000017500000000151012106516400027101 0ustar gregoagregoapackage ca.odell.glazedlists; import java.util.List; /** * An interface for extracting a list of values to be considered by a Matcher * when matching a particular element of a list. * * @author James Lemieux */ public interface Filterator { /** * Extracts the list of filterable values from a given element. * These values will be accessed within a Matcher to judge whether the * element matches some criteria. * * @param baseList a list that the implementor shall add their filter * values to via baseList.add(). This may be a non-empty * List and it is an error to call any method other than add(). * @param element the object to extract the filter values from */ public void getFilterValues(List baseList, E element); }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/migrationkit/0000755000175000017500000000000012106516376026643 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/migrationkit/AbstractFilterList.java0000644000175000017500000001620012106516376033252 0ustar gregoagregoa/* 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. * *

The filter can be static or dynamic. Changing the behaviour of the filter * will change which elements of the source list are included. * *

Extending classes define the filter by implementing the method * {@link #filterMatches(Object)}. * *

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. * *

Warning: This class * breaks the contract required by {@link java.util.List}. See {@link EventList} * for an example. * *

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

* * * * * * * *
EventList Overview
Writable:yes
Concurrency:thread ready, not thread safe
Performance:reads: O(log N), writes O(log N), filter changes O(N)
Memory:0 to 26 bytes per element
Unit Tests:N/A
Issues:N/A
* * @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 Jesse Wilson */ 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}. * *

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. * *

Warning: 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. * *

Warning: 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. * *

Warning: 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 true for elements that match the filter and shall be * included in this {@link EventList} or false for elements that * shall not be included in this {@link EventList}. */ public abstract boolean filterMatches(Object element); /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { // just pass on the changes updates.forwardEvent(listChanges); } /** {@inheritDoc} */ @Override 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() { fireChanged(); } /** {@inheritDoc} */ public boolean matches(Object item) { return filterMatches(item); } public void fireCleared() { fireMatchAll(); } public void fireRelaxed() { fireRelaxed(this); } public void fireConstrained() { fireConstrained(this); } public void fireChanged() { fireChanged(this); } } /** {@inheritDoc} */ @Override public void dispose() { FilterList filteredSource = (FilterList)source; super.dispose(); filteredSource.dispose(); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/migrationkit/swing/0000755000175000017500000000000012106516376027772 5ustar gregoagregoa././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/migrationkit/swing/TextFilterList.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/migrationkit/swing/TextFilterList.ja0000644000175000017500000002147712106516376033247 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.migrationkit.swing; // the core Glazed Lists packages import ca.odell.glazedlists.*; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.swing.TextComponentMatcherEditor; import javax.swing.*; import javax.swing.event.DocumentListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** * An {@link EventList} that shows only elements that contain a filter text string. * The {@link TextFilterList} uses a {@link JTextField} to allow the user to edit * the filter text. As this filter text is edited, the contents of the * {@link TextFilterList} are changed to reflect the elements that match the text. * *

The {@link TextFilterList} either requires that a {@link TextFilterator} be * specified in its constructor, or that every object in the source list implements * the {@link TextFilterable} interface. These are used to specify the {@link String}s * to search for each element. * *

The {@link TextFilterList} initially refilters the list after each change in * the {@link JTextField}. If this live filtering does not have adequate performance, * it can be turned off. In this case, the list will refiltered by pressing * ENTER in the {@link JTextField} and on every action to the {@link ActionListener}. * This {@link ActionListener} will be returned from the method {@link #getFilterActionListener()} * and can be used to refilter in response to a button click. * *

Warning: This class * breaks the contract required by {@link java.util.List}. See {@link EventList} * for an example. * *

* * * * * * * *
EventList Overview
Writable:yes
Concurrency:thread ready, not thread safe
Performance:reads: O(log N), writes O(log N), filter changes O(N)
Memory:O(N)
Unit Tests:N/A
Issues:N/A
* * @deprecated This class uses inheritance when composition is preferrable. Instead * of TextFilterList, use {@link FilterList} and {@link TextComponentMatcherEditor}. * * @author Jesse Wilson */ public class TextFilterList extends TransformedList { /** the text component matcher editor does all the real work */ private TextComponentMatcherEditor matcherEditor; /** the field where the filter strings are edited */ private JTextField filterEdit = null; /** * Creates a {@link TextFilterList} that filters the specified {@link EventList} * of elements that implement the {@link TextFilterable} interface. */ public TextFilterList(EventList source) { this(source, (TextFilterator)null, new JTextField("")); } /** * Creates a {@link TextFilterList} that filters the specified {@link EventList} * of elements using the specified {@link TextFilterator} to get the * {@link String}s to search. */ public TextFilterList(EventList source, TextFilterator filterator) { this(source, filterator, new JTextField("")); } /** * Creates a {@link TextFilterList} that filters the specified {@link EventList} * of elements using the JavaBeans property names specified to get the * {@link String}s to search. * *

Note that the classes which will be obfuscated may not work with * reflection. In this case, implement a {@link TextFilterator} manually. * * @param propertyNames an array of property names in the JavaBeans format. * For example, if your list contains Objects with the methods getFirstName(), * setFirstName(String), getAge(), setAge(Integer), then this array should * contain the two strings "firstName" and "age". This format is specified * by the JavaBeans {@link java.beans.PropertyDescriptor}. */ public TextFilterList(EventList source, String[] propertyNames) { this(source, GlazedLists.textFilterator(propertyNames), new JTextField("")); } /** * Creates a {@link TextFilterList} that filters the specified {@link EventList} * of elements using the JavaBeans property names specified to get the * {@link String}s to search. * *

Note that the classes which will be obfuscated may not work with * reflection. In this case, implement a {@link TextFilterator} manually. * * @param propertyNames an array of property names in the JavaBeans format. * For example, if your list contains Objects with the methods getFirstName(), * setFirstName(String), getAge(), setAge(Integer), then this array should * contain the two strings "firstName" and "age". This format is specified * by the JavaBeans {@link java.beans.PropertyDescriptor}. * @param filterEdit a text field for typing in the filter text. */ public TextFilterList(EventList source, String[] propertyNames, JTextField filterEdit) { this(source, GlazedLists.textFilterator(propertyNames), filterEdit); } /** * Creates a {@link TextFilterList} that filters the specified {@link EventList} * of elements using the specified {@link TextFilterator} to get the * {@link String}s to search. * * @param filterEdit a text field for typing in the filter text. */ public TextFilterList(EventList source, TextFilterator filterator, JTextField filterEdit) { super(new FilterList(source)); this.matcherEditor = new TextComponentMatcherEditor(filterEdit, filterator); this.filterEdit = filterEdit; ((FilterList)this.source).setMatcherEditor(matcherEditor); // handle changes this.source.addListEventListener(this); } /** * Gets the {@link JTextField} used to edit the filter search {@link String}. */ public JTextField getFilterEdit() { return filterEdit; } /** * Sets the {@link JTextField} used to edit the filter search {@link String}. */ public void setFilterEdit(JTextField filterEdit) { if(filterEdit == this.filterEdit) return; // clean up the old matcher editor boolean live = matcherEditor.isLive(); TextFilterator textFilterator = matcherEditor.getFilterator(); matcherEditor.dispose(); // prepare the new matcher editor this.matcherEditor = new TextComponentMatcherEditor(filterEdit, textFilterator, live); ((FilterList)source).setMatcherEditor(matcherEditor); } /** * Directs this filter to respond to changes to the {@link JTextField} as they are * made. This uses a {@link DocumentListener} and every time the * {@link JTextField} is modified, the list is refiltered. * *

To avoid the processing overhead of filtering for each keystroke, use * a not-live filter edit and trigger the {@link ActionListener} using a * button or by pressing ENTER in the {@link JTextField}. */ public void setLive(boolean live) { matcherEditor.setLive(live); } /** * Gets an {@link ActionListener} that refilters the list when it is fired. This * listener can be used to filter when the user presses a button. */ public ActionListener getFilterActionListener() { return new FilterActionListener(); } /** * Implement the {@link ActionListener} interface for text filter updates. When * the user clicks a button (supplied by external code), this * {@link ActionListener} can be used to update the filter in response. */ private class FilterActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { matcherEditor.setFilterText(filterEdit.getText().split("[ \t]")); } } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { // just pass on the changes updates.forwardEvent(listChanges); } /** {@inheritDoc} */ @Override protected boolean isWritable() { return true; } /** {@inheritDoc} */ @Override public void dispose() { FilterList filteredSource = (FilterList)source; super.dispose(); filteredSource.dispose(); matcherEditor.dispose(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/Sequencers.java0000644000175000017500000000551212106516400027111 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.impl.GlazedListsImpl; import java.util.Calendar; import java.util.Date; /** * A factory for creating Sequencers. * * @author James Lemieux */ public final class Sequencers { /** * A dummy constructor to prevent instantiation of this class */ private Sequencers() { throw new UnsupportedOperationException(); } // Sequencers // // // // // // // // // // // // // // // // // // // // // public static SequenceList.Sequencer monthSequencer() { return new MonthSequencer(); } /** * This Sequencer produces a sequence of {@link Date} objects normalized * to the first millisecond of each month. */ private static final class MonthSequencer implements SequenceList.Sequencer { /** A shared Calendar; it is assumed this Sequencer is only access from a single Thread. */ private final Calendar cal = Calendar.getInstance(); /** * The previous month in the sequence. For example: * *

*/ public Date previous(Date date) { if (date == null) throw new IllegalArgumentException("date may not be null"); cal.setTime(date); // if cal is on the month boundary, rollback to the previous month if (GlazedListsImpl.isMonthStart(cal)) cal.add(Calendar.MONTH, -1); // normalize the Date to the first millisecond of the month return GlazedListsImpl.getMonthStart(cal); } /** * The next month in the sequence. For example: * * */ public Date next(Date date) { if (date == null) throw new IllegalArgumentException("date may not be null"); cal.setTime(date); cal.add(Calendar.MONTH, 1); // normalize the Date to the first millisecond of the month return GlazedListsImpl.getMonthStart(cal); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/ThresholdList.java0000644000175000017500000003231312106516400027563 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; // to use standard collections import java.util.Comparator; /** * An {@link EventList} that shows a range of the elements of the source * {@link EventList}. Each element in the source {@link EventList} is assigned * an integer value via an {@link Evaluator}. This integer is used * to determine whether the element fits in the {@link ThresholdList}s range. * *

By modifying the upper and lower thresholds in the range, the list can * be filtered in a simple and powerful way. * *

The {@link ThresholdList} lends itself to use with a slider widget for * manipulating one of the range's endpoints. * *

One use case for {@link ThresholdList} is in a media player application. * By creating a {@link Evaluator} for a song's bitrate, the user could * limit results to MP3 files between 192 and 320kbps. * *

Note that the elements in the {@link ThresholdList} will be presented in * order sorted by their {@link Evaluator} value. * *

This {@link EventList} supports all write operations. * *

Warning: This class * breaks the contract required by {@link java.util.List}. See {@link EventList} * for an example. * *

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

* * * * * * * *
EventList Overview
Writable:yes
Concurrency:thread ready, not thread safe
Performance:reads: O(log N), writes O(log N), change threshold O(log N)
Memory:72 bytes per element
Unit Tests:N/A
Issues: * 47 * 137 * 217 * 218 * 246 * 277 *
* * @author Kevin Maltby */ public final class ThresholdList extends RangeList { /** the lower bound to use to define list containment */ private int lowerThreshold = Integer.MIN_VALUE; /** the upper bound to use to define list containment */ private int upperThreshold = Integer.MAX_VALUE; /** the evaluator to use to compare Objects against the threshold */ private Evaluator evaluator = null; /** a sorted view of the source makes threshold operations really fast */ private final SortedList sortedSource; /** * Creates a {@link ThresholdList} that provides range-filtering based on the * specified {@link EventList} based on the specified integer JavaBean property. */ public ThresholdList(EventList source, String propertyName) { this(source, (Evaluator) GlazedLists.thresholdEvaluator(propertyName)); } /** * Creates a {@link ThresholdList} that provides range-filtering on the * specified {@link EventList} using the specified {@link Evaluator}. */ public ThresholdList(EventList source, Evaluator evaluator) { this(new SortedList(source, new ThresholdComparator(evaluator)), evaluator); } private ThresholdList(SortedList sortedSource, Evaluator evaluator) { super(sortedSource); this.sortedSource = sortedSource; this.evaluator = evaluator; } /** * Sets the lower threshold for this list to be the result of calling * {@link Evaluator#evaluate(Object) evaluate()} on the given object. * *

This list can be used programmatically rather than hooking it up to * a UI component. Calling this method directly while this list * is connected to a particular widget could result in errors. * *

Warning: This method is * thread ready but not thread safe. See {@link EventList} for an example * of thread safe code. */ public void setLowerThreshold(E object) { setLowerThreshold(evaluator.evaluate(object)); } /** * Sets the lower threshold for this list. * *

This list can be used programmatically rather than hooking it up to * a UI component. Calling this method directly while this list * is connected to a particular widget could result in errors. * *

Warning: This method is * thread ready but not thread safe. See {@link EventList} for an example * of thread safe code. */ public void setLowerThreshold(int lowerThreshold) { this.lowerThreshold = lowerThreshold; adjustRange(); } /** * Gets the lower threshold for this list */ public int getLowerThreshold() { return lowerThreshold; } /** * Sets the upper threshold for this list to be the result of calling * {@link Evaluator#evaluate(Object) evaluate()} on the given object. * *

This list can be used programmatically rather than hooking it up to * a UI component. Calling this method directly while this list * is connected to a particular widget could result in errors. * *

Warning: This method is * thread ready but not thread safe. See {@link EventList} for an example * of thread safe code. */ public void setUpperThreshold(E object) { setUpperThreshold(evaluator.evaluate(object)); } /** * Sets the upper threshold for this list. * *

Warning: This method is * thread ready but not thread safe. See {@link EventList} for an example * of thread safe code. */ public void setUpperThreshold(int upperThreshold) { this.upperThreshold = upperThreshold; adjustRange(); } /** * Gets the upper threshold for this list */ public int getUpperThreshold() { return upperThreshold; } /** * A convenience method to allow access to the {@link Evaluator} * that was provided on construction. */ public Evaluator getEvaluator() { return evaluator; } /** {@inheritDoc} */ @Override public boolean contains(Object object) { // Fast fail if the object isn't within the thresholds // Note: this technically breaks the contract for contains. // evaluator.evaluate(object) may throw a ClassCastException if(!withinRange((E)object)) return false; return source.contains(object); } /** {@inheritDoc} */ @Override public int indexOf(Object object) { // Fast fail if the object isn't within the thresholds // Note: this technically breaks the contract for indexOf. // evaluator.evaluate(object) may throw a ClassCastException if(!withinRange((E)object)) return -1; return source.indexOf(object); } /** {@inheritDoc} */ @Override public int lastIndexOf(Object object) { // Fast fail if the object isn't within the thresholds // Note: this technically breaks the contract for lastIndexOf. // evaluator.evaluate(object) may throw a ClassCastException if(!withinRange((E)object)) return -1; return source.lastIndexOf(object); } /** * Test if the specified object is within the range of this {@link ThresholdList}. */ private boolean withinRange(E object) { int objectEvaluation = evaluator.evaluate(object); return objectEvaluation >= lowerThreshold && objectEvaluation <= upperThreshold; } /** {@inheritDoc} */ @Override public void setRange(int startIndex, int endIndex) { // this implementation is slightly inconsistent with the superclass // because the super treats endIndex as exclusive wheras we treat // endIndex as inclusive this.lowerThreshold = sourceIndexToThreshold(startIndex); this.upperThreshold = sourceIndexToThreshold(endIndex); adjustRange(); } /** {@inheritDoc} */ @Override public void setTailRange(int startIndex, int endIndex) { // this implementation is slightly inconsistent with the superclass // because the super treats endIndex as exclusive wheras we treat // endIndex as inclusive this.lowerThreshold = sourceIndexToThreshold(source.size() - startIndex); this.upperThreshold = sourceIndexToThreshold(source.size() - endIndex); adjustRange(); } /** * Given an index into the source {@link EventList}, get the * threshold value for that index. */ private int sourceIndexToThreshold(int sourceIndex) { if(sourceIndex < 0) { return Integer.MIN_VALUE; } else if(sourceIndex < source.size()) { return evaluator.evaluate(source.get(sourceIndex)); } else { return Integer.MIN_VALUE; } } /** {@inheritDoc} */ @Override public int getStartIndex() { return sortedSource.sortIndex(new Integer(lowerThreshold)); } /** {@inheritDoc} */ @Override public int getEndIndex() { // search for the upperThreshold value int index = sortedSource.lastSortIndex(new Integer(upperThreshold)); // if the upperThreshold exists in the sortedSource, convert the exclusive index to an inclusive index if (index < sortedSource.size() && evaluator.evaluate(sortedSource.get(index)) == upperThreshold) index++; return index; } /** {@inheritDoc} */ @Override public void dispose() { sortedSource.dispose(); super.dispose(); } /** * Provide an integer value for a given {@link Object} in a * {@link ThresholdList}. */ public interface Evaluator { /** * Returns an integer value for an {@link Object} to be used to * compare that object against a threshold. This value is * not relative to any other object unlike a {@link Comparator}. */ public int evaluate(E object); } /** * A ThresholdComparator is a simple helper class that wraps * an {@link Evaluator} with a Comparator to * be used for sorting of the ThresholdList. */ static final class ThresholdComparator implements Comparator { /** the underlying evaluator */ private Evaluator evaluator = null; /** * Creates a new ThresholdComparator */ ThresholdComparator(Evaluator evaluator) { this.evaluator = evaluator; } /** * Compares two Objects, and compares them using the result * given when each Object is evaluated using the underlying * {@link Evaluator}. * *

This method is dual-mode as in the case of the Objects passed being * Integers, it returns the value of * ((Integer)alpha).intValue() - ((Integer)beta).intValue(). * This is necessary so that a threshold value can be compared against an * Object, and vice versa. This can cause problems however * if the underlying {@link Evaluator} were to return the negation * of an Integer. */ public int compare(E alpha, E beta) { int alphaValue; if(alpha instanceof Integer) alphaValue = ((Integer)alpha).intValue(); else alphaValue = evaluator.evaluate(alpha); int betaValue; if(beta instanceof Integer) betaValue = ((Integer)beta).intValue(); else betaValue = evaluator.evaluate(beta); if(alphaValue > betaValue) return 1; else if(alphaValue < betaValue) return -1; else return 0; } /** {@inheritDoc} */ @Override public boolean equals(Object o) { if(this == o) return true; if(o == null || getClass() != o.getClass()) return false; final ThresholdComparator that = (ThresholdComparator) o; if(!evaluator.equals(that.evaluator)) return false; return true; } /** {@inheritDoc} */ @Override public int hashCode() { return evaluator.hashCode(); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/TransformedList.java0000644000175000017500000001426112106516400030115 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; // the Glazed Lists' change objects import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import java.util.Collection; /** * A convenience class for {@link EventList}s that decorate another {@link EventList}. * Extending classes transform their source {@link EventList} by modifying the * order, visibility and value of its elements. * *

Extending classes may implement the method {@link #getSourceIndex(int)} to * translate between indices of this and indices of the source. * *

Extending classes may implement the method {@link #isWritable()} to make the * source writable via this API. * *

Extending classes must explicitly call {@link #addListEventListener(ListEventListener)} * to receive change notifications from the source {@link EventList}. * *

Warning: This class is * thread ready but not thread safe. See {@link EventList} for an example * of thread safe code. * * @author Jesse Wilson */ public abstract class TransformedList extends AbstractEventList implements ListEventListener { /** the event list to transform */ protected EventList source; /** * Creates a {@link TransformedList} to transform the specified {@link EventList}. * * @param source the {@link EventList} to transform */ protected TransformedList(EventList source) { super(source.getPublisher()); this.source = source; readWriteLock = source.getReadWriteLock(); } /** * Gets the index in the source {@link EventList} that corresponds to the * specified index. More formally, returns the index such that *
this.get(i) == source.get(getSourceIndex(i)) for all * legal values of i. */ protected int getSourceIndex(int mutationIndex) { return mutationIndex; } /** * Gets whether the source {@link EventList} is writable via this API. * *

Extending classes must override this method in order to make themselves * writable. */ protected abstract boolean isWritable(); /** {@inheritDoc} */ public abstract void listChanged(ListEvent listChanges); /** {@inheritDoc} */ @Override public void add(int index, E value) { if(!isWritable()) throw new IllegalStateException("Non-writable List cannot be modified"); if(index < 0 || index > size()) throw new IndexOutOfBoundsException("Cannot add at " + index + " on list of size " + size()); final int sourceIndex = index < size() ? getSourceIndex(index) : source.size(); source.add(sourceIndex, (S) value); } /** {@inheritDoc} */ @Override public boolean addAll(int index, Collection values) { // nest changes and let the other methods compose the event updates.beginEvent(true); try { return super.addAll(index, values); } finally { updates.commitEvent(); } } /** {@inheritDoc} */ @Override public void clear() { // nest changes and let the other methods compose the event updates.beginEvent(true); try { super.clear(); } finally { updates.commitEvent(); } } /** {@inheritDoc} */ @Override public E get(int index) { if(index < 0 || index >= size()) throw new IndexOutOfBoundsException("Cannot get at " + index + " on list of size " + size()); return (E) source.get(getSourceIndex(index)); } /** {@inheritDoc} */ @Override public E remove(int index) { if(!isWritable()) throw new IllegalStateException("Non-writable List cannot be modified"); if(index < 0 || index >= size()) throw new IndexOutOfBoundsException("Cannot remove at " + index + " on list of size " + size()); return (E) source.remove(getSourceIndex(index)); } /** {@inheritDoc} */ @Override public boolean removeAll(Collection collection) { // nest changes and let the other methods compose the event updates.beginEvent(true); try { return super.removeAll(collection); } finally { updates.commitEvent(); } } /** {@inheritDoc} */ @Override public boolean retainAll(Collection values) { // nest changes and let the other methods compose the event updates.beginEvent(true); try { return super.retainAll(values); } finally { updates.commitEvent(); } } /** {@inheritDoc} */ @Override public E set(int index, E value) { if(!isWritable()) throw new IllegalStateException("List " + this.getClass().getName() + " cannot be modified in the current state"); if(index < 0 || index >= size()) throw new IndexOutOfBoundsException("Cannot set at " + index + " on list of size " + size()); return (E) source.set(getSourceIndex(index), (S) value); } /** {@inheritDoc} */ @Override public int size() { return source.size(); } /** * Releases the resources consumed by this {@link TransformedList} so that it * may eventually be garbage collected. * *

A {@link TransformedList} will be garbage collected without a call to * {@link #dispose()}, but not before its source {@link EventList} is garbage * collected. By calling {@link #dispose()}, you allow the {@link TransformedList} * to be garbage collected before its source {@link EventList}. This is * necessary for situations where a {@link TransformedList} is short-lived but * its source {@link EventList} is long-lived. * *

Warning: It is an error * to call any method on a {@link TransformedList} after it has been disposed. */ public void dispose() { source.removeListEventListener(this); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/TextFilterable.java0000644000175000017500000000201612106516400027706 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import java.util.List; /** * An item that can be compared to a list of filters to see if it matches. * * @see Glazed Lists Tutorial * * @author Jesse Wilson */ public interface TextFilterable { /** * Gets this object as a list of Strings. These Strings * should contain all object information so that it can be compared * to the filter set. * * @param baseList a list that the implementor shall add their filter * strings to via baseList.add(). This may be a non-empty * List and it is an error to call any method other than add(). */ public void getFilterStrings(List baseList); }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/RangeList.java0000644000175000017500000002360512106516400026667 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; /** * This {@link EventList} shows values from a continuous range of indices from * a source {@link EventList}. It can be used to limit the length of a list to * a desired size. * *

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

* * * * * * * *
EventList Overview
Writable:yes
Concurrency:thread ready, not thread safe
Performance:reads: O(1), writes O(1), change range O(1)
Memory:0 bytes per element
Unit Tests:N/A
Issues: * 238 * 278 *
* * @author Jesse Wilson */ public class RangeList extends TransformedList { /** the user-specified range of the source list to include */ private int desiredStart = 0; private int desiredEnd = -1; /** the first index in this list, inclusive */ private int currentStartIndex; /** the last index in this list, exclusive */ private int currentEndIndex; /** * Create a new {@link RangeList} that limits the specified {@link EventList} * to a desired range. */ public RangeList(EventList source) { super(source); currentStartIndex = 0; currentEndIndex = source.size(); source.addListEventListener(this); } /** {@inheritDoc} */ @Override public final void listChanged(ListEvent listChanges) { // This EventList handles changes to the source EventList using a // two-phase approach: // 1. The change event is iterated and the current bound indices are // offset to reflect the change. Each change event within the // range of indices is forwarded. // 2. In the second phase, during setRange(), the current indices // are adjusted back to their previously set 'desired values' // if possible. updates.beginEvent(true); // propagate the event and offset local indices while(listChanges.next()) { int changeType = listChanges.getType(); int changeIndex = listChanges.getIndex(); E oldValue = listChanges.getOldValue(); E newValue = listChanges.getNewValue(); if(changeType == ListEvent.DELETE) { if(changeIndex < currentStartIndex) { currentStartIndex--; currentEndIndex--; } else if(changeIndex < currentEndIndex) { currentEndIndex--; updates.elementDeleted(changeIndex - currentStartIndex, oldValue); } } else if(changeType == ListEvent.INSERT) { if(changeIndex < currentStartIndex) { currentStartIndex++; currentEndIndex++; } else if(changeIndex < currentEndIndex) { currentEndIndex++; updates.elementInserted(changeIndex - currentStartIndex, newValue); } } else if(changeType == ListEvent.UPDATE) { if(changeIndex >= currentStartIndex && changeIndex < currentEndIndex) { updates.elementUpdated(changeIndex - currentStartIndex, oldValue, newValue); } } } // adjust the displayed range to accomodate for the source changes adjustRange(); updates.commitEvent(); } /** * Set the range of values displayed by this {@link RangeList}. * * @param startIndex the first index of the source {@link EventList} to show, inclusive * @param endIndex the last index of the source {@link EventList} to show, exclusive * * @deprecated 2/15/2006 use {@link #setHeadRange(int, int)} instead. The * introduction of {@link #setMiddleRange(int, int)} caused us to want a * consistent naming scheme for all set*Range methods. */ public void setRange(int startIndex, int endIndex) { this.setHeadRange(startIndex, endIndex); } /** * Set the range of values displayed by this {@link RangeList}. * * @param startIndex the first index of the source {@link EventList} to show, inclusive * @param endIndex the last index of the source {@link EventList} to show, exclusive */ public void setHeadRange(int startIndex, int endIndex) { this.desiredStart = startIndex; this.desiredEnd = endIndex; adjustRange(); } /** * Set the range to include the specified indices, the startIndex offset from the * front of the source {@link EventList} and the endIndex offset from the end * of the source {@link EventList}. * *

For example, to include everything but the first element, use * RangeList.setMiddleRange(1, 0);. * *

For example, to include everything but the last element, use * RangeList.setMiddleRange(0, 1);. */ public void setMiddleRange(int startIndex, int endIndex) { this.desiredStart = startIndex; this.desiredEnd = -endIndex - 1; adjustRange(); } /** * Set the range to include the specified indices, offset from the end of * the source {@link EventList}. For example, to show the last five values, use: * RangeList.setTailRange(5, 0); * *

To include the 3rd last and 2nd last values, use: * RangeList.setTailRange(3, 1);. */ public void setTailRange(int startIndex, int endIndex) { this.desiredStart = -startIndex - 1; this.desiredEnd = -endIndex - 1; adjustRange(); } /** * Adjust the range of the {@link RangeList} in response to changes in the * source list or the desired start and end indices. */ protected final void adjustRange() { updates.beginEvent(true); // get the new range int desiredStartIndex = getStartIndex(); int desiredEndIndex = getEndIndex(); // normalize the range so start index is the smallest index and end index is the largest if (desiredEndIndex < desiredStartIndex) { int temp = desiredEndIndex; desiredEndIndex = desiredStartIndex; desiredStartIndex = temp; } // insert before the beginning if(desiredStartIndex < currentStartIndex) { updates.addInsert(0, currentStartIndex - desiredStartIndex - 1); // delete thru to the new beginning } else if(currentStartIndex < desiredStartIndex && currentStartIndex < currentEndIndex) { int deleteThru = Math.min(desiredStartIndex, currentEndIndex); for(int i = currentStartIndex; i < deleteThru; i++) { updates.elementDeleted(0, source.get(i)); } } currentStartIndex = desiredStartIndex; // delete past the end if(desiredEndIndex < currentEndIndex) { for(int i = desiredEndIndex; i < currentEndIndex; i++) { updates.elementDeleted(desiredEndIndex - currentStartIndex, source.get(i)); } // insert thru to the new end } else if(currentEndIndex < desiredEndIndex && desiredStartIndex < desiredEndIndex) { int insertFrom = Math.max(currentEndIndex, currentStartIndex); updates.addInsert(insertFrom - currentStartIndex, desiredEndIndex - currentStartIndex - 1); } currentEndIndex = desiredEndIndex; updates.commitEvent(); } /** {@inheritDoc} */ @Override public final int size() { return currentEndIndex - currentStartIndex; } /** {@inheritDoc} */ @Override protected final int getSourceIndex(int mutationIndex) { return mutationIndex + currentStartIndex; } /** {@inheritDoc} */ @Override protected final boolean isWritable() { return true; } /** * Get the first index of the source {@link EventList} * that is presented in this {@link RangeList}. */ public int getStartIndex() { // translate the positive or negative desired values to indices int desiredStartIndex = desiredStart >= 0 ? desiredStart : source.size() + desiredStart + 1; // adjust the start index to the size of the list if(desiredStartIndex < 0) return 0; if(desiredStartIndex > source.size()) return source.size(); return desiredStartIndex; } /** * Get the first index of the source {@link EventList} * that is beyond the range of this {@link RangeList}. */ public int getEndIndex() { // translate the positive or negative desired values to indices int desiredEndIndex = desiredEnd >= 0 ? desiredEnd : source.size() + desiredEnd + 1; // adjust the end index to the size of the list int desiredStartIndex = getStartIndex(); if(desiredEndIndex < desiredStartIndex) return desiredStartIndex; if(desiredEndIndex > source.size()) return source.size(); return desiredEndIndex; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/gui/0000755000175000017500000000000012106516376024726 5ustar gregoagregoa././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/gui/AbstractTableComparatorChooser.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/gui/AbstractTableComparatorChooser.j0000644000175000017500000003443312106516376033176 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.gui; import ca.odell.glazedlists.SortedList; import ca.odell.glazedlists.impl.gui.MouseKeyboardSortingStrategy; import ca.odell.glazedlists.impl.gui.MouseOnlySortingStrategy; import ca.odell.glazedlists.impl.gui.SortingState; import ca.odell.glazedlists.impl.gui.MouseOnlySortingStrategyWithUndo; import ca.odell.glazedlists.impl.sort.TableColumnComparator; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Comparator; import java.util.List; /** * A TableComparatorChooser is a tool that allows the user to sort a table * widget by clicking on the table's headers. It requires that the table has a * SortedList as a source as the sorting on that list is used. * * @author Kevin Maltby */ public abstract class AbstractTableComparatorChooser { /** * Emulate the sorting behaviour of Windows Explorer and Mac OS X Finder. * *

Single clicks toggles between forward and reverse. If multiple comparators * are available for a particular column, they will be cycled in order. * *

At most one column can be sorted at a time. */ public static final Object SINGLE_COLUMN = new MouseOnlySortingStrategy(false); /** * Sort multiple columns without use of the keyboard. Single clicks cycle * through comparators, double clicks clear all secondary sorts before * performing the normal behaviour. * *

This is the original sorting strategy provided by Glazed Lists, with a * limitation that it is impossible to clear a sort order that is already in * place. It's designed to be used with multiple columns and multiple comparators * per column. * *

The overall behaviour is as follows: * *

  • Click: sort this column. If it's already sorted, reverse the sort order. * If its already reversed, sort using the column's next comparator in forward * order. If there are no more comparators, go to the first comparator. If there * are multiple sort columns, sort this column after those columns. * *
  • Double click: like a single click, but clear all sorting columns first. */ public static final Object MULTIPLE_COLUMN_MOUSE = new MouseOnlySortingStrategy(true); /** * Sort multiple columns without use of the keyboard. Single clicks cycle * through comparators. Single click on the last comparator of the primary * sort column will clear the entire sort (for all columns). * *

    This is an improvement over the original sorting strategy provided by * Glazed Lists, since it gives a reasonable mechanism for clearing a sort * order that is already in place. It's designed to be used with multiple * columns and multiple comparators per column. * *

    The overall behaviour is as follows: * *

  • Click: sort this column. If it's already sorted, reverse the sort order. * If its already reversed, sort using the column's next comparator in forward * order. If there are no more comparators, clear ALL column comparators. * If there are multiple sort columns, sort this column after those columns. */ public static final Object MULTIPLE_COLUMN_MOUSE_WITH_UNDO = new MouseOnlySortingStrategyWithUndo(); /** * Emulate the sorting behaviour of SUN's TableSorter, by Philip Milne et. al. * *

    This is not a direct adaptation since we choose to support potentially * many Comparators per column, whereas TableSorter is limited to one. * *

    For reference, this is TableSorter's behaviour, copied shamelessly * from that project's source file: * *

  • Mouse-click: Clears the sorting gui of all other columns and advances * the sorting gui of that column through three values: * {NOT_SORTED, ASCENDING, DESCENDING} (then back to NOT_SORTED again). * *
  • SHIFT-mouse-click: Clears the sorting gui of all other columns and * cycles the sorting gui of the column through the same three values, * in the opposite order: {NOT_SORTED, DESCENDING, ASCENDING}. * *
  • CONTROL-mouse-click and CONTROL-SHIFT-mouse-click: as above except that the * changes to the column do not cancel the statuses of columns that are * already sorting - giving a way to initiate a compound sort. * * @see Table tutorial */ public static final Object MULTIPLE_COLUMN_KEYBOARD = new MouseKeyboardSortingStrategy(); /** the sorted list to choose the comparators for */ protected SortedList sortedList; /** the columns to sort over */ private TableFormat tableFormat; /** the potentially foreign comparator associated with the sorted list */ protected Comparator sortedListComparator = null; /** manage which columns are sorted and in which order */ protected SortingState sortingState; /** * Create a {@link AbstractTableComparatorChooser} that sorts the specified * {@link SortedList} over the specified columns. */ protected AbstractTableComparatorChooser(SortedList sortedList, TableFormat tableFormat) { this.sortedList = sortedList; this.sortingState = createSortingState(); this.setTableFormat(tableFormat); this.sortingState.addPropertyChangeListener(new SortingStateListener()); } /** * Returns the object which models the current sorting state of all columns * in the table. */ protected SortingState createSortingState() { return new SortingState(this); } /** * Handle changes to the sorting state by applying the new comparator * to the {@link SortedList}. */ private class SortingStateListener implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent propertyChangeEvent) { rebuildComparator(); } } /** * Updates the comparator in use and applies it to the table. */ protected void rebuildComparator() { final Comparator rebuiltComparator = sortingState.buildComparator(); // select the new comparator sortedList.getReadWriteLock().writeLock().lock(); try { sortedListComparator = rebuiltComparator; sortedList.setComparator(rebuiltComparator); } finally { sortedList.getReadWriteLock().writeLock().unlock(); } } /** * Adjusts the TableFormat this comparator chooser uses when selecting * comparators. Calling this method will clear any active sorting. */ protected void setTableFormat(TableFormat tableFormat) { this.tableFormat = tableFormat; // handle a change in the layout of our columns sortingState.rebuildColumns(tableFormat); } /** * Gets the list of comparators for the specified column. The user is * free to add comparators to this list or clear the list if the specified * column cannot be sorted. */ public List getComparatorsForColumn(int column) { return sortingState.getColumns().get(column).getComparators(); } /** * Get the columns that the TableComparatorChooser is sorting by. * * @return a List of Integers. The first Integer is the primary sorting column, * the second is the secondary, etc. This list may be empty but never null. */ public List getSortingColumns() { return sortingState.getSortingColumnIndexes(); } /** * Gets the index comparator in use for the specified column. This comparator * may be retrieved using {@link #getComparatorsForColumn(int)}. * * @return the comparator index for the specified column, or -1 if that column * is not being used to sort. */ public int getColumnComparatorIndex(int column) { return sortingState.getColumns().get(column).getComparatorIndex(); } /** * Gets whether the comparator in use for the specified column is reverse. */ public boolean isColumnReverse(int column) { return sortingState.getColumns().get(column).isReverse(); } /** * Append the comparator specified by the column, comparator index and reverse * parameters to the end of the sequence of comparators this * {@link AbstractTableComparatorChooser} is sorting the {@link SortedList} * by. * *

    Append implies that if this {@link AbstractTableComparatorChooser} * is already sorting that list by another column, this comparator will only * be used to break ties from that {@link Comparator}. If the table is already * sorting by the specified column, it will be silently discarded. * *

    Suppose we're currently not sorting the table, this method will cause * the table to be sorted by the column specified. If we are sorting the table * by some column c, this will sort by that column first and the column * specified here second. * *

    If this {@link AbstractTableComparatorChooser} doesn't support multiple * column sort, this will replace the current {@link Comparator} rather than * appending to it. * * @param column the column to sort by * @param comparatorIndex the comparator to use, specify 0 for the * default comparator. * @param reverse whether to reverse the specified comparator. */ public void appendComparator(int column, int comparatorIndex, boolean reverse) { sortingState.appendComparator(column, comparatorIndex, reverse); sortingState.fireSortingChanged(); } /** * Clear all sorting state and set the {@link SortedList} to use its * natural order. */ public void clearComparator() { sortingState.clearComparators(); sortingState.fireSortingChanged(); } /** * Examines the current {@link Comparator} of the SortedList and * adds icons to the table header renderers in response. * *

    To do this, clicks are injected into each of the * corresponding ColumnClickTrackers. */ protected void redetectComparator(Comparator currentComparator) { sortedListComparator = currentComparator; sortingState.detectStateFromComparator(currentComparator); } /** * Gets the sorting style currently applied to the specified column. */ protected int getSortingStyle(int column) { return sortingState.getColumns().get(column).getSortingStyle(); } /** * Creates a {@link Comparator} that can compare list elements * given a {@link Comparator} that can compare column values for the specified * column. This returns a {@link Comparator} that extracts the table values for * the specified column and then delegates the actual comparison to the specified * comparator. */ public Comparator createComparatorForElement(Comparator comparatorForColumn, int column) { return new TableColumnComparator(tableFormat, column, comparatorForColumn); } /** * Encode the current sorting state as a {@link String}. This specially formatted * {@link String} is ideal for persistence using any preferences API. The * state of this {@link AbstractTableComparatorChooser} can be restored * by passing the return value of this method to {@link #fromString(String)}. */ @Override public String toString() { return sortingState.toString(); } /** *

    This class is capable of representing its own state with a String, to * persist sorting state externally. The format uses zero or more column specifications, * separated by commas. Here are some valid examples: * * * * * * * * *
    String RepresentationDescription
    "column 3"Sort using the column at index 3, using that column's first comparator, in forward order
    "column 3 reversed"Sort using the column at index 3, using that column's first comparator, in reverse order
    "column 3, column 1"Sort using the column at index 3, using that column's first comparator, in forward order
    * then by
    the column at index 1, using that column's first comparator, in forward order.
    "column 3 comparator 2"Sort using the column at index 3, using that column's comparator at index 2, in forward order
    "column 3 comparator 2 reversed"Sort using the column at index 3, using that column's comparator at index 2, in reverse order
    "column 3 reversed, column 1 comparator 2, column 5 comparator 1 reversed, column 0"Sort using the column at index 3, using that column's first comparator, in reverse order
    * then by
    the column at index 1, using that column's comparator at index 2, in forward order
    * then by
    the column at index 5, using that column's comparator at index 1, in reverse order
    * then by
    the column at index 0, using that column's first comparator, in forward order.
    * *

    More formally, the grammar for this String representation is as follows: *
    <COLUMN> = column <COLUMN INDEX> (comparator <COMPARATOR INDEX>)? (reversed)? *
    <COMPARATOR SPEC> = ( <COLUMN> (, <COLUMN>)* )? */ public void fromString(String stringEncoded) { sortingState.fromString(stringEncoded); sortingState.fireSortingChanged(); } public void dispose() { // null out references to potentially long lived objects this.sortedList = null; this.tableFormat = null; this.sortedListComparator = null; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/gui/CheckableTableFormat.java0000644000175000017500000000127612106516376031561 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.gui; /** * Specifies how to check table elements. * * @author Jesse Wilson */ public interface CheckableTableFormat extends TableFormat { /** * Sets the specified object as checked. */ public void setChecked(E baseObject, boolean checked); /** * Gets whether the specified object is checked. */ public boolean getChecked(E baseObject); }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/gui/AdvancedTableFormat.java0000644000175000017500000000325712106516376031426 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.gui; import java.util.Comparator; /** * Allows the ability to specify column class information in addition to the standard * {@link TableFormat} information. * *

    This class can be used as an alternative to the simple {@link TableFormat} * class to provide column class information that is used to determine what cell renderer * and/or editor should be used for a column. If no custom renderers or editors are * required, it is sufficient to implement {@link TableFormat} only. * * @author Rob Eden * @author Jesse Wilson * * @see WritableTableFormat * @see TableFormat */ public interface AdvancedTableFormat extends TableFormat { /** * Returns the most specific superclass for all the cell values in the column. This * is used by the table to set up a default renderer and editor for the column. * * @param column The index of the column being edited. */ public Class getColumnClass(int column); /** * Returns the default {@link Comparator} to use for the specified column. * This {@link Comparator} may be used to determine how a table will be sorted. * * @see ca.odell.glazedlists.GlazedLists * * @return the {@link Comparator} to use or null for an unsortable * column. */ public Comparator getColumnComparator(int column); } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/gui/WritableTableFormat.java0000644000175000017500000000423212106516376031464 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.gui; /** * Specifies how to edit the elements of table. * *

    This class can be used as an alternative to the simple {@link TableFormat} * class to provide editable cells. The * {@link ca.odell.glazedlists.swing.DefaultEventTableModel EventTableModel} detects if a * class implements {@link WritableTableFormat} for modifying the table. If a table * is not editable at all, it is sufficient to implement {@link TableFormat} * only. * * @author Jesse Wilson * * @see AdvancedTableFormat * @see TableFormat */ public interface WritableTableFormat extends TableFormat { /** * For editing fields. This returns true if the specified Object in the * specified column can be edited by the user. * * @param baseObject the Object to test as editable or not. This will be * an element from the source list. * @param column the column to test. * @return true if the object and column are editable, false otherwise. * @since 2004-August-27, as a replacement for isColumnEditable(int). */ public boolean isEditable(E baseObject, int column); /** * Sets the specified field of the base object to the edited value. When * a column of a table is edited, this method is called so that the user * can specify how to modify the base object for each column. * * @param baseObject the Object to be edited. This will be the original * Object from the source list. * @param editedValue the newly constructed value for the edited column * @param column the column which has been edited * @return the revised Object, or null if the revision shall be discarded. * If not null, the EventTableModel will set() this revised value in * the list and overwrite the previous value. */ public E setColumnValue(E baseObject, Object editedValue, int column); }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/gui/TableFormat.java0000644000175000017500000000205212106516376027770 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.gui; /** * Specifies how a set of records are rendered in a table. * * @see ca.odell.glazedlists.GlazedLists#tableFormat(Class,String[],String[]) * * @author Jesse Wilson */ public interface TableFormat { /** * The number of columns to display. */ public int getColumnCount(); /** * Gets the title of the specified column. */ public String getColumnName(int column); /** * Gets the value of the specified field for the specified object. This * is the value that will be passed to the editor and renderer for the * column. If you have defined a custom renderer, you may choose to return * simply the baseObject. */ public Object getColumnValue(E baseObject, int column); }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/io/0000755000175000017500000000000012106516400024535 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/io/FileList.java0000644000175000017500000001132612106516400027116 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.io; // the core Glazed Lists packages import ca.odell.glazedlists.BasicEventList; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.TransformedList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.impl.io.Bufferlo; import ca.odell.glazedlists.impl.io.ListEventToBytes; import ca.odell.glazedlists.impl.pmap.Chunk; import ca.odell.glazedlists.impl.pmap.PersistentMap; import ca.odell.glazedlists.util.concurrent.ReadWriteLock; import java.io.File; import java.io.IOException; import java.util.Iterator; import java.util.SortedMap; import java.util.TreeMap; /** * An {@link EventList} that is persisted to disk. * *

    Warning: This * class is a technology preview and is subject to API changes. * *

    * * * * * * * *
    EventList Overview
    Writable:yes
    Concurrency:Requires {@link ReadWriteLock} for every access, even for single-threaded use
    Performance:N/A
    Memory:O(N)
    Unit Tests:N/A
    Issues:N/A
    * * @author Jesse Wilson */ public final class FileList extends TransformedList { /** the destination file, just for user convenience */ private File file = null; /** how bytes are encoded and decoded */ private ByteCoder byteCoder; /** the underlying storage of ListEvents */ private PersistentMap storage = null; /** the ID of the next update to write to disc */ private int nextUpdateId = 81; /** whether this list can be modified */ private boolean writable = true; /** * Create a {@link FileList} that stores its data in the specified file. */ public FileList(File file, ByteCoder byteCoder) throws IOException { super(new BasicEventList()); this.file = file; this.byteCoder = byteCoder; // store the updates in a persistent map storage = new PersistentMap(file); // sequence the updates SortedMap sequentialUpdates = new TreeMap(); for(Iterator k = storage.keySet().iterator(); k.hasNext(); ) { Integer key = (Integer)k.next(); Bufferlo valueBuffer = ((Chunk)storage.get(key)).getValue(); sequentialUpdates.put(key, valueBuffer); } // replay all the updates from the file for(Iterator u = sequentialUpdates.keySet().iterator(); u.hasNext(); ) { Integer key = (Integer)u.next(); Bufferlo update = (Bufferlo)sequentialUpdates.get(key); ListEventToBytes.toListEvent(update, this, byteCoder); } // prepare the next update id to use if(!sequentialUpdates.isEmpty()) { nextUpdateId = ((Integer)sequentialUpdates.lastKey()).intValue() + 1; } // now that we're up-to-date, listen for further events source.addListEventListener(this); } /** {@inheritDoc} */ @Override public boolean isWritable() { return writable; } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { // write the change to disc try { ListEvent listChangesCopy = listChanges.copy(); Bufferlo listChangesBytes = ListEventToBytes.toBytes(listChangesCopy, byteCoder); storage.put(new Integer(nextUpdateId), new Chunk(listChangesBytes)); nextUpdateId++; } catch(IOException e) { throw new IllegalStateException(e.getMessage()); } // forward the event to interested listeners updates.forwardEvent(listChanges); } /** * Closes this FileList so that it consumes no disc resources. The list may * continue to be read until it is {@link #dispose() disposed}. */ public void close() { if(storage != null) storage.close(); storage = null; writable = false; } /** {@inheritDoc} */ @Override public void dispose() { close(); super.dispose(); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/io/ListPeer.java0000644000175000017500000001155212106516400027133 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.io; // the core Glazed Lists packages import ca.odell.glazedlists.BasicEventList; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.impl.rbp.Peer; import ca.odell.glazedlists.impl.rbp.ResourceStatus; import java.io.IOException; /** * A {@link ListPeer} manages the network resources for publishing and subscribing * to {@link NetworkList}s. * *

    A {@link ListPeer} must have its {@link #start()} method complete successfully * before it can be used to {@link #publish(EventList,String,ByteCoder) publish()} and * {@link #subscribe(String,int,String,ByteCoder) subscribe()} {@link EventList}s. * *

    When a {@link ListPeer} is started, it listens for incoming connections on * the specified port. When it is stopped, all active {@link NetworkList}s are * {@link NetworkList#disconnect() disconnected}, and the port is closed. * *

    To bring and individual {@link NetworkList} online or offline, use its * {@link NetworkList#disconnect() disconnect()} and {@link NetworkList#connect() connect()} * methods. * * @author Jesse Wilson */ public class ListPeer { /** the peer manages the actual resources */ private Peer peer; /** * Creates a new ListPeer that binds to the specified port. */ public ListPeer(int listenPort) { this.peer = new Peer(listenPort); } /** * Starts the peer. This binds to the listen port and allows connections to * be sent and received. * * @throws IOException if the listen port cannot be binded to. This will be due * to the port being in use or as a consequence of insufficient privileges. */ public void start() throws IOException { peer.start(); } /** * Stops the peer. This disconnects all active {@link EventList}s and releases * the listen port. */ public void stop() { peer.stop(); } /** * Prints the full state of this ListPeer. */ void print() { peer.print(); } /** * Publish the specified EventList with the specified name. * * @param source the {@link EventList} to publish. Each change to this {@link EventList} * will be immediately published to all subscribers. * @param path the address that the {@link EventList} shall be published under. * The path must start with a slash character. This must be unique among * all {@link EventList}s published on this {@link ListPeer}. * @param byteCoder a helper that can convert the elements of the {@link EventList} * into binary network-transmittable form. Some general purpose {@link ByteCoder}s * are available in the {@link ca.odell.glazedlists.GlazedLists GlazedLists} * factory class. * @return a simple decorator of the published {@link EventList} * with additional methods to bring the list offline. This list is writable. */ public NetworkList publish(EventList source, String path, ByteCoder byteCoder) { NetworkList published = new NetworkList(source, byteCoder); ResourceStatus resourceStatus = peer.publish(published.getResource(), path); published.setResourceStatus(resourceStatus); published.setWritable(true); return published; } /** * Subscribe to the EventList with the specified name. * * @param host the network hostname of the machine serving the original copy * of the data of interest. Together with the port, this should map * to a proper {@link java.net.InetSocketAddress InetSocketAddress}. * @param port the port the {@link EventList} of interest is being published on. * @param path the address that the {@link EventList} is published under. * @param byteCoder a helper that can convert the binary data from the network * into list elements for this {@link EventList}. Some general purpose {@link ByteCoder}s * are available in the {@link ca.odell.glazedlists.GlazedLists GlazedLists} * factory class. * @return the {@link EventList} that gets its data from the specified remote * source. This {@link EventList} will contain no data until the connection * completes. This list is not writable. */ public NetworkList subscribe(String host, int port, String path, ByteCoder byteCoder) { NetworkList subscribed = new NetworkList(new BasicEventList(), byteCoder); ResourceStatus resourceStatus = peer.subscribe(subscribed.getResource(), host, port, path); subscribed.setResourceStatus(resourceStatus); return subscribed; } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/io/ByteCoder.java0000644000175000017500000000212712106516400027262 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * An utility interface for converting Objects to bytes for storage or network * transport. For some common, general-purpose {@link ByteCoder}s, see the * {@link ca.odell.glazedlists.GlazedLists GlazedLists} factory class. * * @author Jesse Wilson */ public interface ByteCoder { /** * Encode the specified Object over the specified {@link OutputStream}. */ public void encode(Object source, OutputStream target) throws IOException; /** * Decode the Object from the specified {@link InputStream}. The stream should contain * exactly one Object and no further bytes before the end of the stream. */ public Object decode(InputStream source) throws IOException; }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/io/NetworkList.java0000644000175000017500000002554312106516400027676 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.io; // the core Glazed Lists packages import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.TransformedList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.impl.io.Bufferlo; import ca.odell.glazedlists.impl.io.ListEventToBytes; import ca.odell.glazedlists.impl.rbp.Resource; import ca.odell.glazedlists.impl.rbp.ResourceListener; import ca.odell.glazedlists.impl.rbp.ResourceStatus; import ca.odell.glazedlists.impl.rbp.ResourceStatusListener; import ca.odell.glazedlists.util.concurrent.ReadWriteLock; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * An {@link EventList} that is either published to the network or subscribed from * the network. Since list elements must be transmitted over the network, each * {@link NetworkList} requires a {@link ByteCoder} to convert {@link Object}s to * and from bytes. * *

    To instantiate a {@link NetworkList}, use the * {@link ListPeer#subscribe(String,int,String,ByteCoder) subscribe()} * and {@link ListPeer#publish(EventList,String,ByteCoder) publish()} methods * of a started {@link ListPeer}. * *

    {@link NetworkList}s may be taken offline and brought back online with the * {@link #connect()} and {@link #disconnect()} methods. This allows an application * to use a {@link NetworkList} in spite of an unreliable network connection. * *

    As a consequence of imperfect networks, {@link NetworkList}s may sometimes go * offline on their own. Some causes of this include the server program shutting * down or crashing, the local network connection becoming unavailable, or a * problem with the physical link, such as an unplugged cable. * *

    {@link NetworkList}s use a subset of HTTP/1.1 for transport, including * chunked encoding. This protocol brings its own set of advantages: *

  • HTTP is a standard well-understood protocol *
  • Clients may be served even if they are behind NAT or Firewalls *
  • The connection could be proxied by a standard HTTP proxy server, if necessary *
  • In theory, the served port could be shared with another HTTP daemon such as Tomcat * *

    And HTTP brings some disadvantages also: *

  • A persistent connection must be held, even if updates are infrequent *
  • It cannot be differentiated from web traffic for analysis * *

    Warning: The protocol used by * this version of {@link NetworkList} will be incompatible with future versions. * Eventually the protocol will be finalized but the current protocol is a work * in progress. * *

    Warning: This class * breaks the contract required by {@link java.util.List}. See {@link EventList} * for an example. * *

    * * * * * * * *
    EventList Overview
    Writable:yes
    Concurrency:Requires {@link ReadWriteLock} for every access, even for single-threaded use
    Performance:N/A
    Memory:O(N)
    Unit Tests:N/A
    Issues:N/A
    * * @author Jesse Wilson */ public final class NetworkList extends TransformedList { /** listeners to resource changes */ private List resourceListeners = new ArrayList(); /** listeners to status changes */ private List statusListeners = new ArrayList(); /** how bytes are encoded and decoded */ private ByteCoder byteCoder; /** who manages this resource's connection */ private ResourceStatus resourceStatus = null; /** whether this NetworkList is writable via its own API */ private boolean writable = false; /** implementations of ResourceStatusListener and Resource */ private PrivateInterfaces privateInterfaces = new PrivateInterfaces(); /** * Create a {@link NetworkList} that brings the specified source online. */ NetworkList(EventList source, ByteCoder byteCoder) { super(source); this.byteCoder = byteCoder; source.addListEventListener(this); } /** * Sets the ResourceStatus to delegate connection information requests to. */ void setResourceStatus(ResourceStatus resourceStatus) { if(this.resourceStatus != null) throw new IllegalStateException(); this.resourceStatus = resourceStatus; resourceStatus.addResourceStatusListener(privateInterfaces); } /** * Set the NetworkList as writable. */ void setWritable(boolean writable) { this.writable = writable; } /** {@inheritDoc} */ @Override public boolean isWritable() { return writable; } /** * Gets the {@link Resource} that is the peer of this NetworkList. */ Resource getResource() { return privateInterfaces; } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { // notify resource listeners try { ListEvent listChangesCopy = listChanges.copy(); Bufferlo listChangesBytes = ListEventToBytes.toBytes(listChangesCopy, byteCoder); for(int r = 0; r < resourceListeners.size(); r++) { ResourceListener listener = resourceListeners.get(r); listener.resourceUpdated(privateInterfaces, listChangesBytes.duplicate()); } } catch(IOException e) { throw new IllegalStateException(e.getMessage()); } // forward the event updates.forwardEvent(listChanges); } /** * Returns true if this resource is on the network. For published lists, this * requires that the list is being served. For subscribed lists, this requires * that a connection to the server has been established. */ public boolean isConnected() { return resourceStatus.isConnected(); } /** * Attempts to bring this {@link NetworkList} online. When the connection attempt * is successful (or when it fails), all {@link ResourceStatusListener}s will be * notified. */ public void connect() { resourceStatus.connect(); } /** * Attempts to take this {@link NetworkList} offline. When the {@link NetworkList} * is fully disconnected, all {@link ResourceStatusListener}s will be notified. */ public void disconnect() { resourceStatus.disconnect(); } /** * Implementations of all private interfaces. */ private class PrivateInterfaces implements Resource, ResourceStatusListener { /** * Called each time a resource becomes connected. */ public void resourceConnected(ResourceStatus resource) { for(Iterator i = statusListeners.iterator(); i.hasNext(); ) { NetworkListStatusListener listener = (NetworkListStatusListener)i.next(); listener.connected(NetworkList.this); } } /** * Called each time a resource's disconnected status changes. This method may * be called for each attempt it makes to reconnect to the network. */ public void resourceDisconnected(ResourceStatus resource, Exception cause) { for(Iterator i = statusListeners.iterator(); i.hasNext(); ) { NetworkListStatusListener listener = (NetworkListStatusListener)i.next(); listener.disconnected(NetworkList.this, cause); } } /** {@inheritDoc} */ public Bufferlo toSnapshot() { getReadWriteLock().writeLock().lock(); try { return ListEventToBytes.toBytes(NetworkList.this, byteCoder); } catch(IOException e) { throw new IllegalStateException(e.getMessage()); } finally { getReadWriteLock().writeLock().unlock(); } } /** {@inheritDoc} */ public void fromSnapshot(Bufferlo snapshot) { applyCodedEvent(snapshot); } /** {@inheritDoc} */ private void applyCodedEvent(Bufferlo data) { getReadWriteLock().writeLock().lock(); try { updates.beginEvent(true); ListEventToBytes.toListEvent(data, source, byteCoder); updates.commitEvent(); } catch(IOException e) { throw new IllegalStateException(e.getMessage()); } finally { getReadWriteLock().writeLock().unlock(); } } /** {@inheritDoc} */ public void update(Bufferlo delta) { applyCodedEvent(delta); } /** {@inheritDoc} */ public void addResourceListener(ResourceListener listener) { resourceListeners.add(listener); } /** {@inheritDoc} */ public void removeResourceListener(ResourceListener listener) { for(int r = 0; r < resourceListeners.size(); r++) { if(resourceListeners.get(r) == listener) { resourceListeners.remove(r); return; } } } /** {@inheritDoc} */ public ReadWriteLock getReadWriteLock() { return NetworkList.this.getReadWriteLock(); } @Override public String toString() { return NetworkList.this.toString(); } } /** * Registers the specified listener to receive events about the status of this * {@link NetworkList}. */ public void addStatusListener(NetworkListStatusListener listener) { statusListeners.add(listener); } /** * Deregisters the specified listener from receiving events about the status of * this {@link NetworkList}. */ public void removeStatusListener(NetworkListStatusListener listener) { statusListeners.remove(listener); } /** {@inheritDoc} */ @Override public void dispose() { resourceStatus.removeResourceStatusListener(privateInterfaces); disconnect(); super.dispose(); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/io/GlazedListsIO.java0000644000175000017500000000342312106516400030057 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.io; import ca.odell.glazedlists.impl.io.BeanXMLByteCoder; import ca.odell.glazedlists.impl.io.SerializableByteCoder; /** * A factory for creating all sorts of objects to be used with Glazed Lists. * * @author Jesse Wilson */ public final class GlazedListsIO { /** * A dummy constructor to prevent instantiation of this class */ private GlazedListsIO() { throw new UnsupportedOperationException(); } // ByteCoders // // // // // // // // // // // // // // // // // // // // // /** Provide Singleton access for all ByteCoders with no internal state */ private static ByteCoder serializableByteCoder = new SerializableByteCoder(); private static ByteCoder beanXMLByteCoder = new BeanXMLByteCoder(); /** * Creates a {@link ByteCoder} that encodes {@link java.io.Serializable Serializable} * Objects using an {@link java.io.ObjectOutputStream}. */ public static ByteCoder serializableByteCoder() { if(serializableByteCoder == null) serializableByteCoder = new SerializableByteCoder(); return serializableByteCoder; } /** * Creates a {@link ByteCoder} that uses {@link java.beans.XMLEncoder XMLEncoder} and * {@link java.beans.XMLDecoder XMLDecoder} classes from java.beans. Encoded * Objects must be JavaBeans. */ public static ByteCoder beanXMLByteCoder() { if(beanXMLByteCoder == null) beanXMLByteCoder = new BeanXMLByteCoder(); return beanXMLByteCoder; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/io/CachingList.java0000644000175000017500000002477312106516400027605 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.io; // the Glazed Lists' change objects import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.TransformedList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.impl.adt.AgedNode; import ca.odell.glazedlists.impl.adt.AgedNodeComparator; import ca.odell.glazedlists.impl.adt.SparseList; import ca.odell.glazedlists.impl.adt.SparseListNode; import ca.odell.glazedlists.impl.adt.barcode2.Element; import ca.odell.glazedlists.impl.adt.barcode2.SimpleTree; import ca.odell.glazedlists.util.concurrent.Lock; import ca.odell.glazedlists.util.concurrent.ReadWriteLock; // For the execution of the performance test /** * An {@link ca.odell.glazedlists.EventList} that caches elements from its source {@link ca.odell.glazedlists.EventList}. * * It is useful in cases when the {@link #get(int)} method of an * {@link ca.odell.glazedlists.EventList} is expensive. It can also be used when there are too many * elements to keep in memory simultaneously. For caching to be effective, object * access must be clustered. * *

    This {@link ca.odell.glazedlists.EventList} caches the most recently requested n elements. * *

    By overriding the {@link #preFetch(int)} method, you can modify this * CachingList to do predictive lookups for higher performance. * *

    * * * * * * * *
    EventList Overview
    Writable:yes
    Concurrency:thread ready, not thread safe
    Performance:reads: O(log N), writes O(log N)
    Memory:O(N)
    Unit Tests:N/A
    Issues: * 22 * 32 * 43 * 262 *
    * * * * @author Kevin Maltby */ public class CachingList extends TransformedList { /** The cache is implemented using a tree-based cache */ private SimpleTree cache; /** The model of the source list for scalability with minimal memory footprint */ private SparseList indexTree; /** The count of cache hits and cache misses for testing cache success */ private int cacheHits; private int cacheMisses; /** The number of elements in the cache */ private int currentSize = 0; private int maxSize = 0; /** The last inspected size of the source list */ private int lastKnownSize = 0; /** * Creates a {@link CachingList} that caches elements from the specified source * {@link ca.odell.glazedlists.EventList}. * * @param source The source list to use to get values from * @param maxSize The maximum size of the cache */ public CachingList(EventList source, int maxSize) { super(source); readWriteLock = new CacheLock(readWriteLock); this.maxSize = maxSize; cache = new SimpleTree(new AgedNodeComparator()); indexTree = new SparseList(); indexTree.addNulls(0, source.size()); source.addListEventListener(this); lastKnownSize = source.size(); } /** {@inheritDoc} */ @Override public final int size() { return source.size(); } /** {@inheritDoc} */ @Override public final Object get(int index) { if(index >= size()) throw new IndexOutOfBoundsException("cannot get from tree of size " + size() + " at " + index); preFetch(index); return fetch(index, true); } /** * Fetches a particular element. * *

    This might seem redundant with the existence of {@link #get(int)}. * However, the goals of the methods are different. This method exists * to be called by {@link #get(int)} or {@link #preFetch(int)}. * This distinction allows users overriding this class a means of entry * retrieval which does not implicitly execute a pre-fetch. This is * particularly key for users overriding {@link #preFetch(int)} * * @param index The index of the value to retrieve * @param recordHitsOrMisses Whether to increment the hit/miss counters * (this should always be false when called from * {@link #preFetch(int)}). * * @return The value associated with the given index, * or null if the index is not found. */ protected final Object fetch(int index, boolean recordHitsOrMisses) { // attempt to get the element from the cache Object value = null; Element cacheNode = (Element)indexTree.get(index); // The value is cached, return cached value if(cacheNode != null) { if(recordHitsOrMisses) cacheHits ++;; AgedNode agedNode = (AgedNode)cacheNode.get(); value = agedNode.getValue(); cache.remove(cacheNode); SparseListNode indexNode = agedNode.getIndexNode(); indexNode.setValue(cache.addInSortedOrder((byte)1, agedNode, 1)); // The value is not cached, lookup from source and cache } else { if(recordHitsOrMisses) cacheMisses++; // Make room in the cache if it is full if(currentSize >= maxSize) { Element oldestInCache = cache.get(0); cache.remove(oldestInCache); AgedNode oldAgedNode = (AgedNode)oldestInCache.get(); SparseListNode oldIndexNode = oldAgedNode.getIndexNode(); indexTree.set(oldIndexNode.getIndex(), null); currentSize--; } // Cache the value value = source.get(index); indexTree.set(index, Boolean.TRUE); SparseListNode indexNode = indexTree.getNode(index); AgedNode agedNode = new AgedNode(indexNode, value); indexNode.setValue(cache.addInSortedOrder((byte)1, agedNode, 1)); currentSize++; } return value; } /** * Pre-fetches a set of data given the index that was directly requested. * *

    Each application that wishes to take advantage of pre-fetching should * implement this method in a way which best fits their particular use * cases. As such, no default pre-fetch behaviour could really be defined, * and thus this method is empty by default. * *

    Because pre-fetching can modify the cache, child classes of CachingList * should use careful consideration of locking when implementing this method. * * @param index The index that was requested from the cache */ protected void preFetch(int index) { } /** {@inheritDoc} */ @Override protected boolean isWritable() { return true; } /** * Gets the total number of times that this list has fetched its result * from the cache rather than from the source list. * * @return The number of times that this cache provided the result */ public final int getCacheHits() { return cacheHits; } /** * Gets the total number of times that this list has fetched its result * from the source list rather than the cache. * * @return The number of times that this cache couldn't provide the result */ public final int getCacheMisses() { return cacheMisses; } /** * Gets the ratio of cache hits to cache misses. This is a number between * 0 and 1, where 0 means the cache is unused and 1 means the cache was * used exclusively. */ public final float getCacheHitRatio() { if(cacheHits + cacheMisses == 0) return 0.0F; return (float)cacheHits / (float)(cacheHits + cacheMisses); } /** {@inheritDoc} */ @Override public final void listChanged(ListEvent listChanges) { updates.beginEvent(); while(listChanges.next()) { // get the current change info int index = listChanges.getIndex(); int changeType = listChanges.getType(); // Lookup the cache entry for this index if possible Element cacheNode = null; if(index < lastKnownSize) { cacheNode = (Element)indexTree.get(index); } // An INSERT causes the indexes of cached values to be offset. if(changeType == ListEvent.INSERT) { indexTree.add(index, null); // A DELETE causes an entry to be removed and/or the index values to be offset. } else if(changeType == ListEvent.DELETE) { if(cacheNode != null) { cache.remove(cacheNode); currentSize--; } indexTree.remove(index); // An UPDATE causes an existing entry to be removed } else if(changeType == ListEvent.UPDATE) { if(cacheNode != null) { cache.remove(cacheNode); currentSize--; } } updates.addChange(changeType, index); } lastKnownSize = source.size(); updates.commitEvent(); } /** * A special lock to prevent deadlock in CachingList. */ private static class CacheLock implements ReadWriteLock { /** The lock this CacheLock decorates */ private ReadWriteLock sourceLock; /** * Creates a new lock for CachingList. */ public CacheLock(ReadWriteLock sourceLock) { this.sourceLock = sourceLock; } /** * Since reads are write ops on caches, return the lock used for writing. */ public Lock readLock() { return writeLock(); } /** * Return the lock used for writing. */ public Lock writeLock() { return sourceLock.writeLock(); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/io/NetworkListStatusListener.java0000644000175000017500000000161412106516400032601 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.io; // NIO is used for BRP import java.util.EventListener; /** * Listens to the current status of a {@link NetworkList}. * * @author Jesse Wilson */ public interface NetworkListStatusListener extends EventListener { /** * Called each time a {@link NetworkList} becomes connected. */ public void connected(NetworkList list); /** * Called each time a {@link NetworkList}'s connection status changes. This * method may be called for each attempt it makes to reconnect to the network. */ public void disconnected(NetworkList list, Exception reason); }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/BasicEventList.java0000644000175000017500000003120512106516400027651 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEventAssembler; import ca.odell.glazedlists.event.ListEventListener; import ca.odell.glazedlists.event.ListEventPublisher; import ca.odell.glazedlists.impl.SerializedReadWriteLock; import ca.odell.glazedlists.util.concurrent.LockFactory; import ca.odell.glazedlists.util.concurrent.ReadWriteLock; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OptionalDataException; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.RandomAccess; /** * An {@link EventList} that wraps any simple {@link List}, such as {@link ArrayList} * or {@link LinkedList}. * *

    Unlike most {@link EventList}s, this class is {@link Serializable}. When * {@link BasicEventList} is serialized, all of its elements are serialized * and all of its listeners that implement {@link Serializable}. Upon * deserialization, the new copy uses a different {@link #getReadWriteLock() lock} * than its source {@link BasicEventList}. * *

    * * * * * * * *
    EventList Overview
    Writable:yes
    Concurrency:thread ready, not thread safe
    Performance:reads: O(1), writes O(1) amortized
    Memory:O(N)
    Unit Tests:N/A
    Issues:N/A
    * * @author Jesse Wilson */ public final class BasicEventList extends AbstractEventList implements Serializable, RandomAccess { /** For versioning as a {@link Serializable} */ private static final long serialVersionUID = 4883958173323072345L; /** the underlying data list */ private List data; /** * Creates a {@link BasicEventList}. */ public BasicEventList() { this(LockFactory.DEFAULT.createReadWriteLock()); } /** * Creates a {@link BasicEventList} that uses the specified {@link ReadWriteLock} * for concurrent access. */ public BasicEventList(ReadWriteLock readWriteLock) { this(null, readWriteLock); } /** * Creates an empty {@link BasicEventList} with the given * initialCapacity. */ public BasicEventList(int initalCapacity) { this(initalCapacity, null, LockFactory.DEFAULT.createReadWriteLock()); } /** * Creates a {@link BasicEventList} using the specified * {@link ListEventPublisher} and {@link ReadWriteLock}. * * @since 2006-June-12 */ public BasicEventList(ListEventPublisher publisher, ReadWriteLock readWriteLock) { this(10, publisher, readWriteLock); } /** * Creates a {@link BasicEventList} using the specified initial capacity, * {@link ListEventPublisher} and {@link ReadWriteLock}. * * @since 2007-April-19 */ public BasicEventList(int initialCapacity, ListEventPublisher publisher, ReadWriteLock readWriteLock) { super(publisher); this.data = new ArrayList(initialCapacity); this.readWriteLock = (readWriteLock == null) ? LockFactory.DEFAULT.createReadWriteLock() : readWriteLock; } /** * Creates a {@link BasicEventList} that uses the specified {@link List} as * the underlying implementation. * *

    Warning: all editing to * the specified {@link List} must be done through via this * {@link BasicEventList} interface. Otherwise this {@link BasicEventList} will * become out of sync and operations will fail. * * @deprecated As of 2005/03/06, this constructor has been declared unsafe * because the source list is exposed. This allows it to be modified without * the required events being fired. This constructor has been replaced by * the factory method {@link GlazedLists#eventList(Collection)}. */ public BasicEventList(List list) { super(null); this.data = list; this.readWriteLock = LockFactory.DEFAULT.createReadWriteLock(); } /** {@inheritDoc} */ @Override public void add(int index, E element) { // create the change event updates.beginEvent(); updates.elementInserted(index, element); // do the actual add data.add(index, element); // fire the event updates.commitEvent(); } /** {@inheritDoc} */ @Override public boolean add(E element) { // create the change event updates.beginEvent(); updates.elementInserted(size(), element); // do the actual add boolean result = data.add(element); // fire the event updates.commitEvent(); return result; } /** {@inheritDoc} */ @Override public boolean addAll(Collection collection) { return addAll(size(), collection); } /** {@inheritDoc} */ @Override public boolean addAll(int index, Collection collection) { // don't do an add of an empty set if(collection.size() == 0) return false; // create the change event updates.beginEvent(); for(Iterator i = collection.iterator(); i.hasNext(); ) { E value = i.next(); updates.elementInserted(index, value); data.add(index, value); index++; } // fire the event updates.commitEvent(); return !collection.isEmpty(); } /** {@inheritDoc} */ @Override public E remove(int index) { // create the change event updates.beginEvent(); // do the actual remove E removed = data.remove(index); // fire the event updates.elementDeleted(index, removed); updates.commitEvent(); return removed; } /** {@inheritDoc} */ @Override public boolean remove(Object element) { int index = data.indexOf(element); if(index == -1) return false; remove(index); return true; } /** {@inheritDoc} */ @Override public void clear() { // don't do a clear on an empty set if(isEmpty()) return; // create the change event updates.beginEvent(); for(int i = 0, size = size(); i < size; i++) { updates.elementDeleted(0, get(i)); } // do the actual clear data.clear(); // fire the event updates.commitEvent(); } /** {@inheritDoc} */ @Override public E set(int index, E element) { // create the change event updates.beginEvent(); // do the actual set E previous = data.set(index, element); // fire the event updates.elementUpdated(index, previous); updates.commitEvent(); return previous; } /** {@inheritDoc} */ @Override public E get(int index) { return data.get(index); } /** {@inheritDoc} */ @Override public int size() { return data.size(); } /** {@inheritDoc} */ @Override public boolean removeAll(Collection collection) { boolean changed = false; updates.beginEvent(); for(Iterator i = collection.iterator(); i.hasNext(); ) { Object value = i.next(); int index = -1; while((index = indexOf(value)) != -1) { E removed = data.remove(index); updates.elementDeleted(index, removed); changed = true; } } updates.commitEvent(); return changed; } /** {@inheritDoc} */ @Override public boolean retainAll(Collection collection) { boolean changed = false; updates.beginEvent(); int index = 0; while(index < data.size()) { if(collection.contains(data.get(index))) { index++; } else { E removed = data.remove(index); updates.elementDeleted(index, removed); changed = true; } } updates.commitEvent(); return changed; } /** * This method does nothing. It is not necessary to dispose a BasicEventList. */ public void dispose() { } /** * Although {@link EventList}s are not in general, {@link BasicEventList} is * {@link Serializable}. All of the {@link ListEventListener}s that are themselves * {@link Serializable} will be serialized, but others will not. Note that there * is no easy way to access the {@link ListEventListener}s of * an {@link EventList}, particularly after it has been serialized. * *

    As of October 3, 2005, this is the wire format of serialized * {@link BasicEventList}s: *

  • An Object[] containing each of the list's elements *
  • A ListEventListener[] containing only the * listeners that themselves implement {@link Serializable}. Those that * do not will not be serialized. Note that {@link TransformedList}s * such as {@link FilterList} are not {@link Serializable} and will not * be serialized. * *

    As of March 4, 2007, the wire format was extended to include: *

  • the ListEventPublisher *
  • the ReadWriteLock represented as a {@link SerializedReadWriteLock} *

    The motivation for this is documented here. * Serialization streams with the old format are still readable. Serialization streams with * the new format are not downwards-compatible. */ private void writeObject(ObjectOutputStream out) throws IOException { // 1. The elements to write E[] elements = (E[])data.toArray(new Object[data.size()]); // 2. The Listeners to write List> serializableListeners = new ArrayList>(1); for(Iterator> i = updates.getListEventListeners().iterator(); i.hasNext(); ) { ListEventListener listener = i.next(); if(!(listener instanceof Serializable)) continue; serializableListeners.add(listener); } ListEventListener[] listeners = serializableListeners.toArray(new ListEventListener[serializableListeners.size()]); // 3. Write the elements, listeners, publisher and lock out.writeObject(elements); out.writeObject(listeners); out.writeObject(getPublisher()); out.writeObject(getReadWriteLock()); } /** * Peer method to {@link #writeObject(ObjectOutputStream)}. Note that this * is functionally equivalent to a constructor and should validate that * everything is in place including locks, etc. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // 1. Read in the elements final E[] elements = (E[]) in.readObject(); // 2. Read in the listeners final ListEventListener[] listeners = (ListEventListener[]) in.readObject(); // 3. Try to read the ListEventPublisher and ReadWriteLock according to the new wire format try { this.publisher = (ListEventPublisher) in.readObject(); this.updates = new ListEventAssembler(this, publisher); this.readWriteLock = (ReadWriteLock) in.readObject(); } catch (OptionalDataException e) { if (e.eof) // reading old serialization stream without publisher and lock this.readWriteLock = LockFactory.DEFAULT.createReadWriteLock(); else throw e; } // 4. Populate the EventList data this.data = new ArrayList(elements.length); this.data.addAll(Arrays.asList(elements)); // 5. Populate the listeners for(int i = 0; i < listeners.length; i++) { this.updates.addListEventListener(listeners[i]); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/0000755000175000017500000000000012106516356025267 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/AutoCompleteSupport.java0000644000175000017500000034007512106516356032141 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.*; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.gui.TableFormat; import ca.odell.glazedlists.impl.GlazedListsImpl; import ca.odell.glazedlists.impl.filter.SearchTerm; import ca.odell.glazedlists.impl.filter.TextMatcher; import ca.odell.glazedlists.impl.swing.ComboBoxPopupLocationFix; import ca.odell.glazedlists.matchers.Matcher; import ca.odell.glazedlists.matchers.Matchers; import ca.odell.glazedlists.matchers.TextMatcherEditor; import java.awt.Component; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.reflect.Method; import java.text.Format; import java.text.ParsePosition; import java.util.Comparator; import java.util.List; import javax.swing.*; import javax.swing.border.Border; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicComboBoxEditor; import javax.swing.plaf.basic.ComboPopup; import javax.swing.text.*; /** * This class {@link #install}s support for filtering and autocompletion into * a standard {@link JComboBox}. It also acts as a factory class for * {@link #createTableCellEditor creating autocompleting table cell editors}. * *

    All autocompletion behaviour provided is meant to mimic the functionality * of the Firefox address field. To be explicit, the following is a list of * expected behaviours which are installed: * *

    Typing into the ComboBox Editor *

      *
    1. typing any value into the editor when the popup is invisible causes * the popup to appear and its contents to be filtered according to the * editor's text. It also autocompletes to the first item that is * prefixed with the editor's text and selects that item within the popup.
    2. *
    3. typing any value into the editor when the popup is visible causes * the popup to be refiltered according to the editor's text and * reselects an appropriate autocompletion item.
    4. *
    5. typing the down or up arrow keys in the editor when the popup is * invisible causes the popup to appear and its contents to be filtered * according to the editor's text. It also autocompletes to the first * item that is prefixed with the editor's text and selects that item * within the popup.
    6. *
    7. typing the up arrow key when the popup is visible and the selected * element is the first element causes the autocompletion to be cleared * and the popup's selection to be removed.
    8. *
    9. typing the up arrow key when no selection exists causes the last * element of the popup to become selected and used for autocompletion
    10. *
    11. typing the down arrow key when the popup is visible and the selected * element is the last element causes the autocompletion to be cleared * and the popup's selection to be removed
    12. *
    13. typing the down arrow key when no selection exists causes the first * element of the popup to become selected and used for autocompletion
    14. *
    15. typing the delete key while in strict mode will select the prior * text rather than delete it. Attempting to delete all text results in * a beep unless the autcomplete support has been configured to not beep. *
    * * Clicking on the Arrow Button *
      *
    1. clicking the arrow button when the popup is invisible causes the * popup to appear and its contents to be shown unfiltered *
    2. clicking the arrow button when the popup is visible causes the popup * to be hidden *
    * * Sizing the ComboBox Popup *
      *
    1. the popup is always at least as wide as the * autocompleting {@link JComboBox}, but may be wider to accomodate a * {@link JComboBox#getPrototypeDisplayValue() prototype display value} * if a non-null prototype display value exists *
    2. as items are filtered in the ComboBoxModel, the popup height is * adjusted to display between 0 and {@link JComboBox#getMaximumRowCount()} * rows before scrolling the popup *
    * * JComboBox ActionEvents *

    A single {@link ActionEvent} is fired from the JComboBox in these situations: *

      *
    1. the user hits the enter key *
    2. the selected item within the popup is changed (which can happen due * to a mouse click, a change in the autocompletion term, or using the * arrow keys) *
    3. the JComboBox loses focus and contains a value that does not appear * in the ComboBoxModel *
    * * Null Values in the ComboBoxModel *

    null values located in the ComboBoxModel are considered * identical to empty Strings ("") for the purposes of locating autocompletion * terms.

    * * ComboBoxEditor Focus *
      *
    1. the text in the ComboBoxEditor is selected if * {@link #getSelectsTextOnFocusGain()} returns true *
    2. the JPopupMenu is hidden when the ComboBoxEditor loses focus if * {@link #getHidesPopupOnFocusLost()} returns true *
    * * Extracting String Values *

    Each value in the ComboBoxModel must be converted to a String for many * reasons: filtering, setting into the ComboBoxEditor, displaying in the * renderer, etc. By default, JComboBox relies on {@link Object#toString()} * to map elements to their String equivalents. Sometimes, however, toString() * is not a reliable or desirable mechanism to use. To deal with this problem, * AutoCompleteSupport provides an install method that takes a {@link Format} * object which is used to do all converting back and forth between Strings and * ComboBoxModel objects.

    * *

    In order to achieve all of the autocompletion and filtering behaviour, * the following occurs when {@link #install} is called: * *

      *
    • the JComboBox will be made editable *
    • the JComboBox will have a custom ComboBoxModel installed on it * containing the given items *
    • the ComboBoxEditor will be wrapped with functionality and set back * into the JComboBox as the editor *
    • the JTextField which is the editor component for the JComboBox * will have a DocumentFilter installed on its backing Document *
    * * The strategy of this support class is to alter all of the objects which * influence the behaviour of the JComboBox in one single context. With that * achieved, it greatly reduces the cross-functional communication required to * customize the behaviour of JComboBox for filtering and autocompletion. * *

    Warning: This class must be * mutated from the Swing Event Dispatch Thread. Failure to do so will result in * an {@link IllegalStateException} thrown from any one of: * *

      *
    • {@link #install(JComboBox, EventList)} *
    • {@link #install(JComboBox, EventList, TextFilterator)} *
    • {@link #install(JComboBox, EventList, TextFilterator, Format)} *
    • {@link #isInstalled()} *
    • {@link #uninstall()} *
    • {@link #setCorrectsCase(boolean)} *
    • {@link #setStrict(boolean)} *
    • {@link #setBeepOnStrictViolation(boolean)} *
    • {@link #setSelectsTextOnFocusGain(boolean)} *
    • {@link #setHidesPopupOnFocusLost(boolean)} *
    • {@link #setFilterMode(int)} *
    • {@link #setFirstItem(Object)} *
    • {@link #removeFirstItem()} *
    * * @author James Lemieux */ public final class AutoCompleteSupport { private static final ParsePosition PARSE_POSITION = new ParsePosition(0); private static final Class[] VALUE_OF_SIGNATURE = {String.class}; /** Marker object for indicating "not found". */ private static final Object NOT_FOUND = new Object(); // // These member variables control behaviour of the autocompletion support // /** * true if user-specified text is converted into the same case as * the autocompletion term. false will leave user specified text * unaltered. */ private boolean correctsCase = true; /** * false if the user can specify values that do not appear in the * ComboBoxModel; true otherwise. */ private boolean strict = false; /** * true indicates a beep sound should be played to the user to * indicate their error when attempting to violate the {@link #strict} * setting; false indicates we should not beep. */ private boolean beepOnStrictViolation = true; /** * true if the text in the combobox editor is selected when the * editor gains focus; false otherwise. */ private boolean selectsTextOnFocusGain = true; /** * true if the {@link #popupMenu} should always * be hidden when the {@link #comboBoxEditor} loses focus; false * if the default behaviour should be preserved. This exists to provide a * reasonable alternative to the strange default behaviour in JComboBox in * which the tab key will advance focus to the next focusable component and * leave the JPopupMenu visible. */ private boolean hidesPopupOnFocusLost = true; // // These are member variables for convenience // /** The comboBox being decorated with autocomplete functionality. */ private JComboBox comboBox; /** The popup menu of the decorated comboBox. */ private JPopupMenu popupMenu; /** The popup that wraps the popupMenu of the decorated comboBox. */ private ComboPopup popup; /** The arrow button that invokes the popup. */ private JButton arrowButton; /** The model backing the comboBox. */ private final AutoCompleteComboBoxModel comboBoxModel; /** The custom renderer installed on the comboBox or null if one is not required. */ private final ListCellRenderer renderer; /** The EventList which holds the items present in the comboBoxModel. */ private final EventList items; /** The FilterList which filters the items present in the comboBoxModel. */ private final FilterList filteredItems; /** A single-element EventList for storing the optional first element, typically used to represent "no selection". */ private final EventList firstItem; /** The CompositeList which is the union of firstItem and filteredItems to produce all filtered items available in the comboBoxModel. */ private final CompositeList allItemsFiltered; /** The CompositeList which is the union of firstItem and items to produce all unfiltered items available in the comboBoxModel. */ private final CompositeList allItemsUnfiltered; /** The Format capable of producing Strings from ComboBoxModel elements and vice versa. */ private final Format format; /** The MatcherEditor driving the FilterList behind the comboBoxModel. */ private final TextMatcherEditor filterMatcherEditor; /** * The custom ComboBoxEditor that does NOT assume that the text value can * be computed using Object.toString(). (i.e. the default ComboBoxEditor * *does* assume that, but we decorate it and remove that assumption) */ private FormatComboBoxEditor comboBoxEditor; /** The textfield which acts as the editor of the comboBox. */ private JTextField comboBoxEditorComponent; /** The Document backing the comboBoxEditorComponent. */ private AbstractDocument document; /** A DocumentFilter that controls edits to the document. */ private final AutoCompleteFilter documentFilter = new AutoCompleteFilter(); /** The last prefix specified by the user. */ private String prefix = ""; /** The Matcher that decides if a ComboBoxModel element is filtered out. */ private Matcher filterMatcher = Matchers.trueMatcher(); /** true while processing a text change to the {@link #comboBoxEditorComponent}; false otherwise. */ private boolean isFiltering = false; /** Controls the selection behavior of the JComboBox when it is used in a JTable DefaultCellEditor. */ private final boolean isTableCellEditor; // // These listeners work together to enforce different aspects of the autocompletion behaviour // /** * The MouseListener which is installed on the {@link #arrowButton} and * is responsible for clearing the filter and then showing / hiding the * {@link #popup}. */ private ArrowButtonMouseListener arrowButtonMouseListener; /** * A listener which reacts to changes in the ComboBoxModel by * resizing the popup appropriately to accomodate the new data. */ private final ListDataListener listDataHandler = new ListDataHandler(); /** * We ensure the popup menu is sized correctly each time it is shown. * Namely, we respect the prototype display value of the combo box, if * it has one. Regardless of the width of the combo box, we attempt to * size the popup to accomodate the width of the prototype display value. */ private final PopupMenuListener popupSizerHandler = new PopupSizer(); /** * An unfortunately necessary fixer for a misplaced popup. */ private ComboBoxPopupLocationFix popupLocationFix; /** * We ensure that selecting an item from the popup via the mouse never * attempts to autocomplete for fear that we will replace the user's * newly selected item and the item will effectively be unselectable. */ private final MouseListener popupMouseHandler = new PopupMouseHandler(); /** Handles the special case of the backspace key in strict mode and the enter key. */ private final KeyListener strictModeBackspaceHandler = new AutoCompleteKeyHandler(); /** Handles selecting the text in the comboBoxEditorComponent when it gains focus. */ private final FocusListener selectTextOnFocusGainHandler = new ComboBoxEditorFocusHandler(); // // These listeners watch for invalid changes to the JComboBox which break our autocompletion // /** * Watches for changes of the Document which backs comboBoxEditorComponent and uninstalls * our DocumentFilter from the old Document and reinstalls it on the new. */ private final DocumentWatcher documentWatcher = new DocumentWatcher(); /** Watches for changes of the ComboBoxModel and reports them as violations. */ private final ModelWatcher modelWatcher = new ModelWatcher(); /** Watches for changes of the ComboBoxUI and reinstalls the autocompletion support. */ private final UIWatcher uiWatcher = new UIWatcher(); // // These booleans control when certain changes are to be respected and when they aren't // /** true indicates document changes should not be post processed * (i.e. just commit changes to the Document and do not cause any side-effects). */ private boolean doNotPostProcessDocumentChanges = false; /** true indicates attempts to filter the ComboBoxModel should be ignored. */ private boolean doNotFilter = false; /** true indicates attempts to change the document should be ignored. */ private boolean doNotChangeDocument = false; /** true indicates attempts to select an autocompletion term should be ignored. */ private boolean doNotAutoComplete = false; /** true indicates attempts to toggle the state of the popup should be ignored. * In general, the only time we should toggle the state of a popup is due to a users keystroke * (and not programmatically setting the selected item, for example). * * When the JComboBox is used within a TableCellEditor, this value is ALWAYS false, since * we MUST accept keystrokes, even when they are passed second hand to the JComboBox after * it has been installed as the cell editor (as opposed to typed into the JComboBox directly) */ private boolean doNotTogglePopup; /** true indicates attempts to clear the filter when hiding the popup should be ignored. * This is because sometimes we hide and reshow a popup in rapid succession and we want to avoid * the work to unfiltering/refiltering it. */ private boolean doNotClearFilterOnPopupHide = false; // // Values present before {@link #install} executes - and are restored when {@link @uninstall} executes // /** The original setting of the editable field on the comboBox. */ private final boolean originalComboBoxEditable; /** The original model installed on the comboBox. */ private ComboBoxModel originalModel; /** The original ListCellRenderer installed on the comboBox. */ private ListCellRenderer originalRenderer; // // Values present before {@link #decorateCurrentUI} executes - and are restored when {@link @undecorateOriginalUI} executes // /** The original Actions associated with the up and down arrow keys. */ private Action originalSelectNextAction; private Action originalSelectPreviousAction; private Action originalSelectNext2Action; private Action originalSelectPrevious2Action; private Action originalAquaSelectNextAction; private Action originalAquaSelectPreviousAction; /** * This private constructor creates an AutoCompleteSupport object which adds * autocompletion functionality to the given comboBox. In * particular, a custom {@link ComboBoxModel} is installed behind the * comboBox containing the given items. The * filterator is consulted in order to extract searchable * text from each of the items. Non-null format * objects are used to convert ComboBoxModel elements to Strings and back * again for various functions like filtering, editing, and rendering. * * @param comboBox the {@link JComboBox} to decorate with autocompletion * @param items the objects to display in the comboBox * @param filterator extracts searchable text strings from each item * @param format converts combobox elements into strings and vice versa */ private AutoCompleteSupport(JComboBox comboBox, EventList items, TextFilterator filterator, Format format) { this.comboBox = comboBox; this.originalComboBoxEditable = comboBox.isEditable(); this.originalModel = comboBox.getModel(); this.items = items; this.format = format; // only build a custom renderer if the user specified their own Format but has not installed a custom renderer of their own final boolean defaultRendererInstalled = comboBox.getRenderer() instanceof UIResource; this.renderer = format != null && defaultRendererInstalled ? new StringFunctionRenderer() : null; // is this combo box a TableCellEditor? this.isTableCellEditor = Boolean.TRUE.equals(comboBox.getClientProperty("JComboBox.isTableCellEditor")); this.doNotTogglePopup = !isTableCellEditor; // lock the items list for reading since we want to prevent writes // from occurring until we fully initialize this AutoCompleteSupport items.getReadWriteLock().readLock().lock(); try { // build the ComboBoxModel capable of filtering its values this.filterMatcherEditor = new TextMatcherEditor(filterator == null ? new DefaultTextFilterator() : filterator); this.filterMatcherEditor.setMode(TextMatcherEditor.STARTS_WITH); this.filteredItems = new FilterList(items, this.filterMatcherEditor); this.firstItem = new BasicEventList(items.getPublisher(), items.getReadWriteLock()); // the ComboBoxModel always contains the firstItem and a filtered view of all other items this.allItemsFiltered = new CompositeList(items.getPublisher(), items.getReadWriteLock()); this.allItemsFiltered.addMemberList(this.firstItem); this.allItemsFiltered.addMemberList(this.filteredItems); this.comboBoxModel = new AutoCompleteComboBoxModel(this.allItemsFiltered); // we need an unfiltered view in order to try to locate autocompletion terms this.allItemsUnfiltered = new CompositeList(items.getPublisher(), items.getReadWriteLock()); this.allItemsUnfiltered.addMemberList(this.firstItem); this.allItemsUnfiltered.addMemberList(this.items); } finally { items.getReadWriteLock().readLock().unlock(); } // customize the comboBox this.comboBox.setModel(this.comboBoxModel); this.comboBox.setEditable(true); decorateCurrentUI(); // react to changes made to the key parts of JComboBox which affect autocompletion this.comboBox.addPropertyChangeListener("UI", this.uiWatcher); this.comboBox.addPropertyChangeListener("model", this.modelWatcher); this.comboBoxEditorComponent.addPropertyChangeListener("document", this.documentWatcher); } /** * A convenience method to ensure {@link AutoCompleteSupport} is being * accessed from the Event Dispatch Thread. */ private static void checkAccessThread() { if (!SwingUtilities.isEventDispatchThread()) throw new IllegalStateException("AutoCompleteSupport must be accessed from the Swing Event Dispatch Thread, but was called on Thread \"" + Thread.currentThread().getName() + "\""); } /** * A convenience method to unregister and return all {@link ActionListener}s * currently installed on the given comboBox. This is the only * technique we can rely on to prevent the comboBox from * broadcasting {@link ActionEvent}s at inappropriate times. * * This method is the logical inverse of {@link #registerAllActionListeners}. */ private static ActionListener[] unregisterAllActionListeners(JComboBox comboBox) { final ActionListener[] listeners = comboBox.getActionListeners(); for (int i = 0; i < listeners.length; i++) comboBox.removeActionListener(listeners[i]); return listeners; } /** * A convenience method to register all of the given listeners * with the given comboBox. * * This method is the logical inverse of {@link #unregisterAllActionListeners}. */ private static void registerAllActionListeners(JComboBox comboBox, ActionListener[] listeners) { for (int i = 0; i < listeners.length; i++) comboBox.addActionListener(listeners[i]); } /** * A convenience method to search through the given JComboBox for the * JButton which toggles the popup up open and closed. */ private static JButton findArrowButton(JComboBox c) { for (int i = 0, n = c.getComponentCount(); i < n; i++) { final Component comp = c.getComponent(i); if (comp instanceof JButton) return (JButton) comp; } return null; } /** * Decorate all necessary areas of the current UI to install autocompletion * support. This method is called in the constructor and when the comboBox's * UI delegate is changed. */ private void decorateCurrentUI() { // record some original settings of comboBox this.originalRenderer = comboBox.getRenderer(); this.popupMenu = (JPopupMenu) comboBox.getUI().getAccessibleChild(comboBox, 0); this.popup = (ComboPopup) popupMenu; this.arrowButton = findArrowButton(comboBox); // if an arrow button was found, decorate the ComboPopup's MouseListener // with logic that unfilters the ComboBoxModel when the arrow button is pressed if (this.arrowButton != null) { this.arrowButton.removeMouseListener(popup.getMouseListener()); this.arrowButtonMouseListener = new ArrowButtonMouseListener(popup.getMouseListener()); this.arrowButton.addMouseListener(arrowButtonMouseListener); } // start listening for model changes (due to filtering) so we can resize the popup vertically this.comboBox.getModel().addListDataListener(listDataHandler); // calculate the popup's width according to the prototype value, if one exists this.popupMenu.addPopupMenuListener(popupSizerHandler); // fix the popup's location this.popupLocationFix = ComboBoxPopupLocationFix.install(this.comboBox); // start suppressing autocompletion when selecting values from the popup with the mouse this.popup.getList().addMouseListener(popupMouseHandler); // record the original Up/Down arrow key Actions final ActionMap actionMap = comboBox.getActionMap(); this.originalSelectNextAction = actionMap.get("selectNext"); this.originalSelectPreviousAction = actionMap.get("selectPrevious"); this.originalSelectNext2Action = actionMap.get("selectNext2"); this.originalSelectPrevious2Action = actionMap.get("selectPrevious2"); this.originalAquaSelectNextAction = actionMap.get("aquaSelectNext"); this.originalAquaSelectPreviousAction = actionMap.get("aquaSelectPrevious"); final Action upAction = new MoveAction(-1); final Action downAction = new MoveAction(1); // install custom actions for the arrow keys in all non-Apple L&Fs actionMap.put("selectPrevious", upAction); actionMap.put("selectNext", downAction); actionMap.put("selectPrevious2", upAction); actionMap.put("selectNext2", downAction); // install custom actions for the arrow keys in the Apple Aqua L&F actionMap.put("aquaSelectPrevious", upAction); actionMap.put("aquaSelectNext", downAction); // install a custom ComboBoxEditor that decorates the existing one, but uses the // convertToString(...) method to produce text for the editor component (rather than .toString()) this.comboBoxEditor = new FormatComboBoxEditor(comboBox.getEditor()); this.comboBox.setEditor(comboBoxEditor); // add a DocumentFilter to the Document backing the editor JTextField this.comboBoxEditorComponent = (JTextField) comboBox.getEditor().getEditorComponent(); this.document = (AbstractDocument) comboBoxEditorComponent.getDocument(); this.document.setDocumentFilter(documentFilter); // install a custom renderer on the combobox, if we have built one if (this.renderer != null) comboBox.setRenderer(renderer); // add a KeyListener to the ComboBoxEditor which handles the special case of backspace when in strict mode this.comboBoxEditorComponent.addKeyListener(strictModeBackspaceHandler); // add a FocusListener to the ComboBoxEditor which selects all text when focus is gained this.comboBoxEditorComponent.addFocusListener(selectTextOnFocusGainHandler); } /** * Remove all customizations installed to various areas of the current UI * in order to uninstall autocompletion support. This method is invoked * after the comboBox's UI delegate is changed. */ private void undecorateOriginalUI() { // if an arrow button was found, remove our custom MouseListener and // reinstall the normal popup MouseListener if (this.arrowButton != null) { this.arrowButton.removeMouseListener(arrowButtonMouseListener); this.arrowButton.addMouseListener(arrowButtonMouseListener.getDecorated()); } // stop listening for model changes this.comboBox.getModel().removeListDataListener(listDataHandler); // remove the DocumentFilter from the Document backing the editor JTextField this.document.setDocumentFilter(null); // restore the original ComboBoxEditor if our custom ComboBoxEditor is still installed if (this.comboBox.getEditor() == comboBoxEditor) this.comboBox.setEditor(comboBoxEditor.getDelegate()); // stop adjusting the popup's width according to the prototype value this.popupMenu.removePopupMenuListener(popupSizerHandler); // stop fixing the combobox's popup location this.popupLocationFix.uninstall(); // stop suppressing autocompletion when selecting values from the popup with the mouse this.popup.getList().removeMouseListener(popupMouseHandler); final ActionMap actionMap = comboBox.getActionMap(); // restore the original actions for the arrow keys in all non-Apple L&Fs actionMap.put("selectPrevious", originalSelectPreviousAction); actionMap.put("selectNext", originalSelectNextAction); actionMap.put("selectPrevious2", originalSelectPrevious2Action); actionMap.put("selectNext2", originalSelectNext2Action); // restore the original actions for the arrow keys in the Apple Aqua L&F actionMap.put("aquaSelectPrevious", originalAquaSelectPreviousAction); actionMap.put("aquaSelectNext", originalAquaSelectNextAction); // remove the KeyListener from the ComboBoxEditor which handles the special case of backspace when in strict mode this.comboBoxEditorComponent.removeKeyListener(strictModeBackspaceHandler); // remove the FocusListener from the ComboBoxEditor which selects all text when focus is gained this.comboBoxEditorComponent.removeFocusListener(selectTextOnFocusGainHandler); // remove the custom renderer if it is still installed if (this.comboBox.getRenderer() == renderer) this.comboBox.setRenderer(originalRenderer); // erase some original settings of comboBox this.originalRenderer = null; this.comboBoxEditor = null; this.comboBoxEditorComponent = null; this.document = null; this.popupMenu = null; this.popup = null; this.arrowButton = null; } /** * Installs support for autocompletion into the comboBox and * returns the support object that is actually providing those facilities. * The support object is returned so that the caller may invoke * {@link #uninstall} at some later time to remove the autocompletion * features. * *

    This method assumes that the items can be converted into * reasonable String representations via {@link Object#toString()}. * *

    The following must be true in order to successfully install support * for autocompletion on a {@link JComboBox}: * *

      *
    • The JComboBox must use a {@link JTextField} as its editor component *
    • The JTextField must use an {@link AbstractDocument} as its model *
    * * @param comboBox the {@link JComboBox} to decorate with autocompletion * @param items the objects to display in the comboBox * @return an instance of the support class providing autocomplete features * @throws IllegalStateException if this method is called from any Thread * other than the Swing Event Dispatch Thread */ public static AutoCompleteSupport install(JComboBox comboBox, EventList items) { return install(comboBox, items, null); } /** * Installs support for autocompletion into the comboBox and * returns the support object that is actually providing those facilities. * The support object is returned so that the caller may invoke * {@link #uninstall} at some later time to remove the autocompletion * features. * *

    This method assumes that the items can be converted into * reasonable String representations via {@link Object#toString()}. * *

    The filterator will be used to extract searchable text * strings from each of the items. A null * filterator implies the item's toString() method should be used when * filtering it. * *

    The following must be true in order to successfully install support * for autocompletion on a {@link JComboBox}: * *

      *
    • The JComboBox must use a {@link JTextField} as its editor component *
    • The JTextField must use an {@link AbstractDocument} as its model *
    * * @param comboBox the {@link JComboBox} to decorate with autocompletion * @param items the objects to display in the comboBox * @param filterator extracts searchable text strings from each item; * null implies the item's toString() method should be * used when filtering it * @return an instance of the support class providing autocomplete features * @throws IllegalStateException if this method is called from any Thread * other than the Swing Event Dispatch Thread */ public static AutoCompleteSupport install(JComboBox comboBox, EventList items, TextFilterator filterator) { return install(comboBox, items, filterator, null); } /** * Installs support for autocompletion into the comboBox and * returns the support object that is actually providing those facilities. * The support object is returned so that the caller may invoke * {@link #uninstall} at some later time to remove the autocompletion * features. * *

    This method uses the given format to convert the * given items into Strings and back again. In other words, * this method does NOT rely on {@link Object#toString()} * to produce a reasonable String representation of each item. Likewise, * it does not rely on the existence of a valueOf(String) method for * creating items out of Strings as is the default behaviour of JComboBox. * *

    It can be assumed that the only methods called on the given format are: *

      *
    • {@link Format#format(Object)} *
    • {@link Format#parseObject(String, ParsePosition)} *
    * *

    As a convenience, this method will install a custom * {@link ListCellRenderer} on the comboBox that displays the * String value returned by the format. Though this is only * done if the given format is not null and if * the comboBox does not already use a custom renderer. * *

    The filterator will be used to extract searchable text * strings from each of the items. A null * filterator implies one of two default strategies will be used. If the * format is not null then the String value returned from the * format object will be used when filtering a given item. * Otherwise, the item's toString() method will be used when it is filtered. * *

    The following must be true in order to successfully install support * for autocompletion on a {@link JComboBox}: * *

      *
    • The JComboBox must use a {@link JTextField} as its editor component *
    • The JTextField must use an {@link AbstractDocument} as its model *
    * * @param comboBox the {@link JComboBox} to decorate with autocompletion * @param items the objects to display in the comboBox * @param filterator extracts searchable text strings from each item. If the * format is not null then the String value returned from * the format object will be used when filtering a given * item. Otherwise, the item's toString() method will be used when it * is filtered. * @param format a Format object capable of converting items * into Strings and back. null indicates the standard * JComboBox methods of converting are acceptable. * @return an instance of the support class providing autocomplete features * @throws IllegalStateException if this method is called from any Thread * other than the Swing Event Dispatch Thread */ public static AutoCompleteSupport install(JComboBox comboBox, EventList items, TextFilterator filterator, Format format) { checkAccessThread(); final Component editorComponent = comboBox.getEditor().getEditorComponent(); if (!(editorComponent instanceof JTextField)) throw new IllegalArgumentException("comboBox must use a JTextField as its editor component"); if (!(((JTextField) editorComponent).getDocument() instanceof AbstractDocument)) throw new IllegalArgumentException("comboBox must use a JTextField backed by an AbstractDocument as its editor component"); if (comboBox.getModel().getClass() == AutoCompleteSupport.AutoCompleteComboBoxModel.class) throw new IllegalArgumentException("comboBox is already configured for autocompletion"); return new AutoCompleteSupport(comboBox, items, filterator, format); } /** * This method is used to report environmental invariants which are * violated when the user adjusts the combo box in a way that is * incompatible with the requirements for autocompletion. A message can be * specified which will be included in the {@link IllegalStateException} * that is throw out of this method after the autocompletion support is * uninstalled. * * @param message a message to the programmer explaining the environmental * invariant that was violated */ private void throwIllegalStateException(String message) { final String exceptionMsg = message + "\n" + "In order for AutoCompleteSupport to continue to " + "work, the following invariants must be maintained after " + "AutoCompleteSupport.install() has been called:\n" + "* the ComboBoxModel may not be removed\n" + "* the AbstractDocument behind the JTextField can be changed but must be changed to some subclass of AbstractDocument\n" + "* the DocumentFilter on the AbstractDocument behind the JTextField may not be removed\n"; uninstall(); throw new IllegalStateException(exceptionMsg); } /** * A convenience method to produce a String from the given * comboBoxElement. */ private String convertToString(Object comboBoxElement) { if (comboBoxElement == NOT_FOUND) return "NOT_FOUND"; if (format != null) return format.format(comboBoxElement); return comboBoxElement == null ? "" : comboBoxElement.toString(); } /** * Returns the autocompleting {@link JComboBox} or null if * {@link AutoCompleteSupport} has been {@link #uninstall}ed. */ public JComboBox getComboBox() { return this.comboBox; } /** * Returns the {@link TextFilterator} that extracts searchable strings from * each item in the {@link ComboBoxModel}. */ public TextFilterator getTextFilterator() { return this.filterMatcherEditor.getFilterator(); } /** * Returns the filtered {@link EventList} of items which backs the * {@link ComboBoxModel} of the autocompleting {@link JComboBox}. */ public EventList getItemList() { return this.filteredItems; } /** * Returns true if user specified strings are converted to the * case of the autocompletion term they match; false otherwise. */ public boolean getCorrectsCase() { return correctsCase; } /** * If correctCase is true, user specified strings * will be converted to the case of the element they match. Otherwise * they will be left unaltered. * *

    Note: this flag only has meeting when strict mode is turned off. * When strict mode is on, case is corrected regardless of this setting. * * @see #setStrict(boolean) * * @throws IllegalStateException if this method is called from any Thread * other than the Swing Event Dispatch Thread */ public void setCorrectsCase(boolean correctCase) { checkAccessThread(); this.correctsCase = correctCase; } /** * Returns true if the user is able to specify values which do not * appear in the popup list of suggestions; false otherwise. */ public boolean isStrict() { return strict; } /** * If strict is false, the user can specify values * not appearing within the ComboBoxModel. If it is true each * keystroke must continue to match some value in the ComboBoxModel or it * will be discarded. * *

    Note: When strict mode is enabled, all user input is corrected to the * case of the autocompletion term, regardless of the correctsCase setting. * * @see #setCorrectsCase(boolean) * * @throws IllegalStateException if this method is called from any Thread * other than the Swing Event Dispatch Thread */ public void setStrict(boolean strict) { checkAccessThread(); if (this.strict == strict) return; this.strict = strict; // if strict mode was just turned on, ensure the comboBox contains a // value from the ComboBoxModel (i.e. start being strict!) if (strict) { final String currentText = comboBoxEditorComponent.getText(); Object currentItem = findAutoCompleteTerm(currentText); String currentItemText = convertToString(currentItem); boolean itemMatches = currentItem == comboBox.getSelectedItem(); boolean textMatches = GlazedListsImpl.equal(currentItemText, currentText); // select the first element if no autocompletion term could be found if (currentItem == NOT_FOUND && !allItemsUnfiltered.isEmpty()) { currentItem = allItemsUnfiltered.get(0); currentItemText = convertToString(currentItem); itemMatches = currentItem == comboBox.getSelectedItem(); textMatches = GlazedListsImpl.equal(currentItemText, currentText); } // return all elements to the ComboBoxModel applyFilter(""); doNotPostProcessDocumentChanges = true; try { // adjust the editor's text, if necessary if (!textMatches) comboBoxEditorComponent.setText(currentItemText); // adjust the model's selected item, if necessary if (!itemMatches || comboBox.getSelectedIndex() == -1) comboBox.setSelectedItem(currentItem); } finally { doNotPostProcessDocumentChanges = false; } } } /** * Returns true if a beep sound is played when the user attempts * to violate the strict invariant; false if no beep sound is * played. This setting is only respected if {@link #isStrict()} returns * true. * * @see #setStrict(boolean) */ public boolean getBeepOnStrictViolation() { return beepOnStrictViolation; } /** * Sets the policy for indicating strict-mode violations to the user by way * of a beep sound. * * @param beepOnStrictViolation true if a beep sound should be * played when the user attempts to violate the strict invariant; * false if no beep sound should be played * * @throws IllegalStateException if this method is called from any Thread * other than the Swing Event Dispatch Thread */ public void setBeepOnStrictViolation(boolean beepOnStrictViolation) { checkAccessThread(); this.beepOnStrictViolation = beepOnStrictViolation; } /** * Returns true if the combo box editor text is selected when it * gains focus; false otherwise. */ public boolean getSelectsTextOnFocusGain() { return selectsTextOnFocusGain; } /** * If selectsTextOnFocusGain is true, all text in the * editor is selected when the combo box editor gains focus. If it is * false the selection state of the editor is not effected by * focus changes. * * @throws IllegalStateException if this method is called from any Thread * other than the Swing Event Dispatch Thread */ public void setSelectsTextOnFocusGain(boolean selectsTextOnFocusGain) { checkAccessThread(); this.selectsTextOnFocusGain = selectsTextOnFocusGain; } /** * Returns true if the popup menu is hidden whenever the combo * box editor loses focus; false otherwise. */ public boolean getHidesPopupOnFocusLost() { return hidesPopupOnFocusLost; } /** * If hidesPopupOnFocusLost is true, then the popup * menu of the combo box is always hidden whenever the * combo box editor loses focus. If it is false the default * behaviour is preserved. In practice this means that if focus is lost * because of a MouseEvent, the behaviour is reasonable, but if focus is * lost because of a KeyEvent (e.g. tabbing to the next focusable component) * then the popup menu remains visible. * * @throws IllegalStateException if this method is called from any Thread * other than the Swing Event Dispatch Thread */ public void setHidesPopupOnFocusLost(boolean hidesPopupOnFocusLost) { checkAccessThread(); this.hidesPopupOnFocusLost = hidesPopupOnFocusLost; } /** * Returns the manner in which the contents of the {@link ComboBoxModel} * are filtered. This method will return one of * {@link TextMatcherEditor#CONTAINS} or {@link TextMatcherEditor#STARTS_WITH}. * *

    {@link TextMatcherEditor#CONTAINS} indicates elements of the * {@link ComboBoxModel} are matched when they contain the text entered by * the user. * *

    {@link TextMatcherEditor#STARTS_WITH} indicates elements of the * {@link ComboBoxModel} are matched when they start with the text entered * by the user. * *

    In both modes, autocompletion only occurs when a given item starts * with user-specified text. The filter mode only affects the filtering * aspect of autocomplete support. */ public int getFilterMode() { return filterMatcherEditor.getMode(); } /** * Sets the manner in which the contents of the {@link ComboBoxModel} are * filtered. The given mode must be one of * {@link TextMatcherEditor#CONTAINS} or {@link TextMatcherEditor#STARTS_WITH}. * * @throws IllegalStateException if this method is called from any Thread * other than the Swing Event Dispatch Thread * * @see #getFilterMode() */ public void setFilterMode(int mode) { checkAccessThread(); // adjust the MatcherEditor that filters the AutoCompleteComboBoxModel to respect the given mode // but ONLY adjust the contents of the model, avoid changing the text in the JComboBox's textfield doNotChangeDocument = true; try { filterMatcherEditor.setMode(mode); } finally { doNotChangeDocument = false; } } /** * Sets the manner in which the contents of the {@link ComboBoxModel} are * filtered and autocompletion terms are matched. The given strategy must be one of * {@link TextMatcherEditor#IDENTICAL_STRATEGY} or {@link TextMatcherEditor#NORMALIZED_STRATEGY} * or the Unicode strategy of the ICU4J extension. * * @throws IllegalStateException if this method is called from any Thread * other than the Swing Event Dispatch Thread * * @see #getTextMatchingStrategy() */ public void setTextMatchingStrategy(Object strategy) { checkAccessThread(); // adjust the MatcherEditor that filters the AutoCompleteComboBoxModel to respect the given strategy // but ONLY adjust the contents of the model, avoid changing the text in the JComboBox's textfield doNotChangeDocument = true; try { filterMatcherEditor.setStrategy(strategy); // do we need to update the filterMatcher here? } finally { doNotChangeDocument = false; } } /** * Returns the manner in which the contents of the {@link ComboBoxModel} are * filtered and autocompletion terms are matched. The returned strategy is one of * {@link TextMatcherEditor#IDENTICAL_STRATEGY} or {@link TextMatcherEditor#NORMALIZED_STRATEGY} * or the Unicode strategy of the ICU4J extension. */ public Object getTextMatchingStrategy() { return filterMatcherEditor.getStrategy(); } /** * This method set a single optional value to be used as the first element * in the {@link ComboBoxModel}. This value typically represents * "no selection" or "blank". This value is always present and is not * filtered away during autocompletion. * * @param item the first value to present in the {@link ComboBoxModel} */ public void setFirstItem(E item) { checkAccessThread(); doNotChangeDocument = true; firstItem.getReadWriteLock().writeLock().lock(); try { if (firstItem.isEmpty()) firstItem.add(item); else firstItem.set(0, item); } finally { firstItem.getReadWriteLock().writeLock().unlock(); doNotChangeDocument = false; } } /** * Returns the optional single value used as the first element in the * {@link ComboBoxModel} or null if no first item has been set. * * @return the special first value presented in the {@link ComboBoxModel} * or null if no first item has been set */ public E getFirstItem() { firstItem.getReadWriteLock().readLock().lock(); try { return firstItem.isEmpty() ? null : firstItem.get(0); } finally { firstItem.getReadWriteLock().readLock().unlock(); } } /** * Removes and returns the optional single value used as the first element * in the {@link ComboBoxModel} or null if no first item has been * set. * * @return the special first value presented in the {@link ComboBoxModel} * or null if no first item has been set */ public E removeFirstItem() { checkAccessThread(); doNotChangeDocument = true; firstItem.getReadWriteLock().writeLock().lock(); try { return firstItem.isEmpty() ? null : firstItem.remove(0); } finally { firstItem.getReadWriteLock().writeLock().unlock(); doNotChangeDocument = false; } } /** * Returns true if this autocomplete support instance is currently * installed and altering the behaviour of the combo box; false if * it has been {@link #uninstall}ed. * * @throws IllegalStateException if this method is called from any Thread * other than the Swing Event Dispatch Thread */ public boolean isInstalled() { checkAccessThread(); return comboBox != null; } /** * This method removes autocompletion support from the {@link JComboBox} * it was installed on. This method is useful when the {@link EventList} of * items that backs the combo box must outlive the combo box itself. * Calling this method will return the combo box to its original state * before autocompletion was installed, and it will be available for * garbage collection independently of the {@link EventList} of items. * * @throws IllegalStateException if this method is called from any Thread * other than the Swing Event Dispatch Thread */ public void uninstall() { checkAccessThread(); if (this.comboBox == null) throw new IllegalStateException("This AutoCompleteSupport has already been uninstalled"); items.getReadWriteLock().readLock().lock(); try { // 1. stop listening for changes this.comboBox.removePropertyChangeListener("UI", this.uiWatcher); this.comboBox.removePropertyChangeListener("model", this.modelWatcher); this.comboBoxEditorComponent.removePropertyChangeListener("document", this.documentWatcher); // 2. undecorate the original UI components this.undecorateOriginalUI(); // 3. restore the original model to the JComboBox this.comboBox.setModel(originalModel); this.originalModel = null; // 4. restore the original editable flag to the JComboBox this.comboBox.setEditable(originalComboBoxEditable); // 5. dispose of our ComboBoxModel this.comboBoxModel.dispose(); // 6. dispose of our EventLists so that they are severed from the given items EventList this.allItemsFiltered.dispose(); this.allItemsUnfiltered.dispose(); this.filteredItems.dispose(); // null out the comboBox to indicate that this support class is uninstalled this.comboBox = null; } finally { items.getReadWriteLock().readLock().unlock(); } } /** * This method updates the value which filters the items in the * ComboBoxModel. * * @param newFilter the new value by which to filter the item */ private void applyFilter(String newFilter) { // break out early if we're flagged to ignore filter updates for the time being if (doNotFilter) return; // ignore attempts to change the text in the combo box editor while // the filtering is taking place doNotChangeDocument = true; final ActionListener[] listeners = unregisterAllActionListeners(comboBox); isFiltering = true; try { filterMatcherEditor.setFilterText(new String[] {newFilter}); } finally { isFiltering = false; registerAllActionListeners(comboBox, listeners); doNotChangeDocument = false; } } /** * This method updates the {@link #prefix} to be the current value in the * ComboBoxEditor. */ private void updateFilter() { prefix = comboBoxEditorComponent.getText(); if (prefix.length() == 0) filterMatcher = Matchers.trueMatcher(); else filterMatcher = new TextMatcher(new SearchTerm[] {new SearchTerm(prefix)}, GlazedLists.toStringTextFilterator(), TextMatcherEditor.STARTS_WITH, getTextMatchingStrategy()); } /** * A small convenience method to try showing the ComboBoxPopup. */ private void togglePopup() { // break out early if we're flagged to ignore attempts to toggle the popup state if (doNotTogglePopup) return; if (comboBoxModel.getSize() == 0) comboBox.hidePopup(); else if (comboBox.isShowing() && !comboBox.isPopupVisible() && comboBoxEditorComponent.hasFocus()) comboBox.showPopup(); } /** * Performs a linear scan of ALL ITEMS, regardless of the filtering state * of the ComboBoxModel, to locate the autocomplete term. If an exact * match of the given value can be found, then the item is * returned. If an exact match cannot be found, the first term that * starts with the given value is returned. * *

    If no exact or partial match can be located, null is * returned. */ private Object findAutoCompleteTerm(String value) { // determine if our value is empty final boolean prefixIsEmpty = "".equals(value); final Matcher valueMatcher = new TextMatcher(new SearchTerm[] {new SearchTerm(value)}, GlazedLists.toStringTextFilterator(), TextMatcherEditor.STARTS_WITH, getTextMatchingStrategy()); Object partialMatchItem = NOT_FOUND; // search the list of ALL UNFILTERED items for an autocompletion term for the given value for (int i = 0, n = allItemsUnfiltered.size(); i < n; i++) { final E item = allItemsUnfiltered.get(i); final String itemString = convertToString(item); // if we have an exact match, return the given value immediately if (value.equals(itemString)) return item; // if we have not yet located a partial match, check the current itemString for a partial match // (to be returned if an exact match cannot be found) if (partialMatchItem == NOT_FOUND) { if (prefixIsEmpty ? "".equals(itemString) : valueMatcher.matches(itemString)) partialMatchItem = item; } } return partialMatchItem; } /** * This special version of EventComboBoxModel simply marks a flag to * indicate the items in the ComboBoxModel should not be filtered as a * side-effect of setting the selected item. It also marks another flag * to indicate that the selected item is being explicitly set, and thus * autocompletion should not execute and possibly overwrite the * programmer's specified value. */ private class AutoCompleteComboBoxModel extends EventComboBoxModel { public AutoCompleteComboBoxModel(EventList source) { super(source); } /** * Overridden because AutoCompleteSupport needs absolute control over * when a JComboBox's ActionListeners are notified. */ @Override public void setSelectedItem(Object selected) { doNotFilter = true; doNotAutoComplete = true; // remove all ActionListeners from the JComboBox since setting the selected item // would normally notify them, but in normal autocompletion behaviour, we don't want that final ActionListener[] listeners = unregisterAllActionListeners(comboBox); try { super.setSelectedItem(selected); if (comboBoxEditorComponent != null) { // remove any text selection that might exist when an item is selected final int caretPos = comboBoxEditorComponent.getCaretPosition(); comboBoxEditorComponent.select(caretPos, caretPos); } } finally { // reinstall the ActionListeners we removed registerAllActionListeners(comboBox, listeners); doNotFilter = false; doNotAutoComplete = false; } } /** * Overridden because ListEvents produce ListDataEvents from this * ComboBoxModel, which notify the BasicComboBoxUI of the data change, * which in turn tries to set the text of the ComboBoxEditor to match * the text of the selected item. We don't want that. AutoCompleteSupport * is the ultimate authority on the text value in the ComboBoxEditor. * We override this method to set doNotChangeDocument to ensure that * attempts to change the ComboBoxEditor's Document are ignored and * our control is absolute. */ @Override public void listChanged(ListEvent listChanges) { doNotChangeDocument = true; try { super.listChanged(listChanges); } finally { doNotChangeDocument = false; } } } /** * This class is the crux of the entire solution. This custom DocumentFilter * controls all edits which are attempted against the Document of the * ComboBoxEditor component. It is our hook to either control when to respect * edits as well as the side-effects the edit has on autocompletion and * filtering. */ private class AutoCompleteFilter extends DocumentFilter { @Override public void replace(FilterBypass filterBypass, int offset, int length, String string, AttributeSet attributeSet) throws BadLocationException { if (doNotChangeDocument) return; // collect rollback information before performing the replace final String valueBeforeEdit = comboBoxEditorComponent.getText(); final int selectionStart = comboBoxEditorComponent.getSelectionStart(); final int selectionEnd = comboBoxEditorComponent.getSelectionEnd(); // this short-circuit corrects the PlasticLookAndFeel behaviour. Hitting the enter key in Plastic // will cause the popup to reopen because the Plastic ComboBoxEditor forwards on unnecessary updates // to the document, including ones where the text isn't really changing final boolean isReplacingAllText = offset == 0 && document.getLength() == length; if (isReplacingAllText && valueBeforeEdit.equals(string)) return; super.replace(filterBypass, offset, length, string, attributeSet); postProcessDocumentChange(filterBypass, attributeSet, valueBeforeEdit, selectionStart, selectionEnd, true); } @Override public void insertString(FilterBypass filterBypass, int offset, String string, AttributeSet attributeSet) throws BadLocationException { if (doNotChangeDocument) return; // collect rollback information before performing the insert final String valueBeforeEdit = comboBoxEditorComponent.getText(); final int selectionStart = comboBoxEditorComponent.getSelectionStart(); final int selectionEnd = comboBoxEditorComponent.getSelectionEnd(); super.insertString(filterBypass, offset, string, attributeSet); postProcessDocumentChange(filterBypass, attributeSet, valueBeforeEdit, selectionStart, selectionEnd, true); } @Override public void remove(FilterBypass filterBypass, int offset, int length) throws BadLocationException { if (doNotChangeDocument) return; // collect rollback information before performing the remove final String valueBeforeEdit = comboBoxEditorComponent.getText(); final int selectionStart = comboBoxEditorComponent.getSelectionStart(); final int selectionEnd = comboBoxEditorComponent.getSelectionEnd(); super.remove(filterBypass, offset, length); postProcessDocumentChange(filterBypass, null, valueBeforeEdit, selectionStart, selectionEnd, isStrict()); } /** * This method generically post processes changes to the ComboBox * editor's Document. The generic algorithm, regardless of the type of * change, is as follows: * *

      *
    1. save the prefix as the user has entered it *
    2. filter the combo box items against the prefix *
    3. update the text in the combo box editor with an autocomplete suggestion *
    4. try to show the popup, if possible *
    */ private void postProcessDocumentChange(FilterBypass filterBypass, AttributeSet attributeSet, String valueBeforeEdit, int selectionStart, int selectionEnd, boolean allowPartialAutoCompletionTerm) throws BadLocationException { // break out early if we're flagged to not post process the Document change if (doNotPostProcessDocumentChanges) return; final String valueAfterEdit = comboBoxEditorComponent.getText(); // if an autocomplete term could not be found and we're in strict mode, rollback the edit if (isStrict() && (findAutoCompleteTerm(valueAfterEdit) == NOT_FOUND) && !allItemsUnfiltered.isEmpty()) { // indicate the error to the user if (getBeepOnStrictViolation()) UIManager.getLookAndFeel().provideErrorFeedback(comboBoxEditorComponent); // rollback the edit doNotPostProcessDocumentChanges = true; try { comboBoxEditorComponent.setText(valueBeforeEdit); } finally { doNotPostProcessDocumentChanges = false; } // restore the selection as it existed comboBoxEditorComponent.select(selectionStart, selectionEnd); // do not continue post processing changes return; } // record the selection before post processing the Document change // (we'll use this to decide whether to broadcast an ActionEvent when choosing the next selected index) final Object selectedItemBeforeEdit = comboBox.getSelectedItem(); updateFilter(); applyFilter(prefix); selectAutoCompleteTerm(filterBypass, attributeSet, selectedItemBeforeEdit, allowPartialAutoCompletionTerm); togglePopup(); } /** * This method will attempt to locate a reasonable autocomplete item * from all combo box items and select it. It will also populate the * combo box editor with the remaining text which matches the * autocomplete item and select it. If the selection changes and the * JComboBox is not a Table Cell Editor, an ActionEvent will be * broadcast from the combo box. */ private void selectAutoCompleteTerm(FilterBypass filterBypass, AttributeSet attributeSet, Object selectedItemBeforeEdit, boolean allowPartialAutoCompletionTerm) throws BadLocationException { // break out early if we're flagged to ignore attempts to autocomplete if (doNotAutoComplete) return; // determine if our prefix is empty (in which case we cannot use our filterMatcher to locate an autocompletion term) final boolean prefixIsEmpty = "".equals(prefix); // record the original caret position in case we don't want to disturb the text (occurs when an exact autocomplete term match is found) final int originalCaretPosition = comboBoxEditorComponent.getCaretPosition(); // a flag to indicate whether a partial match or exact match exists on the autocomplete term boolean autoCompleteTermIsExactMatch = false; // search the combobox model for a value that starts with our prefix (called an autocompletion term) for (int i = 0, n = comboBoxModel.getSize(); i < n; i++) { String itemString = convertToString(comboBoxModel.getElementAt(i)); // if itemString does not match the prefix, continue searching for an autocompletion term if (prefixIsEmpty ? !"".equals(itemString) : !filterMatcher.matches(itemString)) continue; // record the index and value that are our "best" autocomplete terms so far int matchIndex = i; String matchString = itemString; // search for an *exact* match in the remainder of the ComboBoxModel // before settling for the partial match we have just found for (int j = i; j < n; j++) { itemString = convertToString(comboBoxModel.getElementAt(j)); // if we've located an exact match, use its index and value rather than the partial match if (prefix.equals(itemString)) { matchIndex = j; matchString = itemString; autoCompleteTermIsExactMatch = true; break; } } // if partial autocompletion terms are not allowed, and we only have a partial term, bail early if (!allowPartialAutoCompletionTerm && !prefix.equals(itemString)) return; // either keep the user's prefix or replace it with the itemString's prefix // depending on whether we correct the case if (getCorrectsCase() || isStrict()) { filterBypass.replace(0, prefix.length(), matchString, attributeSet); } else { final String itemSuffix = matchString.substring(prefix.length()); filterBypass.insertString(prefix.length(), itemSuffix, attributeSet); } // select the autocompletion term final boolean silently = isTableCellEditor || GlazedListsImpl.equal(selectedItemBeforeEdit, matchString); selectItem(matchIndex, silently); if (autoCompleteTermIsExactMatch) { // if the term matched the original text exactly, return the caret to its original location comboBoxEditorComponent.setCaretPosition(originalCaretPosition); } else { // select the text after the prefix but before the end of the text (it represents the autocomplete text) comboBoxEditorComponent.select(prefix.length(), document.getLength()); } return; } // reset the selection since we couldn't find the prefix in the model // (this has the side-effect of scrolling the popup to the top) final boolean silently = isTableCellEditor || selectedItemBeforeEdit == null; selectItem(-1, silently); } /** * Select the item at the given index. If * silent is true, the JComboBox will not * broadcast an ActionEvent. */ private void selectItem(int index, boolean silently) { final Object valueToSelect = index == -1 ? null : comboBoxModel.getElementAt(index); // if nothing is changing about the selection, return immediately if (GlazedListsImpl.equal(comboBoxModel.getSelectedItem(), valueToSelect)) return; doNotChangeDocument = true; try { if (silently) comboBoxModel.setSelectedItem(valueToSelect); else comboBox.setSelectedItem(valueToSelect); } finally { doNotChangeDocument = false; } } } /** * Select the item at the given index. This method behaves * differently in strict mode vs. non-strict mode. * *

    In strict mode, the selected index must always be valid, so using the * down arrow key on the last item or the up arrow key on the first item * simply wraps the selection to the opposite end of the model. * *

    In non-strict mode, the selected index can be -1 (no selection), so we * allow -1 to mean "adjust the value of the ComboBoxEditor to be the user's * text" and only wrap to the end of the model when -2 is reached. In short, * -1 is interpreted as "clear the selected item". * -2 is interpreted as "the last element". */ private void selectPossibleValue(int index) { if (isStrict()) { // wrap the index from past the start to the end of the model if (index < 0) index = comboBox.getModel().getSize()-1; // wrap the index from past the end to the start of the model if (index > comboBox.getModel().getSize()-1) index = 0; } else { // wrap the index from past the start to the end of the model if (index == -2) index = comboBox.getModel().getSize()-1; } // check if the index is within a valid range final boolean validIndex = index >= 0 && index < comboBox.getModel().getSize(); // if the index isn't valid, select nothing if (!validIndex) index = -1; // adjust only the value in the comboBoxEditorComponent, but leave the comboBoxModel unchanged doNotPostProcessDocumentChanges = true; try { // select the index if (isTableCellEditor) { // while operating as a TableCellEditor, no ActionListeners must be notified // when using the arrow keys to adjust the selection final ActionListener[] listeners = unregisterAllActionListeners(comboBox); try { comboBox.setSelectedIndex(index); } finally { registerAllActionListeners(comboBox, listeners); } } else { comboBox.setSelectedIndex(index); } // if the original index wasn't valid, we've cleared the selection // and must set the user's prefix into the editor if (!validIndex) { comboBoxEditorComponent.setText(prefix); // don't bother unfiltering the popup since we'll redisplay the popup immediately doNotClearFilterOnPopupHide = true; try { comboBox.hidePopup(); } finally { doNotClearFilterOnPopupHide = false; } comboBox.showPopup(); } } finally { doNotPostProcessDocumentChanges = false; } // if the comboBoxEditorComponent's values begins with the user's prefix, highlight the remainder of the value final String newSelection = comboBoxEditorComponent.getText(); if (filterMatcher.matches(newSelection)) comboBoxEditorComponent.select(prefix.length(), newSelection.length()); } /** * The action invoked by hitting the up or down arrow key. */ private class MoveAction extends AbstractAction { private final int offset; public MoveAction(int offset) { this.offset = offset; } public void actionPerformed(ActionEvent e) { if (comboBox.isShowing()) { if (comboBox.isPopupVisible()) { selectPossibleValue(comboBox.getSelectedIndex() + offset); } else { applyFilter(prefix); comboBox.showPopup(); } } } } /** * This class listens to the ComboBoxModel and redraws the popup if it * must grow or shrink to accomodate the latest list of items. */ private class ListDataHandler implements ListDataListener { private int previousItemCount = -1; private final Runnable checkStrictModeInvariantRunnable = new CheckStrictModeInvariantRunnable(); public void contentsChanged(ListDataEvent e) { final int newItemCount = comboBox.getItemCount(); // if the size of the model didn't change, the popup size won't change if (previousItemCount != newItemCount) { final int maxPopupItemCount = comboBox.getMaximumRowCount(); // if the popup is showing, check if it must be resized if (popupMenu.isShowing()) { if (comboBox.isShowing()) { // if either the previous or new item count is less than the max, // hide and show the popup to recalculate its new height if (newItemCount < maxPopupItemCount || previousItemCount < maxPopupItemCount) { // don't bother unfiltering the popup since we'll redisplay the popup immediately doNotClearFilterOnPopupHide = true; try { comboBox.hidePopup(); } finally { doNotClearFilterOnPopupHide = false; } comboBox.showPopup(); } } else { // if the comboBox is not showing, simply hide the popup to avoid: // "java.awt.IllegalComponentStateException: component must be showing on the screen to determine its location" // this case can occur when the comboBox is used as a TableCellEditor // and is uninstalled (removed from the component hierarchy) before // receiving this callback comboBox.hidePopup(); } } previousItemCount = newItemCount; } // if the comboBoxModel was changed and it wasn't due to the filter changing // (i.e. !isFiltering) and it wasn't because the user selected a new // selectedItem (i.e. !userSelectedNewItem) then those changes may have // invalidated the invariant that strict mode places on the text in the // JTextField, so we must either: // // a) locate the text within the model (proving that the strict mode invariant still holds) // b) set the text to that of the first element in the model (to reestablish the invariant) final boolean userSelectedNewItem = e.getIndex0() == -1 || e.getIndex1() == -1; if (isStrict() && !userSelectedNewItem && !isFiltering) { // notice that instead of doing the work directly, we post a Runnable here // to check the strict mode invariant and repair it if it is broken. That's // important. It's necessary because we must let the current ListEvent // finish its dispatching before we attempt to change the filter of the // filteredItems list by setting new text into the comboBoxEditorComponent SwingUtilities.invokeLater(checkStrictModeInvariantRunnable); } } public void intervalAdded(ListDataEvent e) { contentsChanged(e); } public void intervalRemoved(ListDataEvent e) { contentsChanged(e); } private class CheckStrictModeInvariantRunnable implements Runnable { public void run() { final JTextField editor = comboBoxEditorComponent; if (editor != null) { final String currentText = editor.getText(); final Object item = findAutoCompleteTerm(currentText); String itemText = convertToString(item); // if we did not find the same autocomplete term if (!currentText.equals(itemText)) { // select the first item if we could not find an autocomplete term with the currentText if (item == NOT_FOUND && !allItemsUnfiltered.isEmpty()) itemText = convertToString(allItemsUnfiltered.get(0)); // set the new strict value text into the editor component editor.setText(itemText); } } } } } /** * This class sizes the popup menu of the combo box immediately before * it is shown on the screen. In particular, it will adjust the width * of the popup to accomodate a prototype display value if the combo * box contains one. */ private class PopupSizer implements PopupMenuListener { public void popupMenuWillBecomeVisible(PopupMenuEvent e) { // if the combo box does not contain a prototype display value, skip our sizing logic final Object prototypeValue = comboBox.getPrototypeDisplayValue(); if (prototypeValue == null) return; final JComponent popupComponent = (JComponent) e.getSource(); // attempt to extract the JScrollPane that scrolls the popup if (popupComponent.getComponent(0) instanceof JScrollPane) { final JScrollPane scroller = (JScrollPane) popupComponent.getComponent(0); // fetch the existing preferred size of the scroller, and we'll check if it is large enough final Dimension scrollerSize = scroller.getPreferredSize(); // calculates the preferred size of the renderer's component for the prototype value final Dimension prototypeSize = getPrototypeSize(prototypeValue); // add to the preferred width, the width of the vertical scrollbar, when it is visible prototypeSize.width += scroller.getVerticalScrollBar().getPreferredSize().width; // adjust the preferred width of the scroller, if necessary if (prototypeSize.width > scrollerSize.width) { scrollerSize.width = prototypeSize.width; // set the new size of the scroller scroller.setMaximumSize(scrollerSize); scroller.setPreferredSize(scrollerSize); scroller.setMinimumSize(scrollerSize); } } } private Dimension getPrototypeSize(Object prototypeValue) { // get the renderer responsible for drawing the prototype value ListCellRenderer renderer = comboBox.getRenderer(); if (renderer == null) renderer = new DefaultListCellRenderer(); // get the component from the renderer final Component comp = renderer.getListCellRendererComponent(popup.getList(), prototypeValue, -1, false, false); // determine the preferred size of the component comp.setFont(comboBox.getFont()); return comp.getPreferredSize(); } public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { if (doNotClearFilterOnPopupHide) return; // the popup menu is being hidden, so clear the filter to return the ComboBoxModel to its unfiltered state applyFilter(""); } public void popupMenuCanceled(PopupMenuEvent e) {} } /** * When the user selects a value from the popup with the mouse, we want to * honour their selection *without* attempting to autocomplete it to a new * term. Otherwise, it is possible that selections which are prefixes for * values that appear higher in the ComboBoxModel cannot be selected by the * mouse since they can always be successfully autocompleted to another * term. */ private class PopupMouseHandler extends MouseAdapter { @Override public void mousePressed(MouseEvent e) { doNotAutoComplete = true; } @Override public void mouseReleased(MouseEvent e) { doNotAutoComplete = false; } } /** * When the user clicks on the arrow button, we always clear the * filtering from the model to emulate Firefox style autocompletion. */ private class ArrowButtonMouseListener implements MouseListener { private final MouseListener decorated; public ArrowButtonMouseListener(MouseListener decorated) { this.decorated = decorated; } public void mousePressed(MouseEvent e) { // clear the filter if we're about to hide or show the popup // by clicking on the arrow button (this is EXPLICITLY different // than using the up/down arrow keys to show the popup) applyFilter(""); decorated.mousePressed(e); } public MouseListener getDecorated() { return decorated; } public void mouseClicked(MouseEvent e) { decorated.mouseClicked(e); } public void mouseReleased(MouseEvent e) { decorated.mouseReleased(e); } public void mouseEntered(MouseEvent e) { decorated.mouseEntered(e); } public void mouseExited(MouseEvent e) { decorated.mouseExited(e); } } /** * This KeyListener handles the case when the user hits the backspace key * and the {@link AutoCompleteSupport} is strict. Normally backspace would * delete the selected text, if it existed, or delete the character * immediately preceding the cursor. In strict mode the ComboBoxEditor must * always contain a value from the ComboBoxModel, so the backspace key * NEVER alters the Document. Rather, it alters the * text selection to include one more character to the left. This is a nice * compromise, since the editor continues to retain a valid value from the * ComboBoxModel, but the user may type a key at any point to replace the * selection with another valid entry. * * This KeyListener also makes up for a bug in normal JComboBox when * handling the enter key. Specifically, hitting enter in an stock * JComboBox that is editable produces TWO ActionEvents. * When the enter key is detected we actually unregister all * ActionListeners, process the keystroke as normal, then reregister the * listeners and broadcast an event to them, producing a single ActionEvent. */ private class AutoCompleteKeyHandler extends KeyAdapter { private ActionListener[] actionListeners; @Override public void keyPressed(KeyEvent e) { if (!isTableCellEditor) doNotTogglePopup = false; // this KeyHandler performs ALL processing of the ENTER key otherwise multiple // ActionEvents are fired to ActionListeners by the default JComboBox processing. // To control processing of the enter key, we set a flag to avoid changing the // editor's Document in any way, and also unregister the ActionListeners temporarily. if (e.getKeyChar() == KeyEvent.VK_ENTER) { doNotChangeDocument = true; this.actionListeners = unregisterAllActionListeners(comboBox); } // make sure this backspace key does not modify our comboBoxEditorComponent's Document if (isTrigger(e)) doNotChangeDocument = true; } @Override public void keyTyped(KeyEvent e) { if (isTrigger(e)) { // if no content exists in the comboBoxEditorComponent, bail early if (comboBoxEditorComponent.getText().length() == 0) return; // calculate the current beginning of the selection int selectionStart = Math.min(comboBoxEditorComponent.getSelectionStart(), comboBoxEditorComponent.getSelectionEnd()); // if we cannot extend the selection to the left, indicate the error if (selectionStart == 0) { if (getBeepOnStrictViolation()) UIManager.getLookAndFeel().provideErrorFeedback(comboBoxEditorComponent); return; } // add one character to the left of the selection selectionStart--; // select the text from the end of the Document to the new selectionStart // (which positions the caret at the selectionStart) comboBoxEditorComponent.setCaretPosition(comboBoxEditorComponent.getText().length()); comboBoxEditorComponent.moveCaretPosition(selectionStart); } } @Override public void keyReleased(KeyEvent e) { // resume the ability to modify our comboBoxEditorComponent's Document if (isTrigger(e)) doNotChangeDocument = false; // keyPressed(e) has disabled the JComboBox's normal processing of the enter key // so now it is time to perform our own processing. We reattach all ActionListeners // and simulate exactly ONE ActionEvent in the JComboBox and then reenable Document changes. if (e.getKeyChar() == KeyEvent.VK_ENTER) { updateFilter(); // reregister all ActionListeners and then notify them due to the ENTER key // Note: We *must* check for a null ActionListener[]. The reason // is that it is possible to receive a keyReleased() callback // *without* a corresponding keyPressed() callback! It occurs // when focus is transferred away from the ComboBoxEditor and // then the ENTER key transfers focus back to the ComboBoxEditor. if (actionListeners != null) { registerAllActionListeners(comboBox, actionListeners); comboBox.actionPerformed(new ActionEvent(e.getSource(), e.getID(), null)); } // null out our own reference to the ActionListeners actionListeners = null; // reenable Document changes once more doNotChangeDocument = false; } if (!isTableCellEditor) doNotTogglePopup = true; } private boolean isTrigger(KeyEvent e) { return isStrict() && e.getKeyChar() == KeyEvent.VK_BACK_SPACE; } } /** * To emulate Firefox behaviour, all text in the ComboBoxEditor is selected * from beginning to end when the ComboBoxEditor gains focus if the value * returned from {@link AutoCompleteSupport#getSelectsTextOnFocusGain()} * allows this behaviour. In addition, the JPopupMenu is hidden when the * ComboBoxEditor loses focus if the value returned from * {@link AutoCompleteSupport#getHidesPopupOnFocusLost()} allows this * behaviour. */ private class ComboBoxEditorFocusHandler extends FocusAdapter { @Override public void focusGained(FocusEvent e) { if (getSelectsTextOnFocusGain()) comboBoxEditorComponent.select(0, comboBoxEditorComponent.getText().length()); } @Override public void focusLost(FocusEvent e) { if (comboBox.isPopupVisible() && getHidesPopupOnFocusLost()) comboBox.setPopupVisible(false); } } /** * Watch for a change of the ComboBoxUI and reinstall the necessary * behaviour customizations. */ private class UIWatcher implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent evt) { undecorateOriginalUI(); decorateCurrentUI(); } } /** * Watch for a change of the ComboBoxModel and report it as a violation. */ private class ModelWatcher implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent evt) { throwIllegalStateException("The ComboBoxModel cannot be changed. It was changed to: " + evt.getNewValue()); } } /** * Watch the Document behind the editor component in case it changes. If a * new Document is swapped in, uninstall our DocumentFilter from the old * Document and install it on the new. */ private class DocumentWatcher implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent evt) { final Document newDocument = (Document) evt.getNewValue(); if (!(newDocument instanceof AbstractDocument)) throwIllegalStateException("The Document behind the JTextField was changed to no longer be an AbstractDocument. It was changed to: " + newDocument); // remove our DocumentFilter from the old document document.setDocumentFilter(null); // update the document we track internally document = (AbstractDocument) newDocument; // add our DocumentFilter to the new Document document.setDocumentFilter(documentFilter); } } /** * A custom renderer which honours the custom Format given by the user when * they invoked the install method. */ private class StringFunctionRenderer extends DefaultListCellRenderer { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { String string = convertToString(value); // JLabels require some text before they can correctly determine their height, so we convert "" to " " if (string.length() == 0) string = " "; return super.getListCellRendererComponent(list, string, index, isSelected, cellHasFocus); } } /** * A decorated version of the ComboBoxEditor that does NOT assume that * Object.toString() is the proper way to convert values from the * ComboBoxModel into Strings for the ComboBoxEditor's component. It uses * convertToString(E) instead. * * We implement the UIResource interface here so that changes in the UI * delegate of the JComboBox will *replace* this ComboBoxEditor with one * that is correct for the new L&F. We will then react to the change of UI * delegate by installing a new FormatComboBoxEditor overtop of the * UI Delegate's default ComboBoxEditor. */ private class FormatComboBoxEditor implements ComboBoxEditor, UIResource { /** This is the ComboBoxEditor installed by the current UI Delegate of the JComboBox. */ private final ComboBoxEditor delegate; private Object oldValue; public FormatComboBoxEditor(ComboBoxEditor delegate) { this.delegate = delegate; } public ComboBoxEditor getDelegate() { return delegate; } /** * BasicComboBoxEditor defines this method to call: * * editor.setText(anObject.toString()); * * we intercept and replace it with our own String conversion logic * to remain consistent throughout. */ public void setItem(Object anObject) { oldValue = anObject; ((JTextField) getEditorComponent()).setText(convertToString(anObject)); } /** * BasicComboBoxEditor defines this method to use reflection to try * finding a method called valueOf(String) in order to return the * item. We attempt to find a user-supplied Format before * resorting to the valueOf(String) call. */ public Object getItem() { final String oldValueString = convertToString(oldValue); final String currentString = ((JTextField) getEditorComponent()).getText(); // if the String value in the editor matches the String version of // the last item that was set in the editor, return the item if (GlazedListsImpl.equal(oldValueString, currentString)) return oldValue; // if the user specified a Format, use it if (format != null) return format.parseObject(currentString, PARSE_POSITION); // otherwise, use the default algorithm from BasicComboBoxEditor to produce a value if (oldValue != null && !(oldValue instanceof String)) { try { final Method method = oldValue.getClass().getMethod("valueOf", VALUE_OF_SIGNATURE); return method.invoke(oldValue, new Object[] {currentString}); } catch (Exception ex) { // fail silently and return the current string } } return currentString; } public Component getEditorComponent() { return delegate.getEditorComponent(); } public void selectAll() { delegate.selectAll(); } public void addActionListener(ActionListener l) { delegate.addActionListener(l); } public void removeActionListener(ActionListener l) { delegate.removeActionListener(l); } } /** * This default implementation of the TextFilterator interface uses the * same strategy for producing Strings from ComboBoxModel objects as the * renderer and editor. */ class DefaultTextFilterator implements TextFilterator { public void getFilterStrings(List baseList, E element) { baseList.add(convertToString(element)); } } /** * This extension of DefaultCellEditor exists solely to provide a handle to * the AutoCompleteSupport object that is providing autocompletion * capabilities to the JComboBox. */ public static class AutoCompleteCellEditor extends DefaultCellEditor { private final AutoCompleteSupport autoCompleteSupport; /** * Construct a TableCellEditor using the JComboBox supplied by the * given autoCompleteSupport. Specifically, the JComboBox * is retrieved using {@link AutoCompleteSupport#getComboBox()}. */ public AutoCompleteCellEditor(AutoCompleteSupport autoCompleteSupport) { super(autoCompleteSupport.getComboBox()); this.autoCompleteSupport = autoCompleteSupport; } /** * Returns the AutoCompleteSupport object that controls the * autocompletion behaviour for the JComboBox. */ public AutoCompleteSupport getAutoCompleteSupport() { return autoCompleteSupport; } } /** * This factory method creates and returns a {@link AutoCompleteCellEditor} * which adapts an autocompleting {@link JComboBox} for use as a Table * Cell Editor. The values within the table column are used as * autocompletion terms within the {@link ComboBoxModel}. * *

    This version of createTableCellEditor assumes that the * values stored in the TableModel at the given columnIndex * are all {@link Comparable}, and that the natural ordering defined by * those {@link Comparable} values also determines which are duplicates * (and thus can safely be removed) and which are unique (and thus must * remain in the {@link ComboBoxModel}). * *

    Note that this factory method is only appropriate for use when the * values in the {@link ComboBoxModel} should be the unique set of values * in a table column. If some other list of values will be used then * {@link #createTableCellEditor(EventList)} is the appropriate factory * method to use. * *

    If the appearance or function of the autocompleting {@link JComboBox} * is to be customized, it can be retrieved using * {@link AutoCompleteCellEditor#getComponent()}. * * @param tableFormat specifies how each row object within a table is * broken apart into column values * @param tableData the {@link EventList} backing the TableModel * @param columnIndex the index of the column for which to return a * {@link AutoCompleteCellEditor} * @return a {@link AutoCompleteCellEditor} which contains an autocompleting * combobox whose contents remain consistent with the data in the * table column at the given columnIndex */ public static AutoCompleteCellEditor createTableCellEditor(TableFormat tableFormat, EventList tableData, int columnIndex) { return createTableCellEditor(GlazedLists.comparableComparator(), tableFormat, tableData, columnIndex); } /** * This factory method creates and returns a {@link AutoCompleteCellEditor} * which adapts an autocompleting {@link JComboBox} for use as a Table * Cell Editor. The values within the table column are used as * autocompletion terms within the {@link ComboBoxModel}. * *

    This version of createTableCellEditor makes no * assumption about the values stored in the TableModel at the given * columnIndex. Instead, it uses the given * uniqueComparator to determine which values are duplicates * (and thus can safely be removed) and which are unique (and thus must * remain in the {@link ComboBoxModel}). * *

    Note that this factory method is only appropriate for use when the * values in the {@link ComboBoxModel} should be the unique set of values * in a table column. If some other list of values will be used then * {@link #createTableCellEditor(EventList)} is the appropriate factory * method to use. * *

    If the appearance or function of the autocompleting {@link JComboBox} * is to be customized, it can be retrieved using * {@link AutoCompleteCellEditor#getComponent()}. * * @param uniqueComparator the {@link Comparator} that strips away * duplicate elements from the {@link ComboBoxModel} * @param tableFormat specifies how each row object within a table is * broken apart into column values * @param tableData the {@link EventList} backing the TableModel * @param columnIndex the index of the column for which to return a * {@link AutoCompleteCellEditor} * @return a {@link AutoCompleteCellEditor} which contains an autocompleting * combobox whose contents remain consistent with the data in the * table column at the given columnIndex */ public static AutoCompleteCellEditor createTableCellEditor(Comparator uniqueComparator, TableFormat tableFormat, EventList tableData, int columnIndex) { // use a function to extract all values for the column final FunctionList.Function columnValueFunction = new TableColumnValueFunction(tableFormat, columnIndex); final FunctionList allColumnValues = new FunctionList(tableData, columnValueFunction); // narrow the list to just unique values within the column final EventList uniqueColumnValues = new UniqueList(allColumnValues, uniqueComparator); return createTableCellEditor(uniqueColumnValues); } /** * This factory method creates and returns a {@link AutoCompleteCellEditor} * which adapts an autocompleting {@link JComboBox} for use as a Table * Cell Editor. The values within the source are used as * autocompletion terms within the {@link ComboBoxModel}. * *

    If the appearance or function of the autocompleting {@link JComboBox} * is to be customized, it can be retrieved using * {@link AutoCompleteCellEditor#getComponent()}. * * @param source the source of data for the JComboBox within the table cell editor * @return a {@link AutoCompleteCellEditor} which contains an autocompleting * combobox whose model contents are determined by the given source */ public static AutoCompleteCellEditor createTableCellEditor(EventList source) { // build a special JComboBox used only in Table Cell Editors final JComboBox comboBox = new TableCellComboBox(); comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE); // install autocompletion support on the special JComboBox final AutoCompleteSupport autoCompleteSupport = AutoCompleteSupport.install(comboBox, source); autoCompleteSupport.setSelectsTextOnFocusGain(false); // create an AutoCompleteCellEditor using the AutoCompleteSupport object final AutoCompleteCellEditor cellEditor = new AutoCompleteCellEditor(autoCompleteSupport); cellEditor.setClickCountToStart(2); return cellEditor; } /** * This customized JComboBox is only used when creating an autocompleting * {@link DefaultCellEditor}. It customizes the behaviour of a JComboBox to * make it more appropriate for use as a TableCellEditor. Specifically it * adds the following: * *

      *
    • key presses which start table cell edits are also respected by the * JTextField *
    • the next focusable component for the JTextField is set to be the * JTable when editing begins so that focus returns to the table when * editing stops *
    */ private static final class TableCellComboBox extends JComboBox implements FocusListener { public TableCellComboBox() { // use a customized ComboBoxEditor within this special JComboBox setEditor(new TableCellComboBoxEditor()); // replace the UI Delegate's FocusListener with our own in both // the JComboBox and its ComboBoxEditor replaceUIDelegateFocusListener(getEditor().getEditorComponent(), this); replaceUIDelegateFocusListener(this, this); } /** * This method is a complete hack, but is necessary to achieve the * desired behaviour when using an autocompleting JComboBox in a * TableCellEditor. * * The problem is that when cell editing begins due to a keystroke, * ideally the ComboBoxPopup should be displayed in a filtered state. * But, the FocusListener installed by BasicComboBoxUI actually hides * the ComboBoxPopup due to some phantom focusLost event we receive. * * To solve the problem, we rip out the FocusListener installed by * the BasicComboBoxUI and replace it with our own that does NOT hide * the popup when this JComboBox loses focus. That's with us since * losing focus implies we are committing or cancelling the cell edit * anyway, so the entire editor is about to be removed. */ private static void replaceUIDelegateFocusListener(Component c, FocusListener replacement) { // remove all FocusListeners that appear to be installed by the UIdelegate final FocusListener[] focusListeners = c.getFocusListeners(); for (int i = 0; i < focusListeners.length; i++) if (focusListeners[i].getClass().getName().indexOf("ComboBoxUI") != -1) c.removeFocusListener(focusListeners[i]); c.addFocusListener(replacement); } /** * Repaint and request focus if editable. */ public void focusGained(FocusEvent e) { final ComboBoxEditor currentEditor = getEditor(); if (currentEditor != null && currentEditor.getEditorComponent() != e.getSource()) { repaint(); if (isEditable()) { currentEditor.getEditorComponent().requestFocus(); } } } /** * BasicComboBoxUI.Handler.focusLost screws up the installation of this * JComboBox as a TableCellEditor by hiding the ComboBoxPopup on the * first keystroke and represent the reason why we must tear out the * FocusListener and replace it with one of our own. */ public void focusLost(FocusEvent e) { final ComboBoxEditor currentEditor = getEditor(); if (!e.isTemporary() && currentEditor != null && currentEditor.getEditorComponent() == e.getSource()) { final Object currentItem = currentEditor.getItem(); if (currentItem != null && !currentItem.equals(getSelectedItem())) { fireActionPerformed(currentEditor); } } repaint(); } private void fireActionPerformed(ComboBoxEditor source) { actionPerformed(new ActionEvent(source, 0, "", EventQueue.getMostRecentEventTime(), 0)); } /** * This method is called by Swing when the JComboBox is installed as a * TableCellEditor. It gives the component a chance to process the * KeyEvent. For example, a JTextField will honour the keystroke and * add the letter to its Document. * * Editable JComboBoxes don't provide that expected behaviour out of * the box, so we override this method with logic that gives the editor * component of the JComboBox a chance to respond to the keystroke that * initiated the cell edit. */ @Override protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { // let the textfield have a crack at processing the KeyEvent final TableCellTextField tableCellTextField = (TableCellTextField) getEditor().getEditorComponent(); tableCellTextField.processKeyBinding(ks, e, condition, pressed); // ensure the text field has focus if it is still processing key strokes // (I've seen bad behaviour on windows textfield has no cursor, yet continues to process keystrokes // - this helps to ensure that the textfield actually has focus and thus the cursor) if (!tableCellTextField.hasFocus()) tableCellTextField.requestFocus(); // now let the JComboBox react (important for arrow keys to work as expected) return super.processKeyBinding(ks, e, condition, pressed); } /** * This method is called by Swing when installing this JComboBox as a * TableCellEditor. It ensures that focus will return to the JTable * when the cell edit is complete. * *

    We override this method to ensure that if the JTextField acting * as the editor of the JComboBox has focus when the cell edit is * complete, focus is returned to the JTable in that case as well. */ @Override public void setNextFocusableComponent(Component aComponent) { super.setNextFocusableComponent(aComponent); // set the next focusable component for the editor as well ((JComponent) getEditor().getEditorComponent()).setNextFocusableComponent(aComponent); } /** * A custom BasicComboBoxEditor that builds a custom JTextField with * an extra capability: a public implementation of * {@link TableCellTextField#processKeyBinding} */ private static final class TableCellComboBoxEditor extends BasicComboBoxEditor { public TableCellComboBoxEditor() { // replace the super's editor with a JTextField of our own design editor = new TableCellTextField(); } } /** * This custom JTextField exists solely to make * {@link #processKeyBinding} a public method so that it can be called * from {@link TableCellComboBox#processKeyBinding}. * * This custom JTextField is only used when creating an autocompleting * TableCellEditor via {@link AutoCompleteSupport#createTableCellEditor}. */ private static class TableCellTextField extends JTextField { public TableCellTextField() { super("", 9); } /** * {@inheritDoc} */ @Override public void setText(String newText) { // workaround for bug 4530952 if (!equalsText(newText)) { super.setText(newText); } } private boolean equalsText(String newText) { final String currentText = getText(); return (currentText == null) ? newText == null : currentText.equals(newText); } /** * {@inheritDoc} */ @Override public void setBorder(Border b) { // NOP, we want no border } /** * We override this method to make it public so that it can be * called from {@link TableCellComboBox#processKeyBinding}. * *

    This allows the keystroke which begins a table cell edit to * also contribute a character to this JTextField, thus mimicing * the behaviour of normal editable JTextField table cell editors. */ @Override public boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { return super.processKeyBinding(ks, e, condition, pressed); } } } /** * This function uses a TableFormat and columnIndex to extract all of the * values that are displayed in the given table column. These values are * used as autocompletion terms when editing a cell within that column. */ private static final class TableColumnValueFunction implements FunctionList.Function { private final TableFormat tableFormat; private final int columnIndex; public TableColumnValueFunction(TableFormat tableFormat, int columnIndex) { this.tableFormat = tableFormat; this.columnIndex = columnIndex; } public Object evaluate(E sourceValue) { return tableFormat.getColumnValue(sourceValue, columnIndex); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/EventListModel.java0000644000175000017500000001057412106516356031037 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.TransformedList; import javax.swing.JList; import javax.swing.ListModel; import javax.swing.SwingUtilities; /** * An EventListModel adapts an EventList to the ListModel interface making it * appropriate for use with a {@link JList}. Each element of the list * corresponds to an element in the {@link ListModel}. * *

    The EventListModel class is not thread-safe. Unless * otherwise noted, all methods are only safe to be called from the event * dispatch thread. To do this programmatically, use * {@link SwingUtilities#invokeAndWait(Runnable)}. * * @see Bug 14 * @see Bug 146 * @see Bug 177 * @see Bug 228 * @see SwingUtilities#invokeAndWait(Runnable) * * @deprecated Use {@link DefaultEventListModel} instead. This class will be removed in the GL * 2.0 release. The wrapping of the source list with an EDT safe list has been * determined to be undesirable (it is better for the user to provide their own EDT * safe list). * * @author Jesse Wilson * @author Holger Brands */ public class EventListModel extends DefaultEventListModel { /** indicates, if source list has to be disposed */ private boolean disposeSource; /** * Creates a new model that contains all objects located in the given * source and reacts to any changes in the given * source. */ public EventListModel(EventList source) { super(createProxyList(source)); disposeSource = (this.source != source); } /** * Releases the resources consumed by this {@link EventListModel} so that it * may eventually be garbage collected. * *

    An {@link EventListModel} will be garbage collected without a call to * {@link #dispose()}, but not before its source {@link EventList} is garbage * collected. By calling {@link #dispose()}, you allow the {@link EventListModel} * to be garbage collected before its source {@link EventList}. This is * necessary for situations where an {@link EventListModel} is short-lived but * its source {@link EventList} is long-lived. * *

    Warning: It is an error * to call any method on an {@link EventListModel} after it has been disposed. * As such, this {@link EventListModel} should be detached from its * corresponding Component before it is disposed. */ @Override public void dispose() { if (disposeSource) source.dispose(); super.dispose(); } /** * while holding a read lock, this method wraps the given source list with a swing thread * proxy list. */ private static EventList createProxyList(EventList source) { // lock the source list for reading since we want to prevent writes // from occurring until we fully initialize this EventTableModel EventList result = source; source.getReadWriteLock().readLock().lock(); try { final TransformedList decorated = createSwingThreadProxyList(source); // if the create method actually returned a decorated form of the source, // record it so it may later be disposed if (decorated != null && decorated != source) { result = decorated; } } finally { source.getReadWriteLock().readLock().unlock(); } return result; } /** wraps the given source list with a swing thread proxy list, if necessary */ private static TransformedList createSwingThreadProxyList(EventList source) { return GlazedListsSwing.isSwingThreadProxyList(source) ? null : GlazedListsSwing.swingThreadProxyList(source); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/AdvancedTableModel.java0000644000175000017500000000473112106516356031575 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.gui.TableFormat; import javax.swing.table.TableModel; /** * AdvancedTableModel is the extended interface intended to be implemented by * Glazed Lists table models. It provides additional methods for managing the * {@link TableFormat} and disposing, for example. * * @author Holger Brands */ public interface AdvancedTableModel extends TableModel { /** * Gets the {@link TableFormat} used by this table model. */ TableFormat getTableFormat(); /** * Sets the {@link TableFormat} that will extract column data from each * element. This has some very important consequences. Any cell selections * will be lost - this is due to the fact that the TableFormats may have * different numbers of columns, and JTable has no event to specify columns * changing without rows. */ void setTableFormat(TableFormat tableFormat); /** * Retrieves the value at the specified location from the table. * *

    This may be used by renderers to paint the cells of a row differently * based on the entire value for that row. * * @see #getValueAt(int,int) */ E getElementAt(int index); /** * Releases the resources consumed by this {@link AdvancedTableModel} so that it * may eventually be garbage collected. * *

    An {@link AdvancedTableModel} will be garbage collected without a call to * {@link #dispose()}, but not before its source {@link EventList} is garbage * collected. By calling {@link #dispose()}, you allow the {@link AdvancedTableModel} * to be garbage collected before its source {@link EventList}. This is * necessary for situations where an {@link AdvancedTableModel} is short-lived but * its source {@link EventList} is long-lived. * *

    Warning: It is an error * to call any method on an {@link AdvancedTableModel} after it has been disposed. * As such, this {@link AdvancedTableModel} should be detached from its * corresponding Component before it is disposed. */ void dispose(); } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/MutableListDataEvent.java0000644000175000017500000000370312106516356032156 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; // Swing toolkit stuff for displaying widgets import javax.swing.event.ListDataEvent; /** * The mutable list data event is a list data event that can be rewritten * for performance gains. The class is completely re-implemented and it only * extends ListDataEvent to fit the ListDataListener interface. * * @author Jesse Wilson */ final class MutableListDataEvent extends ListDataEvent { /** what the change is, currently */ private int index0; private int index1; private int type; /** * Creates a new mutable data event that always uses the specified object * as its source. */ public MutableListDataEvent(Object source) { super(source, CONTENTS_CHANGED, 0, 0); } /** * Sets the start and end range for this event. The values are inclusive. */ public void setRange(int index0, int index1) { this.index0 = index0; this.index1 = index1; } /** * Sets the type of change. This value must be either CONTENTS_CHANGED, * INTERVAL_ADDED, or INTERVAL REMOVED. */ public void setType(int type) { this.type = type; } /** * Accessors for the change information do not use any information * in the parent class. */ @Override public int getIndex0() { return index0; } @Override public int getIndex1() { return index1; } @Override public int getType() { return type; } /** * Gets this event as a String for debugging. */ @Override public String toString() { return "" + type + "[" + index0 + "," + index1 + "]"; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/SortableRenderer.java0000644000175000017500000000176212106516356031402 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import javax.swing.*; /** * This interface is intended to be implemented by custom TableCellRenderers * installed on the JTableHeader of a sortable JTable. The custom renderer * need only implement this interface and it will be notified of the * appropriate sorting icon immediately before the renderer is asked to provide * a component. * * @author James Lemieux */ public interface SortableRenderer { /** * Sets the icon to display in order to indicate sorting direction or * null if no sorting is taking place. * * @param sortIcon the Icon indicating the sort direction or * null if there is not sorting */ public void setSortIcon(Icon sortIcon); }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/GlazedListsSwing.java0000644000175000017500000003464312106516356031401 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.ThresholdList; import ca.odell.glazedlists.TransformedList; import ca.odell.glazedlists.gui.TableFormat; import ca.odell.glazedlists.impl.swing.LowerThresholdRangeModel; import ca.odell.glazedlists.impl.swing.SwingThreadProxyEventList; import ca.odell.glazedlists.impl.swing.UpperThresholdRangeModel; import javax.swing.BoundedRangeModel; import javax.swing.SwingUtilities; /** * A factory for creating all sorts of objects to be used with Glazed Lists. * * @author Jesse Wilson */ public final class GlazedListsSwing { /** * A dummy constructor to prevent instantiation of this class */ private GlazedListsSwing() { throw new UnsupportedOperationException(); } // EventLists // // // // // // // // // // // // // // // // // // // // // /** * Wraps the source in an {@link EventList} that fires all of its update * events from the Swing event dispatch thread. */ public static TransformedList swingThreadProxyList(EventList source) { return new SwingThreadProxyEventList(source); } /** * Returns true iff list is an {@link EventList} that fires * all of its update events from the Swing event dispatch thread. */ public static boolean isSwingThreadProxyList(EventList list) { return list instanceof SwingThreadProxyEventList; } // ThresholdRangeModels // // // // // // // // // // // // // // // // // /** * Creates a model that manipulates the lower bound of the specified * ThresholdList. The ThresholdList linked to this model type will contain * a range of Objects between the results of getValue() and getMaximum() * on the BoundedRangeModel. */ public static BoundedRangeModel lowerRangeModel(ThresholdList target) { return new LowerThresholdRangeModel(target); } /** * Creates a model that manipulates the upper bound of the specified * ThresholdList. The ThresholdList linked to this model type will contain * a range of Objects between the results of getMinimum() and getValue() * on the BoundedRangeModel. */ public static BoundedRangeModel upperRangeModel(ThresholdList target) { return new UpperThresholdRangeModel(target); } // TableModel convenience creators /** * Creates a new table model that extracts column data from the given * source using the the given tableFormat. * *

    The returned table model is not thread-safe. Unless otherwise * noted, all methods are only safe to be called from the event dispatch thread. * To do this programmatically, use {@link SwingUtilities#invokeAndWait(Runnable)} and * wrap the source list (or some part of the source list's pipeline) using * {@link GlazedListsSwing#swingThreadProxyList(EventList)}.

    * * @param source the EventList that provides the row objects * @param tableFormat the object responsible for extracting column data * from the row objects */ public static AdvancedTableModel eventTableModel(EventList source, TableFormat tableFormat) { return new DefaultEventTableModel(source, tableFormat); } /** * Creates a new table model that extracts column data from the given source * using the the given tableFormat. While holding a read lock, * this method wraps the source list using * {@link GlazedListsSwing#swingThreadProxyList(EventList)}. *

    * The returned table model is not thread-safe. Unless otherwise noted, all * methods are only safe to be called from the event dispatch thread. *

    * * @param source the EventList that provides the row objects * @param tableFormat the object responsible for extracting column data from the row objects */ public static AdvancedTableModel eventTableModelWithThreadProxyList(EventList source, TableFormat tableFormat) { final EventList proxySource = createSwingThreadProxyList(source); return new DefaultEventTableModel(proxySource, true, tableFormat); } /** * Creates a new table model that renders the specified list with an automatically * generated {@link TableFormat}. It uses JavaBeans and reflection to create * a {@link TableFormat} as specified. * *

    Note that classes that will be obfuscated may not work with * reflection. In this case, implement a {@link TableFormat} manually.

    * *

    The returned table model is not thread-safe. Unless otherwise * noted, all methods are only safe to be called from the event dispatch thread. * To do this programmatically, use {@link SwingUtilities#invokeAndWait(Runnable)} and * wrap the source list (or some part of the source list's pipeline) using * {@link GlazedListsSwing#swingThreadProxyList(EventList)}.

    * * @param source the EventList that provides the row objects * @param propertyNames an array of property names in the JavaBeans format. * For example, if your list contains Objects with the methods getFirstName(), * setFirstName(String), getAge(), setAge(Integer), then this array should * contain the two strings "firstName" and "age". This format is specified * by the JavaBeans {@link java.beans.PropertyDescriptor}. * @param columnLabels the corresponding column names for the listed property * names. For example, if your columns are "firstName" and "age", then * your labels might be "First Name" and "Age". * @param writable an array of booleans specifying which of the columns in * your table are writable. * */ public static AdvancedTableModel eventTableModel(EventList source, String[] propertyNames, String[] columnLabels, boolean[] writable) { return eventTableModel(source, GlazedLists.tableFormat(propertyNames, columnLabels, writable)); } /** * Creates a new table model that renders the specified list with an automatically * generated {@link TableFormat}. It uses JavaBeans and reflection to create * a {@link TableFormat} as specified. While holding a read lock, * this method wraps the source list using * {@link GlazedListsSwing#swingThreadProxyList(EventList)}. * *

    Note that classes that will be obfuscated may not work with * reflection. In this case, implement a {@link TableFormat} manually.

    * *

    The returned table model is not thread-safe. Unless otherwise * noted, all methods are only safe to be called from the event dispatch thread.

    * * @param source the EventList that provides the row objects * @param propertyNames an array of property names in the JavaBeans format. * For example, if your list contains Objects with the methods getFirstName(), * setFirstName(String), getAge(), setAge(Integer), then this array should * contain the two strings "firstName" and "age". This format is specified * by the JavaBeans {@link java.beans.PropertyDescriptor}. * @param columnLabels the corresponding column names for the listed property * names. For example, if your columns are "firstName" and "age", then * your labels might be "First Name" and "Age". * @param writable an array of booleans specifying which of the columns in * your table are writable. * */ public static AdvancedTableModel eventTableModelWithThreadProxyList(EventList source, String[] propertyNames, String[] columnLabels, boolean[] writable) { return eventTableModelWithThreadProxyList(source, GlazedLists.tableFormat(propertyNames, columnLabels, writable)); } // ListSelectionModel convenience creators /** * Creates a new selection model that also presents a list of the selection. * * The {@link AdvancedListSelectionModel} listens to this {@link EventList} in order * to adjust selection when the {@link EventList} is modified. For example, * when an element is added to the {@link EventList}, this may offset the * selection of the following elements. * *

    The returned selection model is not thread-safe. Unless otherwise * noted, all methods are only safe to be called from the event dispatch thread. * To do this programmatically, use {@link SwingUtilities#invokeAndWait(Runnable)} and * wrap the source list (or some part of the source list's pipeline) using * {@link GlazedListsSwing#swingThreadProxyList(EventList)}.

    * * @param source the {@link EventList} whose selection will be managed. This should * be the same {@link EventList} passed to the constructor of your * {@link AdvancedTableModel} or {@link EventListModel}. */ public static AdvancedListSelectionModel eventSelectionModel(EventList source) { return new DefaultEventSelectionModel(source); } /** * Creates a new selection model that also presents a list of the selection. * While holding a read lock, it wraps the source list using * {@link GlazedListsSwing#swingThreadProxyList(EventList)}. The * {@link AdvancedListSelectionModel} listens to this {@link EventList} in order to adjust * selection when the {@link EventList} is modified. For example, when an element is added to * the {@link EventList}, this may offset the selection of the following elements. *

    * The returned selection model is not thread-safe. Unless otherwise noted, * all methods are only safe to be called from the event dispatch thread. *

    * * @param source the {@link EventList} whose selection will be managed. This should be the * same {@link EventList} passed to the constructor of your * {@link AdvancedTableModel} or {@link EventListModel}. */ public static AdvancedListSelectionModel eventSelectionModelWithThreadProxyList(EventList source) { final EventList proxySource = createSwingThreadProxyList(source); return new DefaultEventSelectionModel(proxySource, true); } // EventListModel convenience creators /** * Creates a new list model that contains all objects located in the given * source and reacts to any changes in the given source. * *

    The returned selection model is not thread-safe. Unless otherwise * noted, all methods are only safe to be called from the event dispatch thread. * To do this programmatically, use {@link SwingUtilities#invokeAndWait(Runnable)} and * wrap the source list (or some part of the source list's pipeline) using * {@link GlazedListsSwing#swingThreadProxyList(EventList)}. *

    * * @param source the EventList that provides the elements */ public static DefaultEventListModel eventListModel(EventList source) { return new DefaultEventListModel(source); } /** * Creates a new list model that contains all objects located in the given * source and reacts to any changes in the given source. * While holding a read lock, it wraps the source list using * {@link GlazedListsSwing#swingThreadProxyList(EventList)}. * *

    The returned selection model is not thread-safe. Unless otherwise * noted, all methods are only safe to be called from the event dispatch thread. *

    * * @param source the EventList that provides the elements */ public static DefaultEventListModel eventListModelWithThreadProxyList(EventList source) { final EventList proxySource = createSwingThreadProxyList(source); return new DefaultEventListModel(proxySource, true); } // EventComboBoxModel convenience creators /** * Creates a new combobox model that contains all objects located in the given * source and reacts to any changes in the given source. * *

    The returned combobox model is not thread-safe. Unless otherwise * noted, all methods are only safe to be called from the event dispatch thread. * To do this programmatically, use {@link SwingUtilities#invokeAndWait(Runnable)} and * wrap the source list (or some part of the source list's pipeline) using * {@link GlazedListsSwing#swingThreadProxyList(EventList)}. *

    * * @param source the EventList that provides the elements */ public static DefaultEventComboBoxModel eventComboBoxModel(EventList source) { return new DefaultEventComboBoxModel(source); } /** * Creates a new combobox model that contains all objects located in the given * source and reacts to any changes in the given source. * While holding a read lock, it wraps the source list using * {@link GlazedListsSwing#swingThreadProxyList(EventList)}. * *

    The returned combobox model is not thread-safe. Unless otherwise * noted, all methods are only safe to be called from the event dispatch thread. *

    * * @param source the EventList that provides the elements */ public static DefaultEventComboBoxModel eventComboBoxModelWithThreadProxyList(EventList source) { final EventList proxySource = createSwingThreadProxyList(source); return new DefaultEventComboBoxModel(proxySource, true); } /** Helper method to create a SwingThreadProxyList with read locks. */ private static EventList createSwingThreadProxyList(EventList source) { final EventList result; source.getReadWriteLock().readLock().lock(); try { result = GlazedListsSwing.swingThreadProxyList(source); } finally { source.getReadWriteLock().readLock().unlock(); } return result; } }././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/DefaultEventSelectionModel.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/DefaultEventSelectionModel.jav0000644000175000017500000004063212106516356033213 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.ListSelection; import ca.odell.glazedlists.matchers.Matcher; import java.util.ArrayList; import java.util.List; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; /** * An {@link DefaultEventSelectionModel} is a class that performs two simulaneous * services. It is a {@link ListSelectionModel} to provide selection tracking for a * {@link JTable}. It is also a {@link EventList} that contains the table's selection. * *

    As elements are selected or deselected, the {@link EventList} aspect of this * {@link DefaultEventSelectionModel} changes. Changes to that {@link List} will change the * source {@link EventList}. To modify only the selection, use the * {@link ListSelectionModel}'s methods. * *

    Alongside MULTIPLE_INTERVAL_SELECTION, this {@link ListSelectionModel} * supports an additional selection mode. * MULTIPLE_INTERVAL_SELECTION_DEFENSIVE is a new selection mode. * It is identical to MULTIPLE_INTERVAL_SELECTION in every way but * one. When a row is inserted immediately before a selected row in the * MULTIPLE_INTERVAL_SELECTION mode, it becomes selected. But in * the MULTIPLE_INTERVAL_SELECTION_DEFENSIVE mode, it does not * become selected. To set this mode, use * {@link #setSelectionMode(int) setSelectionMode(ListSelection.MULTIPLE_INTERVAL_SELECTION_DEFENSIVE)}. * *

    {@link DefaultEventSelectionModel} is not thread-safe. Unless otherwise * noted, all methods are only safe to be called from the event dispatch thread. * To do this programmatically, use {@link SwingUtilities#invokeAndWait(Runnable)} and * wrap the source list (or some part of the source list's pipeline) using * GlazedListsSwing#swingThreadProxyList(EventList).

    * * @see Bug 39 * @see Bug 61 * @see Bug 76 * @see Bug 108 * @see Bug 110 * @see Bug 112 * @see Bug 222 * * * @author Jesse Wilson */ public final class DefaultEventSelectionModel implements AdvancedListSelectionModel { /** the event lists that provide an event list view of the selection */ private ListSelection listSelection; /** the source event list. */ private EventList source; /** indicator to dispose source list */ private boolean disposeSource; /** whether the user can modify the selection */ private boolean enabled = true; /** listeners to notify when the selection changes */ private final List listeners = new ArrayList(); /** whether there are a series of changes on the way */ private boolean valueIsAdjusting = false; private int fullChangeStart = -1; private int fullChangeFinish = -1; /** * Creates a new selection model that also presents a list of the selection. * * The {@link DefaultEventSelectionModel} listens to this {@link EventList} in order * to adjust selection when the {@link EventList} is modified. For example, * when an element is added to the {@link EventList}, this may offset the * selection of the following elements. * * @param source the {@link EventList} whose selection will be managed. This should * be the same {@link EventList} passed to the constructor of your * {@link DefaultEventTableModel} or {@link DefaultEventListModel}. */ public DefaultEventSelectionModel(EventList source) { this(source, false); } /** * Creates a new selection model that also presents a list of the selection. The * {@link DefaultEventSelectionModel} listens to this {@link EventList} in order to adjust * selection when the {@link EventList} is modified. For example, when an element is added to * the {@link EventList}, this may offset the selection of the following elements. * * @param source the {@link EventList} whose selection will be managed. This should be the * same {@link EventList} passed to the constructor of your * {@link DefaultEventTableModel} or {@link DefaultEventListModel}. * @param diposeSource true if the source list should be disposed when disposing * this model, false otherwise */ protected DefaultEventSelectionModel(EventList source, boolean disposeSource) { // lock the source list for reading since we want to prevent writes // from occurring until we fully initialize this EventSelectionModel source.getReadWriteLock().readLock().lock(); try { this.source = source; // build a list for reading the selection this.listSelection = new ListSelection(source); this.listSelection.addSelectionListener(new SwingSelectionListener()); } finally { source.getReadWriteLock().readLock().unlock(); } this.disposeSource = disposeSource; } /** * {@inheritDoc} */ public EventList getSelected() { source.getReadWriteLock().readLock().lock(); try { return listSelection.getSelected(); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * {@inheritDoc} */ public EventList getTogglingSelected() { source.getReadWriteLock().readLock().lock(); try { return listSelection.getTogglingSelected(); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * {@inheritDoc} */ public EventList getDeselected() { source.getReadWriteLock().readLock().lock(); try { return listSelection.getDeselected(); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * {@inheritDoc} */ public EventList getTogglingDeselected() { source.getReadWriteLock().readLock().lock(); try { return listSelection.getTogglingDeselected(); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * {@inheritDoc} */ public void setEnabled(boolean enabled) { this.enabled = enabled; } /** * {@inheritDoc} */ public boolean getEnabled() { return enabled; } /** * Listens to selection changes on the {@link ListSelection} and fires * {@link ListSelectionEvent}s to registered listeners. */ private class SwingSelectionListener implements ListSelection.Listener { /** {@inheritDoc} */ public void selectionChanged(int changeStart, int changeEnd) { fireSelectionChanged(changeStart, changeEnd); } } /** * Notify listeners that the selection has changed. * *

    This notifies all listeners with the same immutable * ListSelectionEvent. */ private void fireSelectionChanged(int changeStart, int changeFinish) { // if this is a change in a series, save the bounds of this change if(valueIsAdjusting) { if(fullChangeStart == -1 || changeStart < fullChangeStart) fullChangeStart = changeStart; if(fullChangeFinish == -1 || changeFinish > fullChangeFinish) fullChangeFinish = changeFinish; } // fire the change final ListSelectionEvent event = new ListSelectionEvent(this, changeStart, changeFinish, valueIsAdjusting); for (int i = 0, n = listeners.size(); i < n; i++) listeners.get(i).valueChanged(event); } /** * {@inheritDoc} */ public void invertSelection() { source.getReadWriteLock().writeLock().lock(); try { listSelection.invertSelection(); } finally { source.getReadWriteLock().writeLock().unlock(); } } /** * Change the selection to be between index0 and index1 inclusive. * *

    First this calculates the smallest range where changes occur. This * includes the union of the selection range before and the selection * range specified. It then walks through the change and sets each * index as selected or not based on whether the index is in the * new range. Finally it fires events to both the listening lists and * selection listeners about what changes happened. * *

    If the selection does not change, this will not fire any events. */ public void setSelectionInterval(int index0, int index1) { if(!enabled) return; source.getReadWriteLock().writeLock().lock(); try { listSelection.setSelection(index0, index1); } finally { source.getReadWriteLock().writeLock().unlock(); } } /** * Change the selection to be the set union of the current selection and the indices between index0 and index1 inclusive */ public void addSelectionInterval(int index0, int index1) { if(!enabled) return; source.getReadWriteLock().writeLock().lock(); try { listSelection.select(index0, index1); } finally { source.getReadWriteLock().writeLock().unlock(); } } /** * Change the selection to be the set difference of the current selection and the indices between index0 and index1 inclusive. */ public void removeSelectionInterval(int index0, int index1) { if(!enabled) return; if(index0 == 0 && index1 == 0 && source.isEmpty()) return; // hack for Java 5 compatibility source.getReadWriteLock().writeLock().lock(); try { listSelection.deselect(index0, index1); } finally { source.getReadWriteLock().writeLock().unlock(); } } /** * Returns true if the specified index is selected. If the specified * index has not been seen before, this will return false. This is * in the case where the table painting and selection have fallen * out of sync. Usually in this case there is an update event waiting * in the event queue that notifies this model of the change * in table size. */ public boolean isSelectedIndex(int index) { return (listSelection.isSelected(index)); } /** * Return the first index argument from the most recent call to setSelectionInterval(), addSelectionInterval() or removeSelectionInterval(). */ public int getAnchorSelectionIndex() { return listSelection.getAnchorSelectionIndex(); } /** * Set the anchor selection index. */ public void setAnchorSelectionIndex(int anchorSelectionIndex) { if(!enabled) return; source.getReadWriteLock().writeLock().lock(); try { listSelection.setAnchorSelectionIndex(anchorSelectionIndex); } finally { source.getReadWriteLock().writeLock().unlock(); } } /** * Return the second index argument from the most recent call to setSelectionInterval(), addSelectionInterval() or removeSelectionInterval(). */ public int getLeadSelectionIndex() { return listSelection.getLeadSelectionIndex(); } /** * Set the lead selection index. */ public void setLeadSelectionIndex(int leadSelectionIndex) { if(!enabled) return; source.getReadWriteLock().writeLock().lock(); try { listSelection.setLeadSelectionIndex(leadSelectionIndex); } finally { source.getReadWriteLock().writeLock().unlock(); } } /** * Gets the index of the first selected element. */ public int getMinSelectionIndex() { return listSelection.getMinSelectionIndex(); } /** * Gets the index of the last selected element. */ public int getMaxSelectionIndex() { return listSelection.getMaxSelectionIndex(); } /** * Change the selection to the empty set. */ public void clearSelection() { if(!enabled) return; source.getReadWriteLock().writeLock().lock(); try { listSelection.deselectAll(); } finally { source.getReadWriteLock().writeLock().unlock(); } } /** * Returns true if no indices are selected. */ public boolean isSelectionEmpty() { source.getReadWriteLock().readLock().lock(); try { return listSelection.getSelected().isEmpty(); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * Insert length indices beginning before/after index. */ public void insertIndexInterval(int index, int length, boolean before) { // these changes are handled by the ListSelection } /** * Remove the indices in the interval index0,index1 (inclusive) from the selection model. */ public void removeIndexInterval(int index0, int index1) { // these changes are handled by the ListSelection } /** * This property is true if upcoming changes to the value of the model should be considered a single event. */ public void setValueIsAdjusting(boolean valueIsAdjusting) { this.valueIsAdjusting = valueIsAdjusting; // fire one extra change containing all changes in this set if(!valueIsAdjusting) { if(fullChangeStart != -1 && fullChangeFinish != -1) { source.getReadWriteLock().writeLock().lock(); try { fireSelectionChanged(fullChangeStart, fullChangeFinish); fullChangeStart = -1; fullChangeFinish = -1; } finally { source.getReadWriteLock().writeLock().unlock(); } } } } /** * Returns true if the value is undergoing a series of changes. */ public boolean getValueIsAdjusting() { return valueIsAdjusting; } /** * Set the selection mode. */ public void setSelectionMode(int selectionMode) { source.getReadWriteLock().writeLock().lock(); try { listSelection.setSelectionMode(selectionMode); } finally { source.getReadWriteLock().writeLock().unlock(); } } /** * Returns the current selection mode. */ public int getSelectionMode() { return listSelection.getSelectionMode(); } /** * {@inheritDoc} */ public void addValidSelectionMatcher(Matcher validSelectionMatcher) { listSelection.addValidSelectionMatcher(validSelectionMatcher); } /** * {@inheritDoc} */ public void removeValidSelectionMatcher(Matcher validSelectionMatcher) { listSelection.removeValidSelectionMatcher(validSelectionMatcher); } /** * Add a listener to the list that's notified each time a change to * the selection occurs. * * Note that the change events fired by this class may include rows * that have been removed from the table. For this reason it is * advised not to for() through the changed range without * also verifying that each row is still in the table. */ public void addListSelectionListener(ListSelectionListener listener) { listeners.add(listener); } /** * Remove a listener from the list that's notified each time a change to the selection occurs. */ public void removeListSelectionListener(ListSelectionListener listener) { listeners.remove(listener); } /** * {@inheritDoc} */ public void dispose() { listSelection.dispose(); if (disposeSource) source.dispose(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/DefaultEventTableModel.java0000644000175000017500000002444712106516356032464 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import ca.odell.glazedlists.gui.AdvancedTableFormat; import ca.odell.glazedlists.gui.TableFormat; import ca.odell.glazedlists.gui.WritableTableFormat; import java.awt.EventQueue; import javax.swing.SwingUtilities; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableModel; /** * A {@link TableModel} that holds an {@link EventList}. Each element of the list * corresponds to a row in the {@link TableModel}. The columns of the table are * specified using a {@link TableFormat}. * *

    The EventTableModel class is not thread-safe. Unless otherwise * noted, all methods are only safe to be called from the event dispatch thread. * To do this programmatically, use {@link SwingUtilities#invokeAndWait(Runnable)} and * wrap the source list (or some part of the source list's pipeline) using * GlazedListsSwing#swingThreadProxyList(EventList).

    * * @see Glazed Lists Tutorial * * @see GlazedListsSwing#swingThreadProxyList(EventList) * @see SwingUtilities#invokeAndWait(Runnable) * @see Bug 112 * @see Bug 146 * @see Bug 177 * * @author Jesse Wilson */ public class DefaultEventTableModel extends AbstractTableModel implements AdvancedTableModel, ListEventListener { /** the source of data for this TableModel, which may or may not be {@link #swingThreadSource} */ protected EventList source; /** indicator to dispose source list */ private boolean disposeSource; /** specifies how column data is extracted from each row object */ private TableFormat tableFormat; /** reusable TableModelEvent for broadcasting changes */ private final MutableTableModelEvent tableModelEvent = new MutableTableModelEvent(this); /** * Creates a new table model that extracts column data from the given * source using the the given tableFormat. * * @param source the EventList that provides the row objects * @param tableFormat the object responsible for extracting column data * from the row objects */ public DefaultEventTableModel(EventList source, TableFormat tableFormat) { this(source, false, tableFormat); } /** * Creates a new table model that extracts column data from the given * source using the the given tableFormat. * * @param source the EventList that provides the row objects * @param diposeSource true if the source list should be disposed when disposing * this model, false otherwise * @param tableFormat the object responsible for extracting column data * from the row objects */ protected DefaultEventTableModel(EventList source, boolean disposeSource, TableFormat tableFormat) { this.source = source; this.disposeSource = disposeSource; this.tableFormat = tableFormat; source.addListEventListener(this); } /** * {@inheritDoc} */ public TableFormat getTableFormat() { return tableFormat; } /** * {@inheritDoc} */ public void setTableFormat(TableFormat tableFormat) { this.tableFormat = tableFormat; tableModelEvent.setStructureChanged(); fireTableChanged(tableModelEvent); } /** * {@inheritDoc} */ public E getElementAt(int index) { source.getReadWriteLock().readLock().lock(); try { return source.get(index); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * For implementing the ListEventListener interface. This sends changes * to the table which repaints the table cells. Because this class is * backed by {@link GlazedListsSwing#swingThreadProxyList}, all natural * calls to this method are guaranteed to occur on the Swing EDT. */ public void listChanged(ListEvent listChanges) { handleListChange(listChanges); } /** * Default implementation for converting a {@link ListEvent} to * TableModelEvents. There will be one TableModelEvent per ListEvent block. * Subclasses may choose to implement a different conversion. * * @param listChanges ListEvent to translate */ protected void handleListChange(ListEvent listChanges) { if (!EventQueue.isDispatchThread()) throw new IllegalStateException("Events to " + this.getClass().getSimpleName() + " must arrive on the EDT - consider adding GlazedListsSwing.swingThreadProxyList(source) somewhere in your list pipeline"); // for all changes, one block at a time while (listChanges.nextBlock()) { // get the current change info int startIndex = listChanges.getBlockStartIndex(); int endIndex = listChanges.getBlockEndIndex(); int changeType = listChanges.getType(); // create a table model event for this block tableModelEvent.setValues(startIndex, endIndex, changeType); fireTableChanged(tableModelEvent); } } /** * @return reusable TableModelEvent for broadcasting changes */ protected final MutableTableModelEvent getMutableTableModelEvent() { return tableModelEvent; } /** * Fetch the name for the specified column. */ @Override public String getColumnName(int column) { return tableFormat.getColumnName(column); } /** * The number of rows equals the number of entries in the source event list. */ public int getRowCount() { source.getReadWriteLock().readLock().lock(); try { return source.size(); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * Get the column count as specified by the table format. */ public int getColumnCount() { return tableFormat.getColumnCount(); } /** * Gets the class of elements in the specified column. This behaviour can be * customized by implementing the {@link AdvancedTableFormat} interface. */ @Override public Class getColumnClass(int columnIndex) { // See if the TableFormat is specifies a column class if(tableFormat instanceof AdvancedTableFormat) { return ((AdvancedTableFormat)tableFormat).getColumnClass(columnIndex); // If not, use the default... } else { return super.getColumnClass(columnIndex); } } /** * Retrieves the value at the specified location of the table. */ public Object getValueAt(int row, int column) { source.getReadWriteLock().readLock().lock(); try { return tableFormat.getColumnValue(source.get(row), column); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * Delegates the question of whether the cell is editable or not to the * backing TableFormat if it is a {@link WritableTableFormat}. Otherwise, * the column is assumed to be uneditable. */ @Override public boolean isCellEditable(int row, int column) { if (!(tableFormat instanceof WritableTableFormat)) return false; source.getReadWriteLock().readLock().lock(); try { final E toEdit = source.get(row); return ((WritableTableFormat) tableFormat).isEditable(toEdit, column); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * Attempts to update the object for the given row with the * editedValue. This requires the backing TableFormat * be a {@link WritableTableFormat}. {@link WritableTableFormat#setColumnValue} * is expected to contain the logic for updating the object at the given * row with the editedValue which was in the * given column. */ @Override public void setValueAt(Object editedValue, int row, int column) { // ensure this is a writable table if (!(tableFormat instanceof WritableTableFormat)) throw new UnsupportedOperationException("Unexpected setValueAt() on read-only table"); source.getReadWriteLock().writeLock().lock(); try { // get the object being edited from the source list final E baseObject = source.get(row); // tell the table format to set the value based final WritableTableFormat writableTableFormat = (WritableTableFormat) tableFormat; final E updatedObject = writableTableFormat.setColumnValue(baseObject, editedValue, column); // if the edit was discarded we have nothing to do if (updatedObject != null) { // check if updating the baseObject has caused it to be removed from this // TableModel (FilterList) or moved to another location (SortedList) final boolean baseObjectHasNotMoved = row < getRowCount() && source.get(row) == baseObject; // if the row is still present in its original location, update it to induce a // TableModelEvent that will redraw that row in the table if (baseObjectHasNotMoved) source.set(row, updatedObject); } } finally { source.getReadWriteLock().writeLock().unlock(); } } /** * {@inheritDoc} */ public void dispose() { source.removeListEventListener(this); if (disposeSource) source.dispose(); // this encourages exceptions to be thrown if this model is incorrectly accessed again source = null; } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/EventSelectionModel.java0000644000175000017500000003401612106516356032046 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.TransformedList; import ca.odell.glazedlists.matchers.Matcher; import java.util.List; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionListener; /** * An {@link EventSelectionModel} is a class that performs two simulaneous * services. It is a {@link ListSelectionModel} to provide selection tracking for a * {@link JTable}. It is also a {@link EventList} that contains the table's selection. * *

    As elements are selected or deselected, the {@link EventList} aspect of this * {@link EventSelectionModel} changes. Changes to that {@link List} will change the * source {@link EventList}. To modify only the selection, use the * {@link ListSelectionModel}'s methods. * *

    Alongside MULTIPLE_INTERVAL_SELECTION, this {@link ListSelectionModel} * supports an additional selection mode. * MULTIPLE_INTERVAL_SELECTION_DEFENSIVE is a new selection mode. * It is identical to MULTIPLE_INTERVAL_SELECTION in every way but * one. When a row is inserted immediately before a selected row in the * MULTIPLE_INTERVAL_SELECTION mode, it becomes selected. But in * the MULTIPLE_INTERVAL_SELECTION_DEFENSIVE mode, it does not * become selected. To set this mode, use * {@link #setSelectionMode(int) setSelectionMode(ListSelection.MULTIPLE_INTERVAL_SELECTION_DEFENSIVE)}. * * @see Bug 39 * @see Bug 61 * @see Bug 76 * @see Bug 108 * @see Bug 110 * @see Bug 112 * @see Bug 222 * * @deprecated Use {@link DefaultEventSelectionModel} instead. This class will be removed in the GL * 2.0 release. The wrapping of the source list with an EDT safe list has been * determined to be undesirable (it is better for the user to provide their own EDT * safe list). * * * @author Jesse Wilson */ public final class EventSelectionModel implements AdvancedListSelectionModel { /** The DefaultEventSelectionModel this delegates to. */ private DefaultEventSelectionModel delegateSelectionModel; /** the proxy moves events to the Swing Event Dispatch thread */ protected TransformedList swingThreadSource; /** * Creates a new selection model that also presents a list of the selection. * * The {@link EventSelectionModel} listens to this {@link EventList} in order * to adjust selection when the {@link EventList} is modified. For example, * when an element is added to the {@link EventList}, this may offset the * selection of the following elements. * * @param source the {@link EventList} whose selection will be managed. This should * be the same {@link EventList} passed to the constructor of your * {@link EventTableModel} or {@link EventListModel}. */ public EventSelectionModel(EventList source) { // lock the source list for reading since we want to prevent writes // from occurring until we fully initialize this EventSelectionModel source.getReadWriteLock().readLock().lock(); try { swingThreadSource = GlazedListsSwing.swingThreadProxyList(source); delegateSelectionModel = new DefaultEventSelectionModel(swingThreadSource); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * Add a listener to the list that's notified each time a change to * the selection occurs. * * Note that the change events fired by this class may include rows * that have been removed from the table. For this reason it is * advised not to for() through the changed range without * also verifying that each row is still in the table. */ public void addListSelectionListener(ListSelectionListener listener) { delegateSelectionModel.addListSelectionListener(listener); } /** * Change the selection to be the set union of the current selection and the indices between index0 and index1 inclusive */ public void addSelectionInterval(int index0, int index1) { delegateSelectionModel.addSelectionInterval(index0, index1); } /** * Add a matcher which decides when source elements are valid for selection. * * @param validSelectionMatcher returns true if a source element * can be selected; false otherwise */ public void addValidSelectionMatcher(Matcher validSelectionMatcher) { delegateSelectionModel.addValidSelectionMatcher(validSelectionMatcher); } /** * Change the selection to the empty set. */ public void clearSelection() { delegateSelectionModel.clearSelection(); } /** * Releases the resources consumed by this {@link EventSelectionModel} so that it * may eventually be garbage collected. * *

    An {@link EventSelectionModel} will be garbage collected without a call to * {@link #dispose()}, but not before its source {@link EventList} is garbage * collected. By calling {@link #dispose()}, you allow the {@link EventSelectionModel} * to be garbage collected before its source {@link EventList}. This is * necessary for situations where an {@link EventSelectionModel} is short-lived but * its source {@link EventList} is long-lived. * *

    Warning: It is an error * to call any method on a {@link EventSelectionModel} after it has been disposed. */ public void dispose() { delegateSelectionModel.dispose(); swingThreadSource.dispose(); } /** * Return the first index argument from the most recent call to setSelectionInterval(), addSelectionInterval() or removeSelectionInterval(). */ public int getAnchorSelectionIndex() { return delegateSelectionModel.getAnchorSelectionIndex(); } /** * Gets an {@link EventList} that contains only deselected values and * modifies the source list on mutation. * * Adding and removing items from this list performs the same operation on * the source list. */ public EventList getDeselected() { return delegateSelectionModel.getDeselected(); } /** * Returns whether the EventSelectionModel is editable or not. */ public boolean getEnabled() { return delegateSelectionModel.getEnabled(); } /** * Gets the event list that always contains the current selection. * * @deprecated As of 2005/02/18, the naming of this method became * ambiguous. Please use {@link #getSelected()}. */ public EventList getEventList() { return delegateSelectionModel.getSelected(); } /** * Return the second index argument from the most recent call to setSelectionInterval(), addSelectionInterval() or removeSelectionInterval(). */ public int getLeadSelectionIndex() { return delegateSelectionModel.getLeadSelectionIndex(); } /** * Gets the selection model that provides selection management for a table. * * @deprecated As of 2004/11/26, the {@link EventSelectionModel} implements * {@link ListSelectionModel} directly. */ public ListSelectionModel getListSelectionModel() { return delegateSelectionModel; } /** * Gets the index of the last selected element. */ public int getMaxSelectionIndex() { return delegateSelectionModel.getMaxSelectionIndex(); } /** * Gets the index of the first selected element. */ public int getMinSelectionIndex() { return delegateSelectionModel.getMinSelectionIndex(); } /** * Gets an {@link EventList} that contains only selected * values and modifies the source list on mutation. * * Adding and removing items from this list performs the same operation on * the source list. */ public EventList getSelected() { return delegateSelectionModel.getSelected(); } /** * Returns the current selection mode. */ public int getSelectionMode() { return delegateSelectionModel.getSelectionMode(); } /** * Gets an {@link EventList} that contains only deselected values and * modifies the selection state on mutation. * * Adding an item to this list deselects it and removing an item selects it. * If an item not in the source list is added an * {@link IllegalArgumentException} is thrown */ public EventList getTogglingDeselected() { return delegateSelectionModel.getTogglingDeselected(); } /** * Gets an {@link EventList} that contains only selected * values and modifies the selection state on mutation. * * Adding an item to this list selects it and removing an item deselects it. * If an item not in the source list is added an * {@link IllegalArgumentException} is thrown. */ public EventList getTogglingSelected() { return delegateSelectionModel.getTogglingSelected(); } /** * Returns true if the value is undergoing a series of changes. */ public boolean getValueIsAdjusting() { return delegateSelectionModel.getValueIsAdjusting(); } /** * Insert length indices beginning before/after index. */ public void insertIndexInterval(int index, int length, boolean before) { delegateSelectionModel.insertIndexInterval(index, length, before); } /** * Inverts the current selection. */ public void invertSelection() { delegateSelectionModel.invertSelection(); } /** * Returns true if the specified index is selected. If the specified * index has not been seen before, this will return false. This is * in the case where the table painting and selection have fallen * out of sync. Usually in this case there is an update event waiting * in the event queue that notifies this model of the change * in table size. */ public boolean isSelectedIndex(int index) { return delegateSelectionModel.isSelectedIndex(index); } /** * Returns true if no indices are selected. */ public boolean isSelectionEmpty() { return delegateSelectionModel.isSelectionEmpty(); } /** * Remove the indices in the interval index0,index1 (inclusive) from the selection model. */ public void removeIndexInterval(int index0, int index1) { delegateSelectionModel.removeIndexInterval(index0, index1); } /** * Remove a listener from the list that's notified each time a change to the selection occurs. */ public void removeListSelectionListener(ListSelectionListener listener) { delegateSelectionModel.removeListSelectionListener(listener); } /** * Change the selection to be the set difference of the current selection and the indices between index0 and index1 inclusive. */ public void removeSelectionInterval(int index0, int index1) { delegateSelectionModel.removeSelectionInterval(index0, index1); } /** * Remove a matcher which decides when source elements are valid for selection. * * @param validSelectionMatcher returns true if a source element * can be selected; false otherwise */ public void removeValidSelectionMatcher(Matcher validSelectionMatcher) { delegateSelectionModel .removeValidSelectionMatcher(validSelectionMatcher); } /** * Set the anchor selection index. */ public void setAnchorSelectionIndex(int anchorSelectionIndex) { delegateSelectionModel.setAnchorSelectionIndex(anchorSelectionIndex); } /** * Set the EventSelectionModel as editable or not. This means that the user cannot * manipulate the selection by clicking. The selection can still be changed as * the source list changes. * *

    Note that this will also disable the selection from being modified * programatically. Therefore you must use setEnabled(true) to * modify the selection in code. */ public void setEnabled(boolean enabled) { delegateSelectionModel.setEnabled(enabled); } /** * Set the lead selection index. */ public void setLeadSelectionIndex(int leadSelectionIndex) { delegateSelectionModel.setLeadSelectionIndex(leadSelectionIndex); } /** * Change the selection to be between index0 and index1 inclusive. * *

    First this calculates the smallest range where changes occur. This * includes the union of the selection range before and the selection * range specified. It then walks through the change and sets each * index as selected or not based on whether the index is in the * new range. Finally it fires events to both the listening lists and * selection listeners about what changes happened. * *

    If the selection does not change, this will not fire any events. */ public void setSelectionInterval(int index0, int index1) { delegateSelectionModel.setSelectionInterval(index0, index1); } /** * Set the selection mode. */ public void setSelectionMode(int selectionMode) { delegateSelectionModel.setSelectionMode(selectionMode); } /** * This property is true if upcoming changes to the value of the model should be considered a single event. */ public void setValueIsAdjusting(boolean valueIsAdjusting) { delegateSelectionModel.setValueIsAdjusting(valueIsAdjusting); } /** {@inheritDoc} */ @Override public String toString() { return delegateSelectionModel.toString(); } }././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/TextComponentMatcherEditor.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/TextComponentMatcherEditor.jav0000644000175000017500000002332712106516356033262 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.TextFilterator; import ca.odell.glazedlists.matchers.TextMatcherEditor; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; /** * A MatcherEditor that matches Objects that contain the filter text located * within a {@link Document}. This {@link TextMatcherEditor} is directly * coupled with a Document and fires MatcherEditor changes in response to * Document changes. This matcher is fully concrete and is expected to be used * by Swing applications. * *

    The {@link TextComponentMatcherEditor} constructors require that either a * {@link Document} or a {@link JTextComponent} (from which a {@link Document} * is extracted) be specified. * *

    The MatcherEditor registers itself as a {@link DocumentListener} on the * given Document, or {@link ActionListener} on the {@link JTextComponent} for * non-live filtering. If a {@link JTextComponent} is given on construction, it * is also watched for changes of its Document and the Document used by this * MatcherEditor is updated to reflect the latest Document behind the text * component. * * If this MatcherEditor must be garbage collected before the underlying * Document, or JTextComponent, the listeners can be unregistered by calling * {@link #dispose()}. * * @author James Lemieux */ public class TextComponentMatcherEditor extends TextMatcherEditor { /** the Document that provides the filter values */ private Document document; /** the JTextComponent being observed for actions */ private final JTextComponent textComponent; /** whether we're listening to each keystroke */ private boolean live; /** The listener attached to the given {@link #document}. */ private final FilterHandler filterHandler = new FilterHandler(); /** * Creates a TextMatcherEditor bound to the {@link Document} backing the * given textComponent with the given * textFilterator. * * @param textComponent the text component backed by the {@link Document} * that is the source of text filter values * @param textFilterator an object capable of producing Strings from the * objects being filtered. If textFilterator is * null then all filtered objects are expected to * implement {@link ca.odell.glazedlists.TextFilterable}. */ public TextComponentMatcherEditor(JTextComponent textComponent, TextFilterator textFilterator) { this(textComponent, textFilterator, true); } /** * Creates a TextMatcherEditor bound to the {@link Document} backing the * given textComponent with the given * textFilterator. * * @param textComponent the text component backed by the {@link Document} * that is the source of text filter values * @param textFilterator an object capable of producing Strings from the * objects being filtered. If textFilterator is * null then all filtered objects are expected to * implement {@link ca.odell.glazedlists.TextFilterable}. * @param live true to filter by the keystroke or false * to filter only when {@link java.awt.event.KeyEvent#VK_ENTER Enter} is pressed * within the {@link JTextComponent}. Note that non-live filtering is only * supported if textComponent is a {@link JTextField}. * @throws IllegalArgumentException if the textComponent * is not a {@link JTextField} and non-live filtering is specified. */ public TextComponentMatcherEditor(JTextComponent textComponent, TextFilterator textFilterator, boolean live) { this(textComponent, textComponent.getDocument(), textFilterator, live); } /** * Creates a TextMatcherEditor bound to the given document * with the given textFilterator. * * @param document the {@link Document} that is the source of text filter * values * @param textFilterator an object capable of producing Strings from the * objects being filtered. If textFilterator is * null then all filtered objects are expected to * implement {@link ca.odell.glazedlists.TextFilterable}. */ public TextComponentMatcherEditor(Document document, TextFilterator textFilterator) { this(null, document, textFilterator, true); } /** * This private constructor implements the actual construction work and thus * ensures that all public constructors agree on the construction logic. */ private TextComponentMatcherEditor(JTextComponent textComponent, Document document, TextFilterator textFilterator, boolean live) { super(textFilterator); this.textComponent = textComponent; this.document = document; this.live = live; registerListeners(live); // if the document is non-empty to begin with! refilter(); } /** * Whether filtering occurs by the keystroke or not. */ public boolean isLive() { return live; } /** * Toggle between filtering by the keystroke and not. * * @param live true to filter by the keystroke or false * to filter only when {@link java.awt.event.KeyEvent#VK_ENTER Enter} is pressed * within the {@link JTextComponent}. Note that non-live filtering is only * supported if textComponent is a {@link JTextField}. */ public void setLive(boolean live) { if (live == this.live) return; deregisterListeners(this.live); this.live = live; registerListeners(this.live); } /** * Listen live or on action performed. */ private void registerListeners(boolean live) { if(live) { document.addDocumentListener(filterHandler); } else { if(textComponent == null) throw new IllegalArgumentException("Non-live filtering supported only for JTextField (document provided)"); if(!(textComponent instanceof JTextField)) throw new IllegalArgumentException("Non-live filtering supported only for JTextField (argument class " + textComponent.getClass().getName() + ")"); JTextField textField = (JTextField) textComponent; textField.addActionListener(filterHandler); } if (textComponent != null) textComponent.addPropertyChangeListener(filterHandler); } /** * Stop listening. */ private void deregisterListeners(boolean live) { if (live) { document.removeDocumentListener(filterHandler); } else { JTextField textField = (JTextField) textComponent; textField.removeActionListener(filterHandler); } if (textComponent != null) textComponent.removePropertyChangeListener(filterHandler); } /** * A cleanup method which stops this MatcherEditor from listening to * changes on the underlying {@link Document}, thus freeing the * MatcherEditor or Document to be garbage collected. */ public void dispose() { deregisterListeners(live); } /** * Update the filter text from the contents of the Document. */ private void refilter() { try { final int mode = getMode(); final String text = document.getText(0, document.getLength()); final String[] filters; // in CONTAINS mode we treat the string as whitespace delimited if (mode == CONTAINS) filters = text.split("[ \t]"); // in STARTS_WITH, REGULAR_EXPRESSION, or EXACT modes we use the string in its entirety else if (mode == STARTS_WITH || mode == REGULAR_EXPRESSION || mode == EXACT) filters = new String[] {text}; else throw new IllegalStateException("Unknown mode: " + mode); setFilterText(filters); } catch (BadLocationException ble) { // this shouldn't ever, ever happen throw new RuntimeException(ble); } } /** * This class responds to any change in the Document by setting the filter * text of this TextMatcherEditor to the contents of the Document. */ private class FilterHandler implements DocumentListener, ActionListener, PropertyChangeListener { public void insertUpdate(DocumentEvent e) { refilter(); } public void removeUpdate(DocumentEvent e) { refilter(); } public void changedUpdate(DocumentEvent e) { refilter(); } public void actionPerformed(ActionEvent e) { refilter(); } public void propertyChange(PropertyChangeEvent evt) { if ("document" == evt.getPropertyName()) { // stop listening to the old Document deregisterListeners(live); // start listening to the new Document document = textComponent.getDocument(); registerListeners(live); // refilter based on the new Document refilter(); } } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/TableComparatorChooser.java0000644000175000017500000006743712106516356032555 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.SortedList; import ca.odell.glazedlists.gui.AbstractTableComparatorChooser; import ca.odell.glazedlists.gui.AdvancedTableFormat; import ca.odell.glazedlists.gui.TableFormat; import ca.odell.glazedlists.impl.SortIconFactory; import ca.odell.glazedlists.impl.gui.SortingStrategy; import java.awt.AWTEventMulticaster; import java.awt.Component; import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Comparator; import javax.swing.Icon; import javax.swing.JLabel; import javax.swing.JTable; import javax.swing.SwingConstants; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.plaf.UIResource; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumnModel; /** * A TableComparatorChooser is a tool that allows the user to sort a table by clicking * on the table's headers. It requires that the {@link JTable}s model is an * {@link AdvancedTableModel} with a {@link SortedList} as a source. * *

    This class includes custom arrow icons that indicate the sort * order. The icons used are chosen based on the current Swing look and feel. * Icons are available for the following look and feels: Mac OS X, Metal, Windows. * *

    This class supports multiple sort strategies for each * column, specified by having muliple comparators for each column. This may * be useful when you want to sort a single column in either of two ways. For * example, when sorting movie names, "The Phantom Menace" may be sorted under * "T" for "The", or "P" for "Phantom". * *

    This class supports sorting multiple columns simultaneously. * In this mode, the user clicks a first column to sort by, and then the user * clicks subsequent columns. The list is sorted by the first column and ties * are broken by the second column. * *

    If the {@link AdvancedTableModel} uses an {@link AdvancedTableFormat}, its * {@link AdvancedTableFormat#getColumnComparator} method will be used to * populate the initial column {@link Comparator}s. * * @see Bug 4 * @see Bug 31 * @see Bug 391 * * @author Jesse Wilson */ public class TableComparatorChooser extends AbstractTableComparatorChooser { /** * the header renderer which decorates an underlying renderer * (the table header's default renderer) with a sort arrow icon. */ private SortArrowHeaderRenderer sortArrowHeaderRenderer; /** listen for UI delegate changes to the table header */ private final TableHeaderUIHandler tableHeaderUIHandler = new TableHeaderUIHandler(); /** listen for table and property change events */ private final TableModelHandler tableModelHandler = new TableModelHandler(); /** the table being sorted */ private JTable table; /** listeners to sort change events */ private ActionListener sortListener; /** the sort icons to use */ private static Icon[] icons = SortIconFactory.loadIcons(); /** when somebody clicks on the header, update the sorting state */ private final HeaderClickHandler headerClickHandler; /** * Creates and installs a TableComparatorChooser. * * @deprecated replaced with {@link #install}, which is functionally * identical but uses a more fitting name to convey the action that is * performed and fixes an API flaw by explicitly requiring the TableFormat. */ public TableComparatorChooser(JTable table, SortedList sortedList, boolean multipleColumnSort) { this(table, sortedList, multipleColumnSort ? MULTIPLE_COLUMN_MOUSE : SINGLE_COLUMN); } /** * Creates and installs a TableComparatorChooser. * * @deprecated 9/25/06 replaced with {@link #install}, which is functionally * identical but uses a more fitting name to convey the action that is * performed and fixes an API flaw by explicitly requiring the TableFormat. */ public TableComparatorChooser(JTable table, SortedList sortedList, Object strategy) { this(table, sortedList,strategy,((AdvancedTableModel)table.getModel()).getTableFormat()); } /** * Creates and installs a TableComparatorChooser. * * @param table the table with headers that can be clicked on * @param sortedList the sorted list to update * @param strategy an implementations of {@link ca.odell.glazedlists.impl.gui.SortingStrategy}, typically one of *

      *
    • {@link ca.odell.glazedlists.gui.AbstractTableComparatorChooser#SINGLE_COLUMN} *
    • {@link ca.odell.glazedlists.gui.AbstractTableComparatorChooser#MULTIPLE_COLUMN_MOUSE} *
    • {@link ca.odell.glazedlists.gui.AbstractTableComparatorChooser#MULTIPLE_COLUMN_KEYBOARD} *
    • {@link ca.odell.glazedlists.gui.AbstractTableComparatorChooser#MULTIPLE_COLUMN_MOUSE_WITH_UNDO} * @param tableFormat the TableFormat providing the columns for the table */ protected TableComparatorChooser(JTable table, SortedList sortedList, Object strategy, TableFormat tableFormat) { super(sortedList, tableFormat); validateSortingStrategy(strategy); // save the Swing-specific state this.table = table; this.table.addPropertyChangeListener("model", tableModelHandler); this.table.getTableHeader().addPropertyChangeListener("UI", tableHeaderUIHandler); // wrap the default table header with logic that decorates it with a sorting icon wrapDefaultTableHeaderRenderer(); // listen for events on the specified table table.getModel().addTableModelListener(tableModelHandler); // install the sorting strategy to interpret clicks headerClickHandler = new HeaderClickHandler(table, (SortingStrategy)strategy); } /** * A method to wrap the default renderer of the JTableHeader if it does not * appear to be wrapped already. This is particularly useful when the UI * delegate of the table header changes. */ private void wrapDefaultTableHeaderRenderer() { final TableCellRenderer defaultRenderer = table.getTableHeader().getDefaultRenderer(); final Class defaultRendererType = defaultRenderer == null ? null : defaultRenderer.getClass(); // if the renderer does not appear to be wrapped, do it if (defaultRendererType != SortArrowHeaderRenderer.class && defaultRendererType != null) { // decorate the default table header renderer with sort arrows sortArrowHeaderRenderer = new SortArrowHeaderRenderer(defaultRenderer); table.getTableHeader().setDefaultRenderer(sortArrowHeaderRenderer); } } /** * Installs a new TableComparatorChooser that responds to clicks on the * header of the specified table and uses them to sort the specified * sortedList by delegating to the given strategy * If at any time the table should no longer sort, the behaviour can be * removed calling {@link #dispose()} on the object returned by this method. * *

      This method assumes that the JTable is backed by an AdvancedTableModel * and it is from that AdvancedTableModel that the TableFormat should be * extracted. This is, by far, the typical case and so we provide this * simpler install method for convenience. * * @param table the table with headers that can be clicked on * @param sortedList the sorted list to update * @param strategy an implementations of {@link SortingStrategy}, typically one of *

        *
      • {@link AbstractTableComparatorChooser#SINGLE_COLUMN} *
      • {@link AbstractTableComparatorChooser#MULTIPLE_COLUMN_MOUSE} *
      • {@link AbstractTableComparatorChooser#MULTIPLE_COLUMN_KEYBOARD} *
      • {@link AbstractTableComparatorChooser#MULTIPLE_COLUMN_MOUSE_WITH_UNDO} *
      * @return TableComparatorChooser object that is responsible for translating * mouse clicks on the table header into sorting actions on the sortedList. */ public static TableComparatorChooser install(JTable table, SortedList sortedList, Object strategy) { return install(table, sortedList, strategy, ((AdvancedTableModel)table.getModel()).getTableFormat()); } /** * Installs a new TableComparatorChooser that responds to clicks on the * header of the specified table and uses them to sort the specified * sortedList by delegating to the given strategy * If at any time the table should no longer sort, the behaviour can be * removed calling {@link #dispose()} on the object returned by this method. * *

      This method makes no assumptions about the TableModel implementation * that backs the JTable. As such, it requires the TableFormat as an explicit * parameter and expects the TableFormat to be constant (i.e. never changes) * for the life of the TableComparatorChooser. * * @param table the table with headers that can be clicked on * @param tableFormat the TableFormat providing the columns for the table * @param sortedList the sorted list to update * @param strategy an implementations of {@link SortingStrategy}, typically one of *

        *
      • {@link AbstractTableComparatorChooser#SINGLE_COLUMN} *
      • {@link AbstractTableComparatorChooser#MULTIPLE_COLUMN_MOUSE} *
      • {@link AbstractTableComparatorChooser#MULTIPLE_COLUMN_KEYBOARD} *
      • {@link AbstractTableComparatorChooser#MULTIPLE_COLUMN_MOUSE_WITH_UNDO} *
      * @return TableComparatorChooser object that is responsible for translating * mouse clicks on the table header into sorting actions on the sortedList. */ public static TableComparatorChooser install(JTable table, SortedList sortedList, Object strategy, TableFormat tableFormat) { return new TableComparatorChooser(table, sortedList, strategy, tableFormat); } /** * Ensures the given strategy is an accepted value. It is * possible for people to define their own sorting strategies, so this * validation can only ensure that the given strategy * implements the {@link SortingStrategy} interface. * * @throws IllegalArgumentException if strategy is not an * accepted value */ private static void validateSortingStrategy(Object strategy) { if (!(strategy instanceof SortingStrategy)) throw new IllegalArgumentException("Unrecognized sorting strategy, \"" + strategy + "\", use one of AbstractTableComparatorChooser.SINGLE_COLUMN, AbstractTableComparatorChooser.MULTIPLE_COLUMN_MOUSE, or AbstractTableComparatorChooser.MULTIPLE_COLUMN_KEYBOARD"); } /** * Registers the specified {@link ActionListener} to receive notification whenever * the {@link JTable} is sorted by this {@link TableComparatorChooser}. */ public void addSortActionListener(ActionListener sortActionListener) { sortListener = AWTEventMulticaster.add(sortListener, sortActionListener); } /** * Deregisters the specified {@link ActionListener} to no longer receive * action events. */ public void removeSortActionListener(ActionListener sortActionListener) { sortListener = AWTEventMulticaster.remove(sortListener, sortActionListener); } /** * Decorates and returns the given delegateRenderer with * functionality that attempts to install a sorting icon into the Component * returned by the delegateRenderer. In particular, the * delegateRenderer will be decorated with a sorting icon in * one of two scenarios: * *
        *
      • the delegateRenderer implements {@link SortableRenderer} - in this * case {@link SortableRenderer#setSortIcon setSortIcon} is called on * the delegateRenderer and it is expected to place the icon anywhere * it desires on the Component it returns. This allows maximum * flexibility when displaying the sort icon. * *
      • the Component returned by the delegateRenderer is a JLabel - in * this case {@link JLabel#setIcon setIcon} is called on the JLabel * with the sort icon. This caters to the typical case when a * {@link javax.swing.table.DefaultTableCellRenderer} is used as the * delegateRenderer. *
      * * If neither of these scenarios are true of the given delegateRenderer * then no sort indicator arrows will be added to the renderer's component. * * @param delegateRenderer the TableCellRenderer acting as a table header * renderer and to which a sort icon should be added * @return a TableCellRenderer that attempts to decorate the given * delegateRenderer with a sort icon */ public TableCellRenderer createSortArrowHeaderRenderer(TableCellRenderer delegateRenderer) { return new SortArrowHeaderRenderer(delegateRenderer); } /** * Examines the current {@link Comparator} of the SortedList and * adds icons to the table header renderers in response. * *

      To do this, clicks are injected into each of the * corresponding ColumnClickTrackers. */ @Override protected void redetectComparator(Comparator currentComparator) { super.redetectComparator(currentComparator); // force the table header to redraw itself table.getTableHeader().revalidate(); table.getTableHeader().repaint(); } /** * Updates the comparator in use and applies it to the table. */ @Override protected final void rebuildComparator() { super.rebuildComparator(); // force the table header to redraw itself table.getTableHeader().revalidate(); table.getTableHeader().repaint(); // notify interested listeners that the sorting has changed if(sortListener != null) sortListener.actionPerformed(new ActionEvent(this, 0, "sort")); } /** * Gets the sorting style currently applied to the specified column. */ @Override protected final int getSortingStyle(int column) { return super.getSortingStyle(table.convertColumnIndexToModel(column)); } /** * Determines if the specified mouse event shall be handled by this * {@link TableComparatorChooser}. The default implementation handles only clicks * with the left mouse button. Extending classes can customize which mouse * events the table comparator chooser responds to by overriding this method. * *

      As of 2005/12/20, this method is no longer called when the * corresponding mouse press event was a popup trigger. In effect, if this * is a right-click on Windows or a 'control-click' on the Mac. * *

      As of 2008/02/05, this method is no longer called when the * Cursor over the JTableHeader indicates a column resize is expected to * take place, rather than a change in sort. */ protected boolean isSortingMouseEvent(MouseEvent e) { // skip the sort if it's not button 1 if(e.getButton() != MouseEvent.BUTTON1) return false; // we have no reason to dislike this mouse event! return true; } /** * Set all {@link TableComparatorChooser}s to use the icons from the directory * specified. The folder should contain the following eight icon files: *

    • primary_sorted.png
    • secondary_sorted.png *
    • primary_sorted_alternate.png
    • secondary_sorted_alternate.png *
    • primary_sorted_alternate_reverse.png
    • secondary_sorted_alternate_reverse.png *
    • primary_sorted_reverse.png
    • secondary_sorted_reverse.png * *

      Note that this path must be on the system classpath. It may be within a * jar file. */ public static void setIconPath(String path) { icons = SortIconFactory.loadIcons(path); } /** * Releases the resources consumed by this {@link TableComparatorChooser} so that it * may eventually be garbage collected. * *

      A {@link TableComparatorChooser} will be garbage collected without a call to * {@link #dispose()}, but not before its source {@link EventList} is garbage * collected. By calling {@link #dispose()}, you allow the {@link TableComparatorChooser} * to be garbage collected before its source {@link EventList}. This is * necessary for situations where an {@link TableComparatorChooser} is short-lived but * its source {@link EventList} is long-lived. * *

      Warning: It is an error * to call any method on a {@link TableComparatorChooser} after it has been disposed. */ @Override public void dispose() { super.dispose(); headerClickHandler.dispose(); // if the default renderer within the table header is our sort arrow renderer, // uninstall it by restoring the table header's original default renderer if (table.getTableHeader().getDefaultRenderer() == sortArrowHeaderRenderer) table.getTableHeader().setDefaultRenderer(sortArrowHeaderRenderer.getDelegateRenderer()); // remove our listeners from the table's header and model table.getModel().removeTableModelListener(tableModelHandler); table.removePropertyChangeListener("model", tableModelHandler); table.getTableHeader().removePropertyChangeListener("UI", tableHeaderUIHandler); // null out our table reference for safety's sake table = null; } /** * Nested Listener class handles changes in the UI delegate for the table's * header. It responds by rewrapping the default renderer for the table * header, if it was replaced in the course of installing the new * TableHeaderUI. */ private class TableHeaderUIHandler implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent evt) { wrapDefaultTableHeaderRenderer(); } } /** * Nested Listener class handles TableModelEvents and PropertyChangeEvents. * TableModelEvents tell us when the TableModel's data changes in place. * PropertyChangeEvents tell us when the TableModel has been replaced. */ private class TableModelHandler implements TableModelListener, PropertyChangeListener { /** * This method is only called when the TableModel of the JTable is * changed. It allows us to stop listening to the previous * EventTableModel and start listening to the new one. It also resets * the sorting state of this TableComparatorChooser. */ public void propertyChange(PropertyChangeEvent evt) { // get the two EventTableModels final AdvancedTableModel oldModel = evt.getOldValue() instanceof AdvancedTableModel ? (AdvancedTableModel) evt.getOldValue() : null; final AdvancedTableModel newModel = evt.getNewValue() instanceof AdvancedTableModel ? (AdvancedTableModel) evt.getNewValue() : null; // stop listening for TableModelEvents in the oldModel and start for the newModel, if possible if (oldModel != null) oldModel.removeTableModelListener(this); if (newModel != null) { newModel.addTableModelListener(this); // the table structure has probably changed due to the new EventTableModel // so we reset the TableFormat (which clears the sorting state) setTableFormat(newModel.getTableFormat()); } } /** * When the number of columns changes in the table, we need to * clear the comparators and columns. */ public void tableChanged(TableModelEvent event) { if(event.getFirstRow() == TableModelEvent.HEADER_ROW && event.getColumn() == TableModelEvent.ALL_COLUMNS) { if (table.getModel() instanceof AdvancedTableModel) { // the table structure may have changed due to a change in the table format // so we conservatively reset the TableFormat on this TableComparatorChooser setTableFormat(((AdvancedTableModel) table.getModel()).getTableFormat()); } } // if the comparator has changed final Comparator currentComparator = sortedList.getComparator(); if(currentComparator != sortedListComparator) { redetectComparator(currentComparator); } } } /** * The SortArrowHeaderRenderer simply delegates most of the rendering * to a given delegate renderer, and adds an icon to indicate sorting * direction. This allows TableComparatorChooser to work equally well * with any custom TableCellRenderers that are used as the default * table header renderer. * *

      This class fails to add indicator arrows on table headers where the * default table header render does not return a JLabel or does not * implement {@link SortableRenderer}. * *

      We implement UIResource here so that changes to the UI delegate of * the JTableHeader will replace our renderer with a new one that is * appropriate for the new LaF. We, in turn, react to the change of UI * delegates by *re-wrapping* the new default renderer with our sort icon * injection logic. */ class SortArrowHeaderRenderer implements TableCellRenderer, UIResource { /** the renderer to which we delegate */ private TableCellRenderer delegateRenderer; /** * Creates a new SortArrowHeaderRenderer that attempts to decorate the * given delegateRenderer which a sorting icon. */ public SortArrowHeaderRenderer(TableCellRenderer delegateRenderer) { this.delegateRenderer = delegateRenderer; } /** * Returns the delegate renderer that is decorated with sort arrows. */ public TableCellRenderer getDelegateRenderer() { return delegateRenderer; } /** * Renders the header in the default way but with the addition of an * icon to indicate sorting state. */ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { // if column index is negative, just call the delegate renderer // this is a special case for JideTable with nested table columns if (column < 0) return getDelegateTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); final Icon sortIcon = icons[getSortingStyle(column)]; final Component rendered; // 1. look for our custom SortableRenderer interface if (delegateRenderer instanceof SortableRenderer) { ((SortableRenderer) delegateRenderer).setSortIcon(sortIcon); rendered = getDelegateTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); // 2. Otherwise check whether the rendered component is a JLabel (this is the case of the default header renderer) } else { rendered = getDelegateTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); // we check for a JLabel rather than a DefaultTableCellRenderer to support WinLAF, // which installs a decorator over the DefaultTableCellRenderer if (rendered instanceof JLabel) { final JLabel label = (JLabel) rendered; label.setIcon(sortIcon); label.setHorizontalTextPosition(SwingConstants.LEADING); } } return rendered; } /** * Attempts to retrieve the decorated Component from the delegate * renderer. If a RuntimeException occurs, this method replaces the * delegate renderer with a {@link DefaultTableCellRenderer} and * requests the Component from it. This exists because our decorating * approach is the victim of a SUN bug in WindowsTableHeaderUI: * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6429812 * * See also more information reported by Eric Burke here: * http://stuffthathappens.com/blog/2007/10/02/rich-client-developers-avoid-java-6/ */ private Component getDelegateTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { try { return delegateRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); } catch (RuntimeException e) { delegateRenderer = new DefaultTableCellRenderer(); return delegateRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); } } } /** * Handle clicks to the table's header by adjusting the sorting state. */ private class HeaderClickHandler extends MouseAdapter { private final JTable table; private final SortingStrategy delegate; private boolean mouseEventIsPerformingPopupTrigger = false; public HeaderClickHandler(JTable table, SortingStrategy delegate) { this.table = table; this.delegate = delegate; table.getTableHeader().addMouseListener(this); } @Override public void mouseClicked(MouseEvent e) { // if the MouseEvent is popping up a context menu, do not sort if (mouseEventIsPerformingPopupTrigger) return; // if the cursor indicates we're resizing columns, do not sort if (table.getTableHeader().getCursor() == Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)) return; // check if there is any other reason to ignore this MouseEvent if (!isSortingMouseEvent(e)) return; final TableColumnModel columnModel = table.getColumnModel(); final int viewColumn = columnModel.getColumnIndexAtX(e.getX()); final int column = table.convertColumnIndexToModel(viewColumn); final int clicks = e.getClickCount(); if (clicks >= 1 && column != -1) { final boolean shift = e.isShiftDown(); final boolean control = e.isControlDown() || e.isMetaDown(); delegate.columnClicked(sortingState, column, clicks, shift, control); } } /** * Keep track of whether the mouse is triggering a popup, so we * can avoid sorting the table when the poor user just wants to show * a context menu. */ @Override public void mousePressed(MouseEvent mouseEvent) { this.mouseEventIsPerformingPopupTrigger = mouseEvent.isPopupTrigger(); } public void dispose() { table.getTableHeader().removeMouseListener(this); } } }././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/AdvancedListSelectionModel.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/AdvancedListSelectionModel.jav0000644000175000017500000001002012106516356033152 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.matchers.Matcher; import javax.swing.ListSelectionModel; /** * AdvancedListSelectionModel is an interface defining additional methods * for selection management beyond the standard {@link ListSelectionModel}. * * @author Holger Brands */ public interface AdvancedListSelectionModel extends ListSelectionModel { /** * Gets an {@link EventList} that contains only selected * values and modifies the source list on mutation. * * Adding and removing items from this list performs the same operation on * the source list. */ EventList getSelected(); /** * Gets an {@link EventList} that contains only selected * values and modifies the selection state on mutation. * * Adding an item to this list selects it and removing an item deselects it. * If an item not in the source list is added an * {@link IllegalArgumentException} is thrown. */ EventList getTogglingSelected(); /** * Gets an {@link EventList} that contains only deselected values and * modifies the source list on mutation. * * Adding and removing items from this list performs the same operation on * the source list. */ EventList getDeselected(); /** * Gets an {@link EventList} that contains only deselected values and * modifies the selection state on mutation. * * Adding an item to this list deselects it and removing an item selects it. * If an item not in the source list is added an * {@link IllegalArgumentException} is thrown */ EventList getTogglingDeselected(); /** * Set the EventSelectionModel as editable or not. This means that the user cannot * manipulate the selection by clicking. The selection can still be changed as * the source list changes. * *

      Note that this will also disable the selection from being modified * programatically. Therefore you must use setEnabled(true) to * modify the selection in code. */ void setEnabled(boolean enabled); /** * Returns whether the EventSelectionModel is editable or not. */ boolean getEnabled(); /** * Add a matcher which decides when source elements are valid for selection. * * @param validSelectionMatcher returns true if a source element * can be selected; false otherwise */ void addValidSelectionMatcher(Matcher validSelectionMatcher); /** * Remove a matcher which decides when source elements are valid for selection. * * @param validSelectionMatcher returns true if a source element * can be selected; false otherwise */ void removeValidSelectionMatcher(Matcher validSelectionMatcher); /** * Inverts the current selection. */ void invertSelection(); /** * Releases the resources consumed by this {@link AdvancedListSelectionModel} so that it * may eventually be garbage collected. * *

      An {@link AdvancedListSelectionModel} will be garbage collected without a call to * {@link #dispose()}, but not before its source {@link EventList} is garbage * collected. By calling {@link #dispose()}, you allow the {@link AdvancedListSelectionModel} * to be garbage collected before its source {@link EventList}. This is * necessary for situations where an {@link AdvancedListSelectionModel} is short-lived but * its source {@link EventList} is long-lived. * *

      Warning: It is an error * to call any method on a {@link AdvancedListSelectionModel} after it has been disposed. */ void dispose(); } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/EventTableModel.java0000644000175000017500000001736612106516356031161 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import javax.swing.SwingUtilities; import javax.swing.table.TableModel; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.TransformedList; import ca.odell.glazedlists.gui.TableFormat; /** * A {@link DefaultEventTableModel} that silently wraps it's source list in a * SwingThreadProxyEventList to ensure that events that arrive at the TableModel do so on * the EDT. A {@link TableModel} that holds an {@link EventList}. Each element of the list * corresponds to a row in the {@link TableModel}. The columns of the table are specified using a * {@link TableFormat}. *

      * The EventTableModel class is not thread-safe. Unless otherwise noted, all * methods are only safe to be called from the event dispatch thread. To do this * programmatically, use {@link SwingUtilities#invokeAndWait(Runnable)}. * * @see Glazed Lists Tutorial * @see SwingUtilities#invokeAndWait(Runnable) * @see Bug 112 * @see Bug 146 * @see Bug 177 * * @deprecated Use {@link DefaultEventTableModel} instead. This class will be removed in the GL * 2.0 release. The wrapping of the source list with an EDT safe list has been * determined to be undesirable (it is better for the user to provide their own EDT * safe list). * * @author Jesse Wilson */ public class EventTableModel extends DefaultEventTableModel { /** the proxy moves events to the Swing Event Dispatch thread */ protected TransformedList swingThreadSource; /** * Creates a new table model that extracts column data from the given source * using the the given tableFormat. * * @param source the EventList that provides the row objects * @param tableFormat the object responsible for extracting column data from the row objects * * @deprecated Use {@link DefaultEventTableModel} and * {@link GlazedListsSwing#swingThreadProxyList(EventList)} instead */ public EventTableModel(EventList source, TableFormat tableFormat) { super(source, tableFormat); // lock the source list for reading since we want to prevent writes // from occurring until we fully initialize this EventTableModel // KD 20090306 - Not crazy about these locks, but it was in the original // EventTableModel implementation, so we need to leave it. DefaultTableModel // should be considered to be non-Thread Safe, so source lists should be locked // before constructing a DefaultEventTableModel if there are any potential // race conditions source.getReadWriteLock().readLock().lock(); try { final TransformedList decorated = createSwingThreadProxyList(source); if (decorated != null && decorated != source){ //we need to switch the configuration of the DefaultEventTableModel so it // uses the swingThreadProxyList instead this.source.removeListEventListener(this); this.source = swingThreadSource = decorated; this.source.addListEventListener(this); } } finally { source.getReadWriteLock().readLock().unlock(); } } /** * Creates a new table that renders the specified list with an automatically * generated {@link TableFormat}. It uses JavaBeans and reflection to create * a {@link TableFormat} as specified. * *

      Note that the classes which will be obfuscated may not work with * reflection. In this case, implement a {@link TableFormat} manually. * * @param source the EventList that provides the row objects * @param propertyNames an array of property names in the JavaBeans format. * For example, if your list contains Objects with the methods getFirstName(), * setFirstName(String), getAge(), setAge(Integer), then this array should * contain the two strings "firstName" and "age". This format is specified * by the JavaBeans {@link java.beans.PropertyDescriptor}. * @param columnLabels the corresponding column names for the listed property * names. For example, if your columns are "firstName" and "age", then * your labels might be "First Name" and "Age". * @param writable an array of booleans specifying which of the columns in * your table are writable. * * @deprecated Use {@link GlazedListsSwing#createEventTableModel(EventList, String[], String[], boolean[])} * and {@link GlazedListsSwing#swingThreadProxyList(EventList)} instead */ public EventTableModel(EventList source, String[] propertyNames, String[] columnLabels, boolean[] writable) { this(source, GlazedLists.tableFormat(propertyNames, columnLabels, writable)); } /** * This method exists as a hook for subclasses that may have custom * threading needs within their EventTableModels. By default, this method * will wrap the given source in a SwingThreadProxyList if it * is not already a SwingThreadProxyList. Subclasses may replace this logic * and return either a custom ThreadProxyEventList of their choosing, or * return null or the source unchanged in order * to indicate that NO ThreadProxyEventList is desired. * In these cases it is expected that some external mechanism will ensure * that threading is handled correctly. * * @param source the EventList that provides the row objects * @return the source wrapped in some sort of ThreadProxyEventList if * Thread-proxying is desired, or either null or the * source unchanged to indicate that NO * Thread-proxying is desired */ protected TransformedList createSwingThreadProxyList(EventList source) { return GlazedListsSwing.isSwingThreadProxyList(source) ? null : GlazedListsSwing.swingThreadProxyList(source); } /** * Releases the resources consumed by this {@link EventTableModel} so that it * may eventually be garbage collected. * *

      An {@link EventTableModel} will be garbage collected without a call to * {@link #dispose()}, but not before its source {@link EventList} is garbage * collected. By calling {@link #dispose()}, you allow the {@link EventTableModel} * to be garbage collected before its source {@link EventList}. This is * necessary for situations where an {@link EventTableModel} is short-lived but * its source {@link EventList} is long-lived. * *

      Warning: It is an error * to call any method on an {@link EventTableModel} after it has been disposed. * As such, this {@link EventTableModel} should be detached from its * corresponding Component before it is disposed. */ @Override public void dispose() { // if we created the swingThreadSource then we must also dispose it if (swingThreadSource != null) swingThreadSource.dispose(); swingThreadSource = null; super.dispose(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/UndoSupport.java0000644000175000017500000001715112106516356030441 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.FunctionList; import ca.odell.glazedlists.UndoRedoSupport; import javax.swing.undo.*; import javax.swing.*; /** * This class adapts the generic {@link UndoRedoSupport} provided by Glazed * Lists for specific use with Swing's native {@link UndoManager}. Each * {@link UndoRedoSupport.Edit} produced by Glazed List's * {@link UndoRedoSupport} is adapted to Swing's {@link UndoableEdit} interface * and then {@link UndoManager#addEdit added} into the given UndoManager. * *

      Fine grain control of the {@link UndoableEdit} that is ultimately added * to the {@link UndoableEdit} can be achieved by using * {@link #install(UndoManager, EventList, FunctionList.Function) this} install * method and specifying a custom Function. * * @author James Lemieux */ public final class UndoSupport { /** the manager of all undoable edits for the entire Swing application */ private UndoManager undoManager; /** glazed List's undo/redo support which is being adapted to Swing's native undo/redo framework */ private UndoRedoSupport undoRedoSupport; /** a listener that transforms GL-style edits into Swing-style edits using the {@link #editAdapter} and adds them to the {@link #undoManager} */ private UndoRedoSupport.Listener undoSupportHandler = new UndoSupportHandler(); /** the function which transforms GL-style edits into Swing-style edits */ private FunctionList.Function editAdapter; /** * The private constructor creates an UndoSupport that provides undo/redo * capabilties for changes to the given source. Whenever * changes occur to the source, a GL-style * {@link UndoRedoSupport.Edit} is produced, which is then transformed into * a Swing-style {@link UndoableEdit} and ultimately added to the given * undoManager. * * @param undoManager the manager of all undoable edits for the entire Swing application * @param source the EventList to watch for undoable edits * @param editAdapter the function that converts GL-style edits into Swing-style edits */ private UndoSupport(UndoManager undoManager, EventList source, FunctionList.Function editAdapter) { this.undoManager = undoManager; this.undoRedoSupport = UndoRedoSupport.install(source); this.editAdapter = editAdapter; // begin watching the GL undoRedoSupport for undoable edits this.undoRedoSupport.addUndoSupportListener(undoSupportHandler); } /** * Installs support for undoing/redoing edits on the given * source. Specifically, {@link UndoableEdit}s are added to the * undoManager each time the source changes. * {@link UndoableEdit#undo Undoing} and {@link UndoableEdit#redo redoing} * these edits will unapply/reapply the corresponding changes to the * source. * *

      This method uses a default strategy for mapping the GL-style edits * to {@link UndoableEdit}s. * * @param undoManager the manager of all undoable edits for the entire Swing application * @param source the EventList to watch for undoable edits * @return an instance of the support class providing undo/redo edit features * * @throws IllegalStateException if this method is called from any Thread * other than the Swing Event Dispatch Thread */ public static UndoSupport install(UndoManager undoManager, EventList source) { return install(undoManager, source, new DefaultEditAdapter()); } /** * Installs support for undoing/redoing edits on the given * source. Specifically, {@link UndoableEdit}s are added to the * undoManager each time the source changes. * {@link UndoableEdit#undo Undoing} and {@link UndoableEdit#redo redoing} * these edits will unapply/reapply the corresponding changes to the * source. * *

      This method uses the given editAdapter for mapping the * GL-style edits to {@link UndoableEdit}s. * * @param undoManager the manager of all undoable edits for the entire Swing application * @param source the EventList to watch for undoable edits * @param editAdapter the function that converts GL-style edits into Swing-style edits * @return an instance of the support class providing undo/redo edit features * * @throws IllegalStateException if this method is called from any Thread * other than the Swing Event Dispatch Thread */ public static UndoSupport install(UndoManager undoManager, EventList source, FunctionList.Function editAdapter) { checkAccessThread(); return new UndoSupport(undoManager, source, editAdapter); } /** * This method removes undo/redo support from the {@link EventList} it was * installed on. This method is useful when the {@link EventList} must * outlive the undo/redo support itself. Calling this method will make * this support object available for garbage collection independently of * the {@link EventList} of items. * * @throws IllegalStateException if this method is called from any Thread * other than the Swing Event Dispatch Thread */ public void uninstall() { checkAccessThread(); undoRedoSupport.removeUndoSupportListener(undoSupportHandler); undoRedoSupport.uninstall(); undoSupportHandler = null; undoRedoSupport = null; undoManager = null; editAdapter = null; } /** * A convenience method to ensure {@link UndoSupport} is being accessed * from the Event Dispatch Thread. */ private static void checkAccessThread() { if (!SwingUtilities.isEventDispatchThread()) throw new IllegalStateException("UndoRedoSupport must be accessed from the Swing Event Dispatch Thread, but was called on Thread \"" + Thread.currentThread().getName() + "\""); } /** * Listens for GL edits and responds by transforming them into Swing edits * and posting them to the given UndoManager. */ private class UndoSupportHandler implements UndoRedoSupport.Listener { public void undoableEditHappened(UndoRedoSupport.Edit edit) { undoManager.addEdit(editAdapter.evaluate(edit)); } } /** * The default strategy for transforming GL edits into Swing edits. */ private static class DefaultEditAdapter implements FunctionList.Function { public UndoableEdit evaluate(UndoRedoSupport.Edit edit) { return new EditAdapter(edit); } private static class EditAdapter extends AbstractUndoableEdit { private final UndoRedoSupport.Edit edit; public EditAdapter(UndoRedoSupport.Edit edit) { this.edit = edit; } @Override public void undo() { edit.undo(); } @Override public boolean canUndo() { return edit.canUndo(); } @Override public void redo() { edit.redo(); } @Override public boolean canRedo() { return edit.canRedo(); } } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/DefaultEventListModel.java0000644000175000017500000002050412106516356032336 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import java.awt.EventQueue; import java.util.ArrayList; import java.util.List; import javax.swing.JList; import javax.swing.ListModel; import javax.swing.SwingUtilities; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; /** * A DefaultEventListModel adapts an EventList to the ListModel interface making it * appropriate for use with a {@link JList}. Each element of the list * corresponds to an element in the {@link ListModel}. * *

      The DefaultEventListModel class is not thread-safe. Unless * otherwise noted, all methods are only safe to be called from the event * dispatch thread. To do this programmatically, use {@link SwingUtilities#invokeAndWait(Runnable)} * and wrap the source list (or some part of the source list's pipeline) using * {@link GlazedListsSwing#swingThreadProxyList(EventList)}. * * @see Bug 14 * @see Bug 146 * @see Bug 177 * @see Bug 228 * @see SwingUtilities#invokeAndWait(Runnable) * * @author Jesse Wilson * @author Holger Brands */ public class DefaultEventListModel implements ListEventListener, ListModel { /** the source EventList */ protected EventList source; /** indicator to dispose source list */ private boolean disposeSource; /** whom to notify of data changes */ private final List listeners = new ArrayList(); /** recycle the list data event to prevent unnecessary object creation */ protected final MutableListDataEvent listDataEvent = new MutableListDataEvent(this); /** * Creates a new model that contains all objects located in the given * source and reacts to any changes in the given * source. * * @param source the EventList that provides the elements */ public DefaultEventListModel(EventList source) { this(source, false); } /** * Creates a new model that contains all objects located in the given * source and reacts to any changes in the given * source. * * @param source the EventList that provides the elements * @param diposeSource true if the source list should be disposed when disposing * this model, false otherwise */ protected DefaultEventListModel(EventList source, boolean disposeSource) { this.source = source; this.disposeSource = disposeSource; this.source.addListEventListener(this); } /** * For implementing the ListEventListener interface. This sends changes * to the table which can repaint the table cells. It's checked that all natural * calls to this method arrive on the Swing thread. * *

      This always sends discrete changes for the complete size of the list. * It may be more efficient to implement a threshhold where a large list * of changes are grouped together as a single change. This is how the * ListTable accepts large change events. */ public void listChanged(ListEvent listChanges) { if (!EventQueue.isDispatchThread()) throw new IllegalStateException("Events to " + this.getClass().getSimpleName() + " must arrive on the EDT - consider adding GlazedListsSwing.swingThreadProxyList(source) somewhere in your list pipeline"); // build an "optimized" ListDataEvent describing the precise range of rows in the first block listChanges.nextBlock(); final int startIndex = listChanges.getBlockStartIndex(); final int endIndex = listChanges.getBlockEndIndex(); listDataEvent.setRange(startIndex, endIndex); final int changeType = listChanges.getType(); switch (changeType) { case ListEvent.INSERT: listDataEvent.setType(ListDataEvent.INTERVAL_ADDED); break; case ListEvent.DELETE: listDataEvent.setType(ListDataEvent.INTERVAL_REMOVED); break; case ListEvent.UPDATE: listDataEvent.setType(ListDataEvent.CONTENTS_CHANGED); break; } // if another block exists, fallback to using a generic "data changed" ListDataEvent if (listChanges.nextBlock()) { listDataEvent.setRange(0, Integer.MAX_VALUE); listDataEvent.setType(ListDataEvent.CONTENTS_CHANGED); } fireListDataEvent(listDataEvent); } /** * Returns the value at the specified index. * * @param index the requested index * @return the value at index */ public Object getElementAt(int index) { source.getReadWriteLock().readLock().lock(); try { return source.get(index); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * Gets the size of the list. */ public int getSize() { source.getReadWriteLock().readLock().lock(); try { return source.size(); } finally { source.getReadWriteLock().readLock().unlock(); } } /** * Registers the specified ListDataListener to receive updates whenever * this list changes. * *

      The specified ListDataListener must not save a * reference to the ListDataEvent beyond the end of the notification * method. This is because the ListDataEvent is re-used to increase * the performance of this implementation. */ public void addListDataListener(ListDataListener listDataListener) { listeners.add(listDataListener); } /** * Deregisters the specified ListDataListener from receiving updates * whenever this list changes. */ public void removeListDataListener(ListDataListener listDataListener) { listeners.remove(listDataListener); } /** * Notifies all ListDataListeners about one block of changes in the list. */ protected void fireListDataEvent(ListDataEvent listDataEvent) { // notify all listeners about the event for(int i = 0, n = listeners.size(); i < n; i++) { ListDataListener listDataListener = listeners.get(i); switch (listDataEvent.getType()) { case ListDataEvent.CONTENTS_CHANGED: listDataListener.contentsChanged(listDataEvent); break; case ListDataEvent.INTERVAL_ADDED: listDataListener.intervalAdded(listDataEvent); break; case ListDataEvent.INTERVAL_REMOVED: listDataListener.intervalRemoved(listDataEvent); break; } } } /** * Releases the resources consumed by this {@link DefaultEventListModel} so that it * may eventually be garbage collected. * *

      An {@link DefaultEventListModel} will be garbage collected without a call to * {@link #dispose()}, but not before its source {@link EventList} is garbage * collected. By calling {@link #dispose()}, you allow the {@link DefaultEventListModel} * to be garbage collected before its source {@link EventList}. This is * necessary for situations where an {@link DefaultEventListModel} is short-lived but * its source {@link EventList} is long-lived. * *

      Warning: It is an error * to call any method on an {@link DefaultEventListModel} after it has been disposed. * As such, this {@link DefaultEventListModel} should be detached from its * corresponding Component before it is disposed. */ public void dispose() { source.removeListEventListener(this); if (disposeSource) source.dispose(); // this encourages exceptions to be thrown if this model is incorrectly accessed again source = null; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/DefaultEventComboBoxModel.java0000644000175000017500000000603112106516356033132 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.EventList; import javax.swing.ComboBoxModel; import javax.swing.DefaultComboBoxModel; import javax.swing.SwingUtilities; import javax.swing.event.ListDataEvent; /** * A combo box model for displaying Glazed Lists in a combo box. * *

      The DefaultEventComboBoxModel class is not thread-safe. Unless * otherwise noted, all methods are only safe to be called from the event * dispatch thread. To do this programmatically, use {@link SwingUtilities#invokeAndWait(Runnable)} * and wrap the source list (or some part of the source list's pipeline) using * {@link GlazedListsSwing#swingThreadProxyList(EventList)}. * *

      The implementation of {@link #setSelectedItem} and {@link #getSelectedItem} * is not in any way tied to the contents of the list. * * @see Glazed Lists Tutorial * * @author Jesse Wilson */ public class DefaultEventComboBoxModel extends DefaultEventListModel implements ComboBoxModel { /** the currently selected item which typically belong to the source list */ private Object selected; /** * Creates a new combo box model that contains the elements of the given * source and tracks further changes made to it. * * @param source the EventList that provides the elements */ public DefaultEventComboBoxModel(EventList source) { this(source, false); } /** * Creates a new combo box model that contains the elements of the given * source and tracks further changes made to it. * * @param source the EventList that provides the elements * @param diposeSource true if the source list should be disposed when disposing * this model, false otherwise */ protected DefaultEventComboBoxModel(EventList source, boolean disposeSource) { super(source, disposeSource); } /** * Gets the currently selected item. */ public Object getSelectedItem() { return selected; } /** * Sets the currently selected item. * *

      The selection notification process is very much a hack. This fires * a ListDataEvent where the range is between -1 and -1. This is identical * to the notification process used by the {@link DefaultComboBoxModel}. */ public void setSelectedItem(Object selected) { // if the selected item isn't actually changing values, avoid the work if (this.selected == selected) return; this.selected = selected; listDataEvent.setRange(-1, -1); listDataEvent.setType(ListDataEvent.CONTENTS_CHANGED); fireListDataEvent(listDataEvent); } }././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/SearchEngineTextFieldMatcherEditor.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/SearchEngineTextFieldMatcherEd0000644000175000017500000000604612106516356033141 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.TextFilterator; import ca.odell.glazedlists.matchers.SearchEngineTextMatcherEditor; import ca.odell.glazedlists.matchers.TextMatcherEditor; import javax.swing.JTextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** * A MatcherEditor that matches Objects that contain the filter text located * within a {@link JTextField}. This {@link TextMatcherEditor} is directly * coupled with a JTextField that is meant to emulate a search engine's text * box. This matcher is fully concrete and is expected to be used * by Swing applications that want to present a text filtering interface * similar to that of Google and other search engines. * *

      The MatcherEditor registers itself as an {@link ActionListener} on the * given JTextField. If this MatcherEditor must be garbage collected before * the underlying JTextField, the listener can be unregistered by calling * {@link #dispose()}. * * @author James Lemieux */ public class SearchEngineTextFieldMatcherEditor extends SearchEngineTextMatcherEditor { /** the JTextField being observed for actions */ private final JTextField textField; /** the listener attached to the given {@link #textField} */ private final FilterHandler filterHandler = new FilterHandler(); /** * Creates a TextMatcherEditor bound to the given textField * with the given textFilterator. * * @param textField the text component that edits and supplies text filter values * @param textFilterator an object capable of producing Strings from the * objects being filtered. If textFilterator is * null then all filtered objects are expected to * implement {@link ca.odell.glazedlists.TextFilterable}. */ public SearchEngineTextFieldMatcherEditor(JTextField textField, TextFilterator textFilterator) { super(textFilterator); this.textField = textField; this.textField.addActionListener(this.filterHandler); // if the document is non-empty to begin with! refilter(textField.getText()); } /** * A cleanup method which stops this MatcherEditor from listening to * the underlying {@link JTextField}, thus freeing the * SearchEngineTextMatcherEditor to be garbage collected. */ public void dispose() { textField.removeActionListener(this.filterHandler); } /** * This class responds to ActionEvents from the JTextField by setting the * filter text of this TextMatcherEditor to the contents of the JTextField. */ private class FilterHandler implements ActionListener { public void actionPerformed(ActionEvent e) { refilter(textField.getText()); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/EventComboBoxModel.java0000644000175000017500000000447012106516356031632 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.EventList; import javax.swing.ComboBoxModel; import javax.swing.DefaultComboBoxModel; import javax.swing.event.ListDataEvent; /** * A combo box model for displaying Glazed Lists in a combo box. * *

      The implementation of {@link #setSelectedItem} and {@link #getSelectedItem} * is not in any way tied to the contents of the list. * * @see Glazed Lists Tutorial * * @deprecated Use {@link DefaultEventComboBoxModel} instead. This class will be removed in the GL * 2.0 release. The wrapping of the source list with an EDT safe list has been * determined to be undesirable (it is better for the user to provide their own EDT * safe list). * @author Jesse Wilson */ public class EventComboBoxModel extends EventListModel implements ComboBoxModel { /** the currently selected item which typically belong to the source list */ private Object selected; /** * Creates a new combo box model that contains the elements of the given * source and tracks further changes made to it. */ public EventComboBoxModel(EventList source) { super(source); } /** * Gets the currently selected item. */ public Object getSelectedItem() { return selected; } /** * Sets the currently selected item. * *

      The selection notification process is very much a hack. This fires * a ListDataEvent where the range is between -1 and -1. This is identical * to the notification process used by the {@link DefaultComboBoxModel}. */ public void setSelectedItem(Object selected) { // if the selected item isn't actually changing values, avoid the work if (this.selected == selected) return; this.selected = selected; listDataEvent.setRange(-1, -1); listDataEvent.setType(ListDataEvent.CONTENTS_CHANGED); fireListDataEvent(listDataEvent); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/EventTableColumnModel.java0000644000175000017500000004203712106516356032330 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.TransformedList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import ca.odell.glazedlists.impl.IteratorAsEnumeration; import javax.swing.*; import javax.swing.event.*; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import java.util.Enumeration; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; /** * A {@link TableColumnModel} that holds an {@link EventList}. Each element of * the list corresponds to a {@link TableColumn} in the model. * *

      The EventTableColumnModel class is not thread-safe. * Unless otherwise noted, all methods are only safe to be called from the * event dispatch thread. To do this programmatically, use * {@link SwingUtilities#invokeAndWait(Runnable)}. * * @author James Lemieux */ public class EventTableColumnModel implements TableColumnModel, PropertyChangeListener, ListSelectionListener, ListEventListener { /** the proxy moves events to the Swing Event Dispatch thread */ protected TransformedList swingThreadSource; /** true indicates that disposing this TableColumnModel should dispose of the swingThreadSource as well */ private final boolean disposeSwingThreadSource; /** list of TableColumnModelListeners */ private final EventListenerList listenerList = new EventListenerList(); /** change event (only one needed) */ private final transient ChangeEvent changeEvent = new ChangeEvent(this); /** model for keeping track of column selections */ private ListSelectionModel selectionModel; /** column selection allowed in this column model */ private boolean columnSelectionAllowed; /** width of the margin between each column */ private int columnMargin; /** a local cache of the combined width of all columns */ private int totalColumnWidth; /** * Creates a new model that contains the {@link TableColumn} objects from * the given source. Changes to the source are * reflected in this model. */ public EventTableColumnModel(EventList source) { setSelectionModel(createSelectionModel()); setColumnMargin(1); invalidateWidthCache(); setColumnSelectionAllowed(false); // lock the source list for reading since we want to prevent writes // from occurring until we fully initialize this EventTableColumnModel source.getReadWriteLock().readLock().lock(); try { // ensure all of the TableColumns are non-null for (int i = 0, n = source.size(); i < n; i++) { if (source.get(i) == null) throw new IllegalStateException("null TableColumn objects are not allowed in EventTableColumnModel"); } // start listening to each of the TableColumns for property changes that may resize the table header for (int i = 0, n = source.size(); i < n; i++) source.get(i).addPropertyChangeListener(this); disposeSwingThreadSource = !GlazedListsSwing.isSwingThreadProxyList(source); swingThreadSource = disposeSwingThreadSource ? GlazedListsSwing.swingThreadProxyList(source) : (TransformedList) source; swingThreadSource.addListEventListener(this); } finally { source.getReadWriteLock().readLock().unlock(); } } /** @inheritDoc */ public void addColumn(TableColumn column) { swingThreadSource.getReadWriteLock().writeLock().lock(); try { swingThreadSource.add((T) column); } finally { swingThreadSource.getReadWriteLock().writeLock().unlock(); } } /** @inheritDoc */ public void removeColumn(TableColumn column) { swingThreadSource.getReadWriteLock().writeLock().lock(); try { swingThreadSource.remove(column); } finally { swingThreadSource.getReadWriteLock().writeLock().unlock(); } } /** @inheritDoc */ public void moveColumn(int columnIndex, int newIndex) { if (columnIndex < 0 || columnIndex >= getColumnCount()) throw new IllegalArgumentException("columnIndex out of range"); if (newIndex < 0 || newIndex >= getColumnCount()) throw new IllegalArgumentException("newIndex out of range"); // If the column has not yet moved far enough to change positions // post the event anyway, the "draggedDistance" property of the // tableHeader will say how far the column has been dragged. // Here we are really trying to get the best out of an // API that could do with some rethinking. We preserve backward // compatibility by slightly bending the meaning of these methods. if (columnIndex == newIndex) { fireColumnMoved(new TableColumnModelEvent(this, columnIndex, newIndex)); return; } swingThreadSource.getReadWriteLock().writeLock().lock(); try { final boolean selected = selectionModel.isSelectedIndex(columnIndex); swingThreadSource.add(newIndex, swingThreadSource.remove(columnIndex)); // preserve the selection after the move if one existed if (selected) selectionModel.addSelectionInterval(newIndex, newIndex); } finally { swingThreadSource.getReadWriteLock().writeLock().unlock(); } } /** @inheritDoc */ public void setColumnMargin(int newMargin) { if (newMargin != columnMargin) { columnMargin = newMargin; fireColumnMarginChanged(); } } /** @inheritDoc */ public int getColumnMargin() { return columnMargin; } /** @inheritDoc */ public int getColumnCount() { swingThreadSource.getReadWriteLock().readLock().lock(); try { return swingThreadSource.size(); } finally { swingThreadSource.getReadWriteLock().readLock().unlock(); } } /** @inheritDoc */ public Enumeration getColumns() { return new IteratorAsEnumeration(swingThreadSource.iterator()); } /** @inheritDoc */ public int getColumnIndex(Object identifier) { if (identifier == null) throw new IllegalArgumentException("identifier is null"); swingThreadSource.getReadWriteLock().readLock().lock(); try { for (int i = 0, n = swingThreadSource.size(); i < n; i++) { if (identifier.equals(swingThreadSource.get(i).getIdentifier())) return i; } throw new IllegalArgumentException("Identifier not found"); } finally { swingThreadSource.getReadWriteLock().readLock().unlock(); } } /** @inheritDoc */ public TableColumn getColumn(int columnIndex) { swingThreadSource.getReadWriteLock().readLock().lock(); try { return swingThreadSource.get(columnIndex); } finally { swingThreadSource.getReadWriteLock().readLock().unlock(); } } /** @inheritDoc */ public int getColumnIndexAtX(int x) { if (x < 0) return -1; swingThreadSource.getReadWriteLock().readLock().lock(); try { for (int i = 0, n = swingThreadSource.size(); i < n; i++) { TableColumn column = swingThreadSource.get(i); x = x - column.getWidth(); if (x < 0) return i; } } finally { swingThreadSource.getReadWriteLock().readLock().unlock(); } return -1; } /** @inheritDoc */ public int getTotalColumnWidth() { if (totalColumnWidth == -1) recalcWidthCache(); return totalColumnWidth; } /** * Recalculates the total combined width of all columns. */ private void recalcWidthCache() { swingThreadSource.getReadWriteLock().readLock().lock(); try { totalColumnWidth = 0; for (int i = 0, n = swingThreadSource.size(); i < n; i++) totalColumnWidth += swingThreadSource.get(i).getWidth(); } finally { swingThreadSource.getReadWriteLock().readLock().unlock(); } } /** * Mark the cached value of the total width of all columns as dirty and in * need of being recalculated. */ private void invalidateWidthCache() { totalColumnWidth = -1; } /** @inheritDoc */ public void setColumnSelectionAllowed(boolean flag) { columnSelectionAllowed = flag; } /** @inheritDoc */ public boolean getColumnSelectionAllowed() { return columnSelectionAllowed; } /** @inheritDoc */ public int[] getSelectedColumns() { if (selectionModel != null) { int iMin = selectionModel.getMinSelectionIndex(); int iMax = selectionModel.getMaxSelectionIndex(); if (iMin == -1 || iMax == -1) return new int[0]; int[] rvTmp = new int[1 + (iMax - iMin)]; int n = 0; for (int i = iMin; i <= iMax; i++) { if (selectionModel.isSelectedIndex(i)) { rvTmp[n++] = i; } } int[] rv = new int[n]; System.arraycopy(rvTmp, 0, rv, 0, n); return rv; } return new int[0]; } /** @inheritDoc */ public int getSelectedColumnCount() { if (selectionModel != null) { int iMin = selectionModel.getMinSelectionIndex(); int iMax = selectionModel.getMaxSelectionIndex(); int count = 0; for (int i = iMin; i <= iMax; i++) { if (selectionModel.isSelectedIndex(i)) count++; } return count; } return 0; } /** @inheritDoc */ public void setSelectionModel(ListSelectionModel newModel) { if (newModel == null) throw new IllegalArgumentException("newModel may not be null"); if (newModel != selectionModel) { if (selectionModel != null) selectionModel.removeListSelectionListener(this); selectionModel = newModel; selectionModel.addListSelectionListener(this); } } /** @inheritDoc */ public ListSelectionModel getSelectionModel() { return selectionModel; } /** @inheritDoc */ public void addColumnModelListener(TableColumnModelListener listener) { listenerList.add(TableColumnModelListener.class, listener); } /** @inheritDoc */ public void removeColumnModelListener(TableColumnModelListener listener) { listenerList.remove(TableColumnModelListener.class, listener); } /** * Watch for changes to the column width or preferred column width and * trigger a relayout of the table header when they change. */ public void propertyChange(PropertyChangeEvent evt) { String name = evt.getPropertyName(); if (name == "width" || name == "preferredWidth") { invalidateWidthCache(); fireColumnMarginChanged(); } } public void valueChanged(ListSelectionEvent e) { fireColumnSelectionChanged(e); } public void listChanged(ListEvent listChanges) { // arbitrary changes have occurred so we begin by invalidating the cached total width of all TableColumns invalidateWidthCache(); while (listChanges.next()) { final int index = listChanges.getIndex(); final int changeType = listChanges.getType(); if (changeType == ListEvent.DELETE) { if (selectionModel != null) selectionModel.removeIndexInterval(index, index); final TableColumn oldColumn = listChanges.getOldValue(); oldColumn.removePropertyChangeListener(this); fireColumnRemoved(new TableColumnModelEvent(this, index, 0)); } else if (changeType == ListEvent.INSERT) { final TableColumn newColumn = listChanges.getSourceList().get(index); if (newColumn == null) throw new IllegalStateException("null TableColumn objects are not allowed in EventTableColumnModel"); newColumn.addPropertyChangeListener(this); fireColumnAdded(new TableColumnModelEvent(this, 0, getColumnCount() - 1)); } else if (changeType == ListEvent.UPDATE) { final TableColumn oldColumn = listChanges.getOldValue(); final TableColumn newColumn = listChanges.getSourceList().get(index); if (newColumn == null) throw new IllegalStateException("null TableColumn objects are not allowed in EventTableColumnModel"); if (oldColumn != newColumn) { oldColumn.removePropertyChangeListener(this); newColumn.addPropertyChangeListener(this); } fireColumnMoved(new TableColumnModelEvent(this, index, index)); } } } /** * Releases the resources consumed by this {@link EventTableColumnModel} so that it * may eventually be garbage collected. * *

      An {@link EventTableColumnModel} will be garbage collected without a * call to {@link #dispose()}, but not before its source {@link EventList} * is garbage collected. By calling {@link #dispose()}, you allow the * {@link EventTableColumnModel} to be garbage collected before its source * {@link EventList}. This is necessary for situations where an * {@link EventTableColumnModel} is short-lived but its source * {@link EventList} is long-lived. * *

      Warning: It is an error * to call any method on an {@link EventTableColumnModel} after it has been * disposed. */ public void dispose() { swingThreadSource.getReadWriteLock().readLock().lock(); try { // stop listening to each of the TableColumns for property changes for (int i = 0, n = swingThreadSource.size(); i < n; i++) swingThreadSource.get(i).removePropertyChangeListener(this); swingThreadSource.removeListEventListener(this); // if we created the swingThreadSource then we must also dispose it if (disposeSwingThreadSource) swingThreadSource.dispose(); } finally { swingThreadSource.getReadWriteLock().readLock().unlock(); } // this encourages exceptions to be thrown if this model is incorrectly accessed again swingThreadSource = null; } /** * Creates a new default list selection model. */ protected ListSelectionModel createSelectionModel() { return new DefaultListSelectionModel(); } // // Convenience methods to fire types of TableColumnModelEvent objects to // registered TableColumnModelListeners. // protected void fireColumnAdded(TableColumnModelEvent e) { final Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == TableColumnModelListener.class) ((TableColumnModelListener) listeners[i + 1]).columnAdded(e); } } protected void fireColumnRemoved(TableColumnModelEvent e) { final Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == TableColumnModelListener.class) ((TableColumnModelListener) listeners[i + 1]).columnRemoved(e); } } protected void fireColumnMoved(TableColumnModelEvent e) { final Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == TableColumnModelListener.class) ((TableColumnModelListener) listeners[i + 1]).columnMoved(e); } } protected void fireColumnSelectionChanged(ListSelectionEvent e) { final Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == TableColumnModelListener.class) ((TableColumnModelListener) listeners[i + 1]).columnSelectionChanged(e); } } protected void fireColumnMarginChanged() { final Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == TableColumnModelListener.class) ((TableColumnModelListener) listeners[i + 1]).columnMarginChanged(changeEvent); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/swing/MutableTableModelEvent.java0000644000175000017500000000441012106516356032455 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.swing; // the core Glazed Lists packages import ca.odell.glazedlists.event.ListEvent; import javax.swing.event.TableModelEvent; import javax.swing.table.TableModel; /** * A frequently changing table or a table that changes in several * places simultaneously will cause several TableModelEvents to * be created. This hurts speed. This is a mutable table model * event, so that the object can be recycled. * * @author Jesse Wilson */ public final class MutableTableModelEvent extends TableModelEvent { /** * Constructors simply call the same on the superclass. */ public MutableTableModelEvent(TableModel source) { super(source); } /** * Changes this table model event. The event must not * be changed while it is being viewed by a listener. */ public void setRange(int firstRow, int lastRow) { this.firstRow = firstRow; this.lastRow = lastRow; } public void setType(int type) { this.type = type; } /** * Sets the table model event to notify that the table structure * has changed. */ public void setStructureChanged() { firstRow = HEADER_ROW; lastRow = HEADER_ROW; column = ALL_COLUMNS; type = UPDATE; } /** * Sets the table model event to notify that all table data * has changed. */ public void setAllDataChanged() { firstRow = 0; lastRow = Integer.MAX_VALUE; column = ALL_COLUMNS; type = UPDATE; } /** * Sets the table model event to reflect the specified changes. */ public void setValues(int startIndex, int endIndex, int listChangeType) { this.firstRow = startIndex; this.lastRow = endIndex; if(listChangeType == ListEvent.INSERT) type = INSERT; else if(listChangeType == ListEvent.DELETE) type = DELETE; else if(listChangeType == ListEvent.UPDATE) type = UPDATE; column = ALL_COLUMNS; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/util/0000755000175000017500000000000012106516400025103 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/util/concurrent/0000755000175000017500000000000012106516400027265 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/util/concurrent/LockFactory.java0000644000175000017500000000377412106516400032363 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.util.concurrent; /** * This factory provides an implementation of {@link Lock} that is optimized * for the current Java Virtual Machine. * * @author Rob Eden * @author James Lemieux */ public interface LockFactory { /** The Lock factory for this JVM. */ public static final LockFactory DEFAULT = new DelegateLockFactory(); /** * Create a {@link ReadWriteLock}. */ public ReadWriteLock createReadWriteLock(); /** * Create a {@link Lock}. */ public Lock createLock(); } /** * An implementation of {@link LockFactory} that detects and delegates to * a JVM specific LockFactory implementation optimized for the current JVM. */ class DelegateLockFactory implements LockFactory { /** The true JVM-specific LockFactory to which we delegate. */ private LockFactory delegate; DelegateLockFactory() { try { // if the J2SE 5.0 ReadWriteLock class can be loaded, we're running on a JDK 1.5 VM Class.forName("java.util.concurrent.locks.ReadWriteLock"); // and if we can load our J2SE 5.0 LockFactory implementation // (i.e. it's not a Glazed Lists 1.4 implementation running on a JDK 1.5 VM) // then use the J2SE 5.0 LockFactory implementation delegate = (LockFactory) Class.forName("ca.odell.glazedlists.impl.java15.J2SE50LockFactory").newInstance(); } catch (Throwable t) { // otherwise fall back to a J2SE 1.4 LockFactory delegate = new J2SE14LockFactory(); } } public ReadWriteLock createReadWriteLock() { return delegate.createReadWriteLock(); } public Lock createLock() { return delegate.createLock(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/util/concurrent/Lock.java0000644000175000017500000000227512106516400031026 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.util.concurrent; /** * A lock is a tool for controlling access to a shared resource by multiple threads. * *

      This interface is a back-port of the {@link java.util.concurrent.locks.Lock} * class that first appeared in J2SE 1.5. Due to a requirement for sophisticated * concurrency, this interface has been back-ported for use in J2SE 1.4 (and greater). * It shares similar method signatures to be consistent with the J2SE 1.5 API. * * @see java.util.concurrent.locks.Lock * @see Lock * * @author Jesse Wilson */ public interface Lock { /** * Acquires the lock. */ public void lock(); /** * Acquires the lock only if it is free at the time of invocation. */ public boolean tryLock(); /** * Releases the lock. */ public void unlock(); }././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/util/concurrent/J2SE12LockFactory.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/util/concurrent/J2SE12LockFactory.ja0000644000175000017500000004057312106516400032621 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.util.concurrent; import java.util.HashMap; import java.util.Map; /* File: ReentrantWriterPreferenceReadWriteLock.java Originally written by Doug Lea and released into the public domain. This may be used for any purposes whatsoever without acknowledgment. Thanks for the assistance and support of Sun Microsystems Labs, and everyone contributing, testing, and using this code. History: Date Who What 26aug1998 dl Create public version 7sep2000 dl Readers are now also reentrant 19jan2001 dl Allow read->write upgrades if the only reader 10dec2002 dl Throw IllegalStateException on extra release */ /** * A writer-preference ReadWriteLock that allows both readers and * writers to reacquire * read or write locks in the style of a ReentrantLock. * Readers are not allowed until all write locks held by * the writing thread have been released. * Among other applications, reentrancy can be useful when * write locks are held during calls or callbacks to methods that perform * reads under read locks. *

      * Sample usage. Here is a code sketch showing how to exploit * reentrancy to perform lock downgrading after updating a cache: *

       * class CachedData {
       *   Object data;
       *   volatile boolean cacheValid;
       *   ReentrantWriterPreferenceReadWriteLock rwl = ...
       *
       *   void processCachedData() {
       *     rwl.readLock().acquire();
       *     if (!cacheValid) {
       *
       *        // upgrade lock:
       *        rwl.readLock().release();   // must release first to obtain writelock
       *        rwl.writeLock().acquire();
       *        if (!cacheValid) { // recheck
       *          data = ...
       *          cacheValid = true;
       *        }
       *        // downgrade lock
       *        rwl.readLock().acquire();  // reacquire read without giving up lock
       *        rwl.writeLock().release(); // release write, still hold read
       *     }
       *
       *     use(data);
       *     rwl.readLock().release();
       *   }
       * }
       * 
      * * *

      [ Introduction to this package. ] */ class ReentrantWriterPreferenceReadWriteLock extends WriterPreferenceReadWriteLock { /** Number of acquires on write lock by activeWriter_ thread */ protected long writeHolds_ = 0; /** Number of acquires on read lock by any reader thread */ protected Map readers_ = new HashMap(); /** cache/reuse the special Integer value one to speed up readlocks */ protected static final Integer IONE = new Integer(1); @Override protected boolean allowReader() { return (activeWriter_ == null && waitingWriters_ == 0) || activeWriter_ == Thread.currentThread(); } @Override protected synchronized boolean startRead() { Thread t = Thread.currentThread(); Integer c = readers_.get(t); if (c != null) { // already held -- just increment hold count readers_.put(t, new Integer(c.intValue()+1)); ++activeReaders_; return true; } else if (allowReader()) { readers_.put(t, IONE); ++activeReaders_; return true; } else return false; } @Override protected synchronized boolean startWrite() { if (activeWriter_ == Thread.currentThread()) { // already held; re-acquire ++writeHolds_; return true; } else if (writeHolds_ == 0) { if (activeReaders_ == 0 || (readers_.size() == 1 && readers_.get(Thread.currentThread()) != null)) { activeWriter_ = Thread.currentThread(); writeHolds_ = 1; return true; } else return false; } else return false; } @Override protected synchronized Signaller endRead() { Thread t = Thread.currentThread(); Integer c = readers_.get(t); if (c == null) throw new IllegalMonitorStateException("Attempted to unlock a readlock which was not locked. Please ensure the readlock is always locked and unlocked symmetrically."); --activeReaders_; if (c != IONE) { // more than one hold; decrement count int h = c.intValue()-1; Integer ih = (h == 1) ? IONE : new Integer(h); readers_.put(t, ih); return null; } else { readers_.remove(t); if (writeHolds_ > 0) // a write lock is still held by current thread return null; else if (activeReaders_ == 0 && waitingWriters_ > 0) return writerLock_; else return null; } } @Override protected synchronized Signaller endWrite() { if (activeWriter_ == null) throw new IllegalMonitorStateException("Attempted to unlock a writelock which was not locked. Please ensure the writelock is always locked and unlocked symmetrically."); --writeHolds_; if (writeHolds_ > 0) // still being held return null; else { activeWriter_ = null; if (waitingReaders_ > 0 && allowReader()) return readerLock_; else if (waitingWriters_ > 0) return writerLock_; else return null; } } } /* File: WriterPreferenceReadWriteLock.java Originally written by Doug Lea and released into the public domain. This may be used for any purposes whatsoever without acknowledgment. Thanks for the assistance and support of Sun Microsystems Labs, and everyone contributing, testing, and using this code. History: Date Who What 11Jun1998 dl Create public version 5Aug1998 dl replaced int counters with longs 25aug1998 dl record writer thread 3May1999 dl add notifications on interrupt/timeout */ /** * A ReadWriteLock that prefers waiting writers over * waiting readers when there is contention. This class * is adapted from the versions described in CPJ, improving * on the ones there a bit by segregating reader and writer * wait queues, which is typically more efficient. *

      * The locks are NOT reentrant. In particular, * even though it may appear to usually work OK, * a thread holding a read lock should not attempt to * re-acquire it. Doing so risks lockouts when there are * also waiting writers. *

      [ Introduction to this package. ] */ class WriterPreferenceReadWriteLock implements ReadWriteLock { protected long activeReaders_ = 0; protected Thread activeWriter_ = null; protected long waitingReaders_ = 0; protected long waitingWriters_ = 0; protected final ReaderLock readerLock_ = new ReaderLock(); protected final WriterLock writerLock_ = new WriterLock(); public Lock writeLock() { return writerLock_; } public Lock readLock() { return readerLock_; } /* A bunch of small synchronized methods are needed to allow communication from the Lock objects back to this object, that serves as controller */ protected synchronized void cancelledWaitingReader() { --waitingReaders_; } protected synchronized void cancelledWaitingWriter() { --waitingWriters_; } /** Override this method to change to reader preference */ protected boolean allowReader() { return activeWriter_ == null && waitingWriters_ == 0; } protected synchronized boolean startRead() { boolean allowRead = allowReader(); if (allowRead) ++activeReaders_; return allowRead; } protected synchronized boolean startWrite() { // The allowWrite expression cannot be modified without // also changing startWrite, so is hard-wired boolean allowWrite = (activeWriter_ == null && activeReaders_ == 0); if (allowWrite) activeWriter_ = Thread.currentThread(); return allowWrite; } /* Each of these variants is needed to maintain atomicity of wait counts during wait loops. They could be made faster by manually inlining each other. We hope that compilers do this for us though. */ protected synchronized boolean startReadFromNewReader() { boolean pass = startRead(); if (!pass) ++waitingReaders_; return pass; } protected synchronized boolean startWriteFromNewWriter() { boolean pass = startWrite(); if (!pass) ++waitingWriters_; return pass; } protected synchronized boolean startReadFromWaitingReader() { boolean pass = startRead(); if (pass) --waitingReaders_; return pass; } protected synchronized boolean startWriteFromWaitingWriter() { boolean pass = startWrite(); if (pass) --waitingWriters_; return pass; } /** * Called upon termination of a read. * Returns the object to signal to wake up a waiter, or null if no such */ protected synchronized Signaller endRead() { if (--activeReaders_ == 0 && waitingWriters_ > 0) return writerLock_; else return null; } /** * Called upon termination of a write. * Returns the object to signal to wake up a waiter, or null if no such */ protected synchronized Signaller endWrite() { activeWriter_ = null; if (waitingReaders_ > 0 && allowReader()) return readerLock_; else if (waitingWriters_ > 0) return writerLock_; else return null; } /** * Reader and Writer requests are maintained in two different * wait sets, by two different objects. These objects do not * know whether the wait sets need notification since they * don't know preference rules. So, each supports a * method that can be selected by main controlling object * to perform the notifications. This base class simplifies mechanics. */ protected abstract class Signaller { // base for ReaderLock and WriterLock abstract void signalWaiters(); } protected class ReaderLock extends Signaller implements Lock { public void lock() { //if (Thread.interrupted()) throw new RuntimeException("Lock interrupted", new InterruptedException()); InterruptedException ie = null; synchronized(this) { if (!startReadFromNewReader()) { for (;;) { try { ReaderLock.this.wait(); if (startReadFromWaitingReader()) return; } catch(InterruptedException ex){ cancelledWaitingReader(); ie = ex; break; } } } } if (ie != null) { // fall through outside synch on interrupt. // This notification is not really needed here, // but may be in plausible subclasses writerLock_.signalWaiters(); throw new RuntimeException("Lock interrupted, " + ie); } } public void unlock() { Signaller s = endRead(); if (s != null) s.signalWaiters(); } @Override synchronized void signalWaiters() { ReaderLock.this.notifyAll(); } public boolean tryLock() { long msecs = 0; //if (Thread.interrupted()) throw new RuntimeException("Lock interrupted", new InterruptedException()); InterruptedException ie = null; synchronized(this) { if (msecs <= 0) return startRead(); else if (startReadFromNewReader()) return true; else { long waitTime = msecs; long start = System.currentTimeMillis(); for (;;) { try { ReaderLock.this.wait(waitTime); } catch(InterruptedException ex){ cancelledWaitingReader(); ie = ex; break; } if (startReadFromWaitingReader()) return true; else { waitTime = msecs - (System.currentTimeMillis() - start); if (waitTime <= 0) { cancelledWaitingReader(); break; } } } } } // safeguard on interrupt or timeout: writerLock_.signalWaiters(); if (ie != null) throw new RuntimeException("Lock interrupted, " + ie); else return false; // timed out } } protected class WriterLock extends Signaller implements Lock { public void lock() { //if (Thread.interrupted()) throw new RuntimeException("Lock interrupted", new InterruptedException()); InterruptedException ie = null; synchronized(this) { if (!startWriteFromNewWriter()) { for (;;) { try { WriterLock.this.wait(); if (startWriteFromWaitingWriter()) return; } catch(InterruptedException ex){ cancelledWaitingWriter(); WriterLock.this.notify(); ie = ex; break; } } } } if (ie != null) { // Fall through outside synch on interrupt. // On exception, we may need to signal readers. // It is not worth checking here whether it is strictly necessary. readerLock_.signalWaiters(); throw new RuntimeException("Lock interrupted, " + ie); } } public void unlock(){ Signaller s = endWrite(); if (s != null) s.signalWaiters(); } @Override synchronized void signalWaiters() { WriterLock.this.notify(); } public boolean tryLock() { long msecs = 0; //if (Thread.interrupted()) throw new RuntimeException("Lock interrupted", new InterruptedException()); InterruptedException ie = null; synchronized(this) { if (msecs <= 0) return startWrite(); else if (startWriteFromNewWriter()) return true; else { long waitTime = msecs; long start = System.currentTimeMillis(); for (;;) { try { WriterLock.this.wait(waitTime); } catch(InterruptedException ex){ cancelledWaitingWriter(); WriterLock.this.notify(); ie = ex; break; } if (startWriteFromWaitingWriter()) return true; else { waitTime = msecs - (System.currentTimeMillis() - start); if (waitTime <= 0) { cancelledWaitingWriter(); WriterLock.this.notify(); break; } } } } } readerLock_.signalWaiters(); if (ie != null) throw new RuntimeException("Lock interrupted, " + ie); else return false; // timed out } } }././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/util/concurrent/J2SE14LockFactory.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/util/concurrent/J2SE14LockFactory.ja0000644000175000017500000000257512106516400032623 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.util.concurrent; /** * An implementation of {@link ca.odell.glazedlists.util.concurrent.LockFactory} that has been derived from Doug Lea's * util.concurrent. */ final class J2SE14LockFactory implements LockFactory { /** * Create a {@link ca.odell.glazedlists.util.concurrent.ReadWriteLock}. * *

      The default implementation returns an implementation that has been * derived from Doug Lea's util.concurrent. */ public ReadWriteLock createReadWriteLock() { return new J2SE14ReadWriteLock(); } /** * Create a {@link ca.odell.glazedlists.util.concurrent.Lock}. * *

      The default implementation returns an implementation that has been * derived from Doug Lea's util.concurrent. */ public Lock createLock() { return new J2SE14ReadWriteLock().writeLock(); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/util/concurrent/ReadWriteLock.java0000644000175000017500000000246012106516400032631 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.util.concurrent; /** * A ReadWriteLock maintains a pair of associated locks, one for read-only operations * and one for writing. The read lock may be held simultaneously by multiple reader * threads, so long as there are no writers. The write lock is exclusive. * *

      This interface is a back-port of the {@link java.util.concurrent.locks.ReadWriteLock} * class that first appeared in J2SE 1.5. Due to a requirement for sophisticated * concurrency, this interface has been back-ported for use in J2SE 1.4 (and greater). * It shares similar method signatures to be consistent with the J2SE 1.5 API. * * @see java.util.concurrent.locks.ReadWriteLock * @see ReadWriteLock * * @author Jesse Wilson */ public interface ReadWriteLock { /** * Return the lock used for reading. */ public Lock readLock(); /** * Return the lock used for writing. */ public Lock writeLock(); }././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/util/concurrent/J2SE14ReadWriteLock.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/util/concurrent/J2SE14ReadWriteLock.0000644000175000017500000010264512106516400032566 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.util.concurrent; import ca.odell.glazedlists.impl.SerializedReadWriteLock; import java.io.ObjectStreamException; import java.util.HashMap; /** * An implementation of {@link LockFactory} that has been derived from * backport-util-concurrent. * *

      An implementation of {@link ReadWriteLock} supporting similar * semantics to {@link java.util.concurrent.locks.ReentrantLock}. *

      This class has the following properties: * *

        *
      • Acquisition order * *

        The order of entry * to the read and write lock is unspecified, subject to reentrancy * constraints. A nonfair lock that is continously contended may * indefinitely postpone one or more reader or writer threads, but * will normally have higher throughput than a fair lock. *

        * * DEPARTURE FROM java.util.concurrent: this implementation impose * a writer-preferrence and thus its acquisition order may be different * than in java.util.concurrent. * *

      • Reentrancy * *

        This lock allows both readers and writers to reacquire read or * write locks in the style of a {@code ReentrantLock}. Non-reentrant * readers are not allowed until all write locks held by the writing * thread have been released. * *

        Additionally, a writer can acquire the read lock, but not * vice-versa. Among other applications, reentrancy can be useful * when write locks are held during calls or callbacks to methods that * perform reads under read locks. If a reader tries to acquire the * write lock it will never succeed. * *

      • Lock downgrading *

        Reentrancy also allows downgrading from the write lock to a read lock, * by acquiring the write lock, then the read lock and then releasing the * write lock. However, upgrading from a read lock to the write lock is * not possible. * *

      • Interruption of lock acquisition *

        The read lock and write lock both support interruption during lock * acquisition. * *

      • {@code Condition} support *

        The write lock provides a {@code Condition} implementation that * behaves in the same way, with respect to the write lock, as the * {@code Condition} implementation provided by * {@code ReentrantLock#newCondition} does for {@code ReentrantLock}. * This {@code Condition} can, of course, only be used with the write lock. * *

        The read lock does not support a {@code Condition} and * {@code readLock().newCondition()} throws * {@code UnsupportedOperationException}. * *

      • Instrumentation *

        This class supports methods to determine whether locks * are held or contended. These methods are designed for monitoring * system state, not for synchronization control. *

      * *

      Serialization of this class behaves in the same way as built-in * locks: a deserialized lock is in the unlocked state, regardless of * its state when serialized. * *

      Sample usages. Here is a code sketch showing how to exploit * reentrancy to perform lock downgrading after updating a cache (exception * handling is elided for simplicity): *

       * class CachedData {
       *   Object data;
       *   volatile boolean cacheValid;
       *   ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
       *
       *   void processCachedData() {
       *     rwl.readLock().lock();
       *     if (!cacheValid) {
       *        // Must release read lock before acquiring write lock
       *        rwl.readLock().unlock();
       *        rwl.writeLock().lock();
       *        // Recheck state because another thread might have acquired
       *        //   write lock and changed state before we did.
       *        if (!cacheValid) {
       *          data = ...
       *          cacheValid = true;
       *        }
       *        // Downgrade by acquiring read lock before releasing write lock
       *        rwl.readLock().lock();
       *        rwl.writeLock().unlock(); // Unlock write, still hold read
       *     }
       *
       *     use(data);
       *     rwl.readLock().unlock();
       *   }
       * }
       * 
      * * ReentrantReadWriteLocks can be used to improve concurrency in some * uses of some kinds of Collections. This is typically worthwhile * only when the collections are expected to be large, accessed by * more reader threads than writer threads, and entail operations with * overhead that outweighs synchronization overhead. For example, here * is a class using a TreeMap that is expected to be large and * concurrently accessed. * *
      {@code
       * class RWDictionary {
       *    private final Map m = new TreeMap();
       *    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
       *    private final Lock r = rwl.readLock();
       *    private final Lock w = rwl.writeLock();
       *
       *    public Data get(String key) {
       *        r.lock();
       *        try { return m.get(key); }
       *        finally { r.unlock(); }
       *    }
       *    public String[] allKeys() {
       *        r.lock();
       *        try { return m.keySet().toArray(); }
       *        finally { r.unlock(); }
       *    }
       *    public Data put(String key, Data value) {
       *        w.lock();
       *        try { return m.put(key, value); }
       *        finally { w.unlock(); }
       *    }
       *    public void clear() {
       *        w.lock();
       *        try { m.clear(); }
       *        finally { w.unlock(); }
       *    }
       * }}
      * *

      Implementation Notes

      * *

      This lock supports a maximum of 65535 recursive write locks * and 65535 read locks. Attempts to exceed these limits result in * {@link Error} throws from locking methods. * * @since 1.5 * @author Doug Lea */ public class J2SE14ReadWriteLock implements ReadWriteLock, java.io.Serializable { private static final long serialVersionUID = -3463448656717690166L; final ReadLock readerLock_ = new ReadLock(this); final WriteLock writerLock_ = new WriteLock(this); final Sync sync; /** * Creates a new {@code ReentrantReadWriteLock} with * default (nonfair) ordering properties. */ public J2SE14ReadWriteLock() { this.sync = new NonfairSync(); } public Lock writeLock() { return writerLock_; } public Lock readLock() { return readerLock_; } /** Use a {@link SerializedReadWriteLock} as placeholder in the serialization stream. */ private Object writeReplace() throws ObjectStreamException { return new SerializedReadWriteLock(); } /** * Synchronization implementation for ReentrantReadWriteLock. * Subclassed into fair and nonfair versions. */ private abstract static class Sync implements java.io.Serializable { private static final int NONE = 0; private static final int READER = 1; private static final int WRITER = 2; transient int activeReaders_ = 0; transient Thread activeWriter_ = null; transient int waitingReaders_ = 0; transient int waitingWriters_ = 0; /** Number of acquires on write lock by activeWriter_ thread **/ transient int writeHolds_ = 0; /** Number of acquires on read lock by any reader thread **/ transient HashMap readers_ = new HashMap(); /** cache/reuse the special Integer value one to speed up readlocks **/ static final Integer IONE = new Integer(1); Sync() {} /* Each of these variants is needed to maintain atomicity of wait counts during wait loops. They could be made faster by manually inlining each other. We hope that compilers do this for us though. */ synchronized boolean startReadFromNewReader() { boolean pass = startRead(); if (!pass) ++waitingReaders_; return pass; } synchronized boolean startWriteFromNewWriter() { boolean pass = startWrite(); if (!pass) ++waitingWriters_; return pass; } synchronized boolean startReadFromWaitingReader() { boolean pass = startRead(); if (pass) --waitingReaders_; return pass; } synchronized boolean startWriteFromWaitingWriter() { boolean pass = startWrite(); if (pass) --waitingWriters_; return pass; } /* A bunch of small synchronized methods are needed to allow communication from the Lock objects back to this object, that serves as controller */ synchronized void cancelledWaitingReader() { --waitingReaders_; } synchronized void cancelledWaitingWriter() { --waitingWriters_; } boolean allowReader() { return (activeWriter_ == null && waitingWriters_ == 0) || activeWriter_ == Thread.currentThread(); } synchronized boolean startRead() { Thread t = Thread.currentThread(); Integer c = (Integer) readers_.get(t); if (c != null) { // already held -- just increment hold count readers_.put(t, new Integer(c.intValue() + 1)); ++activeReaders_; return true; } else if (allowReader()) { readers_.put(t, IONE); ++activeReaders_; return true; } else return false; } synchronized boolean startWrite() { if (activeWriter_ == Thread.currentThread()) { // already held; re-acquire ++writeHolds_; return true; } else if (writeHolds_ == 0) { if (activeReaders_ == 0 || (readers_.size() == 1 && readers_.get(Thread.currentThread()) != null)) { activeWriter_ = Thread.currentThread(); writeHolds_ = 1; return true; } else return false; } else return false; } synchronized int endRead() { Thread t = Thread.currentThread(); Integer c = (Integer) readers_.get(t); if (c == null) throw new IllegalMonitorStateException(); --activeReaders_; if (c != IONE) { // more than one hold; decrement count int h = c.intValue() - 1; Integer ih = (h == 1) ? IONE : new Integer(h); readers_.put(t, ih); return NONE; } else { readers_.remove(t); if (writeHolds_ > 0) // a write lock is still held by current thread return NONE; else if (activeReaders_ == 0 && waitingWriters_ > 0) return WRITER; else return NONE; } } synchronized int endWrite() { if (activeWriter_ != Thread.currentThread()) { throw new IllegalMonitorStateException(); } --writeHolds_; if (writeHolds_ > 0) // still being held return NONE; else { activeWriter_ = null; if (waitingReaders_ > 0 && allowReader()) return READER; else if (waitingWriters_ > 0) return WRITER; else return NONE; } } synchronized Thread getOwner() { return activeWriter_; } synchronized int getReadLockCount() { return activeReaders_; } synchronized boolean isWriteLocked() { return activeWriter_ != null; } synchronized boolean isWriteLockedByCurrentThread() { return activeWriter_ == Thread.currentThread(); } synchronized int getWriteHoldCount() { return isWriteLockedByCurrentThread() ? writeHolds_ : 0; } synchronized int getReadHoldCount() { if (activeReaders_ == 0) return 0; Thread t = Thread.currentThread(); Integer i = (Integer)readers_.get(t); return (i == null) ? 0 : i.intValue(); } final synchronized boolean hasQueuedThreads() { return waitingWriters_ > 0 || waitingReaders_ > 0; } final synchronized int getQueueLength() { return waitingWriters_ + waitingReaders_; } private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { in.defaultReadObject(); // readers_ is transient, need to reinitialize. Let's flush the memory // and ensure visibility by synchronizing (all other accesses to // readers_ are also synchronized on "this") synchronized (this) { readers_ = new HashMap(); } } } /** * Nonfair version of Sync */ private static class NonfairSync extends Sync { NonfairSync() {} } /** * The lock returned by method {@link J2SE14ReadWriteLock#readLock}. */ public static class ReadLock implements Lock, java.io.Serializable { private static final long serialVersionUID = -5992448646407690164L; final J2SE14ReadWriteLock lock; /** * Constructor for use by subclasses * * @param lock the outer lock object * @throws NullPointerException if the lock is null */ protected ReadLock(J2SE14ReadWriteLock lock) { if (lock == null) throw new NullPointerException(); this.lock = lock; } /** * Acquires the read lock. * *

      Acquires the read lock if the write lock is not held by * another thread and returns immediately. * *

      If the write lock is held by another thread then * the current thread becomes disabled for thread scheduling * purposes and lies dormant until the read lock has been acquired. */ public void lock() { synchronized (this) { if (lock.sync.startReadFromNewReader()) return; boolean wasInterrupted = Thread.interrupted(); try { while (true) { try { ReadLock.this.wait(); } catch (InterruptedException ex) { wasInterrupted = true; // no need to propagate the potentially masked // signal, since readers are always notified all } if (lock.sync.startReadFromWaitingReader()) return; } } finally { if (wasInterrupted) Thread.currentThread().interrupt(); } } } /** * Acquires the read lock unless the current thread is * {@linkplain Thread#interrupt interrupted}. * *

      Acquires the read lock if the write lock is not held * by another thread and returns immediately. * *

      If the write lock is held by another thread then the * current thread becomes disabled for thread scheduling * purposes and lies dormant until one of two things happens: * *

        * *
      • The read lock is acquired by the current thread; or * *
      • Some other thread {@linkplain Thread#interrupt interrupts} * the current thread. * *
      * *

      If the current thread: * *

        * *
      • has its interrupted status set on entry to this method; or * *
      • is {@linkplain Thread#interrupt interrupted} while * acquiring the read lock, * *
      * * then {@link InterruptedException} is thrown and the current * thread's interrupted status is cleared. * *

      In this implementation, as this method is an explicit * interruption point, preference is given to responding to * the interrupt over normal or reentrant acquisition of the * lock. * * @throws InterruptedException if the current thread is interrupted */ public void lockInterruptibly() throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); InterruptedException ie = null; synchronized (this) { if (!lock.sync.startReadFromNewReader()) { for (; ; ) { try { ReadLock.this.wait(); if (lock.sync.startReadFromWaitingReader()) return; } catch (InterruptedException ex) { lock.sync.cancelledWaitingReader(); ie = ex; break; } } } } if (ie != null) { // fall through outside synch on interrupt. // This notification is not really needed here, // but may be in plausible subclasses lock.writerLock_.signalWaiters(); throw ie; } } /** * Acquires the read lock only if the write lock is not held by * another thread at the time of invocation. * *

      Acquires the read lock if the write lock is not held by * another thread and returns immediately with the value * {@code true}. Even when this lock has been set to use a * fair ordering policy, a call to {@code tryLock()} * will immediately acquire the read lock if it is * available, whether or not other threads are currently * waiting for the read lock. This "barging" behavior * can be useful in certain circumstances, even though it * breaks fairness. If you want to honor the fairness setting * for this lock, then use {@link #tryLock * tryLock(0, TimeUnit.SECONDS) } which is almost equivalent * (it also detects interruption). * *

      If the write lock is held by another thread then * this method will return immediately with the value * {@code false}. * * @return {@code true} if the read lock was acquired */ public boolean tryLock() { return lock.sync.startRead(); } /** * Attempts to release this lock. * *

      If the number of readers is now zero then the lock * is made available for write lock attempts. */ public void unlock() { switch (lock.sync.endRead()) { case Sync.NONE: return; case Sync.READER: lock.readerLock_.signalWaiters(); return; case Sync.WRITER: lock.writerLock_.signalWaiters(); return; } } synchronized void signalWaiters() { notifyAll(); } /** * Returns a string identifying this lock, as well as its lock state. * The state, in brackets, includes the String {@code "Read locks ="} * followed by the number of held read locks. * * @return a string identifying this lock, as well as its lock state */ @Override public String toString() { int r = lock.getReadLockCount(); return super.toString() + "[Read locks = " + r + "]"; } } /** * The lock returned by method {@link J2SE14ReadWriteLock#writeLock}. */ public static class WriteLock implements Lock, java.io.Serializable { private static final long serialVersionUID = -4992448646407690164L; final J2SE14ReadWriteLock lock; /** * Constructor for use by subclasses * * @param lock the outer lock object * @throws NullPointerException if the lock is null */ protected WriteLock(J2SE14ReadWriteLock lock) { if (lock == null) throw new NullPointerException(); this.lock = lock; } /** * Acquires the write lock. * *

      Acquires the write lock if neither the read nor write lock * are held by another thread * and returns immediately, setting the write lock hold count to * one. * *

      If the current thread already holds the write lock then the * hold count is incremented by one and the method returns * immediately. * *

      If the lock is held by another thread then the current * thread becomes disabled for thread scheduling purposes and * lies dormant until the write lock has been acquired, at which * time the write lock hold count is set to one. */ public void lock() { synchronized (this) { if (lock.sync.startWriteFromNewWriter()) return; boolean wasInterrupted = Thread.interrupted(); try { while (true) { try { WriteLock.this.wait(); } catch (InterruptedException ex) { wasInterrupted = true; // no need to notify; if we were notified, // we will act as notified, and succeed in // startWrite and return } if (lock.sync.startWriteFromWaitingWriter()) return; } } finally { if (wasInterrupted) Thread.currentThread().interrupt(); } } } /** * Acquires the write lock unless the current thread is * {@linkplain Thread#interrupt interrupted}. * *

      Acquires the write lock if neither the read nor write lock * are held by another thread * and returns immediately, setting the write lock hold count to * one. * *

      If the current thread already holds this lock then the * hold count is incremented by one and the method returns * immediately. * *

      If the lock is held by another thread then the current * thread becomes disabled for thread scheduling purposes and * lies dormant until one of two things happens: * *

        * *
      • The write lock is acquired by the current thread; or * *
      • Some other thread {@linkplain Thread#interrupt interrupts} * the current thread. * *
      * *

      If the write lock is acquired by the current thread then the * lock hold count is set to one. * *

      If the current thread: * *

        * *
      • has its interrupted status set on entry to this method; * or * *
      • is {@linkplain Thread#interrupt interrupted} while * acquiring the write lock, * *
      * * then {@link InterruptedException} is thrown and the current * thread's interrupted status is cleared. * *

      In this implementation, as this method is an explicit * interruption point, preference is given to responding to * the interrupt over normal or reentrant acquisition of the * lock. * * @throws InterruptedException if the current thread is interrupted */ public void lockInterruptibly() throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); InterruptedException ie = null; synchronized (this) { if (!lock.sync.startWriteFromNewWriter()) { for (; ; ) { try { WriteLock.this.wait(); if (lock.sync.startWriteFromWaitingWriter()) return; } catch (InterruptedException ex) { lock.sync.cancelledWaitingWriter(); WriteLock.this.notify(); ie = ex; break; } } } } if (ie != null) { // Fall through outside synch on interrupt. // On exception, we may need to signal readers. // It is not worth checking here whether it is strictly necessary. lock.readerLock_.signalWaiters(); throw ie; } } /** * Acquires the write lock only if it is not held by another thread * at the time of invocation. * *

      Acquires the write lock if neither the read nor write lock * are held by another thread * and returns immediately with the value {@code true}, * setting the write lock hold count to one. Even when this lock has * been set to use a fair ordering policy, a call to * {@code tryLock()} will immediately acquire the * lock if it is available, whether or not other threads are * currently waiting for the write lock. This "barging" * behavior can be useful in certain circumstances, even * though it breaks fairness. * *

      If the current thread already holds this lock then the * hold count is incremented by one and the method returns * {@code true}. * *

      If the lock is held by another thread then this method * will return immediately with the value {@code false}. * * @return {@code true} if the lock was free and was acquired * by the current thread, or the write lock was already held * by the current thread; and {@code false} otherwise. */ public boolean tryLock() { return lock.sync.startWrite(); } /** * Attempts to release this lock. * *

      If the current thread is the holder of this lock then * the hold count is decremented. If the hold count is now * zero then the lock is released. If the current thread is * not the holder of this lock then {@link * IllegalMonitorStateException} is thrown. * * @throws IllegalMonitorStateException if the current thread does not * hold this lock. */ public void unlock() { switch (lock.sync.endWrite()) { case Sync.NONE: return; case Sync.READER: lock.readerLock_.signalWaiters(); return; case Sync.WRITER: lock.writerLock_.signalWaiters(); return; } } synchronized void signalWaiters() { notify(); } /** * Returns a string identifying this lock, as well as its lock * state. The state, in brackets includes either the String * {@code "Unlocked"} or the String {@code "Locked by"} * followed by the {@linkplain Thread#getName name} of the owning thread. * * @return a string identifying this lock, as well as its lock state */ @Override public String toString() { Thread o = lock.getOwner(); return super.toString() + ((o == null) ? "[Unlocked]" : "[Locked by thread " + o.getName() + "]"); } /** * Queries if this write lock is held by the current thread. * Identical in effect to {@link * java.util.concurrent.locks.ReentrantReadWriteLock#isWriteLockedByCurrentThread}. * * @return {@code true} if the current thread holds this lock and * {@code false} otherwise * @since 1.6 */ public boolean isHeldByCurrentThread() { return lock.sync.isWriteLockedByCurrentThread(); } /** * Queries the number of holds on this write lock by the current * thread. A thread has a hold on a lock for each lock action * that is not matched by an unlock action. Identical in effect * to {@code ReentrantReadWriteLock#getWriteHoldCount}. * * @return the number of holds on this lock by the current thread, * or zero if this lock is not held by the current thread * @since 1.6 */ public int getHoldCount() { return lock.sync.getWriteHoldCount(); } } // Instrumentation and status /** * Returns {@code true} if this lock has fairness set true. * * @return {@code true} if this lock has fairness set true */ public final boolean isFair() { return false; } /** * Returns the thread that currently owns the write lock, or * {@code null} if not owned. When this method is called by a * thread that is not the owner, the return value reflects a * best-effort approximation of current lock status. For example, * the owner may be momentarily {@code null} even if there are * threads trying to acquire the lock but have not yet done so. * This method is designed to facilitate construction of * subclasses that provide more extensive lock monitoring * facilities. * * @return the owner, or {@code null} if not owned */ protected Thread getOwner() { return sync.getOwner(); } /** * Queries the number of read locks held for this lock. This * method is designed for use in monitoring system state, not for * synchronization control. * @return the number of read locks held. */ public int getReadLockCount() { return sync.getReadLockCount(); } /** * Queries if the write lock is held by any thread. This method is * designed for use in monitoring system state, not for * synchronization control. * * @return {@code true} if any thread holds the write lock and * {@code false} otherwise */ public boolean isWriteLocked() { return sync.isWriteLocked(); } /** * Queries if the write lock is held by the current thread. * * @return {@code true} if the current thread holds the write lock and * {@code false} otherwise */ public boolean isWriteLockedByCurrentThread() { return sync.isWriteLockedByCurrentThread(); } /** * Queries the number of reentrant write holds on this lock by the * current thread. A writer thread has a hold on a lock for * each lock action that is not matched by an unlock action. * * @return the number of holds on the write lock by the current thread, * or zero if the write lock is not held by the current thread */ public int getWriteHoldCount() { return sync.getWriteHoldCount(); } /** * Queries the number of reentrant read holds on this lock by the * current thread. A reader thread has a hold on a lock for * each lock action that is not matched by an unlock action. * * @return the number of holds on the read lock by the current thread, * or zero if the read lock is not held by the current thread * @since 1.6 */ public int getReadHoldCount() { return sync.getReadHoldCount(); } /** * Queries whether any threads are waiting to acquire the read or * write lock. Note that because cancellations may occur at any * time, a {@code true} return does not guarantee that any other * thread will ever acquire a lock. This method is designed * primarily for use in monitoring of the system state. * * @return {@code true} if there may be other threads waiting to * acquire the lock */ public final boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } /** * Returns an estimate of the number of threads waiting to acquire * either the read or write lock. The value is only an estimate * because the number of threads may change dynamically while this * method traverses internal data structures. This method is * designed for use in monitoring of the system state, not for * synchronization control. * * @return the estimated number of threads waiting for this lock */ public final int getQueueLength() { return sync.getQueueLength(); } /** * Returns a string identifying this lock, as well as its lock state. * The state, in brackets, includes the String {@code "Write locks ="} * followed by the number of reentrantly held write locks, and the * String {@code "Read locks ="} followed by the number of held * read locks. * * @return a string identifying this lock, as well as its lock state */ @Override public String toString() { return super.toString() + "[Write locks = " + getWriteHoldCount() + ", Read locks = " + getReadLockCount() + "]"; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/ObservableElementList.java0000644000175000017500000005270712106516400031236 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.impl.adt.Barcode; import ca.odell.glazedlists.impl.adt.BarcodeIterator; import java.util.ArrayList; import java.util.EventListener; import java.util.List; /** * A list that fires update events whenever elements are modified in place. * Changes to list elements are detected by registering an appropriate listener * on every list element. Listeners are registered as elements are added to * this list and unregistered as elements are removed from this list. Users * must specify an implementation of a {@link Connector} in the constructor * which contains the necessary logic for registering and unregistering a * listener capable of detecting modifications to an observable list element. * *

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

      * * * * * * * *
      EventList Overview
      Writable:yes
      Concurrency:thread ready, not thread safe; elementChanged(), however, is thread ready
      Performance:inserts: O(1), deletes: O(1), updates: O(1), elementChanged: O(n)
      Memory:8 bytes per element
      Unit Tests:ObservableElementListTest
      Issues:N/A
      * * @see GlazedLists#beanConnector(Class) * @see GlazedLists#beanConnector(Class, String, String) * @see RFE 157 * * @author Jesse Wilson * @author James Lemieux */ public class ObservableElementList extends TransformedList { /** * A list of the observed elements. It is necessary to track the observed * elements since list removals broadcast ListEvents which do not include * the removed element as part of the ListEvent. We use this list to locate * removed elements for the purpose of unregistering listeners from them. * todo remove this list when ListEvent can reliably furnish us with a deleted value */ private List observedElements; /** * The connector object containing the logic for registering and * unregistering a listener that detects changes within the observed * list elements and notifies this list of the change. The registered * listener is responsible for calling {@link #elementChanged(Object)} * to notify this list of the changed object. */ private Connector elementConnector = null; /** * true indicates a single shared EventListener is used for each * element being observed. Consequently, {@link #singleEventListenerRegistry} * is the compact data structure used to track which elements are being * listened to by the {@link #singleEventListener}. false * indicates {@link #multiEventListenerRegistry} is used to track each * individual EventListener installed on each individual list element. */ private boolean singleListenerMode = true; /** * A list which parallels {@link #observedElements}. It stores the unique * {@link EventListener} associated with the observed element at the same * index within {@link #observedElements}. */ private List multiEventListenerRegistry = null; /** * The single {@link EventListener} shared by all list elements if a * common listener is returned from the {@link Connector} of this list. */ private EventListener singleEventListener = null; /** * The compact data structure which identifies the observed elements that * have had the {@link #singleEventListener} registered on them. * {@link Barcode#BLACK} indicates the {@link #singleEventListener} has * been registered on the element at the index; {@link Barcode#WHITE} * indicates no listener was registered on the element at the index. */ private Barcode singleEventListenerRegistry = null; /** * Constructs an ObservableElementList which wraps the given * source and uses the given elementConnector to * register/unregister change listeners on elements of the * source. * * @param source the {@link EventList} to transform * @param elementConnector the {@link Connector} to consult when list * elements are added or removed and thus element listeners must be * registered or unregistered. Note that this constructor attachs * this list to the given elementConnector by calling * {@link Connector#setObservableElementList(ObservableElementList)}. */ public ObservableElementList(EventList source, Connector elementConnector) { super(source); this.elementConnector = elementConnector; // attach this list to the element connector so the listeners know // which List to notify of their modifications this.elementConnector.setObservableElementList(this); // for speed, we add all source elements together, rather than individually this.observedElements = new ArrayList(source); // we initialize the single EventListener registry, as we optimistically // assume we'll be using a single listener for all observed elements this.singleEventListenerRegistry = new Barcode(); this.singleEventListenerRegistry.addWhite(0, source.size()); // add listeners to all source list elements for (int i = 0, n = size(); i < n; i++) { // connect a listener to the element final EventListener listener = this.connectElement(get(i)); // record the listener in the registry this.registerListener(i, listener, false); } // begin listening to the source list source.addListEventListener(this); } @Override public void listChanged(ListEvent listChanges) { if (this.observedElements == null) throw new IllegalStateException("This list has been disposed and can no longer be used."); // add listeners to inserted list elements and remove listeners from deleted elements while(listChanges.next()) { final int changeIndex = listChanges.getIndex(); final int changeType = listChanges.getType(); // register a listener on the inserted object if (changeType == ListEvent.INSERT) { final E inserted = get(changeIndex); this.observedElements.add(changeIndex, inserted); // connect a listener to the freshly inserted element final EventListener listener = this.connectElement(inserted); // record the listener in the registry this.registerListener(changeIndex, listener, false); // unregister a listener on the deleted object } else if (changeType == ListEvent.DELETE) { // try to get the previous value through the ListEvent E deleted = listChanges.getOldValue(); E deletedElementFromPrivateCopy = this.observedElements.remove(changeIndex); // if the ListEvent could give us the previous value, use the value from our private copy of the source if (deleted == ListEvent.UNKNOWN_VALUE) deleted = deletedElementFromPrivateCopy; // remove the listener from the registry final EventListener listener = this.unregisterListener(changeIndex); // disconnect the listener from the freshly deleted element this.disconnectElement(deleted, listener); // register/unregister listeners if the value at the changeIndex is now a different object } else if (changeType == ListEvent.UPDATE) { E previousValue = listChanges.getOldValue(); // if the ListEvent could give us the previous value, use the value from our private copy of the source if (previousValue == ListEvent.UNKNOWN_VALUE) previousValue = this.observedElements.get(changeIndex); final E newValue = get(changeIndex); // if a different object is present at the index if (newValue != previousValue) { this.observedElements.set(changeIndex, newValue); // disconnect the listener from the previous element at the index this.disconnectElement(previousValue, this.getListener(changeIndex)); // connect the listener to the new element at the index final EventListener listener = this.connectElement(newValue); // replace the old listener in the registry with the new listener for the new element this.registerListener(changeIndex, listener, true); } } } listChanges.reset(); this.updates.forwardEvent(listChanges); } /** * A convenience method for adding a listener into the appropriate listener * registry. The listener will be registered at the specified * index and will be added if replace is true * or will replace any existing listener at the index if * replace is false. * * @param index the index of the observed element the listener * is attached to * @param listener the {@link EventListener} registered to the observed * element at the given index * @param replace true indicates the listener should be replaced * at the given index; false indicates it should be added */ private void registerListener(int index, EventListener listener, boolean replace) { if (replace) { // if replace is false, we should call set() on the appropriate registry if (this.singleListenerMode) this.singleEventListenerRegistry.set(index, listener == null ? Barcode.WHITE : Barcode.BLACK, 1); else this.multiEventListenerRegistry.set(index, listener); } else { // if replace is true, we should call replace() on the appropriate registry if (this.singleListenerMode) this.singleEventListenerRegistry.add(index, listener == null ? Barcode.WHITE : Barcode.BLACK, 1); else this.multiEventListenerRegistry.add(index, listener); } } /** * Returns the {@link EventListener} at the given index. * * @param index the location of the {@link EventListener} to be returned * @return the {@link EventListener} at the given index */ private EventListener getListener(int index) { EventListener listener = null; if (this.singleListenerMode) { if (this.singleEventListenerRegistry.get(index) == Barcode.BLACK) listener = this.singleEventListener; } else { listener = this.multiEventListenerRegistry.get(index); } return listener; } /** * A convenience method for removing a listener at the specified * index from the appropriate listener registry. * * @param index the index of the {@link EventListener} to be unregistered * @return the EventListener that was unregistered or null if * no EventListener existed at the given index */ private EventListener unregisterListener(int index) { EventListener listener = null; if (this.singleListenerMode) { if (this.singleEventListenerRegistry.get(index) == Barcode.BLACK) listener = this.singleEventListener; this.singleEventListenerRegistry.remove(index, 1); } else { listener = this.multiEventListenerRegistry.remove(index); } return listener; } /** * A convenience method to connect listeners to the given * listElement. * * @param listElement the list element to connect change listeners to * @return the listener that was connected to the listElement * or null if no listener was registered * @throws IllegalStateException if this list has been disposed and is * thus no longer in a state to be managing listener registrations on * list elements */ private EventListener connectElement(E listElement) { // listeners cannot be installed on null listElements if (listElement == null) return null; // use the elementConnector to install a listener on the listElement final EventListener listener = this.elementConnector.installListener(listElement); // test if the new listener transfers us from single event mode to multi event mode if (this.singleListenerMode && listener != null) { if (this.singleEventListener == null) this.singleEventListener = listener; else if (listener != this.singleEventListener) this.switchToMultiListenerMode(); } return listener; } /** * A convenience method to disconnect the listener from the * given listElement. * * @param listElement the list element to disconnect the * listener from * @throws IllegalStateException if this list has been disposed and is * thus no longer in a state to be managing listener registrations on * list elements */ private void disconnectElement(E listElement, EventListener listener) { if (listElement != null && listener != null) this.elementConnector.uninstallListener(listElement, listener); } /** * This method converts the data structures which are optimized for storing * a single instance of an EventListener shared amongst all observed * elements into data structures which are appropriate for storing * individual instances of EventListeners for each observed element. * *

      Note: this is a one-time switch only and cannot be reversed */ private void switchToMultiListenerMode() { if (!this.singleListenerMode) throw new IllegalStateException(); // build a new data structure appropriate for storing individual // listeners for each observed element this.multiEventListenerRegistry = new ArrayList(this.source.size()); for (int i = 0; i < source.size(); i++) this.multiEventListenerRegistry.add(null); // for each black entry in the singleEventListenerRegistry create an // entry in the multiEventListenerRegistry at the corresponding index // for the singleEventListener for (BarcodeIterator iter = this.singleEventListenerRegistry.iterator(); iter.hasNextBlack();) { iter.nextBlack(); this.multiEventListenerRegistry.set(iter.getIndex(), this.singleEventListener); } // null out the reference to the single EventListener, // since we'll now track the EventListener for each element this.singleEventListener = null; // null out the reference to the single EventList registry, since we // are replacing its listener tracking mechanism with the multiEventListenerRegistry this.singleEventListenerRegistry = null; // indicate this list is no longer in single listener mode meaning we // no longer assume the same listener is installed on every element this.singleListenerMode = false; } @Override protected boolean isWritable() { return true; } /** * Releases the resources consumed by this {@link TransformedList} so that * it may eventually be garbage collected. * * In this case of this {@link TransformedList}, it uses the * {@link Connector} to remove all listeners from their associated list * elements and finally removes the reference to this list from the * Connector by calling * {@link Connector#setObservableElementList(ObservableElementList)} with a * null argument. * *

      Warning: It is an error * to call any method on a {@link TransformedList} after it has been disposed. */ @Override public void dispose() { // remove all listeners from all list elements for (int i = 0, n = this.observedElements.size(); i < n; i++) { final E element = this.observedElements.get(i); final EventListener listener = this.getListener(i); this.disconnectElement(element, listener); } // clear out the reference to this list from the associated connector this.elementConnector.setObservableElementList(null); // null out all references to internal data structures this.observedElements = null; this.multiEventListenerRegistry = null; this.singleEventListener = null; this.singleEventListenerRegistry = null; this.elementConnector = null; super.dispose(); } /** * Handle a listener being notified for the specified listElement. * This method causes a ListEvent to be fired from this EventList indicating * an update occurred at all locations of the given listElement. * *

      Note that listElement must be the exact object located within this list * (i.e. listElement == get(i) for some i >= 0). * *

      This method acquires the write lock for this list before locating the * listElement and broadcasting its update. It is assumed that * this method may be called on any Thread, so to decrease the burdens of * the caller in achieving multi-threaded correctness, this method is * Thread ready. * * @param listElement the list element which has been modified */ public void elementChanged(Object listElement) { if (this.observedElements == null) throw new IllegalStateException("This list has been disposed and can no longer be used."); getReadWriteLock().writeLock().lock(); try { this.updates.beginEvent(); // locate all indexes containing the given listElement for (int i = 0, n = size(); i < n; i++) { final E currentElement = get(i); if (listElement == currentElement) { this.updates.elementUpdated(i, currentElement); } } this.updates.commitEvent(); } finally { getReadWriteLock().writeLock().unlock(); } } /** * An interface defining the methods required for registering and * unregistering change listeners on list elements within an * {@link ObservableElementList}. Implementations typically install a * single listener, such as a {@link java.beans.PropertyChangeListener} on * list elements to detect changes in the state of the element. The * installed listener implementation in turn calls * {@link ObservableElementList#elementChanged(Object)} in order to have * the list broadcast an update at the index of the object. */ public interface Connector { /** * Start listening for events from the specified element. * Alternatively, if the element does not require a * listener to be attached to it (e.g. the element is * immutable), null may be returned to signal that no * listener was installed. * * @param element the element to be observed * @return the listener that was installed on the element * to be used as a parameter to {@link #uninstallListener(Object, EventListener)}. * null is taken to mean no listener was installed * and thus {@link #uninstallListener(Object, EventListener)} need * not be called. */ public EventListener installListener(E element); /** * Stop listening for events from the specified element. * * @param element the element to be observed * @param listener the listener as returned by {@link #installListener(Object)}. */ public void uninstallListener(E element, EventListener listener); /** * Sets the {@link ObservableElementList} to notify when changes occur * on elements. * * @param list the ObservableElementList containing the elements to * observe */ public void setObservableElementList(ObservableElementList list); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/matchers/0000755000175000017500000000000012106516400025734 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/matchers/MatcherEditor.java0000644000175000017500000001251212106516400031332 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.matchers; import ca.odell.glazedlists.FilterList; import java.util.EventListener; import java.util.EventObject; /** * A facility for modifying the {@link Matcher}s which specify the behaviour of a * {@link ca.odell.glazedlists.FilterList FilterList}. * *

      Although this interface is called an Editor, the * implementor should create new {@link Matcher} instances on each * change rather than modifying the existing {@link Matcher}s. This is because * {@link Matcher}s work best when they are immutable. Further information * on this immutability can be found in the {@link Matcher Matcher Javadoc}. * * @author Rob Eden * @author Jesse Wilson * @author James Lemieux */ public interface MatcherEditor { /** * Add a listener to be notified when this editor's {@link Matcher} changes. */ public void addMatcherEditorListener(Listener listener); /** * Remove the listener so that it no longer receives notification when the * {@link Matcher} changes. */ public void removeMatcherEditorListener(Listener listener); /** * Return the current {@link Matcher} specified by this {@link MatcherEditor}. * * @return a non-null {@link Matcher}. */ public Matcher getMatcher(); /** * A MatcherEditor.Listener handles changes fired by a {@link MatcherEditor}. * The most notable implementation will be {@link ca.odell.glazedlists.FilterList FilterList} * which uses these events to update its state. */ public interface Listener extends EventListener { /** * Indicates a changes has occurred in the Matcher produced by the * MatcherEditor. * * @param matcherEvent a MatcherEditor.Event describing the change in the * Matcher produced by the MatcherEditor */ public void changedMatcher(Event matcherEvent); } /** * A MatcherEditor event models a change in the {@link MatcherEditor} that * creates a new {@link Matcher}. * *

      The event gives access to: *

        *
      • the {@link MatcherEditor} which was the source of the change *
      • the new {@link Matcher} which was produced from the MatcherEditor *
      • a type value which indicates whether the new Matcher may be * considered a relaxing, constraining, or changing of the prior Matcher * produced from the MatcherEditor. Special types also exist for the * edge cases where the new Matcher is guaranteed to match everything or * nothing *
      * * The type constants are found in this event class: *
        *
      • {@link #MATCH_ALL} *
      • {@link #MATCH_NONE} *
      • {@link #CONSTRAINED} *
      • {@link #RELAXED} *
      • {@link #CHANGED} *
      extends EventObject { /** Indicates the associated Matcher will match anything. */ public static final int MATCH_ALL = 0; /** Indicates the associated Matcher will match nothing. */ public static final int MATCH_NONE = 1; /** * Indicates the associated Matcher is a constrained version of the * previous Matcher, implying it can be expected to match at most the same * values matched by the previous Matcher, and possibly fewer. */ public static final int CONSTRAINED = 2; /** * Indicates the associated Matcher is a relaxed version of the previous * Matcher, implying it can be expected to match at least the same values * matched by the previous Matcher, and possibly more. */ public static final int RELAXED = 3; /** * Indicates the associated Matcher is a complete change from the previous * Matcher. No guarantees can be made for any values which were matched or * unmatched by the previous Matcher. */ public static final int CHANGED = 4; private MatcherEditor matcherEditor; private final Matcher matcher; private final int type; public Event(MatcherEditor matcherEditor, int changeType, Matcher matcher) { super(matcherEditor); this.matcherEditor = matcherEditor; this.type = changeType; this.matcher = matcher; } public Event(FilterList eventSource, int changeType, Matcher matcher) { super(eventSource); this.type = changeType; this.matcher = matcher; } /** * Get the {@link MatcherEditor} that originated this event, or null * if this event originated directly from a {@link FilterList} in a call * to {@link FilterList#setMatcher(Matcher)}. */ public MatcherEditor getMatcherEditor() { return this.matcherEditor; } public Matcher getMatcher() { return this.matcher; } public int getType() { return this.type; } } }././@LongLink0000000000000000000000000000016300000000000011565 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/matchers/AbstractMatcherEditorListenerSupport.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/matchers/AbstractMatcherEditorListen0000644000175000017500000000526312106516400033262 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.matchers; import javax.swing.event.EventListenerList; /** * Basic building block for {@link MatcherEditor} implementations that handles * the details of dealing with registered {@link MatcherEditor.Listener}s. In * addition, it provides helper methods for creating matcher events. * * @author Holger Brands */ public abstract class AbstractMatcherEditorListenerSupport implements MatcherEditor { /** listeners for this Editor */ private EventListenerList listenerList = new EventListenerList(); /** {@inheritDoc} */ public final void addMatcherEditorListener( MatcherEditor.Listener listener) { listenerList.add(MatcherEditor.Listener.class, listener); } /** {@inheritDoc} */ public final void removeMatcherEditorListener( MatcherEditor.Listener listener) { listenerList.remove(Listener.class, listener); } /** delivers the given matcher event to all registered listeners. */ protected final void fireChangedMatcher(MatcherEditor.Event event) { // Guaranteed to return a non-null array final Object[] listeners = this.listenerList.getListenerList(); // Process the listenerList last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) ((Listener) listeners[i + 1]).changedMatcher(event); } /** creates a changed event. */ protected final MatcherEditor.Event createChangedEvent(Matcher matcher) { return createEvent(Event.CHANGED, matcher); } /** creates a constrained event. */ protected final MatcherEditor.Event createConstrainedEvent( Matcher matcher) { return createEvent(Event.CONSTRAINED, matcher); } /** creates a relaxed event. */ protected final MatcherEditor.Event createRelaxedEvent(Matcher matcher) { return createEvent(Event.RELAXED, matcher); } /** creates a match none event. */ protected final MatcherEditor.Event createMatchNoneEvent( Matcher matcher) { return createEvent(Event.MATCH_NONE, matcher); } /** creates a match all event. */ protected final MatcherEditor.Event createMatchAllEvent( Matcher matcher) { return createEvent(Event.MATCH_ALL, matcher); } /** creates a matcher event for the given type and matcher. */ private MatcherEditor.Event createEvent(int eventType, Matcher matcher) { return new MatcherEditor.Event(this, eventType, matcher); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/matchers/RangeMatcherEditor.java0000644000175000017500000001365312106516400032316 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.matchers; import ca.odell.glazedlists.Filterator; /** * A MatcherEditor that produces Matchers which match Objects if they lie * within a range of {@link Comparable}s. This {@link RangeMatcherEditor} is * not coupled with any UI component that allows the user to edit the range. * That job is left to subclasses. This MatcherEditor is fully concrete, and * may be used directly by headless applications. * *

      The {@link RangeMatcherEditor} requires that either a {@link Filterator} * appropriate for extracting {@link Comparable} objects be specified in its * constructor, or that every Object to be matched is a {@link Comparable}. * * @author James Lemieux */ public class RangeMatcherEditor extends AbstractMatcherEditor { /** the filterator is used as an alternative to implementing the TextFilterable interface */ private final Filterator filterator; /** the Comparable object which starts the current range; null indicates there is no start */ private D currentRangeStart; /** the Comparable object which ends the current range; null indicates there is no end */ private D currentRangeEnd; /** * Creates a {@link RangeMatcherEditor} whose Matchers can test only elements which * are {@link Comparable} objects. * *

      The {@link Matcher}s from this {@link MatcherEditor} will throw a * {@link ClassCastException} when {@link Matcher#matches} is called with * an Object that is not a {@link Comparable}. */ public RangeMatcherEditor() { this(null); } /** * Creates a {@link RangeMatcherEditor} that matches Objects using the * specified {@link Filterator} to get the {@link Comparable}s to search. * * @param filterator the object that will extract filter Comparables from * each object in the source; null indicates * the list elements are Comparables */ public RangeMatcherEditor(Filterator filterator) { this.filterator = filterator; } /** * Get the filterator used to extract Comparables from the matched elements. */ public Filterator getFilterator() { return filterator; } /** * This method is used to change the range currently matched by this * MatcherEditor. When a change to the range is detected, users of this * class are expected to call this method with the new bounds of the * range to be matched. * *

      null values for either newStart or * newEnd indicate there is no start of end to the range * respectively. Consequently, calling setRange(null, null) * causes this matcher editor match all values it filters. * *

      Note: if newStart and newEnd are out of * their natural order with respect to each other, their values are swapped. * For example, setRange(Jan 1, 2006, Jan 1, 1955) would swap * the values so newStart is Jan 1, 1955 and * newEnd is Jan 1, 2006. * * @param newStart the new value marking the start of the range; * null indicates there is no start * @param newEnd the new value marking the start of the range; * null indicates there is no start */ public void setRange(D newStart, D newEnd) { // swap the newStart and newEnd if they are out of order if (newStart != null && newEnd != null && newStart.compareTo(newEnd) > 0) { final D temp = newEnd; newEnd = newStart; newStart = temp; } try { // detect the special case of no range, (which matches all elements) if (newStart == null && newEnd == null) { if (currentRangeStart != null || currentRangeEnd != null) fireMatchAll(); return; } // determine if the change in the range relaxes or constrains the previous range // (if it does both, it is treated as a generic change) boolean isRelaxed = false; boolean isConstrained = false; // determine the type of change that occurred at the start of the range int newStartVsOldStart = compare(newStart, currentRangeStart, true); isRelaxed |= newStartVsOldStart < 0; isConstrained |= newStartVsOldStart > 0; // determine the type of change that occurred at the end of the range int newEndVsOldEnd = compare(newEnd, currentRangeEnd, false); isRelaxed |= newEndVsOldEnd > 0; isConstrained |= newEndVsOldEnd < 0; // construct a matcher describing the new range final Matcher matcher = Matchers.rangeMatcher(newStart, newEnd, filterator); // fire the appropriate matcher event if (isRelaxed && isConstrained) fireChanged(matcher); else if (isRelaxed) fireRelaxed(matcher); else if (isConstrained) fireConstrained(matcher); } finally { currentRangeStart = newStart; currentRangeEnd = newEnd; } } /** * Compare the specified two values, treating null as either before * all other values or after all other values. */ private static int compare(Comparable a, Comparable b, boolean nullsBeforeAll) { if(a == null && b == null) return 0; else if(a == null) return nullsBeforeAll ? -1 : 1; else if(b == null) return nullsBeforeAll ? 1 : -1; else return a.compareTo(b); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/matchers/package.html0000644000175000017500000000043312106516400030215 0ustar gregoagregoa Implementations and classes useful for creating implementations of {@link ca.odell.glazedlists.matchers.MatcherEditor MatcherEditor}/{@link ca.odell.glazedlists.matchers.Matcher Matchers} for use with {@link ca.odell.glazedlists.FilterList FilterLists}. libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/matchers/Matchers.java0000644000175000017500000003160412106516400030351 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.matchers; import ca.odell.glazedlists.Filterator; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.impl.matchers.*; import java.beans.PropertyChangeEvent; import java.lang.reflect.Array; import java.util.*; /** * A factory for creating Matchers. * * @author Jesse Wilson */ public final class Matchers { /** * A dummy constructor to prevent instantiation of this class */ private Matchers() { throw new UnsupportedOperationException(); } // Matcher Editors // // // // // // // // // // // // // // // // // // // /** * Provides a proxy to another MatcherEditor that may go out of scope * without explicitly removing itself from the source MatcherEditor's set * of listeners. *

      *

      This exists to solve a garbage collection problem. Suppose I have a * {@link MatcherEditor} M which is long lived and many * {@link MatcherEditor.Listener}s, t which must listen to M * while they exist. Instead of adding each of the t directly as * listeners of M, add a proxy instead. The proxy will retain a * WeakReference to the t, and will remove itself from * the list of listeners for M. *

      * The {@link MatcherEditor} returned by this method makes implementing the * above scheme trivial. It does two things for you automatically: *

      *

        *
      1. It wraps each {@link MatcherEditor.Listener} passed to * {@link MatcherEditor#addMatcherEditorListener} in a * {@link java.lang.ref.WeakReference} so that the listeners are * garbage collected when they become unreachable. *

        *

      2. It registers itself as a weak listener of the * given matcherEditor so the MatcherEditor returned by * this method will be garbage collected when it becomes unreachable. *
      * * @see java.lang.ref.WeakReference */ public static MatcherEditor weakReferenceProxy(MatcherEditor matcherEditor) { return new WeakReferenceMatcherEditor(matcherEditor); } // Matchers // // // // // // // // // // // // // // // // // // // // // /** * Get a {@link Matcher} that always returns true, therefore matching everything. */ public static Matcher trueMatcher() { return TrueMatcher.getInstance(); } /** * Get a {@link Matcher} that always returns false, therefore matching nothing.. */ public static Matcher falseMatcher() { return FalseMatcher.getInstance(); } /** * Get a {@link Matcher} that returns the opposite of the specified {@link Matcher}. */ public static Matcher invert(Matcher original) { return new NotMatcher(original); } /** * Get a {@link Matcher} that returns returns true iff it is * given a non-null object. */ public static Matcher isNull() { return NullMatcher.getInstance(); } /** * Get a {@link Matcher} that returns returns true iff it is * given a null object. */ public static Matcher isNotNull() { return NotNullMatcher.getInstance(); } /** * Get a {@link Matcher} that returns true iff it is given a * non-null and non-empty String. */ public static Matcher nonNullAndNonEmptyString() { return NonNullAndNonEmptyStringMatcher.getInstance(); } /** * Creates a {@link Matcher} that uses Reflection to compare the expectedValue * of the specified property of an object to the expectedValue. */ public static Matcher beanPropertyMatcher(Class beanClass, String propertyName, Object expectedValue) { return new BeanPropertyMatcher(beanClass, propertyName, expectedValue); } /** * Creates a {@link Matcher} that matches {@link Comparable} objects for * containment within the range between the given start * and end. */ public static Matcher rangeMatcher(D start, D end) { return new RangeMatcher(start, end); } /** * Creates a {@link Matcher} that uses the given filterator * to extract {@link Comparable} objects from filtered objects and compares * those Comparables against the range between the given start * and end. If at least one Comparable returned by the * filterator is within the range, the object is considered * a match. *

      *

      null start or end values are * allowed and are interpreted as "no start" or * "no end" to the range respectively. * * @param start the {@link Comparable} which starts the range * @param end the {@link Comparable} which ends the range * @param filterator the logic for extracting filter {@link Comparable}s * from filtered objects */ public static Matcher rangeMatcher(D start, D end, Filterator filterator) { return new RangeMatcher(start, end, filterator); } /** * Create a {@link Matcher} that uses the given propertyNames to match * {@link PropertyChangeEvent}s by their property name. The concrete behaviour depends on the * matchPropertyNames parameter. If you want to match property change events * against a known set of property names, use a value of true. Alternatively, * when you specify false, the specified property names will serve as an exclude * list, e.g. if an event matches a specified property name, it will be filtered out. *

      *

      These matchers are especially useful as an event matcher in a bean connector. * * @param matchPropertyNames if true, match property change events against the * specified property names, if false filter them * @param propertyNames the property names to consider * @see GlazedLists#beanConnector(Class, Matcher) */ public static Matcher propertyEventNameMatcher(boolean matchPropertyNames, String... propertyNames) { return new PropertyEventNameMatcher(matchPropertyNames, propertyNames); } /** * Iterate through the specified collection and count all elements * that match the specified matcher. * * @return the number of elements in the collection that are matched */ public static int count(Collection collection, Matcher matcher) { int count = 0; for (Iterator i = collection.iterator(); i.hasNext();) if (matcher.matches(i.next())) count++; return count; } /** * Iterate through the specified collection and remove all elements * that don't match the specified matcher. * * @return true if any elements were removed from the specified * {@link Collection} */ public static boolean filter(Collection collection, Matcher matcher) { boolean changed = false; for (Iterator i = collection.iterator(); i.hasNext();) { if (!matcher.matches(i.next())) { i.remove(); changed = true; } } return changed; } /** * Return a new array containing only the items that satisfy * the matcher. * * @param items the array of Objects to search * @param matcher the criteria for considering an element a match * @return a new array containing only the items that satisfy * the matcher */ public static E[] select(E[] items, Matcher matcher) { final Collection selections = select(Arrays.asList(items), matcher); return selections.toArray((E[]) Array.newInstance(items.getClass().getComponentType(), selections.size())); } /** * Add all elements from the given collection that satisfy the * matcher to a new ArrayList. * * @param collection the Collection to search * @param matcher the criteria for considering an element a match * @return a new {@link ArrayList} containing the elements which satisfy * the matcher */ public static Collection select(Collection collection, Matcher matcher) { return select(collection, matcher, new ArrayList()); } /** * Add all elements from the given collection that satisfy the * matcher to the given results Collection. * results can be any Collection that supports the * {@link Collection#add} operation. * * @param collection the Collection to search * @param matcher the criteria for considering an element a match * @param results the Collection into which matching elements are added * @return the results {@link Collection} containing the * elements which satisfy the matcher */ public static Collection select(Collection collection, Matcher matcher, Collection results) { for (Iterator i = collection.iterator(); i.hasNext();) { E element = i.next(); if (matcher.matches(element)) results.add(element); } return results; } /** * Returns true if the given collection contains an * element that satisfies the given matcher; false * otherise. * * @param collection the Collection to search * @param matcher the criteria for considering an element a match * @return true if the given collection contains an * element that satisfies the given matcher; * false otherise */ public static boolean contains(Collection collection, Matcher matcher) { for (Iterator i = collection.iterator(); i.hasNext();) if (matcher.matches(i.next())) return true; return false; } /** * Returns the index of the first element from the given list * that satisfies the matcher or -1 if no such * element exists. * * @param list the List to search * @param matcher the criteria for considering an element a match * @return the index of the first element from the given list * that satisfies the matcher or -1 if no such * element exists */ public static int indexOf(List list, Matcher matcher) { for (int i = 0, n = list.size(); i < n; i++) { if (matcher.matches(list.get(i))) return i; } return -1; } /** * Returns a Matcher which returns a match when any of the * given matchers reports a match. * * @param matchers the Collection of Matchers to combine with an "or" operator * @return a Matcher that combines the matchers via an "or" operator */ public static Matcher or(Matcher... matchers) { return new OrMatcher(matchers); } /** * Returns a Matcher which returns a match when all of the * given matchers report a match. * * @param matchers the Collection of Matchers to combine with an "and" operator * @return a Matcher that combines the matchers via an "and" operator */ public static Matcher and(Matcher... matchers) { return new AndMatcher(matchers); } /** * Returns a Matcher which reports a match when the given object to match * is not null and reports on of the given classes as its type. * * @param classes the object types that are matched * @return a Matcher which reports a match when the given object to match * is not null and reports on of the given classes as its * type */ public static Matcher types(Class... classes) { return new TypeMatcher(classes); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/matchers/TextMatcherEditor.java0000644000175000017500000003233312106516400032202 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.matchers; import ca.odell.glazedlists.TextFilterable; import ca.odell.glazedlists.TextFilterator; import ca.odell.glazedlists.impl.filter.*; import ca.odell.glazedlists.impl.GlazedListsImpl; /** * A matcher editor that matches Objects that contain a filter text string. * This {@link TextMatcherEditor} is not coupled with any UI component that * allows the user to edit the filter text. That job is left to subclasses. * This matcher editor is fully concrete, and may be used directly by headless * applications. * *

      The {@link TextMatcherEditor} requires that either a * {@link TextFilterator} be specified in its constructor, or that every Object * to be matched implements the {@link TextFilterable} interface. These are * used to extract the searchable {@link String}s for each Object. * *

      {@link TextMatcherEditor} is able to operate in one of three modes. *

        *
      • {@link #CONTAINS} will produce {@link Matcher} objects that test if * at least one searchable string for an Object contains * one of the filter strings anywhere within itself. * *
      • {@link #STARTS_WITH} will produce {@link Matcher} objects that test * if at least one searchable string for an Object * begins with at least one of the filter strings. * *
      • {@link #REGULAR_EXPRESSION} will produce {@link Matcher} objects that * test if at least one searchable string for an Object matches, * using regular expression rules, at least one of the filter * strings. *
      * *

      {@link TextMatcherEditor} is able to operate with one of two strategies. *

        *
      • {@link #IDENTICAL_STRATEGY} defines a text match as a precise * character-for-character match between the filters and the text. * *
      • {@link #NORMALIZED_STRATEGY} defines a text match more leniently for * Latin-character based languages. Specifically, diacritics are * stripped from all Latin characters before comparisons are made. * Consequently, filters like "resume" match words like "r�sum�". *
      * * @author James Lemieux * @author Jesse Wilson */ public class TextMatcherEditor extends AbstractMatcherEditor { /** * Matching mode where items are considered a match if at least one of the * filter strings extracted from an object contains one of the given search * strings. */ public static final int CONTAINS = 0; /** * Matching mode where items are considered a match if at least one of the * filter strings extracted from an object starts with one of the given search * strings. */ public static final int STARTS_WITH = 1; /** * Matching mode where items are considered a match using a * {@link java.util.regex.Matcher} produced by compiling a regular * expression into {@link java.util.regex.Pattern}. */ public static final int REGULAR_EXPRESSION = 2; /** * Matching mode where items are considered a match if they are an exact * character for character match with at least one of the filter strings. */ public static final int EXACT = 3; /** * Character comparison strategy that assumes all characters can be * compared directly as though they are ASCII. This implies there is no * fuzzy matching with this strategy - each character must be precisely * matched. */ public static final Object IDENTICAL_STRATEGY = new IdenticalStrategyFactory(); // this would be an inner class if declawer supported it private static class IdenticalStrategyFactory implements TextSearchStrategy.Factory { public TextSearchStrategy create(int mode, String filter) { if (mode == TextMatcherEditor.CONTAINS) { if (filter.length() == 1) { return new SingleCharacterCaseInsensitiveTextSearchStrategy(); } else { return new BoyerMooreCaseInsensitiveTextSearchStrategy(); } } else if (mode == TextMatcherEditor.STARTS_WITH) { return new StartsWithCaseInsensitiveTextSearchStrategy(); } else if (mode == TextMatcherEditor.REGULAR_EXPRESSION) { return new RegularExpressionTextSearchStrategy(); } else if (mode == TextMatcherEditor.EXACT) { return new ExactCaseInsensitiveTextSearchStrategy(); } else { throw new IllegalArgumentException("unrecognized mode: " + mode); } } } /** * Character comparison strategy that assumes all Latin characters should * have their diacritical marks stripped in an effort to normalize words to * their most basic form. This allows a degree of fuzziness within the * matching algorithm, since words like "resume" will match similar words * with diacritics like "r�sum�". This strategy is particularly useful when * the text to be searched contains text like names in foreign languages, * and your application would like search terms such as "Muller" to match * the actual native spelling: "M�ller". */ public static final Object NORMALIZED_STRATEGY = new NormalizedStrategyFactory(); // this would be an inner class if declawer supported it private static class NormalizedStrategyFactory extends IdenticalStrategyFactory { @Override public TextSearchStrategy create(int mode, String filter) { TextSearchStrategy result = super.create(mode, filter); // apply our simple character mapper result.setCharacterMap(GlazedListsImpl.getLatinDiacriticsStripper()); return result; } } /** the filterator is used as an alternative to implementing the TextFilterable interface */ private TextFilterator filterator; /** one of {@link #CONTAINS}, {@link #STARTS_WITH}, or {@link #REGULAR_EXPRESSION} */ private int mode = CONTAINS; /** one of {@link #IDENTICAL_STRATEGY} or {@link #NORMALIZED_STRATEGY} */ private TextSearchStrategy.Factory strategy = (TextSearchStrategy.Factory)IDENTICAL_STRATEGY; /** * Creates a {@link TextMatcherEditor} whose Matchers can test only elements which * implement the {@link TextFilterable} interface. * *

      The {@link Matcher}s from this {@link MatcherEditor} will throw a * {@link ClassCastException} when called with an Object that does not implement * {@link TextFilterable}. */ public TextMatcherEditor() { this(null); } /** * Creates a {@link TextMatcherEditor} that matches Objects using the * specified {@link TextFilterator} to get the {@link String}s to search. * * @param filterator the object that will extract filter Strings from each * object in the source; null indicates the * list elements implement {@link TextFilterable} */ public TextMatcherEditor(TextFilterator filterator) { this.filterator = filterator; } /** * Get the filterator used to extract Strings from the matched elements. */ public TextFilterator getFilterator() { return filterator; } /** * Set the filterator used to extract Strings from the matched elements. */ public void setFilterator(TextFilterator filterator) { if (filterator == this.filterator) return; this.filterator = filterator; // if no filter text exists, no Matcher change is necessary final TextMatcher currentTextMatcher = getCurrentTextMatcher(); if (currentTextMatcher == null) return; fireChanged(currentTextMatcher.newFilterator(filterator)); } /** * Modify the behaviour of this {@link TextMatcherEditor} to one of the * predefined modes. * * @param mode either {@link #CONTAINS}, {@link #STARTS_WITH}, * {@link #REGULAR_EXPRESSION}, or {@link #EXACT} */ public void setMode(int mode) { if (mode != CONTAINS && mode != STARTS_WITH && mode != REGULAR_EXPRESSION && mode != EXACT) throw new IllegalArgumentException("mode must be one of: TextMatcherEditor.CONTAINS, STARTS_WITH, REGULAR_EXPRESSION or EXACT"); if (mode == this.mode) return; // apply the new mode final int oldMode = this.mode; this.mode = mode; // if no filter text exists, no Matcher change is necessary final TextMatcher currentTextMatcher = getCurrentTextMatcher(); if (currentTextMatcher == null) return; if (oldMode == CONTAINS && mode == STARTS_WITH) { // CONTAINS -> STARTS_WITH is a constraining change fireConstrained(currentTextMatcher.newMode(mode)); } else if (oldMode == STARTS_WITH && mode == CONTAINS) { // STARTS_WITH -> CONTAINS is a relaxing change fireRelaxed(currentTextMatcher.newMode(mode)); } else { // otherwise we can't do better than a raw change fireChanged(currentTextMatcher.newMode(mode)); } } /** * Returns the behaviour mode for this {@link TextMatcherEditor}. * * @return one of {@link #CONTAINS} (default), {@link #STARTS_WITH}, * {@link #REGULAR_EXPRESSION}, or {@link #EXACT} */ public int getMode() { return mode; } /** * Modify the character matching strategy of this {@link TextMatcherEditor} * to one of the predefined strategies. See the documentation for each * constant in order contrast the strategies. * * @param strategy either {@link #IDENTICAL_STRATEGY} or {@link #NORMALIZED_STRATEGY} */ public void setStrategy(Object strategy) { if(strategy == this.strategy) return; if(!(strategy instanceof TextSearchStrategy.Factory)) throw new IllegalArgumentException(); this.strategy = (TextSearchStrategy.Factory)strategy; // if no filter text exists, no Matcher change is necessary final TextMatcher currentTextMatcher = getCurrentTextMatcher(); if (currentTextMatcher == null) return; fireChanged(currentTextMatcher.newStrategy(strategy)); } /** * Returns the character comparison strategy for this {@link TextMatcherEditor}. * See the documentation for each constant in order contrast the strategies. * * @return one of {@link #IDENTICAL_STRATEGY} or {@link #NORMALIZED_STRATEGY} */ public Object getStrategy() { return strategy; } /** * Return the current Matcher if it is a {@link TextMatcher} or * null if no current Matcher exists or is something other * than a {@link TextMatcher}. */ protected TextMatcher getCurrentTextMatcher() { final Matcher currentMatcher = getMatcher(); if (currentMatcher instanceof TextMatcher) return ((TextMatcher) currentMatcher); return null; } /** * Adjusts the filters of this {@link TextMatcherEditor} and fires a change * to the {@link Matcher}. * * @param newFilters the {@link String}s representing all of the filter values */ public void setFilterText(String[] newFilters) { // wrap the filter Strings with SearchTerm objects final SearchTerm[] searchTerms = new SearchTerm[newFilters.length]; for (int i = 0; i < searchTerms.length; i++) searchTerms[i] = new SearchTerm(newFilters[i]); // adjust the TextMatcher setTextMatcher(new TextMatcher(searchTerms, getFilterator(), getMode(), getStrategy())); } /** * This method replaces the current Matcher in this TextMatcherEditor with * the newMatcher if it is different. If the current Matcher * is also a TextMatcher then many comparisons between the two in order to * determine if the new Matcher is a strict constrainment or relaxation of * the current TextMatcher. * * @param newMatcher new TextMatcher which defines the text filtering logic */ protected void setTextMatcher(TextMatcher newMatcher) { final TextMatcher oldMatcher = getCurrentTextMatcher(); // fire the event only as necessary if (newMatcher.equals(oldMatcher)) return; // if the newMatcher does not have any search terms then it // automatically matches if (newMatcher.getSearchTerms().length == 0) { if (!isCurrentlyMatchingAll()) fireMatchAll(); return; } // this is the case when the current Matcher is not a TextMatcher if (isCurrentlyMatchingAll()) fireConstrained(newMatcher); else if (TextMatchers.isMatcherRelaxed(oldMatcher, newMatcher)) fireRelaxed(newMatcher); else if (TextMatchers.isMatcherConstrained(oldMatcher, newMatcher)) fireConstrained(newMatcher); else fireChanged(newMatcher); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/matchers/CompositeMatcherEditor.java0000644000175000017500000002304112106516400033214 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.matchers; import ca.odell.glazedlists.BasicEventList; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * A {@link MatcherEditor} composed of zero or more delegate * {@link MatcherEditor}s. * * @author Rob Eden * @author Jesse Wilson */ public class CompositeMatcherEditor extends AbstractMatcherEditor { /** require all matchers in the {@link MatcherEditor} to match */ public static final int AND = 42; /** require any matchers in the {@link MatcherEditor} to match */ public static final int OR = 24; /** the delegates */ private EventList> matcherEditors; /** whether to match with AND or OR */ private int mode = AND; /** listeners for each delegate */ private List matcherEditorListeners = new ArrayList(); /** * Create a {@link CompositeMatcherEditor} that creates Matchers from the union * of the specified {@link EventList} of {@link MatcherEditor}s. The {@link EventList} * must not contain any null values and all elements must * implement {@link MatcherEditor}. */ public CompositeMatcherEditor(EventList> matcherEditors) { this.matcherEditors = matcherEditors; // prepare the initial set for(Iterator> i = matcherEditors.iterator(); i.hasNext(); ) { matcherEditorListeners.add(new DelegateMatcherEditorListener(i.next())); } // handle changes to the list of matchers matcherEditors.addListEventListener(new MatcherEditorsListListener()); // use the initial matcher fireChanged(rebuildMatcher()); } /** * Create a {@link CompositeMatcherEditor}. */ public CompositeMatcherEditor() { this(new BasicEventList>()); } /** * Get the {@link EventList} of {@link MatcherEditor}s that make up this * {@link CompositeMatcherEditor}. The {@link EventList} * must never contain any null values and all elements must * implement {@link MatcherEditor}. */ public EventList> getMatcherEditors() { return matcherEditors; } /** * Rebuild the CompositeMatcher modelled by this editor. */ private Matcher rebuildMatcher() { final Matcher[] matchers = new Matcher[matcherEditors.size()]; for (int i = 0, n = matcherEditors.size(); i < n; i++) { matchers[i] = matcherEditors.get(i).getMatcher(); } if(mode == AND) return Matchers.and(matchers); else if(mode == OR) return Matchers.or(matchers); else throw new IllegalStateException(); } /** * Handle changes to the MatcherEditors. */ private class MatcherEditorsListListener implements ListEventListener> { public void listChanged(ListEvent> listChanges) { // update listeners for the list change boolean inserts = false; boolean deletes = false; boolean wasEmpty = matcherEditorListeners.isEmpty(); while (listChanges.next()) { int index = listChanges.getIndex(); int type = listChanges.getType(); // when a MatcherEditor is added, listen to it if(type == ListEvent.INSERT) { MatcherEditor inserted = matcherEditors.get(index); matcherEditorListeners.add(new DelegateMatcherEditorListener(inserted)); inserts = true; // when a MatcherEditor is removed, stop listening to it } else if(type == ListEvent.DELETE) { DelegateMatcherEditorListener listener = matcherEditorListeners.remove(index); listener.stopListening(); deletes = true; // when a MatcherEditor is updated, update the listener } else if(type == ListEvent.UPDATE) { MatcherEditor updated = matcherEditors.get(index); DelegateMatcherEditorListener listener = matcherEditorListeners.get(index); listener.setMatcherEditor(updated); inserts = true; deletes = true; } } boolean isEmpty = matcherEditorListeners.isEmpty(); // fire events if(mode == AND) { if(inserts && deletes) { fireChanged(rebuildMatcher()); } else if(inserts) { fireConstrained(rebuildMatcher()); } else if(deletes) { if(isEmpty) fireMatchAll(); else fireRelaxed(rebuildMatcher()); } } else if(mode == OR) { if(inserts && deletes) { fireChanged(rebuildMatcher()); } else if(inserts) { if(wasEmpty) fireConstrained(rebuildMatcher()); else fireRelaxed(rebuildMatcher()); } else if(deletes) { if(isEmpty) fireMatchAll(); else fireConstrained(rebuildMatcher()); } } else { throw new IllegalStateException(); } } } /** * Set the match mode for this {@link CompositeMatcherEditor}. * * @param mode either CompositeMatcherEditor.AND to match all * CompositeMatcherEditor.OR to match any. */ public void setMode(int mode) { if(this.mode == mode) return; int oldMode = this.mode; this.mode = mode; // don't fire events if there's no filters if(matcherEditors.isEmpty()) { return; // requiring all to requiring any is a relax } else if(oldMode == AND && mode == OR) { fireRelaxed(rebuildMatcher()); // requiring any to requiring all is a constrain } else if(oldMode == OR && mode == AND) { fireConstrained(rebuildMatcher()); // we don't support this mode } else { throw new IllegalArgumentException(); } } /** * Get the match mode for this {@link CompositeMatcherEditor}. * * @return either CompositeMatcherEditor.AND for match all * CompositeMatcherEditor.OR for match any. */ public int getMode() { return mode; } /** * Listens to a specific MatcherEditor and fires events as that MatcherEditor changes. */ private class DelegateMatcherEditorListener implements Listener { /** the matcher editor this listens to */ private MatcherEditor source; /** * This implementation of this method simply delegates the handling of * the given matcherEvent to one of the protected methods * defined by this class. This clearly separates the logic for each * type of Matcher change. * * @param matcherEvent a MatcherEvent describing the change in the * Matcher produced by the MatcherEditor */ public void changedMatcher(MatcherEditor.Event matcherEvent) { switch (matcherEvent.getType()) { case Event.CONSTRAINED: this.constrained(); break; case Event.RELAXED: this.relaxed(); break; case Event.CHANGED: this.changed(); break; case Event.MATCH_ALL: this.matchAll(); break; case Event.MATCH_NONE: this.matchNone(); break; } } /** * Create a new listener for the specified MatcherEditor. Listening is * started automatically and should be stopped using {@link #stopListening()}. */ private DelegateMatcherEditorListener(MatcherEditor source) { this.source = source; source.addMatcherEditorListener(this); } private void matchAll() { if(matcherEditors.size() == 1) fireMatchAll(); // optimization else fireRelaxed(rebuildMatcher()); } private void matchNone() { if(matcherEditors.size() == 1) fireMatchNone(); // optimization else fireConstrained(rebuildMatcher()); } private void changed() { fireChanged(rebuildMatcher()); } private void constrained() { fireConstrained(rebuildMatcher()); } private void relaxed() { fireRelaxed(rebuildMatcher()); } /** * Start listening to events from the MatcherEditor. */ public void setMatcherEditor(MatcherEditor source) { if(this.source == source) return; stopListening(); this.source = source; source.addMatcherEditorListener(this); } /** * Stop listening to events from the MatcherEditor. */ public void stopListening() { source.removeMatcherEditorListener(this); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/matchers/Matcher.java0000644000175000017500000000224612106516400030166 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.matchers; /** * Determines which values should be filtered. * *

      For best safety, implementations of {@link Matcher} should be * immutable. This * guarantees that {@link ca.odell.glazedlists.FilterList}s can safely call * {@link #matches(Object) matches()} without synchronization. * *

      In order to create dynamic filtering, use a * {@link ca.odell.glazedlists.matchers.MatcherEditor}, which * can create immutable {@link Matcher} Objects each time the matching constraints * change. * * @author Rob Eden * @see ca.odell.glazedlists.FilterList * @see ca.odell.glazedlists.matchers.MatcherEditor */ public interface Matcher { /** * Return true if an item matches a filter. * * @param item The item possibly being filtered. */ public boolean matches(E item); }././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/matchers/SearchEngineTextMatcherEditor.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/matchers/SearchEngineTextMatcherEdit0000644000175000017500000001516512106516400033201 0ustar gregoagregoapackage ca.odell.glazedlists.matchers; import ca.odell.glazedlists.TextFilterable; import ca.odell.glazedlists.TextFilterator; import ca.odell.glazedlists.impl.filter.SearchTerm; import ca.odell.glazedlists.impl.filter.TextMatcher; import ca.odell.glazedlists.impl.filter.TextMatchers; import java.io.Serializable; import java.util.HashSet; import java.util.Set; /** * A MatcherEditor that matches Objects against search text in a format similiar * to search engines. It supports fielded data and search terms (city:Toronto), * phrases (city:"New York"), the "+" or required operator as * well as the "-" or prohibit operator. *

      * This MatcherEditor is fully concrete, but GUI toolkit agnostic, as the search * text is passed into the {@link #refilter(String) refilter} method. *

      *

      * Subclasses for Swing and SWT applications are provided that present a text * filtering interface similar to that of Google and other search engines. *

      * * @see #refilter(String) * @author James Lemieux * @author Holger Brands */ public class SearchEngineTextMatcherEditor extends TextMatcherEditor { /** * the Set of Fields recognized by this TextMatcherEditor when the input * text is parsed into SearchTerms. */ private final Set> fields = new HashSet>(); /** * Creates a SearchEngineTextMatcherEditor whose Matchers can test only * elements which implement the {@link TextFilterable} interface. */ public SearchEngineTextMatcherEditor() { super(); } /** * Creates a SearchEngineTextMatcherEditor with the given * textFilterator. * * @param textFilterator an object capable of producing Strings from the * objects being filtered. If textFilterator is * null then all filtered objects are expected to * implement {@link ca.odell.glazedlists.TextFilterable}. */ public SearchEngineTextMatcherEditor(TextFilterator textFilterator) { super(textFilterator); } /** * Replaces the current set of search fields. This method does not trigger a refilter. * * @param fields the new search fields to use * * @see #refilter(String) */ public void setFields(Set> fields) { this.fields.clear(); this.fields.addAll(fields); } /** * @return a copy of the defined search fields */ public Set> getFields() { return new HashSet>(fields); } /** * Creates and applies a new {@link TextMatcher} based on the given input * text. * * @param inputText input text (not null) that is parsed * into search terms for the new text matcher * @todo explain the supported syntax for the input text in detail */ public void refilter(String inputText) { final SearchTerm[] filterTerms = TextMatchers.parse(inputText, getFields()); setTextMatcher(new TextMatcher(filterTerms, getFilterator(), getMode(), getStrategy())); } /** * A Field object contains information specific to a given field found * within the Objects being text matched. Specifically, a Field object * describes two distinct things: *
        *
      1. what the text is that identifies this Field when parsing the input text of * the {@link #refilter(String) refilter} method *
      2. what TextFilterator to use when extracting all values to text search * when matching an Object *
      * For example, the input text "city:Toronto" indicates that the text * "Toronto" should only be matched against the values of the "city" field * within the Objects being searched. As such, a Field object with "city" as * its name and a TextFilterator that only returns the value of the "city" * field from the Objects being text matched must be present in the Set of * Field objects on the {@link SearchEngineTextMatcherEditor}. */ public static final class Field implements Serializable { /** * The text which which uniquely identifies this Field relative to all * other registered Field objects. */ private final String name; /** * The TextFilterator that extracts only the field values to be * considered when matching a given SearchTerm. */ private final TextFilterator textFilterator; /** * Creates a field with a name and {@link TextFilterator}. * * @param name uniquely identifies this Field relative to all other * registered Field objects * @param textFilterator extracts only the field values to be considered * when matching a given SearchTerm */ public Field(String name, TextFilterator textFilterator) { if (name == null) throw new IllegalArgumentException("name may not be null"); if (textFilterator == null) throw new IllegalArgumentException("textFilterator may not be null"); this.name = name; this.textFilterator = textFilterator; } /** * Returns the text to be located which uniquely identifies this Field. * For example, if this method returns "city", then filter text of * "city:Toronto", when parsed, would construct a SearchTerm for * "Toronto" that reports this Field object from * {@link SearchTerm#getField()}. */ public String getName() { return name; } /** * Returns the TextFilterator capable of extracting only the fields that * should be considered by SearchTerms using this Field. It is this * TextFilterator that contains the custom logic to return a much * smaller subset of the total text-searchable fields on the object. * Often the TextFilterators returned by this method only report the * value of a single field from the Object being matched. */ public TextFilterator getTextFilterator() { return textFilterator; } /** @inheritDoc */ @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Field field = (Field) o; return name.equals(field.name); } /** @inheritDoc */ @Override public int hashCode() { return name.hashCode(); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/matchers/ThreadedMatcherEditor.java0000644000175000017500000003026012106516400032773 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.matchers; import java.util.Iterator; import java.util.LinkedList; import java.util.List; /** * A MatcherEditor which decorates a source MatcherEditor with functionality. * Specifically, this MatcherEditor is meant to act as a buffer for * MatcherEvents to smooth out disparities between the rate at which * MatcherEvents are produced by the source MatcherEditor and the rate at * which they are consumed by registered MatcherEditorListeners.

      * * Internally, a {@link ThreadedMatcherEditor} enqueues MatcherEvents as they * they are received from the source MatcherEditor. The MatcherEvents on the * queue are fired by another Thread as rapidly as the MatcherEditorListeners * can consume them. Two methods exist on this class which enable subclasses * to customize aspects of processing queued MatcherEvents: * *

        *
      1. {@link #executeMatcherEventQueueRunnable(Runnable)} is consulted when * a Thread must be selected to execute the given Runnable which will * drain the queue of MatcherEvents. Subclasses may override to * customize which Thread is used. * *
      2. {@link #coalesceMatcherEvents(List)} is used to compress * many enqueued MatcherEvents into a single representative * MatcherEvent. This implies a contract between all registered * MatcherEditorListeners and this {@link ThreadedMatcherEditor} that * guarantees that processing the coalesced MatcherEvent is equivalent * to processing all MatcherEvents sequentially. *
      * * Typical usage patterns of ThreadedMatcherEditor resemble: * *
       *   MatcherEditor threadedMatcherEditor = new ThreadedMatcherEditor(new AnyMatcherEditor());
       *   FilterList filterList = new FilterList(new BasicEventList(), threadedMatcherEditor);
       * 
      * * @author James Lemieux */ public class ThreadedMatcherEditor extends AbstractMatcherEditorListenerSupport { /** The underlying MatcherEditor whose MatcherEvents are being queued and fired on an alternate Thread. */ private final MatcherEditor source; /** * The LinkedList acting as a queue of MatcherEditor.Event in the order in which they are received * from {@link #source}. We take great care to ensure that the queue's monitor is held before it * is queried or mutated. */ private final List> matcherEventQueue = new LinkedList>(); /** * The MatcherEditorListener which reacts to MatcherEvents from the {@link #source} * by enqueuing them for firing on another Thread at some later time. */ private MatcherEditor.Listener queuingMatcherEditorListener = new QueuingMatcherEditorListener(); /** * true indicates a Thread is currently executing the * {@link #drainMatcherEventQueueRunnable} to drain the {@link #matcherEventQueue}. */ private boolean isDrainingQueue = false; /** * The {@link Runnable} containing the logic to drain the queue of MatcherEvents until it is empty. * The Runnable is executed on a Thread using {@link #executeMatcherEventQueueRunnable(Runnable)}. */ private Runnable drainMatcherEventQueueRunnable = new DrainMatcherEventQueueRunnable(); /** * Creates a ThreadedMatcherEditor which wraps the given source. * MatcherEvents fired from the source will be enqueued within * this MatcherEditor until they are processed on an alternate Thread. * * @param source the MatcherEditor to wrap with buffering functionality * @throws NullPointerException if source is null */ public ThreadedMatcherEditor(MatcherEditor source) { if (source == null) throw new NullPointerException("source may not be null"); this.source = source; this.source.addMatcherEditorListener(this.queuingMatcherEditorListener); } /** * Returns the current Matcher specified by the source {@link MatcherEditor}. * * @return the current Matcher specified by the source {@link MatcherEditor} */ public Matcher getMatcher() { return this.source.getMatcher(); } /** * This method implements the strategy for coalescing many queued * MatcherEvents into a single representative MatcherEvent. Listeners which * process the MatcherEvent returned from this method should match the state * that would exist if each of the matcherEvents were fired * sequentially. In general, any group of matcherEvents can be * succesfully coalesced as a single MatcherEvent with a type of * changed, however, this method's default implementation * uses a few heuristics to do more intelligent coalescing in order to * gain speed improvements: * *
        *
      1. if matcherEvents ends in a MatcherEvent which is a * {@link MatcherEditor.Event#MATCH_ALL} or {@link MatcherEditor.Event#MATCH_NONE} * type, the last MatcherEvent is returned, regardless of previous * MatcherEvents

        * *

      2. if matcherEvents only contains a series of * monotonically constraining MatcherEvents, the final MatcherEvent * is returned

        * *

      3. if matcherEvents only contains a series of * monotonically relaxing MatcherEvents, the final MatcherEvent is * returned

        * *

      4. if matcherEvents contains both constraining and * relaxing MatcherEvents, the final MatcherEvent is returned with * its type as {@link MatcherEditor.Event#CHANGED} *
      * * Note that 1, 2, and 3 above merely represent * safe optimizations of the type of MatcherEvent that can be returned. * It could also have been returned as a MatcherEvent with a type of * {@link MatcherEditor.Event#CHANGED} and be assumed to work correctly, though * potentially less efficiently, since it is a more generic type of change. *

      * * Subclasses with the ability to fire precise MatcherEvents with fine grain * types (i.e. relaxed or constrained) when * coalescing matcherEvents in situations not recounted above * may do so by overiding this method. * * @param matcherEvents an array of MatcherEvents recorded in the order * they were received from the source MatcherEditor * @return a single MatcherEvent which, when fired, will result in the * same state as if all matcherEvents had been fired * sequentially */ protected Event coalesceMatcherEvents(List> matcherEvents) { boolean changeType = false; // fetch the last matcher event - it is the basis of the MatcherEvent which must be returned // all that remains is to determine the type of the MatcherEvent to return final Event lastMatcherEvent = matcherEvents.get(matcherEvents.size()-1); final int lastMatcherEventType = lastMatcherEvent.getType(); // if the last MatcherEvent is a MATCH_ALL or MATCH_NONE type, we can safely return it immediately if (lastMatcherEventType != Event.MATCH_ALL && lastMatcherEventType != Event.MATCH_NONE) { // otherwise determine if any constraining and/or relaxing MatcherEvents exist boolean constrained = false; boolean relaxed = false; for (Iterator> i = matcherEvents.iterator(); i.hasNext(); ) { switch (i.next().getType()) { case Event.MATCH_ALL: relaxed = true; break; case Event.MATCH_NONE: constrained = true; break; case Event.RELAXED: relaxed = true; break; case Event.CONSTRAINED: constrained = true; break; case Event.CHANGED: constrained = relaxed = true; break; } } changeType = constrained && relaxed; } // if both constraining and relaxing MatcherEvents exist, ensure we must return a CHANGED MatcherEvent // otherwise the last MatcherEvent must represent the coalesced MatcherEvent return new MatcherEditor.Event(this, changeType ? Event.CHANGED : lastMatcherEventType, lastMatcherEvent.getMatcher()); } /** * This method executes the given runnable on a Thread. The * particular Thread chosen to execute the Runnable is left as an * implementation detail. By default, a new Thread named * MatcherQueueThread is constructed to execute the * runnable each time this method is called. Subclasses may * override this method to use any Thread selection strategy they wish. * * @param runnable a Runnable to execute on an alternate Thread */ protected void executeMatcherEventQueueRunnable(Runnable runnable) { new Thread(runnable, "MatcherQueueThread").start(); } /** * This MatcherEditorListener enqueues each MatcherEvent it receives in the * order it is received and then schedules a Runnable to drain the queue of * MatcherEvents as soon as possible. */ private class QueuingMatcherEditorListener implements MatcherEditor.Listener { public void changedMatcher(Event matcherEvent) { synchronized(matcherEventQueue) { matcherEventQueue.add(matcherEvent); // if necessary, start a Thread to drain the queue if (!isDrainingQueue) { isDrainingQueue = true; executeMatcherEventQueueRunnable(drainMatcherEventQueueRunnable); } } } } /** * This Runnable contains logic which continues to process batches of * MatcherEvents from the matcherEventQueue until the queue is empty. Each * batch of MatcherEvents includes all MatcherEvents available at the time * the queue is inspected. The MatcherEvents are then coalesced and the * resulting singular MatcherEvent is fired to MatcherEditorListeners * attached to this ThreadedMatcherEditor on a different Thread. When the * fire method returns, the queue is drained again if it has accumulated * MatcherEvents otherwise the DrainMatcherEventQueueRunnable exits. */ private class DrainMatcherEventQueueRunnable implements Runnable { public void run() { while (true) { // acquire the monitor that guards assigning the drainMatcherEventQueueRunnable // to a processing Thread as well as exiting the drainMatcherEventQueueRunnable final Event matcherEvent; synchronized (matcherEventQueue) { // if no work exists in the queue, exit the Runnable if (matcherEventQueue.isEmpty()) { // no matter the circumstance for us exiting the Runnable, // ensure we indicate we are no longer draining the queue isDrainingQueue = false; return; } // fetch a copy of all MatcherEvents currently in the queue matcherEvent = coalesceMatcherEvents(matcherEventQueue); matcherEventQueue.clear(); } try { // coalesce all of the current MatcherEvents to a single representative MatcherEvent // and fire the single coalesced MatcherEvent fireChangedMatcher(matcherEvent); } catch(Error e) { synchronized(matcherEventQueue) { isDrainingQueue = false; } throw e; } catch(RuntimeException e) { synchronized(matcherEventQueue) { isDrainingQueue = false; } throw e; } } } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/matchers/AbstractMatcherEditor.java0000644000175000017500000000615612106516400033025 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.matchers; /** * Basic building block for {@link MatcherEditor} implementations that * handles the details of dealing with registered {@link MatcherEditor.Listener}s. * All {@link MatcherEditor} implementations should extend this class for its * convenience methods. * *

      Extending classes can fire events to registered listeners using the * "fire" methods: *

        *
      • {@link #fireMatchNone()}
      • *
      • {@link #fireConstrained(Matcher)}
      • *
      • {@link #fireChanged(Matcher)}
      • *
      • {@link #fireRelaxed(Matcher)}
      • *
      • {@link #fireMatchAll()}
      • *
      * * @author Rob Eden */ public abstract class AbstractMatcherEditor extends AbstractMatcherEditorListenerSupport { /** the current Matcher in effect */ private Matcher currentMatcher = Matchers.trueMatcher(); /** {@inheritDoc} */ public final Matcher getMatcher() { return currentMatcher; } /** * Indicates that the filter matches all. */ protected final void fireMatchAll() { currentMatcher = Matchers.trueMatcher(); fireChangedMatcher(createMatchAllEvent(currentMatcher)); } /** * Indicates that the filter has changed in an indeterminate way. */ protected final void fireChanged(Matcher matcher) { if (matcher == null) throw new NullPointerException(); currentMatcher = matcher; fireChangedMatcher(createChangedEvent(currentMatcher)); } /** * Indicates that the filter has changed to be more restrictive. This should only be * called if all currently filtered items will remain filtered. */ protected final void fireConstrained(Matcher matcher) { if (matcher == null) throw new NullPointerException(); currentMatcher = matcher; fireChangedMatcher(createConstrainedEvent(currentMatcher)); } /** * Indicates that the filter has changed to be less restrictive. This should only be * called if all currently unfiltered items will remain unfiltered. */ protected final void fireRelaxed(Matcher matcher) { if (matcher == null) throw new NullPointerException(); currentMatcher = matcher; fireChangedMatcher(createRelaxedEvent(currentMatcher)); } /** * Indicates that the filter matches none. */ protected final void fireMatchNone() { currentMatcher = Matchers.falseMatcher(); fireChangedMatcher(createMatchNoneEvent(currentMatcher)); } /** * Returns true if the current matcher will match everything. */ protected final boolean isCurrentlyMatchingAll() { return currentMatcher == Matchers.trueMatcher(); } /** * Returns true if the current matcher will match nothing. */ protected final boolean isCurrentlyMatchingNone() { return currentMatcher == Matchers.falseMatcher(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/matchers/ThresholdMatcherEditor.java0000644000175000017500000002531612106516400033215 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ /* StarLight Systems */ package ca.odell.glazedlists.matchers; import ca.odell.glazedlists.FunctionList; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.impl.GlazedListsImpl; import java.util.Comparator; /** * A {@link MatcherEditor} that filters elements based on whether they are * greater than or less than a threshold. The implementation is based on * elements implementing {@link Comparable} unless the constructor specifies * a {@link Comparator}. * * By default the elements themselves are compared with the threshold value, * however, an optional Function can be provided which can be used to extract * a value that is appropriate for comparison with the threshold. In this way, * ThreshholdMatcherEditor provides a level of indirection when locating the * exact value to compare for a given element. * * @author Rob Eden */ public class ThresholdMatcherEditor extends AbstractMatcherEditor { public static final MatchOperation GREATER_THAN = new MatchOperation(1, false); public static final MatchOperation GREATER_THAN_OR_EQUAL = new MatchOperation(1, true); public static final MatchOperation LESS_THAN = new MatchOperation(-1, false); public static final MatchOperation LESS_THAN_OR_EQUAL = new MatchOperation(-1, true); public static final MatchOperation EQUAL = new MatchOperation(0, true); public static final MatchOperation NOT_EQUAL = new MatchOperation(0, false); private MatchOperation currentMatcher; private Comparator comparator; private MatchOperation operation; private T threshold; private FunctionList.Function function; /** * Construct an instance that will require elements to be greater than the * threshold (which is not initially set) and relies on the threshold * object and elements in the list implementing {@link Comparable}. */ public ThresholdMatcherEditor() { this(null); } /** * Construct an instance that will require elements to be greater than the * given threshold and relies on the threshold object and elements in the * list implementing {@link Comparable}. * * @param threshold the initial threshold, or null if none. */ public ThresholdMatcherEditor(T threshold) { this(threshold, null); } /** * Construct an instance that will require elements to be greater than the * given threshold and relies on the threshold object and elements in the * list implementing {@link Comparable}. * * @param threshold the initial threshold, or null if none. * @param operation the operation to determine what relation list elements * should have to the threshold in order to match (i.e., be visible). * Specifying null will use {@link #GREATER_THAN}. */ public ThresholdMatcherEditor(T threshold, MatchOperation operation) { this(threshold, operation, null); } /** * Construct an instance. * * @param threshold rhe initial threshold, or null if none. * @param operation rhe operation to determine what relation list elements * should have to the threshold in order to match (i.e., be visible). * Specifying null will use {@link #GREATER_THAN}. * @param comparator determines how objects compare. If null, the threshold * object and list elements must implement {@link Comparable}. */ public ThresholdMatcherEditor(T threshold, MatchOperation operation, Comparator comparator) { this(threshold, operation, comparator, null); } /** * Construct an instance. * * @param threshold the initial threshold, or null if none. * @param operation the operation to determine what relation list elements * should have to the threshold in order to match (i.e., be visible). * Specifying null will use {@link #GREATER_THAN}. * @param comparator determines how objects compare with the threshold value. * If null, the threshold object and list elements must implement * {@link Comparable}. * @param function an optional Function which produces a value fit to be * compared against the threshold. This argument is optional, and if * it is null, the raw values will compared against the * threshold. */ public ThresholdMatcherEditor(T threshold, MatchOperation operation, Comparator comparator, FunctionList.Function function) { if (operation == null) operation = GREATER_THAN; if (comparator == null) comparator = (Comparator) GlazedLists.comparableComparator(); if (function == null) function = (FunctionList.Function) GlazedListsImpl.identityFunction(); this.operation = operation; this.comparator = comparator; this.threshold = threshold; this.function = function; // if this is our first matcher, it's automatically a constrain currentMatcher = operation.instance(comparator, threshold, function); fireChanged(currentMatcher); } /** * Update the threshold used to determine what is matched by the list. This coupled * with the {@link #setMatchOperation match operation} determines what's matched. * * @param threshold The threshold, or null to match everything. */ public void setThreshold(T threshold) { this.threshold = threshold; rebuildMatcher(); } /** * See {@link #getThreshold()}. */ public T getThreshold() { return threshold; } /** * Update the operation used to determine what relation list elements should * have to the threshold in order to match (i.e. be visible). Must be non-null. * * @see #GREATER_THAN * @see #GREATER_THAN_OR_EQUAL * @see #LESS_THAN * @see #LESS_THAN_OR_EQUAL * @see #EQUAL * @see #NOT_EQUAL */ public void setMatchOperation(MatchOperation operation) { if (operation == null) throw new IllegalArgumentException("Operation cannot be null"); this.operation = operation; rebuildMatcher(); } /** * See {@link #setMatchOperation}. */ public MatchOperation getMatchOperation() { return operation; } /** * Update the comparator. Setting to null will require that thresholds and elements in * the list implement {@link Comparable}. */ public void setComparator(Comparator comparator) { if (comparator == null) comparator = (Comparator) GlazedLists.comparableComparator(); this.comparator = comparator; rebuildMatcher(); } public Comparator getComparator() { return comparator; } /** {@inheritDoc} */ private void rebuildMatcher() { final MatchOperation newMatcher = operation.instance(comparator, threshold, function); // otherwise test how the matchers relate final boolean moreStrict = newMatcher.isMoreStrict(currentMatcher); final boolean lessStrict = currentMatcher.isMoreStrict(newMatcher); // if they're equal we're done and we won't change the matcher if (!moreStrict && !lessStrict) return; // otherwise, fire the appropriate event currentMatcher = newMatcher; if (moreStrict && lessStrict) fireChanged(currentMatcher); else if (moreStrict) fireConstrained(currentMatcher); else fireRelaxed(currentMatcher); } /** * A {@link MatchOperation} serves as both a {@link Matcher} in and of itself * and as an enumerated type representing its type as an operation. */ private static class MatchOperation implements Matcher { /** the comparator to compare values against */ protected final Comparator comparator; /** the pivot value to compare with */ protected final T threshold; /** either 1 for greater, 0 for equal, or -1 for less than */ private final int polarity; /** either true for equal or false for not equal */ private final boolean inclusive; /** a function which produces a comparable value for a given element */ private final FunctionList.Function function; private MatchOperation(Comparator comparator, T threshold, int polarity, boolean inclusive, FunctionList.Function function) { this.comparator = comparator; this.threshold = threshold; this.polarity = polarity; this.inclusive = inclusive; this.function = function; } private MatchOperation(int polarity, boolean inclusive) { this(null, null, polarity, inclusive, (FunctionList.Function) GlazedListsImpl.identityFunction()); } /** * Factory method to create a {@link MatchOperation} of the same type * as this {@link MatchOperation}. */ private MatchOperation instance(Comparator comparator, T threshold, FunctionList.Function function) { return new MatchOperation(comparator, threshold, polarity, inclusive, function); } /** * Compare this to another {@link MatchOperation}. * * @return true if there exists some Object i such that this.matches(i) * is false when other.matches(i) is * true. Two MatcherOperations can be mutually more * strict than each other. */ boolean isMoreStrict(MatchOperation other) { if(other.polarity != polarity) return true; if(other.comparator != comparator) return true; if(other.threshold == threshold) { if(polarity == 0) return other.inclusive != inclusive; else return (other.inclusive && !inclusive); } else { if(polarity == 0) return true; else if(!matchesThreshold(other.threshold)) return true; } return false; } /** {@inheritDoc} */ public boolean matches(E item) { return matchesThreshold(function.evaluate(item)); } public boolean matchesThreshold(T t) { // compare the extracted value with the threshold final int compareResult = comparator.compare(t, threshold); // item equals threshold, match <=, == and >= if(compareResult == 0) return inclusive; // for == and !=, handle the case when the item is not equal to threshold if(polarity == 0) return !inclusive; // item is below threshold and match <, <= or item is above and match >, >= return ((compareResult < 0) == (polarity < 0)); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/event/0000755000175000017500000000000012106516374025261 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/event/ListEventPublisher.java0000644000175000017500000000605612106516374031726 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.event; // the core Glazed Lists package import ca.odell.glazedlists.CompositeList; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.ListSelection; /** * Define a strategy for managing dependencies in the observer pattern. * * @author Jesse Wilson */ public interface ListEventPublisher { /** * Requires that the specified {@link EventList} be updated before the * specified {@link ListEventListener} which depends on it. Dependencies are * automatically managed by most {@link EventList}s, so this method shall only * be used for {@link EventList}s that have indirect dependencies. * * @deprecated replaced with {@link #setRelatedSubject}, which has different * semantics and takes different arguments, but accomplishes the same goal */ void addDependency(EventList dependency, ListEventListener listener); /** * Removes the specified {@link EventList} as a dependency for the specified * {@link ListEventListener}. This {@link ListEventListener} will continue to * receive {@link ListEvent}s, but there will be no dependency tracking when * such events are fired. * * @deprecated replaced with {@link #clearRelatedSubject}, which has different * semantics and takes different arguments, but accomplishes the same goal */ void removeDependency(EventList dependency, ListEventListener listener); /** * Attach the specified listener to the specified subject, so that when * dependencies are being prepared, notifying the listener will be * considered equivalent to notifying the subject. This makes it possible * to support multiple listeners in a single subject, typically using * inner classes. * *

      For example, the {@link CompositeList} class uses multiple listeners * for a single subject, and uses this method to define that relationship. */ void setRelatedSubject(Object listener, Object relatedSubject); /** * Detach the listener from its related subject. */ void clearRelatedSubject(Object listener); /** * Attach the specified subject to the specified listener, so that the * listener's dependencies are satisfied before the subject is notified. * This makes it possible for a single listener to have multiple subjects, * typically using inner classes. * *

      For example, the {@link ListSelection} class uses a single listener * for multiple subjects (selected and unselected), and uses this method * to define that relationship. */ void setRelatedListener(Object subject, Object relatedListener); /** * Detach the subject from its related listener. */ void clearRelatedListener(Object subject, Object relatedListener); }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/event/ListEvent.java0000644000175000017500000003246012106516374030046 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.event; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.SortedList; import ca.odell.glazedlists.TransformedList; import java.util.EventObject; /** * A ListEvent models a sequence of changes to an {@link EventList}. A * {@link ListEventListener} can be registered on an {@link EventList} to * observe and handle these ListEvents. * *

      * A simple change is characterized by its type and the corresponding * list index. The type indicates the {@link #INSERT}, {@link #UPDATE} or * {@link #DELETE} operation that took place at the specified index. * *

      * Here are some examples: *

        *
      • {@code eventList.add(0, value)} produces an {@link #INSERT} at index 0 * ("I0")
      • *
      • {@code eventList.add(value)} on a list with size 5 produces an * {@link #INSERT} at index 5 ("I5")
      • *
      • {@code eventList.remove(3)} on a list with appropriate size produces a * {@link #DELETE} at index 3 ("D3")
      • *
      • {@code eventList.remove(value)} on a list which contains the value at * index 2 produces a {@link #DELETE} at index 2 ("D2")
      • *
      • {@code eventList.clear()} on a list with size 1 produces a * {@link #DELETE} at index 0 ("D0")
      • *
      • {@code eventList.set(1, value)} on a list with appropriate size produces * an {@link #UPDATE} at index 1 ("U1")
      • *
      * *

      * A ListEvent is capable of representing a sequence of such changes. Consider * this example: * *

       * EventList<String> list = GlazedLists.eventListOf("A", "D", "E");
       * list.addAll(1, GlazedLists.eventListOf("B", "C"));
       * 
      * * This operation inserts element "B" at index 1 ("I1") and element "C" at index * 2 ("I2"). These two changes are not represented as two separate ListEvents * but as a sequence of changes ("I1", "I2") within one ListEvent. Because of * this, the ListEvent is accessed like an iterator with the user iterating over * the contained sequence of changes, see below. * * Here is another example: * *
       * EventList<String> list = GlazedLists.eventListOf("A", "B", "C", "B");
       * list.removeAll(GlazedLists.eventListOf("A", "C"));
       * 
      * * This will remove element "A" at index 0 and element "C" at original index 2. * But the corresponding ListEvent looks like ("D0","D1"). The list index of the * second deletion is automatically adjusted, taking the first deletion into * account. * *

      * Note, that the sequence of changes is ordered by ascending list indexes. * *

      * The typical pattern to iterate over a ListEvent is: * *

       * ListEvent listChanges = ...
       * while (listChanges.next()) {
       *     final int type = listChanges.getType());
       *     final int index = listChanges.getIndex();
       *     // handle insert, update or delete at index
       *     ...
       * }
       * 
      * * If you need to iterate over a ListEvent again, that's of course possible, * just {@link #reset() reset} the ListEvent and iterate again. * *

      * In addition to simple changes, ListEvent supports the view on list changes as * blocks. This basically means, that simple changes of one particular type that * build a continuous range of indexes are grouped together. Consider this * example: * *

       * EventList<String> list = GlazedLists.eventListOf("A", "A", "B", "B", "C", "C");
       * list.removeAll(GlazedLists.eventListOf("A", "C"));
       * 
      * * This deletes all occurences of elements "A" and "C" from the list. So there * is a deletion from index 0 to index 1 (inclusive) and another deletion from * index 2 to 3. So, instead of modeling each change one by one like ("D0", * "D0", "D2", "D2"), you can view the changes in blocks "D0-1" and "D2-3". This * view is exactly what ListEvent offers by iterating the changes in blocks: * *
       * ListEvent listChanges = ...
       * while (listChanges.nextBlock()) {
       *     final int type = listChanges.getType());
       *     final int startIndex = listChanges.getBlockStartIndex();
       *     final int endIndex = listChanges.getBlockEndIndex();
       *     // handle insert, update or delete from startIndex to endIndex
       *     ...
       * }
       * 
      * * In the above example you would have two blocks of changes of type * {@link #DELETE} instead of four simple changes. * * It is up to you, which iteration style you choose. Handling blocks of changes * might yield a better performance. * *

      * While it is possible to change the style during one iteration, it is not * recommended, because you have to be careful to not miss some changes. * Refering to the last example above, the following code would skip the change * "D1": * *

       * ListEvent listChanges = ...// ("D0", "D0", "D2", "D2") vs. ("D0-1", "D2-3")
       * if (listChanges.next()) {
       *     final int type = listChanges.getType();
       *     final int index = listChanges.getIndex();
       *     // handle delete at index 0
       *     // ...
       *     while (listChanges.nextBlock()) {// move to next block starting at index 2, skipping change at index 1, which is part of the first block !
       *         final int type2 = listChanges.getType();
       *         final int startIndex = listChanges.getBlockStartIndex();
       *         final int endIndex = listChanges.getBlockEndIndex();
       *         // handle deletion from startIndex 2 to endIndex 3
       *         ...
       *     }
       * }
       * 
      * * This kind of code is unusual, error-prone and should be avoided. * *

      * There is a special kind of change, a {@link #isReordering() reordering} * ListEvent. It indicates a reordering of the list elements, for example * triggered by setting a new comparator on a {@link SortedList}. * *

      * A reorder event cannot contain other regular changes in the current implementation. * Instead it provides a {@link #getReorderMap() reorder map}, which maps the * new indexes of the list elements to the old indexes. For details of the * mapping see the javadoc of method {@link #getReorderMap()}. * *

      * {@link ListEventListener}s, that don't explicitly check for a reorder event, * will observe a deletion of all list elements with a subsequent re-insertion * instead. * *

      * In the future, ListEvent will provide even more information about the list * changes to be more self-contained: *

        *
      • for deletes, it will provide the deleted element with * {@link #getOldValue()} *
      • for inserts, it will provide the inserted element with * {@link #getNewValue()} *
      • for updates, it will provide the old and new element with * {@link #getOldValue()} and {@link #getNewValue()} *
      * * The methods are currently marked as deprecated and should not be used yet, * because the implementation is a work in progress. * *

      * Note, that providing the old and new elements has an impact on the * granularity of blocks of changes. For example, consider the clear operation * on a list: * *

       * EventList<String> list = GlazedLists.eventListOf("A", "B", "C");
       * list.clear();
       * 
      * * Without considering the old and new elements, the ListEvent would consist of * one block: a deletion from index 0 to 2 ("D0-2"). With the feature of * providing the deleted elements, the ListEvent cannot consist of one block * anymore, because of the additional requirement, that the old element must * have the same value in one block: * *
       * ListEvent listChanges = ...
       * while (listChanges.nextBlock()) {
       *     final int type = listChanges.getType());
       *     final int startIndex = listChanges.getBlockStartIndex();
       *     final int endIndex = listChanges.getBlockEndIndex();
       *     final Object oldValue = listChanges.getOldValue();
       *     final Object newValue = listChanges.getNewValue();
       *     // handle insert, update or delete from startIndex to endIndex
       *     ...
       * }
       * 
      * * So, a sequence of simple changes can only be grouped as block, when the type, as well * as the old and new value are equal. * * @author Jesse Wilson */ public abstract class ListEvent extends EventObject { /** different types of changes */ public static final int DELETE = 0; public static final int UPDATE = 1; public static final int INSERT = 2; /** indicates a removed element whose value is unknown */ public static final Object UNKNOWN_VALUE = new String("UNKNOWN VALUE"); /** Returns a value indicating a removed element whose value is unknown. */ @SuppressWarnings("unchecked") public static final E unknownValue() { return (E) UNKNOWN_VALUE; } /** the list that has changed */ protected EventList sourceList; /** * Create a new list change sequence that uses the source master list * for the source of changes. */ ListEvent(EventList sourceList) { super(sourceList); // keep track of the origin sequence and list this.sourceList = sourceList; } /** * Create a bitwise copy of this {@link ListEvent}. */ public abstract ListEvent copy(); /** * Resets this event's position to the previously-marked position. This should * be used for {@link TransformedList}s that require multiple-passes of the * {@link ListEvent} in order to process it. */ public abstract void reset(); /** * Increments the change sequence to view the next change. This will * return true if such a change exists and false when there is no * change to view. */ public abstract boolean next(); /** * Without incrementing the implicit iterator, this tests if there is another * change to view. The user will still need to call next() to view * such a change. */ public abstract boolean hasNext(); /** * Increments the change sequence to view the next change block. */ public abstract boolean nextBlock(); /** * Tests if this change is a complete reordering of the list. *

      If it's a reordering, you can determine the changed element positions * with the help of the reorder map. * * @see #getReorderMap() */ public abstract boolean isReordering(); /** * Gets the reorder map of this list. Before calling this method, * you should check that {@link #isReordering()} returns true. * *

      The size of the returned array is equal to the list size. * The array value at index i is the previous index (before the reordering) * of the list element at index i (after the reordering). *

      * list before the reordering: "A", "B", "C" *

      * list after the reordering: "C", "B", "A" *

      * The reorder map of the corresponding ListEvent would look like: * map[0]=2; * map[1]=1; * map[2]=0 * * @return an array of integers where the previous index of a value is * stored at the current index of that value. * @throws IllegalStateException if this is not a reordering event * * @see #isReordering() */ public abstract int[] getReorderMap(); /** * Gets the current row index. If the block type is delete, this * will always return the startIndex of the current list change. */ public abstract int getIndex(); /** * Gets the first row of the current block of changes. Inclusive. */ public abstract int getBlockStartIndex(); /** * Gets the last row of the current block of changes. Inclusive. */ public abstract int getBlockEndIndex(); /** * Gets the type of the current change, which should be one of * ListEvent.INSERT, UPDATE, or DELETE. */ public abstract int getType(); /** * Gets the previous value for a deleted or updated element. If that data is * not available, this will return {@link ListEvent#UNKNOWN_VALUE}. * * @deprecated this is a developer preview API that is not * yet fit for human consumption. Hopefully the full implementation is * complete for Glazed Lists 2.0. */ public abstract E getOldValue(); /** * Gets the current value for an inserted or updated element. If that data is * not available, this will return {@link ListEvent#UNKNOWN_VALUE}. * * @deprecated this is a developer preview API that is not * yet fit for human consumption. Hopefully the full implementation is * complete for Glazed Lists 2.0. */ public abstract E getNewValue(); /** * Gets the number of blocks currently remaining in this atomic change. * * @deprecated this method depends on a particular implementation of * how list events are stored internally, and this implementation has * since changed. */ public abstract int getBlocksRemaining(); /** * Gets the List where this event originally occured. */ public EventList getSourceList() { return sourceList; } /** * Gets this event as a String. This simply iterates through all blocks * and concatenates them. */ @Override public abstract String toString(); }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/event/Tree4DeltasListEvent.java0000644000175000017500000001021412106516374032100 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.event; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.impl.event.BlockSequence; import ca.odell.glazedlists.impl.event.Tree4Deltas; /** * A list event that iterates {@link Tree4Deltas} as the * datastore. * * @author Jesse Wilson */ class Tree4DeltasListEvent extends ListEvent { private Tree4Deltas.Iterator deltasIterator; private BlockSequence.Iterator linearIterator; private ListEventAssembler deltasAssembler; public Tree4DeltasListEvent(ListEventAssembler deltasAssembler, EventList sourceList) { super(sourceList); this.deltasAssembler = deltasAssembler; } /** * Create a copy of this list event. */ @Override public ListEvent copy() { Tree4DeltasListEvent result = new Tree4DeltasListEvent(deltasAssembler, sourceList); result.deltasIterator = deltasIterator != null ? deltasIterator.copy() : null; result.linearIterator = linearIterator != null ? linearIterator.copy() : null; result.deltasAssembler = deltasAssembler; return result; } @Override public void reset() { // prefer to use the linear blocks, which are faster if(deltasAssembler.getUseListBlocksLinear()) { this.linearIterator = deltasAssembler.getListBlocksLinear().iterator(); this.deltasIterator = null; // otherwise use the deltas, which are more general } else { this.deltasIterator = deltasAssembler.getListDeltas().iterator(); this.linearIterator = null; } } @Override public boolean next() { if(linearIterator != null) return linearIterator.next(); else return deltasIterator.next(); } @Override public boolean hasNext() { if(linearIterator != null) return linearIterator.hasNext(); else return deltasIterator.hasNext(); } @Override public boolean nextBlock() { if(linearIterator != null) return linearIterator.nextBlock(); else return deltasIterator.nextNode(); } @Override public boolean isReordering() { return (deltasAssembler.getReorderMap() != null); } @Override public int[] getReorderMap() { int[] reorderMap = deltasAssembler.getReorderMap(); if(reorderMap == null) throw new IllegalStateException("Cannot get reorder map for a non-reordering change"); return reorderMap; } @Override public int getIndex() { if(linearIterator != null) return linearIterator.getIndex(); else return deltasIterator.getIndex(); } @Override public int getBlockStartIndex() { if(linearIterator != null) return linearIterator.getBlockStart(); else return deltasIterator.getIndex(); } @Override public int getBlockEndIndex() { if(linearIterator != null) return linearIterator.getBlockEnd() - 1; else return deltasIterator.getEndIndex() - 1; } @Override public int getType() { if(linearIterator != null) { return linearIterator.getType(); } else { return deltasIterator.getType(); } } @Override public E getOldValue() { if(linearIterator != null) { return (E)linearIterator.getOldValue(); } else { return (E)deltasIterator.getOldValue(); } } @Override public E getNewValue() { // TODO(jessewilson): return ListEvent.unknownValue(); } @Override public int getBlocksRemaining() { throw new UnsupportedOperationException(); } @Override public String toString() { if(linearIterator != null) { return "ListEvent: " + deltasAssembler.getListBlocksLinear().toString(); } else { return "ListEvent: " + deltasAssembler.getListDeltas().toString(); } } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/event/ListEventAssembler.java0000644000175000017500000004216512106516374031707 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.event; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.impl.Preconditions; import ca.odell.glazedlists.impl.WeakReferenceProxy; import ca.odell.glazedlists.impl.event.BlockSequence; import ca.odell.glazedlists.impl.event.Tree4Deltas; import java.util.ConcurrentModificationException; import java.util.List; /** * Models a continuous stream of changes on a list. Changes of the same type * that occur on a continuous set of rows are grouped into blocks * automatically for performance benefits. * *

      Atomic sets of changes may involve many lines of changes and many blocks * of changes. They are committed to the queue in one action. No other threads * should be creating a change on the same list change queue when an atomic * change is being created. * * @author Jesse Wilson */ public final class ListEventAssembler { /** the list that this tracks changes for */ protected EventList sourceList; /** non-null if an event is currently pending */ private Thread eventThread; /** the event level is the number of nested events */ protected int eventLevel = 0; /** whether to allow nested events */ protected boolean allowNestedEvents = true; /** the current reordering array if this change is a reorder */ protected int[] reorderMap = null; /** prefer to use the linear blocks, which are more performant but handle only a subset of all cases */ private BlockSequence blockSequence = new BlockSequence(); private boolean useListBlocksLinear = false; /** fall back to list tree4deltas, which are capable of all list changes */ private Tree4Deltas listDeltas = new Tree4Deltas(); private final SequenceDependenciesEventPublisher publisher; private final ListEvent listEvent; private final ListEventFormat eventFormat = new ListEventFormat(); /** true if we're waiting on the publisher to distribute our event */ private boolean eventIsBeingPublished = false; /** * Create a new {@link ListEventPublisher} for an {@link EventList} not attached * to any other {@link EventList}s. */ public static ListEventPublisher createListEventPublisher() { return new SequenceDependenciesEventPublisher(); } /** * Creates a new ListEventAssembler that tracks changes for the specified list. */ public ListEventAssembler(EventList sourceList, ListEventPublisher publisher) { this.sourceList = sourceList; this.publisher = (SequenceDependenciesEventPublisher) publisher; this.listEvent = new Tree4DeltasListEvent(this, sourceList); } /** * Starts a new atomic change to this list change queue. * *

      This simple change event does not support change events nested within. * To allow other methods to nest change events within a change event, use * beginEvent(true). */ public void beginEvent() { beginEvent(false); } /** * Starts a new atomic change to this list change queue. This signature * allows you to specify allowing nested changes. This simply means that * you can call other methods that contain a beginEvent(), commitEvent() * block and their changes will be recorded but not fired. This allows * the creation of list modification methods to call simpler list modification * methods while still firing a single ListEvent to listeners. * * @see Bug 52 * * @param allowNestedEvents false to throw an exception * if another call to beginEvent() is made before * the next call to commitEvent(). Nested events allow * multiple method's events to be composed into a single * event. */ public synchronized void beginEvent(boolean allowNestedEvents) { // complain if we cannot nest any further if(!this.allowNestedEvents) { throw new ConcurrentModificationException("Cannot begin a new event while another event is in progress by thread, " + eventThread.getName()); } this.allowNestedEvents = allowNestedEvents; if(allowNestedEvents || (eventLevel == 0 && eventThread != null)) { listDeltas.setAllowContradictingEvents(true); } // prepare for a new event if we haven't already if(eventThread == null) { this.eventThread = Thread.currentThread(); useListBlocksLinear = true; } // track how deeply nested we are eventLevel++; } /** * Add to the current ListEvent the insert of the element at * the specified index, with the specified previous value. */ public void elementInserted(int index, E newValue) { addChange(ListEvent.INSERT, index, index, ListEvent.unknownValue(), newValue); } /** * Add to the current ListEvent the update of the element at the specified * index, with the specified previous value. */ public void elementUpdated(int index, E oldValue, E newValue) { addChange(ListEvent.UPDATE, index, index, oldValue, newValue); } /** * Add to the current ListEvent the removal of the element at the specified * index, with the specified previous value. */ public void elementDeleted(int index, E oldValue) { addChange(ListEvent.DELETE, index, index, oldValue, ListEvent.unknownValue()); } /** * @deprecated replaced with {@link #elementUpdated(int, Object, Object)}. */ public void elementUpdated(int index, E oldValue) { elementUpdated(index, oldValue, ListEvent.unknownValue()); } /** * Adds a block of changes to the set of list changes. The change block * allows a range of changes to be grouped together for efficiency. * *

      One or more calls to this method must be prefixed by a call to * beginEvent() and followed by a call to commitEvent(). * * @deprecated replaced with {@link #elementInserted}, {@link #elementUpdated} * and {@link #elementDeleted}. */ public void addChange(int type, int startIndex, int endIndex) { addChange(type, startIndex, endIndex, ListEvent.unknownValue(), ListEvent.unknownValue()); } /** * Convenience method for appending a single change of the specified type. * * @deprecated replaced with {@link #elementInserted}, {@link #elementUpdated} * and {@link #elementDeleted}. */ public void addChange(int type, int index) { addChange(type, index, index); } /** * Convenience method for appending a single insert. * * @deprecated replaced with {@link #elementInserted}. */ public void addInsert(int index) { addChange(ListEvent.INSERT, index); } /** * Convenience method for appending a single delete. * * @deprecated replaced with {@link #elementDeleted}. */ public void addDelete(int index) { addChange(ListEvent.DELETE, index); } /** * Convenience method for appending a single update. * * @deprecated replaced with {@link #elementUpdated}. */ public void addUpdate(int index) { addChange(ListEvent.UPDATE, index); } /** * Convenience method for appending a range of inserts. * * @deprecated replaced with {@link #elementInserted}. */ public void addInsert(int startIndex, int endIndex) { addChange(ListEvent.INSERT, startIndex, endIndex); } /** * Convenience method for appending a range of deletes. * * @deprecated replaced with {@link #elementDeleted}. */ public void addDelete(int startIndex, int endIndex) { addChange(ListEvent.DELETE, startIndex, endIndex); } /** * Convenience method for appending a range of updates. * * @deprecated replaced with {@link #elementUpdated}. */ public void addUpdate(int startIndex, int endIndex) { addChange(ListEvent.UPDATE, startIndex, endIndex); } /** * Adds a block of changes to the set of list changes. The change block * allows a range of changes to be grouped together for efficiency. * * @param endIndex the inclusive end index */ private void addChange(int type, int startIndex, int endIndex, E oldValue, E newValue) { // try the linear holder first if(useListBlocksLinear) { final boolean success = blockSequence.addChange(type, startIndex, endIndex + 1, oldValue, newValue); if (success) return; // convert from linear to tree4deltas listDeltas.addAll(blockSequence); useListBlocksLinear = false; } // try the good old reliable tree4deltas switch (type) { case ListEvent.INSERT: listDeltas.targetInsert(startIndex, endIndex + 1, newValue); break; case ListEvent.UPDATE: listDeltas.targetUpdate(startIndex, endIndex + 1, oldValue, newValue); break; case ListEvent.DELETE: listDeltas.targetDelete(startIndex, endIndex + 1, oldValue); break; } } /** * Sets the current event as a reordering. Reordering events cannot be * combined with other events. */ public void reorder(int[] reorderMap) { if(!isEventEmpty()) throw new IllegalStateException("Cannot combine reorder with other change events"); // can't reorder an empty list, see bug 91 if(reorderMap.length == 0) return; addChange(ListEvent.DELETE, 0, reorderMap.length - 1, ListEvent.unknownValue(), ListEvent.unknownValue()); addChange(ListEvent.INSERT, 0, reorderMap.length - 1, ListEvent.unknownValue(), ListEvent.unknownValue()); this.reorderMap = reorderMap; } /** * Forwards the event. This is a convenience method that does the following: *
      1. beginEvent() *
      2. For all changes in sourceEvent, apply those changes to this *
      3. commitEvent() * *

      Note that this method should be preferred to manually forwarding events * because it is heavily optimized. * *

      Note that currently this implementation does a best effort to preserve * reorderings. This means that a reordering is lost if it is combined with * any other ListEvent. */ public void forwardEvent(ListEvent listChanges) { beginEvent(false); this.reorderMap = null; if(isEventEmpty() && listChanges.isReordering()) { reorder(listChanges.getReorderMap()); } else { while(listChanges.next()) { int type = listChanges.getType(); int index = listChanges.getIndex(); E oldValue = (E) listChanges.getOldValue(); E newValue = (E) listChanges.getNewValue(); addChange(type, index, index, oldValue, newValue); } listChanges.reset(); } commitEvent(); } /** * Commits the current atomic change to this list change queue. This will * notify all listeners about the change. * *

      If the current event is nested within a greater event, this will simply * change the nesting level so that further changes are applied directly to the * parent change. */ public synchronized void commitEvent() { // complain if we have no event to commit if(eventLevel == 0) throw new IllegalStateException("Cannot commit without an event in progress"); // we are one event less nested eventLevel--; allowNestedEvents = true; // if this is the last stage, sort and fire if (eventLevel != 0) { return; } if (isEventEmpty()) { cleanup(); return; } // we've already fired this event, we're just adding to it if(eventIsBeingPublished) { return; } eventIsBeingPublished = true; publisher.fireEvent(sourceList, listEvent, eventFormat); } /** * Discards the current atomic change to this list change queue. This does * not notify any listeners about any changes. * *

      The caller of this method is responsible for returning the EventList * to its state before the event began. If they fail to do so, the EventList * pipeline may be in an inconsistent state. * *

      If the current event is nested within a greater event, this will * discard changes at the current nesting level and that further changes * are still applied directly to the parent change. */ public synchronized void discardEvent() { // complain if we have no event to commit if(eventLevel == 0) throw new IllegalStateException("Cannot discard without an event in progress"); // we are one event less nested eventLevel--; allowNestedEvents = true; // if this is the last stage, clean it up if(eventLevel == 0) { cleanup(); } } /** * Returns true if the current atomic change to this list change * queue is empty; false otherwise. * * @return true if the current atomic change to this list change * queue is empty; false otherwise */ public boolean isEventEmpty() { return useListBlocksLinear ? blockSequence.isEmpty() : listDeltas.isEmpty(); } /** * Registers the specified listener to be notified whenever new changes * are appended to this list change sequence. * *

      For each listener, a ListEvent is created, which provides * a read-only view to the list changes in the list. The same * ListChangeView object is used for all notifications to the specified * listener, so if a listener does not process a set of changes, those * changes will persist in the next notification. * * @param listChangeListener event listener != null * @throws NullPointerException if the specified listener is null */ public synchronized void addListEventListener(ListEventListener listChangeListener) { Preconditions.checkNotNull(listChangeListener, "ListEventListener is undefined"); publisher.addListener(sourceList, listChangeListener, eventFormat); } /** * Removes the specified listener from receiving notification when new * changes are appended to this list change sequence. * *

      This uses the == identity comparison to find the listener * instead of equals(). This is because multiple Lists may be * listening and therefore equals() may be ambiguous. * * @param listChangeListener event listener != null * @throws NullPointerException if the specified listener is null * @throws IllegalArgumentException if the specified listener wasn't added before */ public synchronized void removeListEventListener(ListEventListener listChangeListener) { Preconditions.checkNotNull(listChangeListener, "ListEventListener is undefined"); publisher.removeListener(sourceList, listChangeListener); } /** * Get all {@link ListEventListener}s observing the {@link EventList}. */ public List> getListEventListeners() { return publisher.getListeners(sourceList); } // these method sare used by the ListEvent boolean getUseListBlocksLinear() { return useListBlocksLinear; } Tree4Deltas getListDeltas() { return listDeltas; } BlockSequence getListBlocksLinear() { return blockSequence; } int[] getReorderMap() { return reorderMap; } /** * Cleanup all temporary variables necessary while events are being fired. */ private void cleanup() { eventThread = null; blockSequence.reset(); listDeltas.reset(sourceList.size()); reorderMap = null; listDeltas.setAllowContradictingEvents(false); // force cleanup of iterator which still could reference old data listEvent.reset(); } /** * Adapt {@link SequenceDependenciesEventPublisher.EventFormat} for use with {@link ListEvent}s. */ private class ListEventFormat implements SequenceDependenciesEventPublisher.EventFormat,ListEventListener,ListEvent> { public void fire(EventList subject, ListEvent event, ListEventListener listener) { event.reset(); listener.listChanged((ListEvent) event); } public void postEvent(EventList subject) { cleanup(); eventIsBeingPublished = false; } public boolean isStale(EventList subject, ListEventListener listener) { if(listener instanceof WeakReferenceProxy && ((WeakReferenceProxy)listener).getReferent() == null) { ((WeakReferenceProxy)listener).dispose(); return true; } return false; } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/event/ListEventListener.java0000644000175000017500000000346112106516374031553 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.event; // standard java event and observer import java.util.EventListener; /** * Listens and responds to changes in a dynamic list of objects. This could be * implemented by a GUI widget such as a table or combo box to repaint, add, or * remove elements when the underlying data changes. * *

      List changes are represented by a {@link ListEvent}. * *

      When a thread requires notification on the Swing thread for GUI display, the * user should not add the implementation of this interface as a listener * directly. Instead use a EventThreadProxy, which receives * events on the list thread and then fires them on the Swing thread. * * @see Glazed Lists Tutorial * @see ListEvent * * @author Jesse Wilson */ public interface ListEventListener extends EventListener { /** * When the underlying list changes, this notification allows the * object to repaint itself or update itself as necessary. * *

      It is mandatory that the calling thread has obtained the write lock * on the source list. This is because the calling thread will have written * to the source list to cause this event. This condition guarantees that * no writes can occur while the listener is handling this event. * It is an error to write to the source list while processing an event. * * @param listChanges a {@link ListEvent} describing the changes to the list */ public void listChanged(ListEvent listChanges); }././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/event/SequenceDependenciesEventPublisher.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/event/SequenceDependenciesEventPubli0000644000175000017500000004712312106516374033270 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.event; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.impl.adt.IdentityMultimap; import java.io.ObjectStreamException; import java.io.Serializable; import java.util.*; /** * Manage listeners, firing events, and making sure that events arrive in order. * *

      This manages listeners across multiple objects in a pipeline of observables * and their listeners. It implements Martin Fowler's * EventAggregator * design. * *

      To guarantee a safe notification order, this class makes sure that all an * object's dependencies have been notified of a particular event before that * object is itself notified. This is tricky because it requires us to interrupt * the event flow and control its flow. In this class, event flow is controlled * by queueing events and not necessarily firing them during the {@link #fireEvent} * method. * * @author Jesse Wilson */ final class SequenceDependenciesEventPublisher implements ListEventPublisher, Serializable { /** For versioning as a {@link Serializable} */ private static final long serialVersionUID = -8228256898169043019L; /** keep track of how many times the fireEvent() method is on the stack */ private transient int reentrantFireEventCount; /** subject to cleanup when this event is completely distributed */ private transient final Map subjectsToCleanUp = new IdentityHashMap(); /** for proper dependency management, when a listener and subject aren't the same identity */ private transient final Map listenersToRelatedSubjects = new IdentityHashMap(); /** the last listener notified, the next one will be beyond it in the list */ private transient int nextToNotify; /** * A mix of different subjects and listeners pairs in a deliberate order. * We should be careful not to make changes to this list directly and instead * create a copy as necessary */ private transient List subjectAndListeners = Collections.emptyList(); /** * We use copy-on-write on the listeners list. This is a copy of the * listeners list as it looked immediately before the current change * started. If there is no change going on (reentrantFireEventCount == 0), * then this should be null. */ private transient List subjectsAndListenersForCurrentEvent; /** Returns a proper initialized publisher object during deserialization. */ private Object readResolve() throws ObjectStreamException { return new SequenceDependenciesEventPublisher(); } /** * Rebuild the subject and listeners list so that all required invariants * are met with respect to notification order. That is, for any listener * T, all of the subjects S that T listens to have been updated before T * receives a change event from any S. * *

      This implementation still has some problems and work left to do: *

    • it's big! Can we optimize it? Perhaps shortcutting all the graph * work for simple cases (the 99% case) *
    • it's complex! Can we simplify it? *
    • could we keep the datastructures around? it may be wasteful to * reconstruct them every single time a listener is added */ private List orderSubjectsAndListeners(List subjectsAndListeners) { // since we're regenerating the subjectAndListeners list, clear it and re-add the elements List result = new ArrayList(); // HashMaps of unprocessed elements, keyed by both source and target IdentityMultimap sourceToPairs = new IdentityMultimap(); IdentityMultimap targetToPairs = new IdentityMultimap(); // everything that has all of its listeners already notified in subjectAndListeners Map satisfied = new IdentityHashMap(); // everything that has a listener already notified List satisfiedToDo = new ArrayList(); // prepare the initial collections: maps that show how each element is // used as source and target in directed edges, plus a list of nodes // that have no incoming edges for(int i = 0, size = subjectsAndListeners.size(); i < size; i++) { SubjectAndListener subjectAndListener = subjectsAndListeners.get(i); Object source = subjectAndListener.subject; Object target = getRelatedSubject(subjectAndListener.listener); sourceToPairs.addValue(source, subjectAndListener); targetToPairs.addValue(target, subjectAndListener); satisfied.remove(target); if(targetToPairs.count(source) == 0) { satisfied.put(source, Boolean.TRUE); } } // start with the initial set of sources that don't have dependencies satisfiedToDo.addAll(satisfied.keySet()); // We have a subject which has all of its dependencies satisfied. // ie. all edges where this subject is a target are already in // subjectAndListeners. Now we want to find further edges from this // subject to further objects. We find all of its listeners, and look // for one of them where all of its dependencies are in the // satisfied list. If we find such a listener, add all its edges // to subjectAndListeners and enque it to find it's listeners // iteratively while(!satisfiedToDo.isEmpty()) { // for everything that's not a target, Object subject = satisfiedToDo.remove(0); // get all listeners to this subject, we try this set because // we know at least one of their edges is satisfied, and // we hope that all of their edges is satisfied. List sourceTargets = sourceToPairs.get(subject); // can we satisfy this target? tryEachTarget: for(int t = 0, targetsSize = sourceTargets.size(); t < targetsSize; t++) { Object sourceTarget = getRelatedSubject(sourceTargets.get(t).listener); // make sure we can satisfy this if all its sources are in satisfiedSources List allSourcesForSourceTarget = targetToPairs.get(sourceTarget); // we've since processed this entire target, we shouldn't process it twice if(allSourcesForSourceTarget.size() == 0) continue; for(int s = 0, sourcesSize = allSourcesForSourceTarget.size(); s < sourcesSize; s++) { SubjectAndListener sourceAndTarget = allSourcesForSourceTarget.get(s); if(!satisfied.containsKey(sourceAndTarget.subject)) { continue tryEachTarget; } } // we know we can satisfy this target, add all its edges result.addAll(allSourcesForSourceTarget); targetToPairs.remove(sourceTarget); // this target is no longer considered a target, since all // its dependencies are satisfied satisfiedToDo.add(sourceTarget); satisfied.put(sourceTarget, Boolean.TRUE); } } // if there's remaining targets, we never covered everything if(!targetToPairs.isEmpty()) { throw new IllegalStateException("Listener cycle detected, " + targetToPairs.values()); } // success! return result; } private Object getRelatedSubject(Object listener) { Object subject = listenersToRelatedSubjects.get(listener); if(subject == null) return listener; return subject; } /** * Register the specified listener to receive events from the specified * subject whenever they are fired. */ public synchronized void addListener(Subject subject, Listener listener, EventFormat eventFormat) { List unordered = updateListEventListeners(subject, listener, null, eventFormat); subjectAndListeners = orderSubjectsAndListeners(unordered); } /** * Deregister the specified listener from recieving events from the specified * subject. */ public synchronized void removeListener(Object subject, Object listener) { subjectAndListeners = updateListEventListeners(subject, null, listener, null); } /** * Support method for adding and removing listeners, that also cleans up * stale listeners, such as those from weak references. * * @param listenerToAdd a listener to be added, or null * @param listenerToRemove a listener to be removed, or null */ private List updateListEventListeners(Subject subject, Listener listenerToAdd, Listener listenerToRemove, EventFormat eventFormat) { // we'll want to output a copy of all the listeners int anticipatedSize = this.subjectAndListeners.size() + (listenerToAdd == null ? - 1 : 1); List result = new ArrayList(anticipatedSize); // walk through, adding all the old listeners to the new listeners list, // unless a particular listener is slated for removal for some reaosn for(int i = 0, n = subjectAndListeners.size(); i < n; i++) { final SubjectAndListener originalSubjectAndListener = subjectAndListeners.get(i); // if we're supposed to remove this listener, skip it if(originalSubjectAndListener.listener == listenerToRemove && originalSubjectAndListener.subject == subject) { listenerToRemove = null; continue; } // if this listener is stale, skip it if(originalSubjectAndListener.eventFormat.isStale(originalSubjectAndListener.subject, originalSubjectAndListener.listener)) { continue; } // this listener's still good, keep it! result.add(originalSubjectAndListener); } // sanity check to ensure we found the listener we were asked to remove, if any if(listenerToRemove != null) { throw new IllegalArgumentException("Cannot remove nonexistent listener " + listenerToRemove); } // add the listener we were asked to add, if any if(listenerToAdd != null) { result.add(new SubjectAndListener(subject, listenerToAdd, eventFormat)); } return result; } /** {@inheritDoc} */ public void setRelatedListener(Object subject, Object relatedListener) { // force the dependency by adding a listener that just doesn't // do anything. This will make sure that subject is always after // related listener in the dependencies graph addListener(relatedListener, subject, NoOpEventFormat.INSTANCE); } /** {@inheritDoc} */ public void clearRelatedListener(Object subject, Object relatedListener) { removeListener(relatedListener, subject); } /** {@inheritDoc} */ public void addDependency(EventList dependency, ListEventListener listener) { // do nothing } /** {@inheritDoc} */ public void removeDependency(EventList dependency, ListEventListener listener) { // do nothing } /** {@inheritDoc} */ public void setRelatedSubject(Object listener, Object relatedSubject) { if(relatedSubject != null) { listenersToRelatedSubjects.put(listener, relatedSubject); } else { listenersToRelatedSubjects.remove(listener); } } /** {@inheritDoc} */ public void clearRelatedSubject(Object listener) { listenersToRelatedSubjects.remove(listener); } /** * Get all listeners of the specified object. */ public synchronized List getListeners(Object subject) { List result = new ArrayList(); for(int i = 0, size = subjectAndListeners.size(); i < size; i++) { SubjectAndListener subjectAndListener = subjectAndListeners.get(i); if(subjectAndListener.subject != subject) continue; result.add(subjectAndListener.listener); } return result; } /** * Notify all listeners of the specified subject of the specified event. * * @param subject the event's source * @param event the event to send to all listeners * @param eventFormat the mechanism to notify listeners of the event, also * used for a callback when this event is complete */ public void fireEvent(Subject subject, Event event, EventFormat eventFormat) { // keep the subjects and listeners as they are at the beginning of // the topmost event, the list won't change because we copy on write if(reentrantFireEventCount == 0) { subjectsAndListenersForCurrentEvent = subjectAndListeners; nextToNotify = Integer.MAX_VALUE; } // keep track of whether this method is being reentered because one // event caused another event. If so, we'll fire later reentrantFireEventCount++; try { // record this subject as firing an event, so we can clean up later EventFormat previous = subjectsToCleanUp.put(subject, eventFormat); if(previous != null) throw new IllegalStateException("Reentrant fireEvent() by \"" + subject + "\""); // Mark the listeners who need this event int subjectAndListenersSize = subjectsAndListenersForCurrentEvent.size(); // was i = lastNotified + 1 for(int i = 0; i < subjectAndListenersSize; i++) { SubjectAndListener subjectAndListener = subjectsAndListenersForCurrentEvent.get(i); if(subjectAndListener.subject != subject) continue; if(i < nextToNotify) nextToNotify = i; subjectAndListener.addPendingEvent(event); } // If this method is reentrant, let someone higher up the stack handle this if(reentrantFireEventCount != 1) return; // remember any runtime exceptions thrown to rethrow later RuntimeException toRethrow = null; // fire events to listeners in order while(true) { SubjectAndListener nextToFire = null; // find the next listener still pending for(int i = nextToNotify; i < subjectAndListenersSize; i++) { SubjectAndListener subjectAndListener = subjectsAndListenersForCurrentEvent.get(i); if(subjectAndListener.hasPendingEvent()) { nextToFire = subjectAndListener; nextToNotify = i + 1; break; } } // there's nobody to notify, we're done firing events if(nextToFire == null) break; // notify this listener try { nextToFire.firePendingEvent(); } catch(RuntimeException e) { if(toRethrow == null) toRethrow = e; } } // clean up all the subjects now that we're done firing events for(Iterator> i = subjectsToCleanUp.entrySet().iterator(); i.hasNext(); ) { Map.Entry subjectAndEventFormat = i.next(); try { subjectAndEventFormat.getValue().postEvent(subjectAndEventFormat.getKey()); } catch(RuntimeException e) { if(toRethrow == null) toRethrow = e; } } subjectsToCleanUp.clear(); // this event is completely finished subjectsAndListenersForCurrentEvent = null; // rethrow any exceptions if(toRethrow != null) throw toRethrow; } finally { reentrantFireEventCount--; } } /** * Adapt any observer-style interface to a common format. */ public interface EventFormat { /** * Fire the specified event to the specified listener. */ void fire(Subject subject, Event event, Listener listener); /** * A callback made only after all listeners of the specified subject * have been notified of the specified event. This can be used as * a hook to clean up temporary datastructures for that event. */ void postEvent(Subject subject); /** * Whether the listener is still valid. Usually a listener becomes stale * when a weak reference goes out of scope. If this method returns true, * the listener will be silently removed and no longer receive events. */ boolean isStale(Subject subject, Listener listener); } /** * An EventFormat used to specify explicit dependencies, but that doesn't * actually fire events. * * @see {@link ListEventPublisher#setRelatedListener} */ private static class NoOpEventFormat implements SequenceDependenciesEventPublisher.EventFormat { public static final SequenceDependenciesEventPublisher.EventFormat INSTANCE = new NoOpEventFormat(); public void fire(Object subject, Object event, Object listener) { throw new UnsupportedOperationException(); } public void postEvent(Object subject) { throw new UnsupportedOperationException(); } public boolean isStale(Object subject, Object listener) { return false; } } /** * Manage a subject/listener pair, plus a possible event that is queued to * be fired to the listener from the subject. */ private static class SubjectAndListener { private final Subject subject; private final Listener listener; private final EventFormat eventFormat; private Event pendingEvent; public SubjectAndListener(Subject subject, Listener listener, EventFormat eventFormat) { this.subject = subject; this.listener = listener; this.eventFormat = eventFormat; } public boolean hasPendingEvent() { return pendingEvent != null; } public void addPendingEvent(Event pendingEvent) { if(this.pendingEvent != null) throw new IllegalStateException(); if(pendingEvent == null) throw new IllegalStateException(); this.pendingEvent = pendingEvent; } public void firePendingEvent() { assert(pendingEvent != null); try { eventFormat.fire(subject, pendingEvent, listener); } finally { pendingEvent = null; } } @Override public String toString() { String separator = hasPendingEvent() ? ">>>" : "-->"; return subject + separator + listener; } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/AbstractEventList.java0000644000175000017500000007416712106516400030411 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; // the Glazed Lists' change objects import ca.odell.glazedlists.event.ListEventAssembler; import ca.odell.glazedlists.event.ListEventListener; import ca.odell.glazedlists.event.ListEventPublisher; import ca.odell.glazedlists.impl.EventListIterator; import ca.odell.glazedlists.impl.GlazedListsImpl; import ca.odell.glazedlists.impl.SimpleIterator; import ca.odell.glazedlists.impl.SubEventList; import ca.odell.glazedlists.util.concurrent.ReadWriteLock; import java.lang.reflect.Array; import java.util.*; /** * A convenience class that implements common functionality for all {@link EventList}s. * *

      If you are creating a custom {@link EventList}, consider extending the more * feature-rich {@link TransformedList}. * *

      Documentation Note: Javadoc tags have been copied from the {@link List} API * because the javadoc tool does not inherit external descriptions. * * @author Jesse Wilson */ public abstract class AbstractEventList implements EventList { /** the change event and notification system */ protected ListEventAssembler updates = null; /** the read/write lock provides mutual exclusion to access */ protected ReadWriteLock readWriteLock = null; /** the publisher manages the distribution of changes */ protected ListEventPublisher publisher = null; /** * Creates an {@link AbstractEventList} that sends events using the specified * {@link ListEventPublisher}. * * @param publisher the channel for event distribution. If this is null, * then a new {@link ListEventPublisher} will be created. */ protected AbstractEventList(ListEventPublisher publisher) { if(publisher == null) publisher = ListEventAssembler.createListEventPublisher(); this.publisher = publisher; updates = new ListEventAssembler(this, publisher); } /** * Create an {@link AbstractEventList} that sends events with the default * {@link ListEventPublisher}. */ protected AbstractEventList() { this(null); } /** {@inheritDoc} */ public ListEventPublisher getPublisher() { return publisher; } /** {@inheritDoc} */ public ReadWriteLock getReadWriteLock() { return readWriteLock; } /** {@inheritDoc} */ public void addListEventListener(ListEventListener listChangeListener) { updates.addListEventListener(listChangeListener); } /** {@inheritDoc} */ public void removeListEventListener(ListEventListener listChangeListener) { updates.removeListEventListener(listChangeListener); } /** * Returns the number of elements in this list. If this list contains * more than Integer.MAX_VALUE elements, returns * Integer.MAX_VALUE. * * @return the number of elements in this list. */ public abstract int size(); /** * Returns true if this list contains no elements. * * @return true if this list contains no elements. */ public boolean isEmpty() { return (size() == 0); } /** * Returns true if this list contains the specified element. * More formally, returns true if and only if this list contains * at least one element e such that * (o==null ? e==null : o.equals(e)). * * @param object element whose presence in this list is to be tested. * @return true if this list contains the specified element. * @throws ClassCastException if the type of the specified element * is incompatible with this list (optional). * @throws NullPointerException if the specified element is null and this * list does not support null elements (optional). */ public boolean contains(Object object) { // for through this, looking for the lucky object for(Iterator i = iterator(); i.hasNext(); ) { if(GlazedListsImpl.equal(object, i.next())) return true; } // not found return false; } /** * Returns an iterator over the elements in this list in proper sequence. * *

      The returned {@link Iterator} will become inconsistent if the * {@link EventList} that it views is modified. To overcome this problem, * use {@link #listIterator()}. When used concurrently, the returned * {@link Iterator} requires locking via this list's * {@link #getReadWriteLock() ReadWriteLock}. * * @return an iterator over the elements in this list in proper sequence. */ public Iterator iterator() { return new SimpleIterator(this); } /** * Returns an array containing all of the elements in this list in proper * sequence. Obeys the general contract of the * Collection.toArray method. * * @return an array containing all of the elements in this list in proper * sequence. * @see Arrays#asList */ public Object[] toArray() { // copy values into the array Object[] array = new Object[size()]; int index = 0; for(Iterator i = iterator(); i.hasNext(); ) { array[index] = i.next(); index++; } return array; } /** * Returns an array containing all of the elements in this list in proper * sequence; the runtime type of the returned array is that of the * specified array. Obeys the general contract of the * Collection.toArray(Object[]) method. * * @param array the array into which the elements of this list are to * be stored, if it is big enough; otherwise, a new array of the * same runtime type is allocated for this purpose. * @return an array containing the elements of this list. * * @throws ArrayStoreException if the runtime type of the specified array * is not a supertype of the runtime type of every element in * this list. * @throws NullPointerException if the specified array is null. */ public T[] toArray(T[] array) { // create an array of the same type as the array passed if (array.length < size()) { array = (T[]) Array.newInstance(array.getClass().getComponentType(), size()); } else if(array.length > size()) { array[size()] = null; } // copy values into the array int index = 0; for(Iterator i = iterator(); i.hasNext(); ) { array[index] = (T) i.next(); index++; } return array; } /** * Appends the specified element to the end of this list (optional * operation). * *

      Lists that support this operation may place limitations on what * elements may be added to this list. In particular, some * lists will refuse to add null elements, and others will impose * restrictions on the type of elements that may be added. List * classes should clearly specify in their documentation any restrictions * on what elements may be added. * * @param value element to be appended to this list. * @return true (as per the general contract of the * Collection.add method). * * @throws UnsupportedOperationException if the add method is not * supported by this list. * @throws ClassCastException if the class of the specified element * prevents it from being added to this list. * @throws NullPointerException if the specified element is null and this * list does not support null elements. * @throws IllegalArgumentException if some aspect of this element * prevents it from being added to this list. */ public boolean add(E value) { final int initialSize = this.size(); this.add(this.size(), value); return this.size() != initialSize; } /** * Removes the first occurrence in this list of the specified element * (optional operation). If this list does not contain the element, it is * unchanged. More formally, removes the element with the lowest index i * such that (o==null ? get(i)==null : o.equals(get(i))) (if * such an element exists). * * @param toRemove element to be removed from this list, if present. * @return true if this list contained the specified element. * @throws ClassCastException if the type of the specified element * is incompatible with this list (optional). * @throws NullPointerException if the specified element is null and this * list does not support null elements (optional). * @throws UnsupportedOperationException if the remove method is * not supported by this list. */ public boolean remove(Object toRemove) { int index = indexOf(toRemove); if(index == -1) return false; this.remove(index); return true; } /** * Returns true if this list contains all of the elements of the * specified collection. * * @param values collection to be checked for containment in this list. * @return true if this list contains all of the elements of the * specified collection. * @throws ClassCastException if the types of one or more elements * in the specified collection are incompatible with this * list (optional). * @throws NullPointerException if the specified collection contains one * or more null elements and this list does not support null * elements (optional). * @throws NullPointerException if the specified collection is * null. * @see #contains(Object) */ public boolean containsAll(Collection values) { // look for something that is missing for(Iterator i = values.iterator(); i.hasNext(); ) { Object a = i.next(); if(!contains(a)) return false; } // contained everything we looked for return true; } /** * Appends all of the elements in the specified collection to the end of * this list, in the order that they are returned by the specified * collection's iterator (optional operation). The behavior of this * operation is unspecified if the specified collection is modified while * the operation is in progress. (Note that this will occur if the * specified collection is this list, and it's nonempty.) * * @param values collection whose elements are to be added to this list. * @return true if this list changed as a result of the call. * * @throws UnsupportedOperationException if the addAll method is * not supported by this list. * @throws ClassCastException if the class of an element in the specified * collection prevents it from being added to this list. * @throws NullPointerException if the specified collection contains one * or more null elements and this list does not support null * elements, or if the specified collection is null. * @throws IllegalArgumentException if some aspect of an element in the * specified collection prevents it from being added to this * list. * @see #add(Object) */ public boolean addAll(Collection values) { return addAll(size(), values); } /** * Inserts all of the elements in the specified collection into this * list at the specified position (optional operation). Shifts the * element currently at that position (if any) and any subsequent * elements to the right (increases their indices). The new elements * will appear in this list in the order that they are returned by the * specified collection's iterator. The behavior of this operation is * unspecified if the specified collection is modified while the * operation is in progress. (Note that this will occur if the specified * collection is this list, and it's nonempty.) * * @param index index at which to insert first element from the specified * collection. * @param values elements to be inserted into this list. * @return true if this list changed as a result of the call. * * @throws UnsupportedOperationException if the addAll method is * not supported by this list. * @throws ClassCastException if the class of one of elements of the * specified collection prevents it from being added to this * list. * @throws NullPointerException if the specified collection contains one * or more null elements and this list does not support null * elements, or if the specified collection is null. * @throws IllegalArgumentException if some aspect of one of elements of * the specified collection prevents it from being added to * this list. * @throws IndexOutOfBoundsException if the index is out of range (index * < 0 || index > size()). */ public boolean addAll(int index, Collection values) { // don't do an add of an empty set if(index < 0 || index > size()) throw new IndexOutOfBoundsException("Cannot add at " + index + " on list of size " + size()); if(values.size() == 0) return false; final int initializeSize = this.size(); for (Iterator iter = values.iterator(); iter.hasNext();) { this.add(index, iter.next()); // advance the insertion location if its within the size of the list if (index < this.size()) index++; } return this.size() != initializeSize; } /** * Removes from this list all the elements that are contained in the * specified collection (optional operation). * * @param values collection that defines which elements will be removed from * this list. * @return true if this list changed as a result of the call. * * @throws UnsupportedOperationException if the removeAll method * is not supported by this list. * @throws ClassCastException if the types of one or more elements * in this list are incompatible with the specified * collection (optional). * @throws NullPointerException if this list contains one or more * null elements and the specified collection does not support * null elements (optional). * @throws NullPointerException if the specified collection is * null. * @see #remove(Object) * @see #contains(Object) */ public boolean removeAll(Collection values) { boolean changed = false; for(Iterator i = iterator(); i.hasNext(); ) { if(values.contains(i.next())) { i.remove(); changed = true; } } return changed; } /** * Retains only the elements in this list that are contained in the * specified collection (optional operation). In other words, removes * from this list all the elements that are not contained in the specified * collection. * * @param values collection that defines which elements this set will retain. * * @return true if this list changed as a result of the call. * * @throws UnsupportedOperationException if the retainAll method * is not supported by this list. * @throws ClassCastException if the types of one or more elements * in this list are incompatible with the specified * collection (optional). * @throws NullPointerException if this list contains one or more * null elements and the specified collection does not support * null elements (optional). * @throws NullPointerException if the specified collection is * null. * @see #remove(Object) * @see #contains(Object) */ public boolean retainAll(Collection values) { boolean changed = false; for(Iterator i = iterator(); i.hasNext();) { if(!values.contains(i.next())) { i.remove(); changed = true; } } return changed; } /** * Removes all of the elements from this list (optional operation). This * list will be empty after this call returns (unless it throws an * exception). * * @throws UnsupportedOperationException if the clear method is * not supported by this list. */ public void clear() { for(Iterator i = iterator(); i.hasNext();) { i.next(); i.remove(); } } /** * Compares the specified object with this list for equality. Returns * true if and only if the specified object is also a list, both * lists have the same size, and all corresponding pairs of elements in * the two lists are equal. (Two elements e1 and * e2 are equal if (e1==null ? e2==null : * e1.equals(e2)).) In other words, two lists are defined to be * equal if they contain the same elements in the same order. This * definition ensures that the equals method works properly across * different implementations of the List interface. * * @param object the object to be compared for equality with this list. * @return true if the specified object is equal to this list. */ @Override public boolean equals(Object object) { if(object == this) return true; if(object == null) return false; if(!(object instanceof List)) return false; // ensure the lists are the same size List otherList = (List)object; if(otherList.size() != size()) return false; // compare element wise, via iterators Iterator iterA = iterator(); Iterator iterB = otherList.iterator(); while(iterA.hasNext() && iterB.hasNext()) { if(!GlazedListsImpl.equal(iterA.next(), iterB.next())) return false; } // if we haven't failed yet, they match return true; } /** * Returns the hash code value for this list. The hash code of a list * is defined to be the result of the following calculation: *

           *  hashCode = 1;
           *  Iterator i = list.iterator();
           *  while (i.hasNext()) {
           *      Object obj = i.next();
           *      hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
           *  }
           * 
      * This ensures that list1.equals(list2) implies that * list1.hashCode()==list2.hashCode() for any two lists, * list1 and list2, as required by the general * contract of Object.hashCode. * * @return the hash code value for this list. * @see Object#hashCode() * @see Object#equals(Object) * @see #equals(Object) */ @Override public int hashCode() { int hashCode = 1; for(Iterator i = iterator(); i.hasNext(); ) { E a = i.next(); hashCode = 31 * hashCode + (a == null ? 0 : a.hashCode()); } return hashCode; } /** * Returns the element at the specified position in this list. * * @param index index of element to return. * @return the element at the specified position in this list. * * @throws IndexOutOfBoundsException if the index is out of range (index * < 0 || index >= size()). */ public abstract E get(int index); /** * Replaces the element at the specified position in this list with the * specified element (optional operation). * * @param index index of element to replace. * @param value element to be stored at the specified position. * @return the element previously at the specified position. * * @throws UnsupportedOperationException if the set method is not * supported by this list. * @throws ClassCastException if the class of the specified element * prevents it from being added to this list. * @throws NullPointerException if the specified element is null and * this list does not support null elements. * @throws IllegalArgumentException if some aspect of the specified * element prevents it from being added to this list. * @throws IndexOutOfBoundsException if the index is out of range * (index < 0 || index >= size()). */ public E set(int index, E value) { throw new UnsupportedOperationException("this list does not support set()"); } /** * Inserts the specified element at the specified position in this list * (optional operation). Shifts the element currently at that position * (if any) and any subsequent elements to the right (adds one to their * indices). * * @param index index at which the specified element is to be inserted. * @param value element to be inserted. * * @throws UnsupportedOperationException if the add method is not * supported by this list. * @throws ClassCastException if the class of the specified element * prevents it from being added to this list. * @throws NullPointerException if the specified element is null and * this list does not support null elements. * @throws IllegalArgumentException if some aspect of the specified * element prevents it from being added to this list. * @throws IndexOutOfBoundsException if the index is out of range * (index < 0 || index > size()). */ public void add(int index, E value) { throw new UnsupportedOperationException("this list does not support add()"); } /** * Removes the element at the specified position in this list (optional * operation). Shifts any subsequent elements to the left (subtracts one * from their indices). Returns the element that was removed from the * list. * * @param index the index of the element to removed. * @return the element previously at the specified position. * * @throws UnsupportedOperationException if the remove method is * not supported by this list. * @throws IndexOutOfBoundsException if the index is out of range (index * < 0 || index >= size()). */ public E remove(int index) { throw new UnsupportedOperationException("this list does not support remove()"); } /** * Returns the index in this list of the first occurrence of the specified * element, or -1 if this list does not contain this element. * More formally, returns the lowest index i such that * (o==null ? get(i)==null : o.equals(get(i))), * or -1 if there is no such index. * * @param object element to search for. * @return the index in this list of the first occurrence of the specified * element, or -1 if this list does not contain this element. * @throws ClassCastException if the type of the specified element * is incompatible with this list (optional). * @throws NullPointerException if the specified element is null and this * list does not support null elements (optional). */ public int indexOf(Object object) { // for through this, looking for the lucky object int index = 0; for(Iterator i = iterator(); i.hasNext(); ) { if(GlazedListsImpl.equal(object, i.next())) return index; else index++; } // not found return -1; } /** * Returns the index in this list of the last occurrence of the specified * element, or -1 if this list does not contain this element. * More formally, returns the highest index i such that * (o==null ? get(i)==null : o.equals(get(i))), * or -1 if there is no such index. * * @param object element to search for. * @return the index in this list of the last occurrence of the specified * element, or -1 if this list does not contain this element. * @throws ClassCastException if the type of the specified element * is incompatible with this list (optional). * @throws NullPointerException if the specified element is null and this * list does not support null elements (optional). */ public int lastIndexOf(Object object) { // for through this, looking for the lucky object for(int i = size() - 1; i >= 0; i--) { if(GlazedListsImpl.equal(object, get(i))) return i; } // not found return -1; } /** * Returns a list iterator of the elements in this list (in proper * sequence). * *

      Unlike the {@link ListIterator} from a regular {@link List}, the * {@link EventList}'s {@link ListIterator} will remain consistent even if the * {@link EventList} is changed externally. Note that when used concurrently, * the returned {@link ListIterator} requires locking via this list's * {@link #getReadWriteLock() ReadWriteLock}. * * @return a list iterator of the elements in this list (in proper * sequence). */ public ListIterator listIterator() { return listIterator(0); } /** * Returns a list iterator of the elements in this list (in proper * sequence), starting at the specified position in this list. The * specified index indicates the first element that would be returned by * an initial call to the next method. An initial call to * the previous method would return the element with the * specified index minus one. * *

      Unlike the {@link ListIterator} from a regular {@link List}, the * {@link EventList}'s {@link ListIterator} will remain consistent even if the * {@link EventList} is changed externally. Note that when used concurrently, * the returned {@link ListIterator} requires locking via this list's * {@link #getReadWriteLock() ReadWriteLock}. * * @param index index of first element to be returned from the * list iterator (by a call to the next method). * @return a list iterator of the elements in this list (in proper * sequence), starting at the specified position in this list. * @throws IndexOutOfBoundsException if the index is out of range (index * < 0 || index > size()). */ public ListIterator listIterator(int index) { return new EventListIterator(this, index); } /** * Returns a view of the portion of this list between the specified * fromIndex, inclusive, and toIndex, exclusive. (If * fromIndex and toIndex are equal, the returned list is * empty.) *

      Unlike the standard {@link List#subList(int,int) List.subList()} * method, the {@link List} returned by this method will continue to be * consistent even if the {@link EventList} it views is modified, * structurally or otherwise. The returned {@link List} can always be safely * cast to {@link EventList}. Note that when used concurrently, the returned * {@link List} requires locking via this list's * {@link #getReadWriteLock() ReadWriteLock}. * *

      This method eliminates the need for explicit range operations (of * the sort that commonly exist for arrays). Any operation that expects * a list can be used as a range operation by passing a subList view * instead of a whole list. For example, the following idiom * removes a range of elements from a list: *

           *      list.subList(from, to).clear();
           * 
      * Similar idioms may be constructed for indexOf and * lastIndexOf, and all of the algorithms in the * Collections class can be applied to a subList. * * @param fromIndex low endpoint (inclusive) of the subList. * @param toIndex high endpoint (exclusive) of the subList. * @return a view of the specified range within this list. * * @throws IndexOutOfBoundsException for an illegal endpoint index value * (fromIndex < 0 || toIndex > size || fromIndex > toIndex). */ public List subList(int fromIndex, int toIndex) { return new SubEventList(this, fromIndex, toIndex, true); } /** * Returns a string representation of this collection. The string * representation consists of a list of the collection's elements in the * order they are returned by its iterator, enclosed in square brackets * ("[]"). Adjacent elements are separated by the characters * ", " (comma and space). Elements are converted to strings as * by String.valueOf(Object). * *

      This implementation creates an empty string buffer, appends a left * square bracket, and iterates over the collection appending the string * representation of each element in turn. After appending each element * except the last, the string ", " is appended. Finally a right * bracket is appended. A string is obtained from the string buffer, and * returned. * * @return a string representation of this collection. */ @Override public String toString() { StringBuffer result = new StringBuffer(); result.append("["); for(Iterator i = iterator(); i.hasNext(); ) { result.append(String.valueOf(i.next())); if(i.hasNext()) result.append(", "); } result.append("]"); return result.toString(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/CollectionList.java0000644000175000017500000004672012106516400027731 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import ca.odell.glazedlists.impl.adt.Barcode; import ca.odell.glazedlists.impl.adt.barcode2.Element; import ca.odell.glazedlists.impl.adt.barcode2.SimpleTree; import ca.odell.glazedlists.impl.adt.barcode2.SimpleTreeIterator; import java.util.Collections; import java.util.List; /** * A list that acts like a tree in that it contains child elements to nodes * contained in another list. An example usage would be to wrap a parent list * containing albums and use the CollectionList to display the songs on the * album. * *

      The actual mapping from the parent list to the child list (album to * songs in the above example) is done by a {@link CollectionList.Model} that * is provided to the constructor. * *

      Note that any {@link EventList}s returned by the {@link CollectionList.Model} * must use the same {@link ca.odell.glazedlists.event.ListEventPublisher} and * {@link ca.odell.glazedlists.util.concurrent.ReadWriteLock} as this * {@link CollectionList}. This is necessary in order for CollectionList to * operate correctly under mult-threaded conditions. An * {@link IllegalArgumentException} will be raised if this invariant is violated. * *

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

      * * * * * * * *
      EventList Overview
      Writable:only {@link #set(int,Object)} and {@link #remove(int)}
      Concurrency:thread ready, not thread safe
      Performance:reads: O(log N), writes O(log N)
      Memory:96 bytes per element
      Unit Tests:N/A
      Issues: * 96 * 162 * 257 * 265 *
      * * @see CollectionList.Model * * @author Rob Eden * @author Jesse Wilson */ public class CollectionList extends TransformedList implements ListEventListener { /** This is a hack - we need a temporary value when inserting into IndexedTrees, and this is the one we use. */ private final ChildElement EMPTY_CHILD_ELEMENT = new SimpleChildElement(Collections.emptyList(), null); /** used to extract children */ private final Model model; /** * Barcode containing the node mappings. There is a black node for each parent * followed by a white node for each of its children. */ private final Barcode barcode = new Barcode(); /** the Lists and EventLists that this is composed of */ private final SimpleTree> childElements = new SimpleTree>(); /** * Create a {@link CollectionList} with its contents being the children of * the elements in the specified source {@link EventList}. * * @param source an EventList of Objects from each of which the * model can extract child elements * @param model the logic for extracting children from each * source element */ public CollectionList(EventList source, Model model) { super(source); if(model == null) throw new IllegalArgumentException("model cannot be null"); this.model = model; // sync the current size and indexes for(int i = 0, n = source.size(); i < n; i++) { List children = model.getChildren(source.get(i)); // update the list of child lists Element> node = childElements.add(i, EMPTY_CHILD_ELEMENT, 1); node.set(createChildElementForList(children, node)); // update the barcode barcode.addBlack(barcode.size(), 1); if(!children.isEmpty()) barcode.addWhite(barcode.size(), children.size()); } // listen for changes source.addListEventListener(this); } /** * @return false because we cannot support {@link #add(int, Object)}; * though we do support {@link #set(int, Object)} and {@link #remove(int)} */ @Override protected boolean isWritable() { return false; } /** {@inheritDoc} */ @Override public int size() { // size of the child nodes only return barcode.whiteSize(); } /** {@inheritDoc} */ @Override public E get(int index) { // get the child final ChildElement childElement = getChildElement(index); final int childIndexInParent = barcode.getWhiteSequenceIndex(index); return childElement.get(childIndexInParent); } /** {@inheritDoc} */ @Override public E set(int index, E value) { // set on the child final ChildElement childElement = getChildElement(index); final int childIndexInParent = barcode.getWhiteSequenceIndex(index); return childElement.set(childIndexInParent, value); } /** {@inheritDoc} */ @Override public E remove(int index) { // remove from the child final ChildElement childElement = getChildElement(index); final int childIndexInParent = barcode.getWhiteSequenceIndex(index); return childElement.remove(childIndexInParent); } /** * Return the index of the first child in the CollectionList for the given * parent index. This can be very useful for things like selecting the * children in a CollectionList when the parent is selected in another list. * * @see #childEndingIndex */ public int childStartingIndex(int parentIndex) { if(parentIndex < 0) throw new IndexOutOfBoundsException("Invalid index: " + parentIndex); if(parentIndex >= source.size()) throw new IndexOutOfBoundsException("Invalid index: " + parentIndex); // get the index of the next node // find the index of the black node with that index final int parentFullIndex = barcode.getIndex(parentIndex, Barcode.BLACK); final int childFullIndex = parentFullIndex + 1; // ff this node has no children, the next node index will be past the size or black if(childFullIndex >= barcode.size()) return -1; if(barcode.get(childFullIndex) != Barcode.WHITE) return -1; // return the child index final int childIndex = childFullIndex - (parentIndex+1); assert(barcode.getWhiteIndex(childFullIndex) == childIndex); return childIndex; } /** * Return the index of the last child in the CollectionList for the given * parent index. This can be very useful for things like selecting the * children in a CollectionList when the parent is selected in another list. * * @see #childStartingIndex */ public int childEndingIndex(int parentIndex) { if(parentIndex < 0) throw new IndexOutOfBoundsException("Invalid index: " + parentIndex); if(parentIndex >= source.size()) throw new IndexOutOfBoundsException("Invalid index: " + parentIndex); // Get the index of the next node // Find the index of the black node with that index final int nextParentFullIndex = (parentIndex == barcode.blackSize() - 1) ? barcode.size() : barcode.getIndex(parentIndex + 1, Barcode.BLACK); final int lastWhiteBeforeNextParent = nextParentFullIndex - 1; // If this node has no children, the next node index will be past the size or black if(barcode.get(lastWhiteBeforeNextParent) == Barcode.BLACK) return -1; // return the child index final int childIndex = lastWhiteBeforeNextParent - (parentIndex+1); assert(barcode.getWhiteIndex(lastWhiteBeforeNextParent) == childIndex); return childIndex; } /** * Handle changes in the parent list. We'll need to update our node list * sizes. */ @Override public void listChanged(ListEvent listChanges) { // need to process the changes so that our size caches are up to date. updates.beginEvent(); while(listChanges.next()) { int index = listChanges.getIndex(); int type = listChanges.getType(); // insert means we'll need to insert a new node in the array if(type == ListEvent.INSERT) { handleInsert(index); } else if(type == ListEvent.DELETE) { handleDelete(index); // treat like a delete and then an add: } else if(type == ListEvent.UPDATE) { handleDelete(index); handleInsert(index); } } updates.commitEvent(); } /** @inheritDoc */ @Override public void dispose() { super.dispose(); // iterate over all child elements and dispose them final SimpleTreeIterator> treeIterator = new SimpleTreeIterator>(childElements); while(treeIterator.hasNext()) { treeIterator.next(); treeIterator.value().dispose(); } } /** * Helper for {@link #listChanged(ListEvent)} when inserting. */ private void handleInsert(int parentIndex) { // find the index of the black node with that index int absoluteIndex = getAbsoluteIndex(parentIndex); // find the size of the new node and add it to the total S parent = source.get(parentIndex); List children = model.getChildren(parent); // update the list of child lists Element> node = childElements.add(parentIndex, EMPTY_CHILD_ELEMENT, 1); node.set(createChildElementForList(children, node)); // update the barcode barcode.addBlack(absoluteIndex, 1); if(!children.isEmpty()) barcode.addWhite(absoluteIndex + 1, children.size()); // add events int childIndex = absoluteIndex - parentIndex; for(int i = 0; i < children.size(); i++) { E element = children.get(i); updates.elementInserted(childIndex, element); } } /** * Helper for {@link #listChanged(ListEvent)} when deleting. */ private void handleDelete(int sourceIndex) { // find the index of the black node with that index final int parentIndex = getAbsoluteIndex(sourceIndex); final int nextParentIndex = getAbsoluteIndex(sourceIndex + 1); final int childCount = nextParentIndex - parentIndex - 1; // subtract one for the parent // record the delete events first while the deleted values still exist if(childCount > 0) { int firstDeletedChildIndex = parentIndex - sourceIndex; int firstNotDeletedChildIndex = firstDeletedChildIndex + childCount; for (int i = firstDeletedChildIndex; i < firstNotDeletedChildIndex; i++) { updates.elementDeleted(firstDeletedChildIndex, get(i)); } } // update the list of child lists Element> removedChildElement = childElements.get(sourceIndex); childElements.remove(removedChildElement); removedChildElement.get().dispose(); // update the barcode barcode.remove(parentIndex, 1 + childCount); // delete the parent and all children } /** * Get the child element for the specified child index. */ private ChildElement getChildElement(int childIndex) { if(childIndex < 0) throw new IndexOutOfBoundsException("Invalid index: " + childIndex); if(childIndex >= size()) throw new IndexOutOfBoundsException("Index: " + childIndex + ", Size: " + size()); // get the child element int parentIndex = barcode.getBlackBeforeWhite(childIndex); return childElements.get(parentIndex).get(); } /** * Create a {@link ChildElement} for the specified List. */ private ChildElement createChildElementForList(List children, Element> node) { if(children instanceof EventList) return new EventChildElement((EventList) children, node); else return new SimpleChildElement(children, node); } /** * Get the absolute index for the specified parent index. This may be * virtual if the parent index is one greater than the last element. This * is useful for calculating the size of a range by using the location of * its follower. */ private int getAbsoluteIndex(int parentIndex) { if(parentIndex < barcode.blackSize()) return barcode.getIndex(parentIndex, Barcode.BLACK); if(parentIndex == barcode.blackSize()) return barcode.size(); throw new IndexOutOfBoundsException(); } /** * Models a list held by the CollectionList. */ private interface ChildElement { public E get(int index); public E remove(int index); public E set(int index, E element); public void dispose(); } /** * Provides the logic to map a parent object (e.g. an album) to its * children (e.g. the songs on the album). Serves basically the same * purpose as {@link javax.swing.tree.TreeModel} does to a JTree in Swing. * * @see CollectionList * @see GlazedLists#listCollectionListModel() */ public interface Model { /** * Return a list of the child nodes for a parent node. * * @param parent the parent node. * @return a List containing the child nodes. */ public List getChildren(E parent); } /** * Manages a standard List that does not implement {@link EventList}. */ private class SimpleChildElement implements ChildElement { private final List children; private final Element> node; public SimpleChildElement(List children, Element> node) { this.children = children; this.node = node; } public E get(int index) { return children.get(index); } public E remove(int index) { E removed = children.remove(index); // update the barcode int parentIndex = childElements.indexOfNode(node, (byte)0); int absoluteIndex = getAbsoluteIndex(parentIndex); int firstChildIndex = absoluteIndex + 1; barcode.remove(firstChildIndex + index, 1); // forward the offset event int childOffset = absoluteIndex - parentIndex; updates.beginEvent(); updates.elementDeleted(index + childOffset, removed); updates.commitEvent(); // all done return removed; } public E set(int index, E element) { E replaced = children.set(index, element); // forward the offset event int parentIndex = childElements.indexOfNode(node, (byte)0); int absoluteIndex = getAbsoluteIndex(parentIndex); int childOffset = absoluteIndex - parentIndex; updates.beginEvent(); updates.elementUpdated(index + childOffset, replaced); updates.commitEvent(); // all done return replaced; } public void dispose() { // do nothing } } /** * Monitors changes to a member EventList and forwards changes to all * listeners of the CollectionList. */ private class EventChildElement implements ChildElement, ListEventListener { private final EventList children; private final Element> node; public EventChildElement(EventList children, Element> node) { this.children = children; this.node = node; // ensure the EventList of children uses the same publisher as the CollectionList if(!getPublisher().equals(children.getPublisher())) throw new IllegalArgumentException("If a CollectionList.Model returns EventLists, those EventLists must use the same ListEventPublisher as the CollectionList"); // ensure the EventList of children uses the same locks as the CollectionList if(!getReadWriteLock().equals(children.getReadWriteLock())) throw new IllegalArgumentException("If a CollectionList.Model returns EventLists, those EventLists must use the same ReadWriteLock as the CollectionList"); children.getPublisher().setRelatedSubject(this, CollectionList.this); children.addListEventListener(this); } public E get(int index) { return children.get(index); } public E remove(int index) { // events will be fired from this call return children.remove(index); } public E set(int index, E element) { // events will be fired from this call return children.set(index, element); } public void listChanged(ListEvent listChanges) { int parentIndex = childElements.indexOfNode(node, (byte)1); int absoluteIndex = getAbsoluteIndex(parentIndex); int nextNodeIndex = getAbsoluteIndex(parentIndex+1); // update the barcode int firstChildIndex = absoluteIndex + 1; int previousChildrenCount = nextNodeIndex - firstChildIndex; if(previousChildrenCount > 0) barcode.remove(firstChildIndex, previousChildrenCount); if(!children.isEmpty()) barcode.addWhite(firstChildIndex, children.size()); // get the offset of this child list int childOffset = absoluteIndex - parentIndex; // forward the offset event updates.beginEvent(); while(listChanges.next()) { int index = listChanges.getIndex(); int type = listChanges.getType(); int overallIndex = index + childOffset; switch (type) { case ListEvent.INSERT: updates.elementInserted(overallIndex, listChanges.getNewValue()); break; case ListEvent.UPDATE: updates.elementUpdated(overallIndex, listChanges.getOldValue(), listChanges.getNewValue()); break; case ListEvent.DELETE: updates.elementDeleted(overallIndex, listChanges.getOldValue()); break; } } updates.commitEvent(); } public void dispose() { children.removeListEventListener(this); children.getPublisher().clearRelatedSubject(this); } @Override public String toString() { return "[" + childElements.indexOfNode(node, (byte)0) + ":" + children + "]"; } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/0000755000175000017500000000000012106516372025077 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/IteratorAsEnumeration.java0000644000175000017500000000254612106516372032235 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; import java.util.Enumeration; import java.util.Iterator; /** * This simple class adapts a given {@link Iterator} to the Enumeration * interface. It is useful when implementing existing JDK interfaces backed by * EventLists which must return Enumerations. * * @author James Lemieux */ public final class IteratorAsEnumeration implements Enumeration { /** The delegate Iterator which appears to be an Enumeration to the world. */ private final Iterator iterator; /** * Construct an Enumeration which is backed by the given * iterator. * * @throws IllegalArgumentException if iterator is null */ public IteratorAsEnumeration(Iterator iterator) { if (iterator == null) throw new IllegalArgumentException("iterator may not be null"); this.iterator = iterator; } /** @inheritDoc */ public boolean hasMoreElements() { return iterator.hasNext(); } /** @inheritDoc */ public E nextElement() { return iterator.next(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/functions/0000755000175000017500000000000012106516366027112 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/functions/ConstantFunction.java0000644000175000017500000000127512106516366033261 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.functions; import ca.odell.glazedlists.FunctionList; /** * A function function that always returnss the same value regardless of the * input. * * @author James Lemieux */ public class ConstantFunction implements FunctionList.Function { private final V value; public ConstantFunction(V value) { this.value = value; } public V evaluate(E sourceValue) { return value; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/GroupingListMultiMap.java0000644000175000017500000006130012106516372032041 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; import ca.odell.glazedlists.*; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import java.util.*; /** * This multimap implementation sits atop an {@link EventList} and makes it * accessible via the convenient {@link Map} interface. It is constructed with * a {@link FunctionList.Function} which is used to create the keys of the map. * The values of the map are the lists of values from the {@link EventList} * which all map to a common key. * *

      For example, an {@link EventList} containing * *

       * {Cherry, Plum, Cranberry, Pineapple, Banana, Prune}
       * 
      * * paired with a Function that returns the first letter of the fruit name * produces the multi map: * *
       * "B" -> {Banana}
       * "C" -> {Cherry, Cranberry}
       * "P" -> {Plum, Pineapple, Prune}
       * 
      * * @author James Lemieux */ public class GroupingListMultiMap implements DisposableMap>, ListEventListener> { /** The raw values of this Map in an {@link EventList}. */ private final GroupingList groupingList; /** The values of this Map in an {@link EventList}. */ private final FunctionList, List> valueList; /** The keys of this Map (used to remove entries from the {@link #delegate}) */ private final List keyList; /** The keys of this Map made to look like a Set (it is built lazily in {@link #keySet()}) */ private Set keySet; /** The function which produces keys for this multimap. */ private final FunctionList.Function keyFunction; /** The delegate Map which is kept in synch with {@link #groupingList} changes. */ private final Map> delegate; /** The set of Map.Entry objects in this Map (it is built lazily in {@link #entrySet()}) */ private Set>> entrySet; /** * Construct a multimap which maps the keys produced by the * keyFunction, to groups of values from source * that agree on their keys. * * @param source the raw data which has not yet been grouped * @param keyFunction the function capable of producing the keys of this * {@link Map} from each value * @param keyGrouper the comparator that groups together values which * have the same key according to the given keyFunction */ public GroupingListMultiMap(EventList source, FunctionList.Function keyFunction, Comparator keyGrouper) { if (keyFunction == null) throw new IllegalArgumentException("keyFunction may not be null"); if (keyGrouper == null) throw new IllegalArgumentException("keyGrouper may not be null"); this.keyFunction = keyFunction; // construct a GroupingList which groups together the source elements for common keys this.groupingList = new GroupingList(source, new FunctionComparator(keyFunction, keyGrouper)); // wrap each List in the GroupingList in a layer that enforces the keyFunction constraints for writes this.valueList = new FunctionList, List>(this.groupingList, new ValueListFunction()); this.valueList.addListEventListener(this); // it is important that the keyList is a BasicEventList since we use its ListIterator, which remains // consistent with changes to its underlying data (any other Iterator would throw a ConcurrentModificationException) this.keyList = new BasicEventList(this.groupingList.size()); this.delegate = new HashMap>(this.groupingList.size()); // initialize both the keyList and the delegate Map for (Iterator> i = this.valueList.iterator(); i.hasNext();) { final List value = i.next(); final K key = key(value); this.keyList.add(key); this.delegate.put(key, value); } } /** {@inheritDoc} */ public void dispose() { valueList.removeListEventListener(this); valueList.dispose(); groupingList.dispose(); keySet = null; entrySet = null; keyList.clear(); delegate.clear(); } /** {@inheritDoc} */ public int size() { return delegate.size(); } /** {@inheritDoc} */ public boolean isEmpty() { return delegate.isEmpty(); } /** {@inheritDoc} */ public boolean containsKey(Object key) { return delegate.containsKey(key); } /** {@inheritDoc} */ public boolean containsValue(Object value) { return delegate.containsValue(value); } /** {@inheritDoc} */ public List get(Object key) { return delegate.get(key); } /** {@inheritDoc} */ public List put(K key, List value) { checkKeyValueAgreement(key, value); final List removed = remove(key); groupingList.add(value); return removed; } /** {@inheritDoc} */ public void putAll(Map> m) { // verify the contents of the given Map and ensure all key/value pairs agree with the keyFunction for (Iterator>> i = m.entrySet().iterator(); i.hasNext();) { final Entry> entry = i.next(); final K key = entry.getKey(); final List value = entry.getValue(); checkKeyValueAgreement(key, value); } // remove all values currently associated with the keys for (Iterator i = m.keySet().iterator(); i.hasNext();) remove(i.next()); // add all new values into this Map groupingList.addAll(m.values()); } /** * This convenience method ensures that the key matches the * key values produced by each of the value objects. If a * mismatch is found, an {@link IllegalArgumentException} is thrown. * * @param key the expected key value of each value object * @param value the value objects which should produce the given key when * run through the key function */ private void checkKeyValueAgreement(K key, Collection value) { for (Iterator i = value.iterator(); i.hasNext();) checkKeyValueAgreement(key, i.next()); } /** * This convenience method ensures that the key matches the * key value produced for the value object. If a * mismatch is found, an {@link IllegalArgumentException} is thrown. * * @param key the expected key value of each value object * @param value the value object which should produce the given key when * run through the key function */ private void checkKeyValueAgreement(K key, V value) { final K k = key(value); if (!GlazedListsImpl.equal(key, k)) throw new IllegalArgumentException("The calculated key for the given value (" + k + ") does not match the given key (" + key + ")"); } /** {@inheritDoc} */ public void clear() { groupingList.clear(); } /** {@inheritDoc} */ public List remove(Object key) { final int index = keyList.indexOf(key); return index == -1 ? null : groupingList.remove(index); } /** {@inheritDoc} */ public Collection> values() { return groupingList; } /** {@inheritDoc} */ public Set keySet() { if (keySet == null) keySet = new KeySet(); return keySet; } /** {@inheritDoc} */ public Set>> entrySet() { if (entrySet == null) entrySet = new EntrySet(); return entrySet; } /** @inheritDoc */ @Override public boolean equals(Object o) { return delegate.equals(o); } /** @inheritDoc */ @Override public int hashCode() { return delegate.hashCode(); } /** * Updates this MultiMap datastructure to reflect changes in the underlying * {@link GroupingList}. Specifically, new entries are added to this * MultiMap by calculating a key using the key function of this MultiMap. * * Interestingly, we don't have to handle the UPDATE events here. The * entries in the delegate map are silenty updated in place since the List * we were given by the GroupingList is simply mutated. INSERTS and * DELETES, however, require actual changes to the delegate map, and thus * are processed here accordingly. * * @param listChanges an event describing the changes in the GroupingList */ public void listChanged(ListEvent> listChanges) { while (listChanges.next()) { final int changeIndex = listChanges.getIndex(); final int changeType = listChanges.getType(); if (changeType == ListEvent.INSERT) { final List inserted = listChanges.getSourceList().get(changeIndex); final K key = key(inserted); keyList.add(changeIndex, key); delegate.put(key, inserted); } else if (changeType == ListEvent.DELETE) { final K deleted = keyList.remove(changeIndex); delegate.remove(deleted); } } } /** * Uses the key function to return the key for a given list of values. * * @param values a non-empty list of values from the source * {@link GroupingList} which share the same key value * @return the shared key which maps to each of the given values */ private K key(List values) { return key(values.get(0)); } /** * Uses the key function to return the key for a given value. * * @param value a single value from the source list * @return the key which maps to the given value */ private K key(V value) { return keyFunction.evaluate(value); } /** * This private {@link Set} implementation represents the {@link Map.Entry} * objects within this MultiMap. All mutating methods are implemented to * "write through" to the backing {@link EventList} which ensures that both * the {@link EventList} and this MultiMap always remain in sync. */ private class EntrySet extends AbstractSet>> { /** {@inheritDoc} */ @Override public int size() { return keyList.size(); } /** {@inheritDoc} */ @Override public Iterator>> iterator() { return new EntrySetIterator(keyList.listIterator()); } /** {@inheritDoc} */ @Override public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; final Entry> e = (Entry>) o; final K key = e.getKey(); final List value = e.getValue(); final List mapValue = GroupingListMultiMap.this.get(key); return GlazedListsImpl.equal(value, mapValue); } /** {@inheritDoc} */ @Override public boolean remove(Object o) { if (!contains(o)) return false; GroupingListMultiMap.this.remove(((Map.Entry) o).getKey()); return true; } /** {@inheritDoc} */ @Override public void clear() { GroupingListMultiMap.this.clear(); } } /** * This private {@link Iterator} implementation iterates the {@link Set} of * {@link Map.Entry} objects within this MultiMap. All mutating methods are * implemented to "write through" to the backing {@link EventList} which * ensures that both the {@link EventList} and this MultiMap always remain * in sync. * *

      Note: This implementation returns a new * {@link Map.Entry} object each time {@link #next} is called. Identity is * not preserved. */ private class EntrySetIterator implements Iterator>> { /** The delegate Iterator walks a List of keys for the MultiMap. */ private final ListIterator keyIter; /** * Construct a new EntrySetIterator using a delegate Iterator that * walks the keys of the MultMap. * * @param keyIter a {@link ListIterator} that walks the keys of the MultiMap */ EntrySetIterator(ListIterator keyIter) { this.keyIter = keyIter; } /** {@inheritDoc} */ public boolean hasNext() { return keyIter.hasNext(); } /** * Returns a new {@link Map.Entry} each time this method is called. */ public Entry> next() { final K key = keyIter.next(); return new MultiMapEntry(key, get(key)); } /** {@inheritDoc} */ public void remove() { final int index = keyIter.previousIndex(); if (index == -1) throw new IllegalStateException("Cannot remove() without a prior call to next()"); groupingList.remove(index); } } /** * This is an implementation of the {@link Map.Entry} interface that is * appropriate for this MultiMap. All mutating methods are implemented to * "write through" to the backing {@link EventList} which ensures that * both the {@link EventList} and this MultiMap always remain in sync. */ private class MultiMapEntry implements Map.Entry> { /** The MultiMap key for this Entry object. */ private final K key; /** The MultiMap value for this Entry object. */ private List value; /** * Constructs a new MultiMapEntry with the given key and * initial value. */ MultiMapEntry(K key, List value) { if (value == null) throw new IllegalArgumentException("value cannot be null"); this.value = value; this.key = key; } /** {@inheritDoc} */ public K getKey() { return key; } /** {@inheritDoc} */ public List getValue() { return value; } /** * Since {@link GroupingList} is particular about the identity of the * Lists it contains, and this MultiMap uses those same * Lists as its values, this method is implemented to simply * replace the contents of the List with the contents * of the given newValue. So, the data is changed, but the * identity of the List in the MultiMap and {@link GroupingList} is not. * * @param newValue the new values use as elements of the value List * @return the old value List of this Entry */ public List setValue(List newValue) { // ensure all of the newValue elements agree with the key of this Entry checkKeyValueAgreement(getKey(), newValue); // record the old value List elements (to return) final List oldValue = new ArrayList(value); // replace all elements within the List // // (GroupingList actually removes Lists the moment they become *empty* // so we first insert the new values rather than removing the old values // to avoid the temporary existence of an empty List) value.addAll(newValue); value.removeAll(oldValue); return oldValue; } /** * Two MultiMapEntry entry objects are equal iff their keys and values * are equal. */ @Override public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry) o; final boolean keysEqual = GlazedListsImpl.equal(getKey(), e.getKey()); return keysEqual && GlazedListsImpl.equal(getValue(), e.getValue()); } /** {@inheritDoc} */ @Override public int hashCode() { return (key == null ? 0 : key.hashCode()) ^ value.hashCode(); } /** {@inheritDoc} */ @Override public String toString() { return getKey() + "=" + getValue(); } } /** * This private {@link Set} implementation represents the keys within this * MultiMap. All mutating methods are implemented to "write through" to the * backing {@link EventList} which ensures that both the {@link EventList} * and this MultiMap always remain in sync. */ private class KeySet extends AbstractSet { /** {@inheritDoc} */ @Override public int size() { return keyList.size(); } /** {@inheritDoc} */ @Override public Iterator iterator() { return new KeySetIterator(keyList.listIterator()); } /** {@inheritDoc} */ @Override public boolean contains(Object o) { return GroupingListMultiMap.this.containsKey(o); } /** {@inheritDoc} */ @Override public boolean remove(Object o) { return GroupingListMultiMap.this.remove(o) != null; } /** {@inheritDoc} */ @Override public void clear() { GroupingListMultiMap.this.clear(); } } /** * This private {@link Iterator} implementation iterates the {@link Set} of * keys within this MultiMap. All mutating methods are implemented to * "write through" to the backing {@link EventList} which ensures that both * the {@link EventList} and this MultiMap always remain in sync. */ private class KeySetIterator implements Iterator { /** The delegate Iterator walks a List of keys for the MultiMap. */ private final ListIterator keyIter; /** * Construct a new KeySetIterator using a delegate Iterator that walks * the list of unique keys of the MultMap. * * @param keyIter a {@link ListIterator} that walks the keys of the MultiMap */ KeySetIterator(ListIterator keyIter) { this.keyIter = keyIter; } /** {@inheritDoc} */ public boolean hasNext() { return keyIter.hasNext(); } /** {@inheritDoc} */ public K next() { return keyIter.next(); } /** {@inheritDoc} */ public void remove() { final int index = keyIter.previousIndex(); if (index == -1) throw new IllegalStateException("Cannot remove() without a prior call to next()"); groupingList.remove(index); } } /** * This Comparator first runs each value through a * {@link FunctionList.Function} to produce key objects which are then * compared to determine a relative ordering using the given delegate * {@link Comparator}. */ private final class FunctionComparator implements Comparator { /** A Comparator that orders {@link Comparable} objects. */ private final Comparator delegate; /** A function that extracts {@link Comparable} values from given objects. */ private final FunctionList.Function function; /** * Construct a new FunctionComparator that uses the given * function to extract {@link Comparable} values from * given objects. */ FunctionComparator(FunctionList.Function function, Comparator delegate) { this.function = function; this.delegate = delegate; } /** {@inheritDoc} */ public int compare(V o1, V o2) { final K k1 = function.evaluate(o1); final K k2 = function.evaluate(o2); return delegate.compare(k1, k2); } } /** * This Function wraps each List produced by the GroupingList with a layer * that ensures that mutations to it don't violate the keyFunction * constraints required by this MultiMap. */ private final class ValueListFunction implements FunctionList.Function, List> { public List evaluate(List sourceValue) { return new ValueList(sourceValue); } } /** * This class wraps each element of the GroupingList with a layer of * checking to ensure that mutations to it don't violate the keyFunction * constraints required by this MultiMap. */ private final class ValueList implements List { /** The List that actually implements the List operations */ private final List delegate; /** The key that all values in this List must share. */ private final K key; public ValueList(List delegate) { this.delegate = delegate; this.key = key(delegate.get(0)); } public int size() { return delegate.size(); } public boolean isEmpty() { return delegate.isEmpty(); } public boolean contains(Object o) { return delegate.contains(o); } public Iterator iterator() { return delegate.iterator(); } public Object[] toArray() { return delegate.toArray(); } public T[] toArray(T[] a) { return delegate.toArray(a); } public boolean add(V o) { checkKeyValueAgreement(key, o); return delegate.add(o); } public boolean addAll(Collection c) { checkKeyValueAgreement(key, c); return delegate.addAll(c); } public boolean addAll(int index, Collection c) { checkKeyValueAgreement(key, c); return delegate.addAll(index, c); } public void add(int index, V element) { checkKeyValueAgreement(key, element); delegate.add(index, element); } public V set(int index, V element) { checkKeyValueAgreement(key, element); return delegate.set(index, element); } public List subList(int fromIndex, int toIndex) { return new ValueList(delegate.subList(fromIndex, toIndex)); } public ListIterator listIterator() { return new ValueListIterator(delegate.listIterator()); } public ListIterator listIterator(int index) { return new ValueListIterator(delegate.listIterator(index)); } public boolean remove(Object o) { return delegate.remove(o); } public boolean containsAll(Collection c) { return delegate.containsAll(c); } public boolean removeAll(Collection c) { return delegate.removeAll(c); } public boolean retainAll(Collection c) { return delegate.retainAll(c); } public void clear() { delegate.clear(); } @Override public boolean equals(Object o) { return delegate.equals(o); } @Override public int hashCode() { return delegate.hashCode(); } public V get(int index) { return delegate.get(index); } public V remove(int index) { return delegate.remove(index); } public int indexOf(Object o) { return delegate.indexOf(o); } public int lastIndexOf(Object o) { return delegate.lastIndexOf(o); } @Override public String toString() { return delegate.toString(); } /** * This class wraps the normal ListIterator returned by the GroupingList * elements with extra checking to ensure mutations to it don't violate * the keyFunction constraints required by this MultiMap. */ private final class ValueListIterator implements ListIterator { private final ListIterator delegate; public ValueListIterator(ListIterator delegate) { this.delegate = delegate; } public void set(V o) { checkKeyValueAgreement(key, o); delegate.set(o); } public void add(V o) { checkKeyValueAgreement(key, o); delegate.add(o); } public boolean hasNext() { return delegate.hasNext(); } public V next() { return delegate.next(); } public boolean hasPrevious() { return delegate.hasPrevious(); } public V previous() { return delegate.previous(); } public int nextIndex() { return delegate.nextIndex(); } public void remove() { delegate.remove(); } public int previousIndex() { return delegate.previousIndex(); } } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/Diff.java0000644000175000017500000002563312106516372026623 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; import ca.odell.glazedlists.EventList; import java.util.*; /** * Implementation of Eugene W. Myer's paper, "An O(ND) Difference Algorithm and * Its Variations", the same algorithm found in GNU diff. * *

      Note that this is a cleanroom implementation of this popular algorithm * that is particularly suited for the Java programmer. The variable names are * descriptive and the approach is more object-oriented than Myer's sample * algorithm. * * @author Jesse Wilson */ public final class Diff { /** * Convenience method for {@link #replaceAll(EventList,List,boolean,Comparator) * replaceAll()} that uses {@link Object#equals(Object)} to determine * equality. */ public static void replaceAll(EventList target, List source, boolean updates) { replaceAll(target, source, updates, (Comparator)GlazedListsImpl.equalsComparator()); } /** * 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. * * @param comparator a {@link Comparator} to use to test only for equality. * This comparator shall return 0 to signal that two elements are * equal, and nonzero otherwise. * @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, Comparator comparator) { DiffMatcher listDiffMatcher = new ListDiffMatcher(target, source, comparator); List editScript = shortestEditScript(listDiffMatcher); // target is x axis. Changes in X mean advance target index // source is y axis. Changes to y mean advance source index int targetIndex = 0; int sourceIndex = 0; // walk through points, applying changes as they arrive Point previousPoint = null; for(Iterator i = editScript.iterator(); i.hasNext();) { Point currentPoint = i.next(); // skip the first point if(previousPoint == null) { previousPoint = currentPoint; continue; } // figure out what the relationship in the values is int deltaX = currentPoint.getX() - previousPoint.getX(); int deltaY = currentPoint.getY() - previousPoint.getY(); // handle an update if(deltaX == deltaY) { if(updates) { for(int u = 0; u < deltaX; u++) { target.set(targetIndex + u, source.get(sourceIndex + u)); } } targetIndex += deltaX; sourceIndex += deltaY; // handle a remove } else if(deltaX == 1 && deltaY == 0) { target.remove(targetIndex); // handle an insert } else if(deltaX == 0 && deltaY == 1) { target.add(targetIndex, source.get(sourceIndex)); sourceIndex++; targetIndex++; // should never be reached } else { throw new IllegalStateException(); } // the next previous point is this current point previousPoint = currentPoint; } } /** * Calculate the length of the longest common subsequence for the specified * input. */ private static List shortestEditScript(DiffMatcher input) { // calculate limits based on the size of the input matcher int N = input.getAlphaLength(); int M = input.getBetaLength(); Point maxPoint = new Point(N, M); int maxSteps = N + M; // use previous round furthest reaching D-path to determine the // new furthest reaching (D+1)-path Map furthestReachingPoints = new HashMap(); // walk through in stages, each stage adding one non-diagonal. // D == count of non-diagonals in current stage for(int D = 0; D <= maxSteps; D++) { // exploit diagonals in order to save storing both X and Y // diagonal k means every point on k, (k = x - y) for(int k = -D; k <= D; k += 2) { // the furthest reaching D-path on the left and right diagonals // either of these may be null. The terms 'below left' and 'above // right' refer to the diagonals that the points are on and may // not be representative of the point positions Point belowLeft = furthestReachingPoints.get(new Integer(k - 1)); Point aboveRight = furthestReachingPoints.get(new Integer(k + 1)); // the new furthest reaching point to create Point point; // first round: we have matched zero in word X if(furthestReachingPoints.isEmpty()) { point = new Point(0, 0); // if this is the leftmost diagonal, or the left edge is // further than the right edge, our new X is that value and // our y is one greater (shift verically by one) } else if(k == -D || (k != D && belowLeft.getX() < aboveRight.getX())) { point = aboveRight.createDeltaPoint(0, 1); // if the right edge is further than the left edge, use that // x and keep y the same (shift horizontally by one) } else { point = belowLeft.createDeltaPoint(1, 0); } // match as much diagonal as possible from the previous endpoint while(point.isLessThan(maxPoint) && input.matchPair(point.getX(), point.getY())) { point = point.incrementDiagonally(); } // save this furthest reaching path furthestReachingPoints.put(new Integer(k), point); // if we're past the end, we have a solution! if(point.isEqualToOrGreaterThan(maxPoint)) { return point.trail(); } } } // no solution was found throw new IllegalStateException(); } /** * Models an X and Y point in a path. The top-left corner of the axis is the point (0, * 0). This is the lowest point in both the x and y dimensions. Negative points are * not allowed. */ private static class Point { private int x = 0; private int y = 0; private Point predecessor = null; /** * Create a new point with the specified coordinates and no predecessor. */ public Point(int x, int y) { this.x = x; this.y = y; } /** * Creates a new point from this point by shifting its values as specified. The * new point keeps a reference to its source in order to create a path later. */ public Point createDeltaPoint(int deltaX, int deltaY) { Point result = new Point(x + deltaX, y + deltaY); result.predecessor = this; return result; } /** * Shifts x and y values down and to the * right by one. */ public Point incrementDiagonally() { Point result = createDeltaPoint(1, 1); // shortcut to the predecessor (to save memory!) if(predecessor != null) { int deltaX = result.x - predecessor.x; int deltaY = result.y - predecessor.y; if(deltaX == deltaY) { result.predecessor = this.predecessor; } } return result; } public int getX() { return x; } public int getY() { return y; } public boolean isLessThan(Point other) { return x < other.x && y < other.y; } public boolean isEqualToOrGreaterThan(Point other) { return x >= other.x && y >= other.y; } @Override public String toString() { return "(" + x + "," + y + ")"; } /** * Get a trail from the original point to this point. This is a list of * all points created via a series of {@link #createDeltaPoint(int,int)} * calls. */ public List trail() { List reverse = new ArrayList(); Point current = this; while (current != null) { reverse.add(current); current = current.predecessor; } Collections.reverse(reverse); return reverse; } } /** * Determines if the values at the specified points match or not. * *

      This class specifies that each element should specify a character value. * This is for testing and debugging only and it is safe for implementing * classes to throw {@link UnsupportedOperationException} for both the * {@link #alphaAt(int)} and {@link #betaAt(int)} methods. */ interface DiffMatcher { public int getAlphaLength(); public int getBetaLength(); public boolean matchPair(int alphaIndex, int betaIndex); /** * Output a character representing the specified element, for * the convenience of testing. */ public char alphaAt(int index); public char betaAt(int index); } /** * Matcher for Lists. */ static class ListDiffMatcher implements DiffMatcher { private List alpha; private List beta; private Comparator comparator; public ListDiffMatcher(List alpha, List beta, Comparator comparator) { this.alpha = alpha; this.beta = beta; this.comparator = comparator; } public int getAlphaLength() { return alpha.size(); } public char alphaAt(int index) { return alpha.get(index).toString().charAt(0); } public char betaAt(int index) { return beta.get(index).toString().charAt(0); } public int getBetaLength() { return beta.size(); } public boolean matchPair(int alphaIndex, int betaIndex) { return (comparator.compare(alpha.get(alphaIndex), beta.get(betaIndex)) == 0); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/SortIconFactory.java0000644000175000017500000000723012106516372031034 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; // to match icons to the current look and feel import javax.swing.*; import java.net.URL; import java.util.HashMap; import java.util.Map; /** * A Factory to provide access to sort-arrow icons for table * headers which match the host Look and Feel. * * @author Kevin Maltby */ public final class SortIconFactory { /** a map of look and feels to resource paths for icons */ private static final String resourceRoot = "resources"; private static final String defaultResourcePath = "aqua"; private static final Map lookAndFeelResourcePathMap = new HashMap(); static { lookAndFeelResourcePathMap.put("Mac OS X Aqua", "aqua"); lookAndFeelResourcePathMap.put("Metal/Steel", "metal"); lookAndFeelResourcePathMap.put("Metal/Ocean", "ocean"); lookAndFeelResourcePathMap.put("Classic Windows", "windows"); lookAndFeelResourcePathMap.put("Windows XP", "windowsxp"); lookAndFeelResourcePathMap.put("Windows Vista", "windowsxp"); // TODO(jessewilson) make Vista-specific icons lookAndFeelResourcePathMap.put("WinLAF", "windowsxp"); } /** the icons to use for indicating sort order */ private static Icon[] defaultIcons = null; private static String[] iconFileNames = { "unsorted.png", "primary_sorted.png", "primary_sorted_reverse.png", "primary_sorted_alternate.png", "primary_sorted_alternate_reverse.png", "secondary_sorted.png", "secondary_sorted_reverse.png", "secondary_sorted_alternate.png", "secondary_sorted_alternate_reverse.png" }; /** * A dummy constructor to prevent instantiation of this class */ private SortIconFactory() { throw new UnsupportedOperationException(); } /** * Loads the set of icons that match the current UIManager. */ public static Icon[] loadIcons() { // if we've already loaded the default icons, return them if(defaultIcons != null) return defaultIcons; // detect the current look & feel String lookAndFeelName = UIManager.getLookAndFeel().getName(); if(lookAndFeelName.equals("Metal")) lookAndFeelName = PLAFDetector.getMetalTheme(); else if(lookAndFeelName.equals("Windows")) lookAndFeelName = PLAFDetector.getWindowsTheme(); String resourcePath = lookAndFeelResourcePathMap.get(lookAndFeelName); if(resourcePath == null) resourcePath = defaultResourcePath; // save and return the default icons defaultIcons = loadIcons(resourceRoot + "/" + resourcePath); return defaultIcons; } /** * Loads the set of icons from the specified path. */ public static Icon[] loadIcons(String path) { // use the classloader to look inside this jar file ClassLoader jarLoader = SortIconFactory.class.getClassLoader(); // load each icon as a resource from the source .jar file // (we intentionally start at index 1 for now because unsorted.png, the 0th iconFileName, is not yet used) Icon[] pathIcons = new Icon[iconFileNames.length]; for(int i = 1; i < pathIcons.length; i++) { URL iconLocation = jarLoader.getResource(path + "/" + iconFileNames[i]); if(iconLocation != null) pathIcons[i] = new ImageIcon(iconLocation); } // return the loaded result return pathIcons; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/testing/0000755000175000017500000000000012106516360026551 5ustar gregoagregoa././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/testing/ListConsistencyListener.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/testing/ListConsistencyListener0000644000175000017500000002351712106516360033347 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.testing; // Java collections are used for underlying data storage import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import java.util.ArrayList; import java.util.List; /** * A very basic listener that ensures that lists are kept consistent and that * the change events are consistent. * * @author Jesse Wilson */ public class ListConsistencyListener { /** a second copy of the list data */ private List expected; /** a name for reporting problems with the list */ private String name; /** the source list to compare against */ private EventList source; /** whether to cough out changes to the console as they happen */ private boolean verbose = false; /** whether to fail when the removed element is incorrectly reported */ private boolean previousElementTracked = true; /** count the number of changes per event */ private List changeCounts = new ArrayList(); private List reorderings = new ArrayList(); /** * Creates a new ListConsistencyListener that ensures events from the source * list are consistent. * * @param verbose whether to print changes to the console as they happne */ private ListConsistencyListener(EventList source, String name, boolean verbose) { this.source = source; this.name = name != null ? name : source.getClass().getName(); this.verbose = verbose; // populate the list of expected values expected = new ArrayList(source); // handle changes to the source list source.addListEventListener(new ListChangeHandler()); } /** * Creates a new ListConsistencyListener and installs it as a listener on * the specified source {@link EventList}. Every time that list changes, this * listener will verify the change reported equals the change applied. */ public static ListConsistencyListener install(EventList source, String name, boolean verbose) { return new ListConsistencyListener(source, name, verbose); } public static ListConsistencyListener install(EventList source) { return install(source, null, false); } /** * Gets the number of events that have occured thus far. */ public int getEventCount() { return changeCounts.size(); } /** * Gets the number of changes for the specified event. */ public int getChangeCount(int event) { return changeCounts.get(event).intValue(); } /** * Get whether the specified event was a reordering. */ public boolean isReordering(int event) { return reorderings.get(event).booleanValue(); } /** * Validate that this list is as expected. */ public void assertConsistent() { assertTrue(expected.size() == source.size()); for(int i = 0; i < expected.size(); i++) { assertTrue("Different elements at " + i + " (expected=" + expected.get(i) + ", is=" + source.get(i), expected.get(i) == source.get(i)); } } public void assertTrue(boolean condition) { if(!condition) { System.out.println(""); } assertTrue("Assertion failed", condition); } public void assertTrue(String message, boolean condition) { if(!condition) { throw new IllegalStateException(message); } } /** * Configure whether errors shall be thrown if the previous value isn't * what's expected. */ public void setPreviousElementTracked(boolean previousElementTracked) { this.previousElementTracked = previousElementTracked; } /** * When the source {@link EventList} is changed, make sure the event reported * describes the differences between before and after. */ private class ListChangeHandler implements ListEventListener { public void listChanged(ListEvent listChanges) { try { assertTrue(source == listChanges.getSource()); assertEventsInIncreasingOrder(listChanges); // print the changes if necessary if(verbose) System.out.println(name + ": " + listChanges + ", size: " + source.size() + ", source: " + source); // record the changed indices List changedIndices = new ArrayList(); // keep track of the highest change index so far int highestChangeIndex = 0; // handle sorting events if(listChanges.isReordering()) { int[] reorderMap = listChanges.getReorderMap(); assertTrue(expected.size() == reorderMap.length); List newExpectedValues = new ArrayList(expected.size()); for(int i = 0; i < reorderMap.length; i++) { newExpectedValues.add(i, expected.get(reorderMap[i])); changedIndices.add(new Integer(i)); } expected = newExpectedValues; changeCounts.add(new Integer(2 * reorderMap.length)); reorderings.add(Boolean.TRUE); // handle regular events } else { // for all changes, one index at a time int changesForEvent = 0; while(listChanges.next()) { changesForEvent++; // get the current change info int changeIndex = listChanges.getIndex(); int changeType = listChanges.getType(); // save this index for validation later changedIndices.add(new Integer(changeIndex)); // make sure the change indices are positive and not descreasing assertTrue(changeIndex >= 0); assertTrue(changeIndex >= highestChangeIndex); highestChangeIndex = changeIndex; // verify the index is small enough, and adjust the size if(changeType == ListEvent.INSERT) { E inserted = source.get(changeIndex); expected.add(changeIndex, inserted); if(previousElementTracked) { Object reportedNew = listChanges.getNewValue(); // assertTrue(inserted == reportedNew); } } else if(changeType == ListEvent.DELETE) { Object removed = expected.remove(changeIndex); if(previousElementTracked) { Object reportedRemoved = listChanges.getOldValue(); assertTrue(removed == reportedRemoved); } } else if(changeType == ListEvent.UPDATE) { E updated = source.get(changeIndex); E replaced = expected.set(changeIndex, updated); if(previousElementTracked) { Object reportedReplaced = listChanges.getOldValue(); assertTrue(replaced == reportedReplaced); // Object reportedNew = listChanges.getNewValue(); // assertTrue(updated == reportedNew); } } } changeCounts.add(new Integer(changesForEvent)); reorderings.add(Boolean.FALSE); } // verify the source is consistent with what we expect assertConsistent(); } catch (RuntimeException unexpected) { throw new RuntimeException("Failure for " + name, unexpected); } } @Override public String toString() { return "ConsistencyListener:" + name; } } /** * Ensure that events in the specified event flow in the legal order. */ public static void assertEventsInIncreasingOrder(ListEvent listChanges) { listChanges.reset(); StringBuffer changeDescription = new StringBuffer(); int previousChangeIndex = -1; int previousChangeType = ListEvent.DELETE; boolean increasingOrder = true; while(listChanges.next()) { int changeIndex = listChanges.getIndex(); int changeType = listChanges.getType(); // maintain the change string if(changeType == ListEvent.UPDATE) { changeDescription.append("U"); } else if(changeType == ListEvent.INSERT) { changeDescription.append("I"); } else if(changeType == ListEvent.DELETE) { changeDescription.append("D"); } changeDescription.append(changeIndex); // see if this was a failure if(changeIndex < previousChangeIndex || (changeIndex == previousChangeIndex && previousChangeType != ListEvent.DELETE)) { increasingOrder = false; changeDescription.append("*"); } // prepare for the next change changeDescription.append(" "); previousChangeIndex = changeIndex; previousChangeType = changeType; } if(!increasingOrder) { System.out.println("List changes not in increasing order: " + changeDescription); // Assert.fail("List changes not in increasing order: " + changeDescription); } // reset the list iterator for other handlers listChanges.reset(); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/testing/GlazedListsTests.java0000644000175000017500000002453012106516360032670 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.testing; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.FunctionList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import ca.odell.glazedlists.matchers.Matcher; import java.io.*; import java.util.*; /** * A factory class useful for testing! * * @author Jesse Wilson */ public class GlazedListsTests { private static final Comparator FIRST_LETTER_COMPARATOR = new FirstLetterComparator(); private static final Comparator LAST_LETTER_COMPARATOR = new LastLetterComparator(); private static final FunctionList.Function FIRST_LETTER_FUNCTION = new FirstLetterFunction(); /** * A dummy constructor to prevent instantiation of this class */ private GlazedListsTests() { throw new UnsupportedOperationException(); } /** * Convert a String like "Apple Banana Cat" into a single list: * { "Apple", "Banana", "Cat" } */ public static List delimitedStringToList(String delimited) { final String[] strings = delimited.split("\\s"); List result = new ArrayList(strings.length); for (int i = 0; i < strings.length; i++) { result.add(strings[i]); } return result; } /** * Convert the characters of the specified String to a list. */ public static List stringToList(CharSequence chars) { List result = new ArrayList(chars.length()); for (int i = 0; i < chars.length(); i++) { result.add(chars.subSequence(i, i+1).toString()); } return result; } /** * Convert a String like "AA,BB,CDE" into three Lists: * { [ A A ], [ B B ], [ C D E ] } */ public static List> stringToLists(CharSequence chars) { List> result = new ArrayList>(); String[] strings = chars.toString().split(","); for (int i = 0; i < strings.length; i++) result.add(stringToList(strings[i])); return result; } /** * Convert an array of Strings into a List of characters. */ public static List stringsToList(CharSequence[] data) { List result = new ArrayList(); for (int i = 0; i < data.length; i++) result.addAll(stringToList(data[i])); return result; } /** * Convert the specified int[] array to a List of Integers. */ public static List intArrayToIntegerCollection(int[] values) { List result = new ArrayList(); for (int i = 0; i < values.length; i++) result.add(new Integer(values[i])); return result; } /** * This matcher matches everything greater than its minimum. */ public static Matcher matchAtLeast(int minimum) { return new AtLeastMatcher(minimum); } /** * Serialize the specified object to bytes, then deserialize it back. */ public static T serialize(T object) throws IOException, ClassNotFoundException { return (T)fromBytes(toBytes(object)); } public static byte[] toBytes(Object object) throws IOException { ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); ObjectOutputStream objectsOut = new ObjectOutputStream(bytesOut); objectsOut.writeObject(object); return bytesOut.toByteArray(); } public static Object fromBytes(byte[] bytes) throws IOException, ClassNotFoundException { ByteArrayInputStream bytesIn = new ByteArrayInputStream(bytes); ObjectInputStream objectsIn = new ObjectInputStream(bytesIn); return objectsIn.readObject(); } public static String toString(byte[] bytes) { StringBuffer result = new StringBuffer(); for(int b = 0; b < bytes.length; b++) { result.append(bytes[b] < 0 ? "-" : " "); String hexString = Integer.toString(Math.abs(bytes[b]), 16); while(hexString.length() < 2) hexString = "0" + hexString; result.append("0x").append(hexString).append(", "); if(b % 16 == 15) result.append("\n"); } return result.toString(); } private static class AtLeastMatcher implements Matcher { private final int minimum; public AtLeastMatcher(int minimum) { this.minimum = minimum; } public boolean matches(Number value) { return value.intValue() >= minimum; } } /** * Returns a comparator for comparing Strings based solely on their first * character. null Strings are not tolerated and the * Comparator will throw {@link NullPointerException}. */ public static Comparator getFirstLetterComparator() { return FIRST_LETTER_COMPARATOR; } private static class FirstLetterComparator implements Comparator { public int compare(String o1, String o2) { return o1.charAt(0) - o2.charAt(0); } } /** * Returns a comparator for comparing Strings based solely on their last * character. null Strings are not tolerated and the * Comparator will throw {@link NullPointerException}. */ public static Comparator getLastLetterComparator() { return LAST_LETTER_COMPARATOR; } private static class LastLetterComparator implements Comparator { public int compare(String o1, String o2) { return o1.charAt(o1.length()-1) - o2.charAt(o2.length()-1); } } /** * A comparator for comparing integer arrays, which are particularly well * suited to sorting and filtering tests. */ public static Comparator intArrayComparator(int index) { return new IntArrayComparator(index); } private static class IntArrayComparator implements Comparator { public int index; public IntArrayComparator(int index) { this.index = index; } public int compare(int[] a, int[] b) { return a[index] - b[index]; } } public static FunctionList.Function getFirstLetterFunction() { return FIRST_LETTER_FUNCTION; } private static final class FirstLetterFunction implements FunctionList.Function { public String evaluate(String sourceValue) { return String.valueOf(sourceValue.charAt(0)); } } public static Date createDate(int year, int month, int date) { Calendar cal = Calendar.getInstance(); cal.clear(); cal.set(Calendar.YEAR, year); cal.set(Calendar.MONTH, month); cal.set(Calendar.DATE, date); return cal.getTime(); } /** * Create a Runnable which executes the following logic repeatedly for the * given duration: * *

        *
      1. acquires the write lock for the list *
      2. adds the value to the end of the list *
      3. releases the write lock for the list *
      4. pauses for the given pause (in milliseconds) *
      */ public static Runnable createJerkyAddRunnable(EventList list, E value, long duration, long pause) { return new JerkyAddRunnable(list, value, duration, pause); } private static final class JerkyAddRunnable implements Runnable { private final EventList list; private final Object value; private final long duration; private final long pause; public JerkyAddRunnable(EventList list, Object value, long duration, long pause) { if (duration < 1) throw new IllegalArgumentException("duration must be non-negative"); if (pause < 1) throw new IllegalArgumentException("pause must be non-negative"); this.list = list; this.value = value; this.duration = duration; this.pause = pause; } public void run() { final long endTime = System.currentTimeMillis() + this.duration; while (System.currentTimeMillis() < endTime) { // acquire the write lock and add a new element this.list.getReadWriteLock().writeLock().lock(); try { this.list.add(this.value); } finally { this.list.getReadWriteLock().writeLock().unlock(); } // pause before adding another element try { Thread.sleep(this.pause); } catch (InterruptedException e) { // best attempt only } } } } /** * Counts the number of ListEvents fired. */ public static class ListEventCounter implements ListEventListener { private int count = 0; public void listChanged(ListEvent listChanges) { count++; } public int getCountAndReset() { int result = count; count = 0; return result; } } /** * This listener records the source of the last ListEvent received. This is * useful for testing ListEventListener serialization. */ public static class SerializableListener implements ListEventListener, Serializable { private static EventList lastSource = null; public void listChanged(ListEvent listChanges) { lastSource = listChanges.getSourceList(); } public static EventList getLastSource() { return lastSource; } } /** * This listener is not serializable, but it shouldn't prevent serialization on an observing * {@link EventList}. */ public static class UnserializableListener implements ListEventListener { private static EventList lastSource = null; public void listChanged(ListEvent listChanges) { lastSource = listChanges.getSourceList(); } public static EventList getLastSource() { return lastSource; } } }././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/testing/AtLeastMatcherEditor.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/testing/AtLeastMatcherEditor.ja0000644000175000017500000000216412106516360033100 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.testing; import ca.odell.glazedlists.matchers.AbstractMatcherEditor; /** * A MatcherEditor for minimum Number values. * * @author Jesse Wilson */ public class AtLeastMatcherEditor extends AbstractMatcherEditor { private int minimum; public AtLeastMatcherEditor() { this(0); } public AtLeastMatcherEditor(int minimum) { this.minimum = minimum; fireChanged(GlazedListsTests.matchAtLeast(minimum)); } public void setMinimum(int value) { if(value < minimum) { this.minimum = value; fireRelaxed(GlazedListsTests.matchAtLeast(minimum)); } else if(value == minimum) { // do nothing } else { this.minimum = value; fireConstrained(GlazedListsTests.matchAtLeast(minimum)); } } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/Preconditions.java0000644000175000017500000001451412106516372030567 0ustar gregoagregoa/* * Copyright (C) 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ca.odell.glazedlists.impl; /** * Simple static methods to be called at the start of your own methods to verify * correct arguments and state. This allows constructs such as *
       *     if (count <= 0) {
       *       throw new IllegalArgumentException("must be positive: " + count);
       *     }
      *

      * to be replaced with the more compact *

       *     checkArgument(count > 0, "must be positive: %s", count);
      *

      * *

      This class contains modifications by Jesse Wilson to include it in the * Glazed Lists project. The original, unmodified version of this class can be * found here. * * @author Kevin Bourrillion * @author Jesse Wilson */ public final class Preconditions { private Preconditions() { } /** * Ensures the truth of an expression involving one or more parameters to the * calling method. */ public static void checkArgument(boolean expression) { if (!expression) { throw new IllegalArgumentException(); } } /** * Ensures the truth of an expression involving one or more parameters to the * calling method. */ public static void checkArgument(boolean expression, Object errorMessage) { if (!expression) { throw new IllegalArgumentException(String.valueOf(errorMessage)); } } /** * Ensures the truth of an expression involving one or more parameters to the * calling method. */ public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs)); } } /** * Ensures the truth of an expression involving the state of the calling * instance, but not involving any parameters to the calling method. */ public static void checkState(boolean expression) { if (!expression) { throw new IllegalStateException(); } } /** * Ensures the truth of an expression involving the state of the calling * instance, but not involving any parameters to the calling method. */ public static void checkState(boolean expression, Object errorMessage) { if (!expression) { throw new IllegalStateException(String.valueOf(errorMessage)); } } /** * Ensures the truth of an expression involving the state of the calling * instance, but not involving any parameters to the calling method. */ public static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { throw new IllegalStateException(format(errorMessageTemplate, errorMessageArgs)); } } /** * Ensures that an object reference passed as a parameter to the calling * method is not null. */ public static T checkNotNull(T reference) { if (reference == null) { throw new NullPointerException(); } return reference; } /** * Ensures that an object reference passed as a parameter to the calling * method is not null. */ public static T checkNotNull(T reference, Object errorMessage) { if (reference == null) { throw new NullPointerException(String.valueOf(errorMessage)); } return reference; } /** * Ensures that an object reference passed as a parameter to the calling * method is not null. */ public static T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) { if (reference == null) { // If either of these parameters is null, the right thing happens anyway throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs)); } return reference; } /** * Substitutes each {@code %s} in {@code template} with an argument. These * are matched by position - the first {@code %s} gets {@code args[0]}, etc. * If there are more arguments than placeholders, the unmatched arguments will * be appended to the end of the formatted message in square braces. * * @param template a non-null string containing 0 or more {@code %s} * placeholders. * @param args the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. Arguments can be null. */ // VisibleForTesting static String format(String template, Object... args) { // start substituting the arguments into the '%s' placeholders StringBuilder builder = new StringBuilder(template.length() + 16 * args.length); int templateStart = 0; int i = 0; while (i < args.length) { int placeholderStart = template.indexOf("%s", templateStart); if (placeholderStart == -1) { break; } builder.append(template.substring(templateStart, placeholderStart)); builder.append(args[i++]); templateStart = placeholderStart + 2; } builder.append(template.substring(templateStart)); // if we run out of placeholders, append the extra args in square braces if (i < args.length) { builder.append(" ["); builder.append(args[i++]); while (i < args.length) { builder.append(", "); builder.append(args[i++]); } builder.append("]"); } return builder.toString(); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/ListCollectionListModel.java0000644000175000017500000000136112106516372032507 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; // the core Glazed Lists package import ca.odell.glazedlists.CollectionList; import java.util.Collections; import java.util.List; /** * Returns the List itself for a List of Lists. * * @author Jesse Wilson */ public class ListCollectionListModel implements CollectionList.Model,E> { public List getChildren(List parent) { if(parent == null) return Collections.emptyList(); return parent; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/SimpleFunctionList.java0000644000175000017500000000321612106516372031537 0ustar gregoagregoapackage ca.odell.glazedlists.impl; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.TransformedList; import ca.odell.glazedlists.FunctionList.Function; import ca.odell.glazedlists.event.ListEvent; /** * A {@link TransformedList} that maps each element of a source list to a target element by use * of a specified {@link Function}. *

      * Warning: This class is thread ready but not * thread safe. See {@link EventList} for an example of thread safe code. * * @see TransformedList * @author Holger Brands */ public final class SimpleFunctionList extends TransformedList { /** function to transform each list element. */ private final Function function; /** * Constructs a SimpleFunctionList that maps the elements of the given source list by the * specified function. * * @param source the source list to transform * @param function the mapping function */ public SimpleFunctionList(EventList source, Function function) { super(source); Preconditions.checkNotNull(function, "mapping function is undefined"); this.function = function; source.addListEventListener(this); } /** * {@inheritDoc} */ @Override public E get(int index) { final S elem = source.get(index); return function.evaluate(elem); } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { updates.forwardEvent(listChanges); } /** {@inheritDoc} */ @Override protected boolean isWritable() { return false; } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/ctp/0000755000175000017500000000000012106516356025667 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/ctp/CTPConnectionManager.java0000644000175000017500000001122512106516356032474 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.ctp; // NIO is used for CTP import ca.odell.glazedlists.impl.nio.NIODaemon; import ca.odell.glazedlists.impl.nio.NIOServer; import java.io.IOException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; /** * The CTPConnectionManager provides managed access to multiple CTP connections * for both incoming and outgoing data. * *

      Each instance of this class owns a single thread which is used to perform * all read and write operations on all connections. A pool of other threads are * used to notify the handlers of the data and status of a connection. * * @author Jesse Wilson */ public final class CTPConnectionManager implements NIOServer { /** default port to bind to */ private static final int DEFAULT_PORT = 5309; /** port to listen for incoming connections */ private int listenPort = -1; /** factory for handlers of incoming connections */ private CTPHandlerFactory handlerFactory; /** the I/O event queue daemon */ private NIODaemon nioDaemon = null; /** * Creates a connection manager that handles incoming connections using the * specified connect handler. This binds to the default port. */ public CTPConnectionManager(CTPHandlerFactory handlerFactory) { this(handlerFactory, DEFAULT_PORT); } /** * Creates a connection manager that handles incoming connections using the * specified connect handler. This binds to the specified port. */ public CTPConnectionManager(CTPHandlerFactory handlerFactory, int listenPort) { this.handlerFactory = handlerFactory; this.listenPort = listenPort; } /** * Starts the CTPConnectionManager listening to incoming connections and * managing outgoing connections. * * @return true if the server successfully binds to the listen port. */ public synchronized boolean start() throws IOException { // verify we haven't already started if(nioDaemon != null) throw new IllegalStateException(); // start the nio daemon nioDaemon = new NIODaemon(); nioDaemon.start(); // start the server try { nioDaemon.invokeAndWait(new StartServer(this, listenPort)); } catch(RuntimeException e) { nioDaemon.stop(); if(e.getCause() instanceof IOException) throw (IOException)e.getCause(); else throw e; } // success return true; } /** * Stops the CTPConnectionManager and closes all connections. */ public void stop() { nioDaemon.stop(); } /** * Get the daemon that does all the threading and selection. */ public NIODaemon getNIODaemon() { return nioDaemon; } /** * Handle an incoming connection. * *

      This creates a CTPServerProtocol to handle the connection. * * @return the SelectionKey that is attached to the created connection. */ public void handleAccept(SelectionKey key, Selector selector) { // construct the channels and selectors SocketChannel channel = null; SelectionKey channelKey = null; try { // peel the connection from the SocketChannel ServerSocketChannel server = (ServerSocketChannel)key.channel(); channel = server.accept(); // configure the channel for no-blocking and selection if(channel == null) return; channel.configureBlocking(false); channelKey = channel.register(selector, 0); } catch(IOException e) { // the accept failed, there's nothing to clean up return; } // construct handlers for this connection CTPHandler handler = handlerFactory.constructHandler(); CTPConnection server = CTPConnection.server(channelKey, handler, this); channelKey.attach(server); server.handleConnect(); } /** * Connect to the specified host. */ public void connect(CTPHandler handler, String host, int port) { nioDaemon.invokeLater(new OpenConnection(this, handler, host, port)); } public void connect(CTPHandler handler, String host) { connect(handler, host, DEFAULT_PORT); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/ctp/CloseConnection.java0000644000175000017500000000571312106516356031625 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.ctp; // NIO is used for CTP import java.io.IOException; import java.util.Collections; import java.util.logging.Level; import java.util.logging.Logger; /** * Closes a connection on the NIO thread. * * @author Jesse Wilson */ class CloseConnection implements Runnable { /** logging */ private static Logger logger = Logger.getLogger(CloseConnection.class.toString()); /** the target connection */ private CTPConnection connection; /** the reason for the connection to be closed */ private Exception reason; /** * Creates a CTPConnectionToClose that closes the specified connection. */ public CloseConnection(CTPConnection connection, Exception reason) { this.connection = connection; this.reason = reason; } /** * Runs this task. */ public void run() { // if this is already closed, we're done if(connection.state == CTPConnection.STATE_CLOSED_PERMANENTLY) return; // close is not a result of a connection error, so say goodbye if(reason == null || !(reason instanceof IOException)) { // if we haven't yet responded, respond now if(connection.state == CTPConnection.STATE_SERVER_AWAITING_REQUEST) { connection.state = CTPConnection.STATE_SERVER_CONSTRUCTING_RESPONSE; connection.sendResponse(CTPConnection.RESPONSE_ERROR, Collections.EMPTY_MAP); // if we've already responded, send an empty chunk } else if(connection.state == CTPConnection.STATE_READY) { connection.sendChunk(null); } } // try to flush what we have left try { connection.writer.writeToChannel(connection.socketChannel, connection.selectionKey); } catch(IOException e) { // if this flush failed, there's nothing we can do } if(connection.writer.length() > 0) logger.warning("Close proceeding with unsent data"); // close the socket try { connection.socketChannel.close(); connection.selectionKey.cancel(); } catch(IOException e) { // if this close failed, there's nothing we can do } // log the close if(reason != null) { logger.log(Level.WARNING, "Closed connection to " + connection + " due to " + reason, reason); } else { logger.info("Closed connection to " + connection); } // close the connection for use connection.state = CTPConnection.STATE_CLOSED_PERMANENTLY; connection.handler.connectionClosed(connection, reason); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/ctp/StartServer.java0000644000175000017500000000431612106516356031022 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.ctp; // NIO is used for CTP import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.nio.channels.SelectionKey; import java.nio.channels.ServerSocketChannel; import java.util.logging.Logger; /** * A task that starts the CTP Connection manager. * * @author Jesse Wilson */ class StartServer implements Runnable { /** logging */ private static Logger logger = Logger.getLogger(StartServer.class.toString()); /** the I/O event queue daemon */ private CTPConnectionManager connectionManager = null; /** port to listen for incoming connections */ private int listenPort = -1; /** * Create a new CTPStartUp that starts a server using the specified * NIODaemon. */ public StartServer(CTPConnectionManager connectionManager, int listenPort) { this.connectionManager = connectionManager; this.listenPort = listenPort; } /** * Runs the specified task. */ public void run() { try { // open a channel and bind ServerSocketChannel serverChannel = ServerSocketChannel.open(); ServerSocket serverSocket = serverChannel.socket(); serverSocket.setReuseAddress(false); // fix for Apple JVM bug 3922515 InetSocketAddress listenAddress = new InetSocketAddress(listenPort); serverSocket.bind(listenAddress); // prepare for non-blocking, selectable IO serverChannel.configureBlocking(false); serverChannel.register(connectionManager.getNIODaemon().getSelector(), SelectionKey.OP_ACCEPT); connectionManager.getNIODaemon().setServer(connectionManager); // bind success logger.info("Connection Manager ready, listening on " + listenAddress); } catch(IOException e) { throw new RuntimeException(e); } } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/ctp/CTPHandler.java0000644000175000017500000000311612106516356030457 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.ctp; import ca.odell.glazedlists.impl.io.Bufferlo; /** * A callback interface for classes that implement a CTPConnection. * * @author Jesse Wilson */ public interface CTPHandler { /** * Handles the connection being ready for chunks to be sent. */ public void connectionReady(CTPConnection source); /** * Handles reception of the specified chunk of data. This chunk should be able * to be cleanly concatenated with the previous and following chunks without * problem by the reader. * * @param data A list of ByteBuffers containing the bytes for this chunk. The * relevant bytes start at data.position() and end at data.limit(). These * buffers are only valid for the duration of this method call. */ public void receiveChunk(CTPConnection source, Bufferlo data); /** * Handles the connection being closed by the remote client. This will also * be called if there is a connection error, which is the case when a remote * host sends data that cannot be interpretted by CTPConnection. * * @param reason An exception if the connection was closed as the result of * a failure. This may be null. */ public void connectionClosed(CTPConnection source, Exception reason); } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/ctp/OpenConnection.java0000644000175000017500000000443512106516356031461 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.ctp; // NIO is used for CTP import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; /** * A OpenConnection models a desired connection. It is a temporary object used * by the connection manager to be passed between threads. * *

      A OpenConnection is created for each call to the connect() method, and * queued until it can be processed by the CTP thread. * * @author Jesse Wilson */ class OpenConnection implements Runnable { /** the place to connect to */ private CTPConnectionManager connectionManager; private String host; private int port; private CTPHandler handler; /** * Create a new CTPConnectionToEstablish. */ public OpenConnection(CTPConnectionManager connectionManager, CTPHandler handler, String host, int port) { this.connectionManager = connectionManager; this.handler = handler; this.host = host; this.port = port; } /** * Establish the connection. This creates a CTPProtocol for the client and * registers it with the selector. */ public void run() { CTPConnection client = null; try { // prepare a channel to connect InetSocketAddress address = new InetSocketAddress(host, port); SocketChannel channel = SocketChannel.open(); // configure the channel for no-blocking and selection channel.configureBlocking(false); SelectionKey selectionKey = channel.register(connectionManager.getNIODaemon().getSelector(), SelectionKey.OP_CONNECT); // prepare the handler for the connection client = CTPConnection.client(host, selectionKey, handler, connectionManager); selectionKey.attach(client); // connect (non-blocking) channel.connect(address); } catch(IOException e) { handler.connectionClosed(client, e); } } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/ctp/SendChunk.java0000644000175000017500000000317412106516356030421 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.ctp; // NIO is used for CTP import ca.odell.glazedlists.impl.io.Bufferlo; import java.io.IOException; /** * Sends a chunk of data on the NIO thread. * * @author Jesse Wilson */ class SendChunk implements Runnable { /** the destination */ private CTPConnection connection; /** the content */ private Bufferlo data; /** * Create a new SendChunk. */ public SendChunk(CTPConnection connection, Bufferlo data) { this.connection = connection; this.data = data; } /** * Writes the data. */ public void run() { if(connection.state != CTPConnection.STATE_READY) throw new IllegalStateException(); try { // calculate the total bytes remaining int totalRemaining = (data != null) ? data.length() : 0; // write the chunk String chunkSizeInHex = Integer.toString(totalRemaining, 16); connection.writer.write(chunkSizeInHex); connection.writer.write("\r\n"); if(data != null) connection.writer.append(data); connection.writer.write("\r\n"); connection.writer.writeToChannel(connection.socketChannel, connection.selectionKey); } catch(IOException e) { connection.close(e); } } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/ctp/CTPConnection.java0000644000175000017500000005151712106516356031211 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.ctp; // NIO is used for CTP import ca.odell.glazedlists.impl.io.Bufferlo; import ca.odell.glazedlists.impl.nio.NIOAttachment; import java.io.EOFException; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.text.ParseException; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; import java.util.logging.Logger; /** * The CTPConnection is base class for building a client or server implementation * of Chunked Transfer Protocol. This protocol is a subset of HTTP/1.1 with special * interest to chunked encoding. * *

      Although HTTP/1.1 is designed such that all compliant implementations can * interoperate, this is not the case for CTPConnection. This specialized HTTP/1.1 * client is only capable of interoperating with other clients that implement the * same subset of HTTP/1.1. Known limitations of this HTTP/1.1 implementation: *

    • it can read and write only chunked-encoding *
    • it can only read and write a single URI, "/glazedlists" *
    • as a client, it sends only the headers, "Host", "Transfer-Encoding" *
    • as a server, it sends only the header, "Transfer-Encoding" *
    • it interprets only the header, "Transfer-Encoding". * * @author Jesse Wilson */ public final class CTPConnection implements NIOAttachment { // client: // AWAITING_CONNECT // --[connect]--> // CLIENT_CONSTRUCTING_REQUEST // --[send request]--> // CLIENT_AWAITING_RESPONSE // --[received response]--> // READY // server: // AWAITING_CONNECT // --[connect]--> // SERVER_AWAITING_REQUEST // --[received request]--> // SERVER_CONSTRUCTING_RESPONSE // --[sent response]--> // READY // // requested shutdown: // READY // --[close()]--> // --[sent close chunk]--> // --[cleanup]--> // CLOSED_PERMANENTLY // // received shutdown: // READY // --[received close chunk]--> // RECEIVED_CLOSE // --[cleanup]--> // CLOSED_PERMANENTLY // // IO error: // READY // --[IO error]--> // --[cleanup]--> // CLOSED_PERMANENTLY // /** logging */ private static Logger logger = Logger.getLogger(CTPConnection.class.toString()); /** track the current state of this protocol */ static final int STATE_SERVER_AWAITING_CONNECT = 0; static final int STATE_CLIENT_AWAITING_CONNECT = 1; static final int STATE_CLIENT_CONSTRUCTING_REQUEST = 2; static final int STATE_SERVER_AWAITING_REQUEST = 3; static final int STATE_SERVER_CONSTRUCTING_RESPONSE = 4; static final int STATE_CLIENT_AWAITING_RESPONSE = 5; static final int STATE_READY = 6; static final int STATE_RECEIVED_CLOSE = 7; static final int STATE_CLOSED_PERMANENTLY = 8; /** standard HTTP response headers, see HTTP/1.1 RFC, 6.1.1 */ static final int RESPONSE_OK = 200; static final int RESPONSE_ERROR = 500; /** the current state of this protocol */ int state = -1; /** the key to this protocol's channel */ SelectionKey selectionKey = null; /** the channel where communication occurs */ SocketChannel socketChannel = null; /** parse the input channel */ private Bufferlo parser; /** write the output channel */ Bufferlo writer; /** the handler to delegate data interpretation to */ CTPHandler handler; /** the manager that owns this connection */ CTPConnectionManager manager; /** the remote host */ String remoteHost = "remotehost"; /** the local host, as known by the client */ String localHost = "localhost"; /** the only URI allowed by CTPConnection */ static final String CTP_URI = "/glazedlists"; /** if our source is not chunked, we have to break up chunks arbitrarily */ boolean sourceChunked = false; /** * Creates a new CTPConnection. * * @param selectionKey the connection managed by this higher-level protocol. */ private CTPConnection(SelectionKey selectionKey, CTPHandler handler, CTPConnectionManager manager) { if(selectionKey == null) throw new IllegalArgumentException(); this.selectionKey = selectionKey; this.handler = handler; this.manager = manager; this.socketChannel = (SocketChannel)selectionKey.channel(); this.parser = new Bufferlo(); this.writer = new Bufferlo(); } /** * Create a new CTPConnection for use as a client. */ static CTPConnection client(String host, SelectionKey selectionKey, CTPHandler handler, CTPConnectionManager manager) { CTPConnection client = new CTPConnection(selectionKey, handler, manager); client.state = STATE_CLIENT_AWAITING_CONNECT; client.remoteHost = host; return client; } /** * Create a new CTPConnection for use as a server. */ static CTPConnection server(SelectionKey selectionKey, CTPHandler handler, CTPConnectionManager manager) { CTPConnection server = new CTPConnection(selectionKey, handler, manager); server.state = STATE_SERVER_AWAITING_CONNECT; server.remoteHost = ((InetSocketAddress)server.socketChannel.socket().getRemoteSocketAddress()).getAddress().getHostAddress(); return server; } /** * Gets the hostname that the local party is referred to by the remote party. */ public String getLocalHost() { return localHost; } public int getLocalPort() { return socketChannel.socket().getLocalPort(); } /** * Gets the hostname that the local party uses to refer to the remote party. */ public String getRemoteHost() { return remoteHost; } public int getRemotePort() { return socketChannel.socket().getPort(); } /** * Handles the incoming bytes. */ public void handleRead() { // read at least a byte of data try { int bytesIn = parser.readFromChannel(socketChannel); if(bytesIn < 0) throw new EOFException("End of stream"); } catch(IOException e) { close(e); } // continually handle incoming data boolean satisfied = true; while(satisfied) { // read a request if(state == STATE_SERVER_AWAITING_REQUEST) { satisfied = handleRequest(); // read a response } else if(state == STATE_CLIENT_AWAITING_RESPONSE) { satisfied = handleResponse(); // read a chunk } else if(state == STATE_READY) { satisfied = handleChunk(); // we don't know what to read } else { throw new IllegalStateException("Cannot handle read from state " + state); } } } /** * When we can write, flush the output stream. */ public void handleWrite() { // do the write try { writer.writeToChannel(socketChannel, selectionKey); } catch(IOException e) { close(e); } } /** * When connected, prepare the higher-level connection. */ public void handleConnect() { // finish up the connect() process try { socketChannel.finishConnect(); } catch(IOException e) { close(e); return; } // the connection is successful for a client if(state == STATE_CLIENT_AWAITING_CONNECT) { logger.fine("Opened connection to " + this); selectionKey.interestOps(SelectionKey.OP_READ); state = STATE_CLIENT_CONSTRUCTING_REQUEST; sendRequest(CTP_URI, Collections.EMPTY_MAP); // the connection is successful for a server } else if(state == STATE_SERVER_AWAITING_CONNECT) { logger.fine("Accepted connection from " + this); selectionKey.interestOps(SelectionKey.OP_READ); state = STATE_SERVER_AWAITING_REQUEST; // invalid state } else { throw new IllegalStateException(); } } /** * Sends the request header to the server. * * @param uri the web address of the target page * @param headers a Map of HTTP request headers. See HTTP/1.1 RFC, 6.2. This can * be null to indicate no headers. */ void sendRequest(String uri, Map headers) { if(state != STATE_CLIENT_CONSTRUCTING_REQUEST) throw new IllegalStateException(); try { // write the request line writer.write("POST "); writer.write(uri); writer.write(" HTTP/1.1\r\n"); // write all the headers provided, plus some extras Map responseHeaders = new TreeMap(); responseHeaders.putAll(headers); responseHeaders.put("Transfer-Encoding", "chunked"); responseHeaders.put("Host", remoteHost); writeHeaders(responseHeaders); writer.write("\r\n"); writer.writeToChannel(socketChannel, selectionKey); // we're waiting for the response state = STATE_CLIENT_AWAITING_RESPONSE; } catch(IOException e) { close(e); } } /** * Handle a request by parsing the contents of the input buffer. If the input * buffer does not contain a complete request, this will return. If it does * contain a request: *
    • the request will be processed *
    • the buffer will be advanced, and *
    • the state will be updated * * @return whether the expected data was read fully. This returns false if there * was an insufficient amount of data to satisfy the request. */ private boolean handleRequest() { if(state != STATE_SERVER_AWAITING_REQUEST) throw new IllegalStateException(); try { // if the entire header has not loaded, load more if(parser.indexOf("\\r\\n\\r\\n") == -1) return false; // parse the status line parser.consume("POST( )+"); String uri = parser.readUntil("( )+"); parser.consume("HTTP\\/1\\.1 *"); parser.consume("\\r\\n"); // parse the headers Map headers = readHeaders(); handleHeaders(headers); parser.consume("\\r\\n"); // handle the request if(CTP_URI.equals(uri)) { state = STATE_SERVER_CONSTRUCTING_RESPONSE; sendResponse(RESPONSE_OK, Collections.EMPTY_MAP); return true; } else { close(new Exception("Could not find URI \"" + uri + "\"")); return false; } } catch(ParseException e) { close(new IOException("Failed to decode HTTP request, " + e.getMessage())); return false; } catch(IOException e) { close(e); return false; } } /** * Sends the response header to the client. * * @param code an HTTP response code such as 200 (OK). See HTTP/1.1 RFC, 6.1.1 * @param headers a Map of HTTP response headers. See HTTP/1.1 RFC, 6.2. This can * be null to indicate no headers. */ void sendResponse(int code, Map headers) { if(state != STATE_SERVER_CONSTRUCTING_RESPONSE) throw new IllegalStateException(); try { // write the status line if(code == RESPONSE_OK) { writer.write("HTTP/1.1 200 OK\r\n"); } else if(code == RESPONSE_ERROR) { writer.write("HTTP/1.1 500 Error\r\n"); } else { throw new IllegalArgumentException("Unsupported code: " + code); } // write all the headers provided, plus some extras Map responseHeaders = new TreeMap(); responseHeaders.putAll(headers); responseHeaders.put("Transfer-Encoding", "chunked"); writeHeaders(responseHeaders); writer.write("\r\n"); writer.writeToChannel(socketChannel, selectionKey); // we're ready logger.info("Accepted connection from " + this); state = STATE_READY; handler.connectionReady(this); } catch(IOException e) { close(e); } } /** * Handle a response by parsing the contents of the input buffer. If the input * buffer does not contain a complete response, this will return. If it does * contain a response: *
    • the response will be processed *
    • the buffer will be advanced, and *
    • the state will be updated * * @return whether the expected data was read fully. This returns false if there * was an insufficient amount of data to satisfy the request. */ private boolean handleResponse() { if(state != STATE_CLIENT_AWAITING_RESPONSE) throw new IllegalStateException(); try { // if the entire header has not loaded, load more if(parser.indexOf("\\r\\n\\r\\n") == -1) return false; // parse the status line parser.consume("HTTP\\/1\\.1( )+"); String codeString = parser.readUntil("( )+"); int code = Integer.parseInt(codeString); String description = parser.readUntil("\\r\\n"); // parse the headers Map headers = readHeaders(); handleHeaders(headers); parser.consume("\\r\\n"); // handle the response if(code == RESPONSE_OK) { logger.info("Established connection to " + this); state = STATE_READY; handler.connectionReady(this); return true; } else { close(null); return false; } } catch(ParseException e) { close(new IOException("Failed to decode HTTP request, " + e.getMessage())); return false; } catch(NumberFormatException e) { close(new IOException("Failed to decode HTTP request, " + e.getMessage())); return false; } catch(IOException e) { close(e); return false; } } /** * Sends the specified chunk of data immediately. This chunk should be able to * be cleanly concatenated with the previous and following chunks without * problem by the reader. * * @param data A non-empty list of non-empty ByteBuffers containing the bytes for this chunk. The * relevant bytes start at data.position() and end at data.limit(). This * buffer needs to be valid for the duration of this method call, but * is safe to modify afterwards. */ public void sendChunk(Bufferlo data) { manager.getNIODaemon().invokeAndWait(new SendChunk(this, data)); } /** * Handle a chunk by parsing the contents of the input buffer. If the input * buffer does not contain a complete chunk, this will return. If it does * contain a chunk: *
    • the chunk will be processed *
    • if the chunk is empty, the connection will be closed * *

      It is possible that the source stream is not chunked, as a consequence * of an interfering proxy server. In this case, we arbitrarily form our own * chunks which are as large as possible. * * @return whether the expected data was read fully. This returns false if there * was an insufficient amount of data to satisfy the request. */ private boolean handleChunk() { try { if(sourceChunked) { // if the chunk size has not loaded, load more int chunkEndIndex = parser.indexOf("\\r\\n"); if(chunkEndIndex == -1) return false; // calculate the chunk size String chunkSizeInHex = parser.readUntil("(\\;[^\\r\\n]*)?\\r\\n", false); int chunkSize = Integer.parseInt(chunkSizeInHex, 16); // if the full chunk has not loaded, load more int bytesRequired = chunkEndIndex + 2 + chunkSize + 2; if(parser.length() < bytesRequired) { return false; } // load the chunk parser.consume("[^\\r\\n]*\\r\\n"); Bufferlo chunkData = parser.consume(chunkSize); parser.consume("\\r\\n"); // handle the chunk if(chunkData.length() > 0) { handler.receiveChunk(this, chunkData); return true; } else { close(); return false; } } else { Bufferlo chunkData = parser.consume(parser.length()); // handle the simulated chunk if(chunkData.length() > 0) { handler.receiveChunk(this, chunkData); return true; } else { return false; } } } catch(NumberFormatException e) { close(new IOException("Failed to decode HTTP request, " + e.getMessage())); return false; } catch(ParseException e) { close(new IOException("Failed to decode HTTP request, " + e.getMessage())); return false; } } /** * Gets this protocol as a String for debugging. */ @Override public String toString() { return localHost + ":" + getLocalPort() + "<->" + remoteHost + ":" + getRemotePort(); } /** * Closes the connection to the client. As specified by the HTTP/1.1 RFC, * this sends a single empty chunk and closes the TCP/IP connection. */ public synchronized void close() { //return close(null); close(null); } /** * Close the connection to the client. * *

      The closing behaviour is dictated by the current state of the connection * and the reason for closing. *

    • If the reason is an IOException, the socket is closed immediately *
    • Otherwise a "goodbye" message is sent to notify the other party of the close *
    • If the state is READY, the goodbye is a single 0-byte chunk *
    • If the state is SERVER_AWAITING_REQUEST, the goodbye is a request error *
    • If the state is RECEIVED_CLOSE, no goodbye message is sent */ public void close(Exception reason) { manager.getNIODaemon().invokeLater(new CloseConnection(this, reason)); //return false; } /** * Writes the specified set of headers, one per line in standard HTTP form. */ private void writeHeaders(Map headers) throws IOException { for(Iterator i = headers.entrySet().iterator(); i.hasNext(); ) { Map.Entry mapEntry = (Map.Entry)i.next(); writer.write(mapEntry.getKey().toString()); writer.write(": "); writer.write(mapEntry.getValue().toString()); writer.write("\r\n"); } } /** * Reads the headers, one per line in standard HTTP form. */ private Map readHeaders() throws IOException, ParseException { Map headers = new TreeMap(); while(true) { if(parser.indexOf("\\r\\n") == 0) break; String key = parser.readUntil("\\:( )*"); String value = parser.readUntil("\\r\\n"); headers.put(key, value); } return headers; } /** * Handles the Map of headers. This adjusts the state of the CTPConnection * in response to the headers. * *

      Currently, this recognizes the following headers: *

    • Transfer-Encoding, if "chunked", then this expects chunked HTTP transfer. */ private void handleHeaders(Map headers) { // whether this is a chunked conversation if("chunked".equals(headers.get("Transfer-Encoding"))) { sourceChunked = true; } else { sourceChunked = false; } // the name the remote host uses to refer to this host String headerLocalHost = (String)headers.get("Host"); if(headerLocalHost != null) localHost = headerLocalHost; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/ctp/CTPHandlerFactory.java0000644000175000017500000000127112106516356032007 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.ctp; /** * The CTPHandlerFactory provides a factory to handle incoming connections. * * @author Jesse Wilson */ public interface CTPHandlerFactory { /** * Upon a connect, a CTPHandler is required to handle the data of this connection. * The returned CTPHandler will be delegated to handle the connection's data. */ public CTPHandler constructHandler(); } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/0000755000175000017500000000000012106516364026365 5ustar gregoagregoa././@LongLink0000000000000000000000000000017500000000000011570 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/BoyerMooreCaseInsensitiveTextSearchStrategy.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/BoyerMooreCaseInsensitiv0000644000175000017500000001374712106516364033256 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.filter; import java.util.Arrays; /** * This implementation of {@link TextSearchStrategy} implements a * simple version of the Boyer-Moore text searching algorithm, generally * considered to be the fastest known text searching algorithm. * * @author James Lemieux */ public class BoyerMooreCaseInsensitiveTextSearchStrategy extends AbstractTextSearchStrategy { /** The number of characters, starting at 0, to cache. */ private static final int CHARACTER_CACHE_SIZE = 256; /** The length of the subtext to locate. */ private int subtextLength; /** The last index in the subtext */ private int lastSubtextIndex; /** The array of characters comprising the subtext. */ private char[] subtextCharsUpper; private char[] subtextCharsLower; /** The Boyer-Moore shift table reduced to only 256 elements rather than all 95,221 Unicode 3.2 characters. */ private int[] shiftTable = new int[CHARACTER_CACHE_SIZE]; /** * This method builds a shortened version of the Boyer-Moore shift table. * The shift table normally contains an entry for each letter in the * text search alphabet. Since this search strategy covers all valid * Unicode 3.2 characters, the shift table would normally contain 95,221 * entries. US-ASCII comprises 99% of the alphabet used for most searched * messages, so we exploit this fact by reducing our shift table to just * 256 entries to save on initialization time. We convert each character to * its shift table index by modding it by 256. This creates the possibility * of shift table collisions since more than one Unicode character will map * to the same integer in the range [0..255]. Note however, that this only * causes the Boyer-Moore algorithm to run suboptimally, not incorrectly. * Since the number of collisions encountered is low in practice (based on * our assumption that 99% of the alphabet used for most search messages is * US-ASCII), it should have little effect on the execution time of * {@link #indexOf(String)}. * * @param subtext the String to locate in {@link #indexOf(String)} */ public void setSubtext(String subtext) { // record the length of the subtext this.subtextLength = subtext.length(); // record the last index of the subtext this.lastSubtextIndex = this.subtextLength-1; // extract the upper case version of the subtext into an easily accessible char[] this.subtextCharsUpper = subtext.toUpperCase().toCharArray(); this.subtextCharsLower = subtext.toLowerCase().toCharArray(); // initialize the shift table with the maximum shift -> the length of the subtext Arrays.fill(this.shiftTable, 0, this.shiftTable.length, this.subtextLength); // for each character in the subtext, calculate its maximum safe shift distance for(int i = 0; i < this.lastSubtextIndex; i++) { // adjust the shift table entry for the upper case letter this.shiftTable[this.subtextCharsUpper[i] % CHARACTER_CACHE_SIZE] = this.lastSubtextIndex - i; // if the upper case letter isn't the same as the lower case letter then // adjust the shift table entry for the lower case letter as well if (this.subtextCharsUpper[i] != this.subtextCharsLower[i]) this.shiftTable[this.subtextCharsLower[i] % CHARACTER_CACHE_SIZE] = this.lastSubtextIndex - i; } } /** {@inheritDoc} */ public int indexOf(String text) { // ensure we are in a state to search the text if(this.subtextCharsUpper == null) { throw new IllegalStateException("setSubtext must be called with a valid value before this method can operate"); } // initialize some variables modified within the text search loop int textPosition = this.lastSubtextIndex; char textChar = ' '; int subtextPosition; final int textLength = text.length(); // search through text until the textPosition exceeds the textLength while(textPosition < textLength) { // reset the comparison position within the subtext to the END of the subtext subtextPosition = this.lastSubtextIndex; if(subtextPosition >= 0) { // locate the character in the text to be compared against textChar = map(text.charAt(textPosition)); // check for matching character from the end to the beginning of the subtext while(subtextPosition >= 0 && (this.subtextCharsLower[subtextPosition] == textChar || this.subtextCharsUpper[subtextPosition] == textChar)) { // the text char and subtext char matched, so shift both positions left and recompare subtextPosition--; textPosition--; // calculate the next character of the text to compare if(textPosition != -1) { textChar = map(text.charAt(textPosition)); } } } // subtextPosition == -1 indicates we have successfully matched the // entire subtext from last char to first char so return the matching index if(subtextPosition == -1) { return textPosition + 1; } // otherwise we had a mismatch, so calculate the maximum safe shift // and move ahead to the next search position in the text textPosition += Math.max(this.shiftTable[textChar % CHARACTER_CACHE_SIZE], this.subtextLength-subtextPosition); } // if we fall out of the search loop then we couldn't find the subtext return -1; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/TextMatcher.java0000644000175000017500000001714512106516364031470 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.filter; import ca.odell.glazedlists.TextFilterable; import ca.odell.glazedlists.TextFilterator; import ca.odell.glazedlists.matchers.Matcher; import ca.odell.glazedlists.matchers.TextMatcherEditor; import java.util.*; /** * Matcher for matching text. * * @author James Lemieux * @author Jesse Wilson */ public class TextMatcher implements Matcher { /** the filterator is used as an alternative to implementing the TextFilterable interface */ private final TextFilterator filterator; /** one of {@link TextMatcherEditor#CONTAINS}, {@link TextMatcherEditor#STARTS_WITH} or {@link TextMatcherEditor#REGULAR_EXPRESSION} */ private final int mode; /** one of {@link TextMatcherEditor#IDENTICAL_STRATEGY}, {@link TextMatcherEditor#NORMALIZED_STRATEGY} or {@link ca.odell.glazedlists.matchers.GlazedListsICU4J#UNICODE_TEXT_SEARCH_STRATEGY} */ private final Object strategy; /** the search terms being matched */ private final SearchTerm[] searchTerms; /** a parallel array to locate filter substrings in arbitrary text */ private final TextSearchStrategy[] filterStrategies; /** a heavily recycled list of filter Strings, call clear() before use */ private final List filterStrings = new ArrayList(); /** * @param searchTerms an array of search terms to be matched * @param filterator the object that will extract filter Strings from each * object to be matched; null indicates the objects * implement {@link TextFilterable} * @param mode one of {@link TextMatcherEditor#CONTAINS}, * {@link TextMatcherEditor#STARTS_WITH} or {@link TextMatcherEditor#REGULAR_EXPRESSION} * which indicates where to locate the search terms for a successful match * @param strategy one of {@link TextMatcherEditor#IDENTICAL_STRATEGY}, * {@link TextMatcherEditor#NORMALIZED_STRATEGY} or * {@link ca.odell.glazedlists.matchers.GlazedListsICU4J#UNICODE_TEXT_SEARCH_STRATEGY} * which indicates what kind of algorithm to use when determining a match */ public TextMatcher(SearchTerm[] searchTerms, TextFilterator filterator, int mode, Object strategy) { if (mode == TextMatcherEditor.REGULAR_EXPRESSION && strategy == TextMatcherEditor.NORMALIZED_STRATEGY) throw new IllegalArgumentException("TextMatcher does not support normalized character matching with Regular Expressions"); this.filterator = filterator; this.searchTerms = TextMatchers.normalizeSearchTerms(searchTerms, (TextSearchStrategy.Factory)strategy); this.mode = mode; this.strategy = strategy; // build the parallel list of TextSearchStrategies for the new searchTerms filterStrategies = new TextSearchStrategy[this.searchTerms.length]; for(int i = 0; i < this.searchTerms.length; i++) { filterStrategies[i] = selectTextSearchStrategy(this.searchTerms[i], mode, (TextSearchStrategy.Factory)strategy); } } /** * Returns the behaviour mode which indicates where to locate the search * terms for a successful match. * * @return one of {@link TextMatcherEditor#CONTAINS}, {@link TextMatcherEditor#STARTS_WITH} * or {@link TextMatcherEditor#REGULAR_EXPRESSION} */ public int getMode() { return mode; } /** * Returns the strategy which indicates what kind of algorithm to use when determining a match. * * @return one of {@link TextMatcherEditor#IDENTICAL_STRATEGY}, {@link TextMatcherEditor#NORMALIZED_STRATEGY} or * {@link ca.odell.glazedlists.matchers.GlazedListsICU4J#UNICODE_TEXT_SEARCH_STRATEGY} */ public Object getStrategy() { return strategy; } /** * Returns the searchTerms strings matched by this {@link TextMatcher}. */ public SearchTerm[] getSearchTerms() { return searchTerms; } /** * Returns the search term strings matched by this {@link TextMatcher}. */ public String[] getSearchTermStrings() { final String[] strings = new String[searchTerms.length]; for (int i = 0; i < searchTerms.length; i++) strings[i] = searchTerms[i].getText(); return strings; } /** {@inheritDoc} */ public boolean matches(E element) { return TextMatchers.matches(filterStrings, filterator, searchTerms, filterStrategies, element); } /** * Return a new TextMatcher identical to this TextMatcher save for the * given mode. */ public TextMatcher newMode(int mode) { return new TextMatcher(searchTerms, filterator, mode, strategy); } /** * Return a new TextMatcher identical to this TextMatcher save for the * given filterator. */ public TextMatcher newFilterator(TextFilterator filterator) { return new TextMatcher(searchTerms, filterator, mode, strategy); } /** * Return a new TextMatcher identical to this TextMatcher save for the * given strategy. */ public TextMatcher newStrategy(Object strategy) { return new TextMatcher(searchTerms, filterator, mode, strategy); } /** * This local factory method allows fine grained control over the choice of * text search strategies for a given filter. * * @param filter the filter for which to locate a TextSearchStrategy * @param mode the type of search behaviour to use; either * {@link TextMatcherEditor#CONTAINS}, {@link TextMatcherEditor#STARTS_WITH} * or {@link TextMatcherEditor#REGULAR_EXPRESSION} * @param strategy a hint about the character matching strategy to use; either * {@link TextMatcherEditor#IDENTICAL_STRATEGY}, {@link TextMatcherEditor#NORMALIZED_STRATEGY} * or {@link ca.odell.glazedlists.matchers.GlazedListsICU4J#UNICODE_TEXT_SEARCH_STRATEGY} * @return a TextSearchStrategy capable of locating the given * filter within arbitrary text */ private static TextSearchStrategy selectTextSearchStrategy(SearchTerm filter, int mode, TextSearchStrategy.Factory strategy) { final TextSearchStrategy result = strategy.create(mode, filter.getText()); result.setSubtext(filter.getText()); return result; } /** * TextMatcher objects are considered equal if they agree on the mode, * strategy, and set of SearchTerms. */ @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TextMatcher that = (TextMatcher) o; Set thisSearchTerms = new HashSet(Arrays.asList(searchTerms)); Set thatSearchTerms = new HashSet(Arrays.asList(that.searchTerms)); if (mode != that.mode) return false; if (!thisSearchTerms.equals(thatSearchTerms)) return false; if (!strategy.equals(that.strategy)) return false; return true; } /** @inheritDoc */ @Override public int hashCode() { int result; result = mode; result = 31 * result + strategy.hashCode(); result = 31 * result + new HashSet(Arrays.asList(searchTerms)).hashCode(); return result; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/TextMatchers.java0000644000175000017500000005073512106516364031655 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.filter; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.TextFilterable; import ca.odell.glazedlists.TextFilterator; import ca.odell.glazedlists.impl.GlazedListsImpl; import ca.odell.glazedlists.matchers.Matcher; import ca.odell.glazedlists.matchers.Matchers; import ca.odell.glazedlists.matchers.SearchEngineTextMatcherEditor; import ca.odell.glazedlists.matchers.TextMatcherEditor; import java.util.*; /** * Common services required when implementing a Matcher that performs text * matching. * * @author James Lemieux */ public final class TextMatchers { /** A Comparator that orders SearchTerms according to the length of their text. */ private static final Comparator SEARCHTERM_LENGTH_COMPARATOR = new SearchTermLengthComparator(); /** A Matcher that only accepts non-negated SearchTerms. */ private static final Matcher NON_NEGATED_MATCHER = Matchers.beanPropertyMatcher(SearchTerm.class, "negated", Boolean.FALSE); /** A Matcher that only accepts negated SearchTerms. */ private static final Matcher NEGATED_MATCHER = Matchers.beanPropertyMatcher(SearchTerm.class, "negated", Boolean.TRUE); /** A Matcher that only accepts SearchTerms with null Fields. */ private static final Matcher NO_FIELD_MATCHER = Matchers.beanPropertyMatcher(SearchTerm.class, "negated", null); /** A Matcher that only accepts SearchTerms without null Fields. */ private static final Matcher FIELD_MATCHER = Matchers.invert(NO_FIELD_MATCHER); /** * Execute the logic that determines whether the given element * is matched by all of the given filterStrategies. An optional * filterator can be supplied which is responsible for * extracting all of the filter strings from the given element. * The given filterStrings is passed into this method simply * to avoid reallocating a new List object each time this method is called. * The caller may and should recycle the filterStrings List. * * @param filterStrings a recyclable List into which the filter Strings can stored * @param filterator the logic capable of extracting filtering Strings from the element * @param searchTerms SearchTerm objects defining each piece of search text as well as metadata about the text * @param filterStrategies the optimized logic for locating given search text within the filterStrings * @param element the list element on which we are text filtering * @return true if all filterStrategies located * matching text within the filterStrings extracted from * the given element */ public static boolean matches(List filterStrings, TextFilterator filterator, SearchTerm[] searchTerms, TextSearchStrategy[] filterStrategies, E element) { boolean filterStringsPopulated = false; // ensure each filter matches at least one field filters: for(int f = 0; f < filterStrategies.length; f++) { // get the text search strategy for the current filter TextSearchStrategy textSearchStrategy = filterStrategies[f]; SearchTerm searchTerm = searchTerms[f]; final SearchEngineTextMatcherEditor.Field searchTermField = searchTerm.getField(); // if the SearchTerm has a Field, use its TextFilterator to extract the filterStrings final List strings; if (searchTermField != null) { strings = searchTerm.getFieldFilterStrings(); // populate the strings for this object using the SearchTerm's TextFilterator strings.clear(); searchTermField.getTextFilterator().getFilterStrings(strings, element); } else { if (!filterStringsPopulated) { // populate the strings for this object filterStrings.clear(); if(filterator == null) { ((TextFilterable)element).getFilterStrings(filterStrings); } else { filterator.getFilterStrings(filterStrings, element); } filterStringsPopulated = true; } strings = filterStrings; } if(searchTerm.isNegated()) { // search through all fields for the current filter for(int i = 0, n = strings.size(); i < n; i++) { Object filterString = strings.get(i); // the call to .toString() appears redundant, but is not, since we // are backwards compatible with old behaviour which allows arbitrary // objects in the filterStrings list // if a match was found, then we have violated the negated search term if(filterString != null && textSearchStrategy.indexOf(filterString.toString()) != -1) return false; } // the text for the negated search term could not be located, so it is a match! } else { // search through all fields for the current filter for(int i = 0, n = strings.size(); i < n; i++) { Object filterString = strings.get(i); // the call to .toString() appears redundant, but is not, since we // are backwards compatible with old behaviour which allows arbitrary // objects in the filterStrings list // if a match was found, then proceed to the next filter string if(filterString != null && textSearchStrategy.indexOf(filterString.toString()) != -1) continue filters; } // no field matched this filter return false; } } // all filters have been matched return true; } /** * This convenience method returns a copy of the searchTerms * with null and "" values removed. It also removes irrelevant * search filters which are filters strings that do not add to the * precision of the string filtering. It also orders the search filters by * length so that the most discriminating filters occur near the start of * the array. * * The negated flag indicates whether the * searchTerms must be present (negated == false) or absent * (negated == true) in the target text for the SearchTerm to be considered * a match. * *

      For example, if searchTerms contained both * "black" and "blackened" then the return value * would not contain "black" since "blackened" is * a more precise search term that contains "black". * *

      If {"this", "blackened"} are passed into this method as * the searchTerms, they'll be returned as * {"blackened", "this"} since "blackened" is * longer and thus faster to search for (via Boyer Moore) than the shorter * filter string, "this". * * @param searchTerms an array of Strings to normalize * @param negated true if the searchTerms are all negated and must * be absent in the target text; false if they are not negated * and thus should be present in the target text * @return a copy of the minimal array of searchTerms in * the order of longest to shortest */ private static List normalizeSearchTerms(List searchTerms, boolean negated) { List result = new ArrayList(searchTerms); // filter out null and 0-length SearchTerms - they have no filtering value for(Iterator i = result.iterator(); i.hasNext();) { SearchTerm searchTerm = i.next(); if(searchTerm == null || searchTerm.getText().length() == 0) i.remove(); } // remove the filters that are not minimal (i.e. "blackened" removes "black") for(int i = 0; i < result.size(); i++) { SearchTerm termI = result.get(i); // attempt to find another SearchTerm that contains termI to prove // that one of termI or termJ is unnecessary for(int j = 0; j < result.size(); j++) { SearchTerm termJ = result.get(j); if(i != j && termJ.getText().indexOf(termI.getText()) != -1) { if(negated) { if(termJ.isRequired()) continue; result.remove(j); } else { if(termI.isRequired()) continue; result.remove(i); break; } } } } // order the elements of the list according to their lengths // so the most discriminating filter strings are considered first Collections.sort(result, negated ? GlazedLists.reverseComparator(SEARCHTERM_LENGTH_COMPARATOR) : SEARCHTERM_LENGTH_COMPARATOR); return result; } /** * This convenience function maps each of the filters by * running each of their characters through the characterMap. * It also removes unnecessary SearchTerms which are not marked as * required. * * @param filters the filter Strings to be normalized * @param strategy the strategy for mapping a character * @return mapped versions of the filter Strings */ public static SearchTerm[] normalizeSearchTerms(SearchTerm[] filters, TextSearchStrategy.Factory strategy) { // if the "normalized latin strategy" is used, strip the diacritics if (strategy == TextMatcherEditor.NORMALIZED_STRATEGY) { final char[] mapper = GlazedListsImpl.getLatinDiacriticsStripper(); final SearchTerm[] mappedFilters = new SearchTerm[filters.length]; // map the filter Strings by running each character through the characterMap for (int i = 0; i < filters.length; i++) { final String filter = filters[i].getText(); final char[] mappedFilter = new char[filter.length()]; // map each character in the filter for (int j = 0; j < filter.length(); j++) { char c = filter.charAt(j); mappedFilter[j] = c < mapper.length ? mapper[c] : c; } // record the mapped filter mappedFilters[i] = filters[i].newSearchTerm(new String(mappedFilter)); } filters = mappedFilters; } // fetch all negated and non-negated SearchTerm object into two different Lists final SearchTerm[] nonNullFieldSearchTerms = Matchers.select(filters, NO_FIELD_MATCHER); final SearchTerm[] nullFieldSearchTerms = Matchers.select(filters, FIELD_MATCHER); // fetch all negated and non-negated SearchTerm object into two different Lists final List negatedUnrequiredSearchTerms = Arrays.asList(Matchers.select(nullFieldSearchTerms, NEGATED_MATCHER)); final List nonNegatedUnrequiredSearchTerms = Arrays.asList(Matchers.select(nullFieldSearchTerms, NON_NEGATED_MATCHER)); // reassemble a super List of all normalized (necessary) SearchTerms final Collection allSearchTerms = new ArrayList(filters.length); allSearchTerms.addAll(Arrays.asList(nonNullFieldSearchTerms)); allSearchTerms.addAll(normalizeSearchTerms(negatedUnrequiredSearchTerms, true)); allSearchTerms.addAll(normalizeSearchTerms(nonNegatedUnrequiredSearchTerms, false)); // return the normalized SearchTerms as an array return allSearchTerms.toArray(new SearchTerm[allSearchTerms.size()]); } /** * Parse the given text and produce an array of * {@link SearchTerm} objects that describe text to match as well as * metadata about the way it should be used. * * @param text the raw text entered by a user * @return SearchTerm an object encapsulating a single raw search term as * well as metadata related to the use of the SearchTerm */ public static SearchTerm[] parse(String text) { return parse(text, Collections.>emptySet()); } /** * Parse the given text and produce an array of * {@link SearchTerm} objects that describe text to match as well as * metadata about the way it should be used. When parsing the text, the * given Set of {@link SearchEngineTextMatcherEditor.Field} objects * should be considered to detect when the user has entered a * field-specific SearchTerm. * * @param text the raw text entered by a user * @param fields a Set of objects describing each field that can be independently matched * @return SearchTerm an object encapsulating a single raw search term as * well as metadata related to the use of the SearchTerm */ public static SearchTerm[] parse(String text, Set> fields) { final List> searchTerms = new ArrayList>(); // map each field name to the corresponding field final Map> fieldMap = new HashMap>(); for (Iterator> f = fields.iterator(); f.hasNext();) { SearchEngineTextMatcherEditor.Field field = f.next(); fieldMap.put(field.getName(), field); } StringBuffer searchTermText = new StringBuffer(); SearchEngineTextMatcherEditor.Field field = null; boolean negated = false, required = false, insideTerm = false, insideQuotedTerm = false; // step through the text one character at a time for (int i = 0, n = text.length(); i < n; i++) { final char c = text.charAt(i); if (insideTerm) { // determine if the current character signifies the end of the term text final boolean endOfTerm = c == '"' || (!insideQuotedTerm && Character.isWhitespace(c)); if (endOfTerm) { if (searchTermText.length() > 0) { // record the current SearchTerm searchTerms.add(new SearchTerm(searchTermText.toString(), negated, required, field)); } // reset the state for collecting the next SearchTerm searchTermText = new StringBuffer(); field = null; negated = required = insideTerm = insideQuotedTerm = false; } else { // if a colon is encountered and a field does not yet exist, // check if the colon signifies the end of a known field name if (c == ':' && field == null && !insideQuotedTerm) { field = fieldMap.get(searchTermText.toString()); // if a field was located, clear the searchTermText as it contains the field name if (field != null) { searchTermText = new StringBuffer(); negated = required = insideTerm = insideQuotedTerm = false; continue; } } searchTermText.append(c); } } else { // clear the state and continue searching for the next term if (Character.isWhitespace(c)) { field = null; negated = required = insideTerm = insideQuotedTerm = false; continue; } switch (c) { // quotes mean a term has started and contains no text yet case '"': insideTerm = true; insideQuotedTerm = true; break; // plus means the term that immediately follows is required case '+': required = true; break; // minus means the term that immediately follows must NOT be found case '-': negated = true; break; // any other character is the first character in a new SearchTerm default: searchTermText.append(c); insideTerm = true; break; } } } // if a SearchTerm is left hanging, use it as well if (searchTermText.length() > 0) searchTerms.add(new SearchTerm(searchTermText.toString(), negated, required, field)); return searchTerms.toArray(new SearchTerm[searchTerms.size()]); } /** * A method to determine if newMatcher is an absolute * constrainment of oldMatcher, meaning it is guaranteed to * match fewer items than the oldMatcher. * * @param oldMatcher the old TextMatcher being replaced * @param newMatcher the new TextMatcher to be used * @return true iff the newMatcher is guaranteed to * match the same or fewer items than oldMatcher */ public static boolean isMatcherConstrained(TextMatcher oldMatcher, TextMatcher newMatcher) { // equal TextMatchers are never considered constrained or relaxed if (oldMatcher.equals(newMatcher)) return false; // if the strategies don't match we cannot report a constrainment if (oldMatcher.getStrategy() != newMatcher.getStrategy()) return false; // if the mode went from STARTS_WITH to CONTAINS the TextMatcher cannot be a constrainment if (oldMatcher.getMode() == TextMatcherEditor.STARTS_WITH && newMatcher.getMode() == TextMatcherEditor.CONTAINS) return false; // if either mode is REGULAR_EXPRESSION we cannot reliably report a constrainment if (oldMatcher.getMode() == TextMatcherEditor.REGULAR_EXPRESSION || newMatcher.getMode() == TextMatcherEditor.REGULAR_EXPRESSION) return false; // if either mode is EXACT we cannot reliably report a constrainment if (oldMatcher.getMode() == TextMatcherEditor.EXACT || newMatcher.getMode() == TextMatcherEditor.EXACT) return false; // extract the SearchTerms for comparison final SearchTerm[] oldTerms = oldMatcher.getSearchTerms(); final SearchTerm[] newTerms = newMatcher.getSearchTerms(); // we search the newTerms to locate an oldTerm whose matching power isn't covered oldTermsCoveredByNew: for (int i = 0; i < oldTerms.length; i++) { for (int j = 0; j < newTerms.length; j++) { if (newTerms[j].equals(oldTerms[i])) continue oldTermsCoveredByNew; if (newTerms[j].isConstrainment(oldTerms[i])) continue oldTermsCoveredByNew; } return false; } return true; } /** * A method to determine if newMatcher is an absolute * relaxation of oldMatcher, meaning it is guaranteed to * match more items than the oldMatcher. * * @param oldMatcher the old TextMatcher being replaced * @param newMatcher the new TextMatcher to be used * @return true iff the newMatcher is guaranteed to * match the same or more items than oldMatcher */ public static boolean isMatcherRelaxed(TextMatcher oldMatcher, TextMatcher newMatcher) { return isMatcherConstrained(newMatcher, oldMatcher); } /** * This Comparator orders {@link SearchTerm}s in descending order by their text lengths. */ private static final class SearchTermLengthComparator implements Comparator { /** {@inheritDoc} */ public int compare(SearchTerm a, SearchTerm b) { return b.getText().length() - a.getText().length(); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/TextSearchStrategy.java0000644000175000017500000000521412106516364033027 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.filter; /** * This interface defines a Strategy for locating a particular subtext within * another (typically larger) text. Implementations may make assumptions about * the texts in order to gain performance benefits. Users of this interface * must call {@link #setSubtext(String)} before * {@link #indexOf(String)} or indexOf will throw an * {@link IllegalStateException}. * * @author James Lemieux */ public interface TextSearchStrategy { /** * Sets the strategy, if any, to map the characters being compared * during a text search. A null normalizer indicates the raw * characters should be used during the text search. * * @param charMap the strategy to use when normalizing characters * immediately before comparing them for equality */ public void setCharacterMap(char[] charMap); /** * Sets the subtext to locate when {@link #indexOf(String)} is called. * Implementations should build any special data structures they may need * concerning the subtext in this method. * * @param subtext the String to locate in {@link #indexOf(String)} */ public void setSubtext(String subtext); /** * Returns the index of the first occurrence of subtext within * text; or -1 if subtext does not * occur within text. Implementations must throw an * {@link IllegalStateException} if {@link #setSubtext(String)} has * not yet been called. * * @param text String in which to locate subtext * @return the index of the first occurrence of subtext within * text; or -1 * @throws IllegalStateException if no subtext has been set */ public int indexOf(String text); /** * The factory for building implementations of {@link TextSearchStrategy} * which is used as an identifier for the strategy itself. */ public interface Factory { /** * Build a new TextSearchStrategy for the specified mode and filter text. * * @param mode either {@code TextMatcherEditor.CONTAINS} or * {@code TextMatcherEditor.STARTS_WITH}. * @param filter the search string to match against */ public TextSearchStrategy create(int mode, String filter); } }././@LongLink0000000000000000000000000000017500000000000011570 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/StartsWithCaseInsensitiveTextSearchStrategy.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/StartsWithCaseInsensitiv0000644000175000017500000001073312106516364033300 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.filter; /** * This implementation of {@link TextSearchStrategy} matches a given text * against the prefix of a given string. If the given string starts with the * prefix, the index 0 is returned; otherwise -1 is returned. * * @author James Lemieux */ public class StartsWithCaseInsensitiveTextSearchStrategy extends AbstractTextSearchStrategy { /** One of two strategies for the indexOf method; one is optimized for single character matching */ private IndexOfStrategy indexOfStrategy; /** * This method selects one of two strategies used by {@link #indexOf(String)} * when testing the prefix of a given string. One strategy is tuned for * performance in the special case of matching a single character prefix. * * @param subtext the String check for as the prefix in {@link #indexOf(String)} */ public void setSubtext(String subtext) { if (subtext.length() == 1) indexOfStrategy = new SingleCharacterIndexOfStrategy(subtext.charAt(0)); else indexOfStrategy = new MultiCharacterIndexOfStrategy(subtext); } /** * The nature of this TextSearchStrategy is to check only the first * characters of the given text against a known prefix. * Consequently, this method will only return -1 in the case * of no match or 0 when the prefix does match. * * @param text the prefix to match * @return 0 if the prefix was matched; -1 if it * was not */ public int indexOf(String text) { // ensure we are in a state to search the text if (indexOfStrategy == null) throw new IllegalStateException("setSubtext must be called with a valid value before this method can operate"); return indexOfStrategy.indexOf(text); } /** * Implementations of this interface are used to provide the return value * for {@link StartsWithCaseInsensitiveTextSearchStrategy#indexOf}. */ private interface IndexOfStrategy { public int indexOf(String text); } /** * This implementation of IndexOfStrategy is optimized for the case when * the prefix is precisely one character long. */ private class SingleCharacterIndexOfStrategy implements IndexOfStrategy { /** The upper and lower case versions of the prefix to match. */ private final char upperCase; private final char lowerCase; public SingleCharacterIndexOfStrategy(char c) { this.upperCase = Character.toUpperCase(c); this.lowerCase = Character.toLowerCase(c); } public int indexOf(String text) { // if the text is not long enough to match the subtext, bail early if (text.length() < 1) return -1; char c = map(text.charAt(0)); return (c == this.upperCase || c == this.lowerCase) ? 0 : -1; } } /** * This implementation of IndexOfStrategy executes the normal case of * prefixes with length > 1. */ private class MultiCharacterIndexOfStrategy implements IndexOfStrategy { /** The length of the subtext to locate. */ private final int subtextLength; /** The array of characters comprising the subtext. */ private char[] subtextCharsUpper; private char[] subtextCharsLower; public MultiCharacterIndexOfStrategy(String prefix) { // record the length of the prefix this.subtextLength = prefix.length(); // extract the upper case version of the prefix into an easily accessible char[] this.subtextCharsUpper = prefix.toUpperCase().toCharArray(); this.subtextCharsLower = prefix.toLowerCase().toCharArray(); } public int indexOf(String text) { // if the text is not long enough to match the subtext, bail early if (text.length() < subtextLength) return -1; for (int i = 0; i < subtextLength; i++) { char c = map(text.charAt(i)); if (subtextCharsLower[i] != c && subtextCharsUpper[i] != c) return -1; } return 0; } } }././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/AbstractTextSearchStrategy.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/AbstractTextSearchStrate0000644000175000017500000000406612106516364033237 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.filter; /** * An abstract base class to ease the burden of implementing the * {@link TextSearchStrategy} interface. This base class provides the * {@link #map(char)} method which consults an internal character map to map * characters as necessary. * *

      Some implementations may support mapping characters to other characters * in order to support fuzzy matching. For example, diacritical marks could be * stripped during the text search by mapping characters like '' to 'e'. * * @author James Lemieux */ abstract class AbstractTextSearchStrategy implements TextSearchStrategy { /** * The strategy for mapping each character immediately before it is * compared with a target character; null if no mapping * should be performed. */ char[] characterMap; /** * Sets the strategy used to map characters immediately before they * are compared. For example, if the characterMap * converts '' to 'e', then it allows "resume" to match "rsum". By * plugging in arbitrary characterMap strategies, the * "fuzziness" of text matches can be controlled. */ public void setCharacterMap(char[] characterMap) { this.characterMap = characterMap; } /** * A convenience method to map the given character if a character map has * been specified. If either a character map does not exist, or the * character map does not define a mapping for c, then * c is returned unchanged. * * @param c the character to be mapped * @return the character c is mapped to, or c if * no mapping exists */ char map(char c) { return characterMap != null && c < characterMap.length ? characterMap[c] : c; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/SearchTerm.java0000644000175000017500000001642412106516364031274 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.filter; import ca.odell.glazedlists.impl.GlazedListsImpl; import ca.odell.glazedlists.matchers.SearchEngineTextMatcherEditor; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * A SearchTerm object stores metadata around a single piece of text to be * located. Search engines like Google capture more information than the simple * search text to be located. This object tracks three other pieces of metadata * in addition to the text to be located: * *

        *
      • {@link #isNegated()} returns true if the search term should * NOT appear within the target text * *
      • {@link #isRequired()} returns true if the search term MUST be * used and cannot be discarded for any reason. (sometimes search engines * discard frivolous search terms like "and", "of", and "the". * *
      • {@link #getField()} controls the TextFilterator that produces the * values to be searched when locating this SearchTerm. Specifically, a * null Field indicates the TextFilterator of the * {@link TextMatcher} will be used to produce values to be searched. * A non-null Field will be used to obtain the TextFilterator that * produces the values to be searched. *
      * * @author James Lemieux */ public final class SearchTerm implements Serializable { // the text to be located private final String text; // true if this search term should NOT be found in the target text private final boolean negated; // true if this search term should be included absolutely private final boolean required; // When field is non-null, it contains the TextFilterator to use to extract // the values this SearchTerm should consider when matching a given object. // A null value indicates the values extracted by the TextMatcher are to be // used. private final SearchEngineTextMatcherEditor.Field field; /** * A recyclable list of filter strings extracted by the TextFilterator of * the {@link #field}. */ private final List fieldFilterStrings = new ArrayList(); /** * Construct a new SearchTerm with the given text * that is neither negated nor required. */ public SearchTerm(String text) { this(text, false, false, null); } /** * Construct a new SearchTerm with the given text * that is negated and required according to the given booleans. */ public SearchTerm(String text, boolean negated, boolean required, SearchEngineTextMatcherEditor.Field field) { if (text == null) throw new IllegalArgumentException("text may not be null"); this.text = text; this.negated = negated; this.required = required; this.field = field; } /** * Returns the text to be located within a target text. */ public String getText() { return text; } /** * Returns true if the text must NOT be located within a target * text; false if the text MUST be located within a target text. */ public boolean isNegated() { return negated; } /** * Returns true if this SearchTerm must be * included within a search and cannot be discarded for any reason. For * example, even if it contains typically frivolous search text such as * "the", "and" or "of". */ public boolean isRequired() { return required; } /** * Returns the object that provides a custom TextFilterator to use when * locating this SearchTerm within an object. When this method returns * null it indicates that the TextFilterator of the * TextMatcher should be used to extract values from a target object. */ public SearchEngineTextMatcherEditor.Field getField() { return field; } List getFieldFilterStrings() { return fieldFilterStrings; } /** * Return a new SearchTerm with identical information save for * the given text. */ public SearchTerm newSearchTerm(String text) { return new SearchTerm(text, isNegated(), isRequired(), getField()); } /** * Returns true if the given term is * guaranteed to match fewer text strings than this * SearchTerm; false otherwise. This method is the mirror opposite * of {@link #isRelaxation(SearchTerm)}. * * @param term the SearchTerm to be tested for constrainment * @return true if the given term is * guaranteed to match fewer text strings than this * SearchTerm; false otherwise * * @see #isRelaxation(SearchTerm) */ boolean isConstrainment(SearchTerm term) { // if they're negated state doesn't match then we cannot really compare these search terms if (isNegated() != term.isNegated()) return false; // if they have a field that doesn't match then we cannot really compare these search terms if (!GlazedListsImpl.equal(getField(), term.getField())) return false; // if the text is equal then no strict constrainment exists if (getText().equals(term.getText())) return false; // otherwise constrainment is determined by whether we can locate one's text within the other return isNegated() ? term.getText().indexOf(getText()) != -1 : getText().indexOf(term.getText()) != -1; } /** * Returns true if the given term is * guaranteed to match more text strings than this * SearchTerm; false otherwise. This method is the mirror opposite * of {@link #isConstrainment(SearchTerm)}. * * @param term the SearchTerm to be tested for constrainment * @return true if the given term is * guaranteed to match more text strings than this * SearchTerm; false otherwise * * @see #isRelaxation(SearchTerm) */ boolean isRelaxation(SearchTerm term) { return term.isConstrainment(this); } /** @inheritDoc */ @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SearchTerm that = (SearchTerm) o; if (negated != that.negated) return false; if (required != that.required) return false; if (field != null ? !field.equals(that.field) : that.field != null) return false; if (!text.equals(that.text)) return false; return true; } /** @inheritDoc */ @Override public int hashCode() { int result; result = text.hashCode(); result = 31 * result + (negated ? 1 : 0); result = 31 * result + (required ? 1 : 0); result = 31 * result + (field != null ? field.hashCode() : 0); return result; } }././@LongLink0000000000000000000000000000020200000000000011557 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/SingleCharacterCaseInsensitiveTextSearchStrategy.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/SingleCharacterCaseInsen0000644000175000017500000000467712106516364033155 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.filter; /** * This implementation of TextSearchStrategy searches the * source text for a single character - the first character of the given * subtext. This implementation is optimized for use when the subtext is * precisely 1 character. * * @author James Lemieux */ public class SingleCharacterCaseInsensitiveTextSearchStrategy extends AbstractTextSearchStrategy { /** The single character to locate. */ private char subtextCharLower; private char subtextCharUpper; /** true if subtext has been set; false otherwise. */ private boolean subtextInitialized = false; /** * Sets the subtext to locate found when {@link #indexOf(String)} is called. * This method is expected to be called with a String of length 1. * * @param subtext the String containing the single character to locate in * {@link #indexOf(String)} * @throws IllegalArgumentException if subtext is * null or does not contain precisely 1 * character */ public void setSubtext(String subtext) { if (subtext == null) throw new IllegalArgumentException("subtext may not be null"); if (subtext.length() != 1) throw new IllegalArgumentException("subtext (" + subtext + ") must contain a single character"); final char c = subtext.charAt(0); this.subtextCharLower = Character.toLowerCase(c); this.subtextCharUpper = Character.toUpperCase(c); this.subtextInitialized = true; } /** {@inheritDoc} */ public int indexOf(String text) { // ensure we are in a state to search the text if(!this.subtextInitialized) throw new IllegalStateException("setSubtext must be called with a valid value before this method can operate"); char firstChar; // search for subtextChar in the given text for(int i = 0; i < text.length(); i++) { firstChar = map(text.charAt(i)); if(firstChar == this.subtextCharLower || firstChar == this.subtextCharUpper) { return i; } } // we didn't find the subtextChar so return -1 return -1; } }././@LongLink0000000000000000000000000000016500000000000011567 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/RegularExpressionTextSearchStrategy.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/RegularExpressionTextSea0000644000175000017500000000166512106516364033277 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.filter; import java.util.regex.Pattern; import java.util.regex.Matcher; /** * This implementation of {@link TextSearchStrategy} matches a given text * against a regular expression. If the regular expression matches, the start * position of the match is returned. If there is no match, -1 is returned. * * @author Wim Deblauwe */ public class RegularExpressionTextSearchStrategy extends AbstractTextSearchStrategy { private Matcher matcher; public void setSubtext(String regex) { matcher = Pattern.compile(regex).matcher(""); } public int indexOf(String text) { return matcher.reset(text).matches() ? matcher.start() : -1; } }././@LongLink0000000000000000000000000000017000000000000011563 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/ExactCaseInsensitiveTextSearchStrategy.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/ExactCaseInsensitiveText0000644000175000017500000000201612106516364033235 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.filter; /** * This implementation of {@link TextSearchStrategy} matches a given text * against an exact subtext. If the subtext matches the given text character * for character it is considered a match and the index of 0 is returned. If * there is no match, -1 is returned. * * @author James Lemieux */ public class ExactCaseInsensitiveTextSearchStrategy extends StartsWithCaseInsensitiveTextSearchStrategy { private int subtextLength; @Override public void setSubtext(String subtext) { super.setSubtext(subtext); this.subtextLength = subtext.length(); } @Override public int indexOf(String text) { if (text.length() != subtextLength) return -1; return super.indexOf(text); } }././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/StringTextFilterator.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/StringTextFilterator.jav0000644000175000017500000000134512106516364033241 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.filter; import ca.odell.glazedlists.TextFilterator; import java.util.List; /** * TextFilterator that uses an Object's toString() value. * * @author James Lemieux * @author Jesse Wilson */ public class StringTextFilterator implements TextFilterator { /** {@inheritDoc} */ public void getFilterStrings(List baseList, E element) { if (element != null) baseList.add(element.toString()); } }././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/StringLengthComparator.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/filter/StringLengthComparator.j0000644000175000017500000000115712106516364033204 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.filter; import java.util.Comparator; /** * This Comparator orders {@link String}s in descending order by their lengths. * * @author James Lemieux */ public final class StringLengthComparator implements Comparator { /** {@inheritDoc} */ public int compare(String a, String b) { return b.length() - a.length(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/0000755000175000017500000000000012106516372025647 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/AgedNode.java0000644000175000017500000000400112106516372030153 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt; /** * A thin wrapper class for Objects to be inserted in the * age sorted tree representation of the cache. * * @author Kevin Maltby */ public final class AgedNode { /** provide times of nodes relative to one-another */ private static long nextTimestamp = 0; /** The corresponding node in the index tree */ private SparseListNode indexNode = null; /** The timestamp corresponding to the last access of this node */ private long timestamp = 0; /** The value to this node */ private Object value = null; /** * Creates a new AgedNode object to store in the age sorted tree * * @param indexNode The related node in the indexing tree * @param value The value to assign to this node */ public AgedNode(SparseListNode indexNode, Object value) { this.indexNode = indexNode; this.value = value; timestamp = nextTimestamp(); } private static synchronized final long nextTimestamp() { return nextTimestamp++; } /** * Retrieves the value of this node. * * This is an access, and as such, this node must be re-ordered * * @return The value of this node */ public Object getValue() { timestamp = nextTimestamp(); return value; } /** * Retrieves the index node for this node. * * @return The index node */ public SparseListNode getIndexNode() { return indexNode; } /** * Retrieves the age of this node. * * @return The age of this node */ public long getTimestamp() { return timestamp; } @Override public String toString() { return "[AgedNode: " + value + "]"; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/0000755000175000017500000000000012106516370027326 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/TreeMacros.m40000644000175000017500000000341212106516370031634 0ustar gregoagregoa M4 Macros m4_divert(-1) # forloop(i, from, to, stmt) m4_define(`forloop', `m4_pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')m4_popdef(`$1')') m4_define(`_forloop', `$4`'m4_ifelse($1, `$3', , `m4_define(`$1', m4_incr($1))_forloop(`$1', `$2', `$3', `$4')')') m4_divert m4_define(`VAR_LAST_COLOR_INDEX', `m4_eval(VAR_COLOUR_COUNT-1)') m4_define(`originalCounti', ``originalCount'indexToBit($1)') m4_define(`indexToBit', `m4_eval(`2 ** $1')') m4_define(`counti', ``count'indexToBit($1)') m4_define(COLORED_START, `m4_ifelse(VAR_COLOUR_COUNT,1,$1,') m4_define(COLORED_END, `)') m4_define(WIDE_NODES_START, `m4_ifelse(VAR_WIDE_NODES,false,$1,') m4_define(WIDE_NODES_END, `)') # define a function NODE_WIDTH(boolean) to get the node's size for this color m4_ifelse(VAR_WIDE_NODES,true,` m4_define(NODE_WIDTH, size) ',` m4_define(NODE_WIDTH, $1 ? 1 : 0) ') # define a function NODE_SIZE(node, colors) to no node.nodeSize() m4_ifelse(VAR_WIDE_NODES,true,` m4_ifelse(VAR_COLOR_COUNT,1,` m4_define(NODE_SIZE,$1.size) ',` m4_define(NODE_SIZE,$1.nodeSize($2)) ') ',` m4_define(NODE_SIZE, 1) ') # define a function to refresh counts m4_ifelse(VAR_WIDE_NODES,false,` m4_define(REFRESH_COUNTS,$1.refreshCounts(!zeroQueue.contains($1)); `EXAMPLE_START') ',` m4_define(REFRESH_COUNTS,$1.refreshCounts(); `EXAMPLE_START') ') # multiple values m4_define(`VAR_LAST_TYPE_INDEX', `m4_eval(VAR_TYPE_COUNT-1)') m4_define(`Ti', `T'$1) m4_define(`ti', `t'$1) m4_define(NODENAME_START, `BciiNode 'TYPELIST_START) m4_define(NODENAME_END, TYPELIST_END) m4_define(TYPELIST_START, `<'forloop(`i', 0, VAR_LAST_TYPE_INDEX, ` COMMA_ONE_PLUS(i) Ti(i)')`> M4_COMMENT(') m4_define(TYPELIST_END, `)') m4_define(COMMA_ONE_PLUS, `m4_ifelse($1,0,,COMMAPLACEHOLDER)') libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/BciiTree.java0000644000175000017500000013532212106516370031665 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt.barcode2; import ca.odell.glazedlists.GlazedLists; import java.util.ArrayList; import java.util.Comparator; import java.util.List; /* m4_include(source/ca/odell/glazedlists/impl/adt/barcode2/JavaMacros.m4) m4_include(source/ca/odell/glazedlists/impl/adt/barcode2/TreeMacros.m4) */ /*[ BEGIN_M4_JAVA ]*/ /** * Our second generation tree class. * *

      Currently the API for this class is fairly low-level, particularly the * use of bytes as color values. This is an implementation detail, * exposed to maximize performance. Wherever necessary, consider creating a * facade around this Tree class that provides methods more appropriate * for your particular application. * *

      This is a prototype replacement for the Barcode class that adds support * for up to seven different colors. As well, this supports values in the node. * It will hopefully also replace our IndexedTree class. This class * is designed after those two classes and hopefully improves upon them in * a few interesting ways: *

    • Avoid recursion wherever possible to increase performance *
    • Be generic to simplify handling of black/white values. These can be * handled in one case rather than one case for each. *
    • Make the node class a dataholder only and put most of the logic in * the tree class. This allows us to share different Node classes with * different memory requirements, while using the same logic class * *

      This class came into being so we could use a tree to replace * ListEventBlocks, which has only mediocre performance, particularly * due to having to sort elements. As well, we might be able to keep a moved * value in the tree, to support moved elements in ListEvents. * * @author Jesse Wilson */ public class BciiTree/*[ TYPELIST_START ]*/ /*[ TYPELIST_END ]*/ { /*[ COLORED_START ]*/ /** the colors in the tree, used for printing purposes only */ private final ListToByteCoder coder; /*[ COLORED_END ]*/ /** the tree's root, or null for an empty tree */ private /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ root = null; /** * a list to add all nodes to that must be removed from * the tree. The nodes are removed only after the tree has been modified, * which allows us a chance to do rotations without losing our position * in the tree. */ private final List /*[ NODENAME_END ]*/> zeroQueue = new ArrayList /*[ NODENAME_END ]*/>(); /** * The comparator to use when performing ordering operations on the tree. * Sometimes this tree will not be sorted, so in such situations this * comparator will not be used. */ private final Comparator comparator; /** * @param coder specifies the node colors * @param comparator the comparator to use when ordering values within the * tree. If this tree is unsorted, use the one-argument constructor. */ public BciiTree/**/(/*[ COLORED_START ]*/ ListToByteCoder coder, /*[ COLORED_END ]*/ Comparator comparator) { /*[ COLORED_START ]*/ if(coder == null) throw new NullPointerException("Coder cannot be null."); /*[ COLORED_END ]*/ if(comparator == null) throw new NullPointerException("Comparator cannot be null."); /*[ COLORED_START ]*/ this.coder = coder; /*[ COLORED_END ]*/ this.comparator = comparator; } /** * @param coder specifies the node colors */ public BciiTree/**/(/*[ COLORED_START ]*/ ListToByteCoder coder /*[ COLORED_END ]*/) { this(/*[ COLORED_START ]*/ coder, /*[ COLORED_END ]*/ (Comparator)GlazedLists.comparableComparator()); } /*[ COLORED_START ]*/ public ListToByteCoder getCoder() { return coder; } /*[ COLORED_END ]*/ public Comparator getComparator() { return comparator; } /** * Get the tree element at the specified index relative to the specified index * colors. * *

      This method is an hotspot, so its crucial that it run as efficiently * as possible. */ public Element get(int index /*[ COLORED_START ]*/, byte indexColors /*[ COLORED_END ]*/) { if(root == null) throw new IndexOutOfBoundsException(); // go deep, looking for our node of interest /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ node = root; while(true) { assert(node != null); assert(index >= 0); // recurse on the left /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ nodeLeft = node.left; int leftSize = nodeLeft != null ? nodeLeft./*[ COLORED_START(count1) ]*/ size(indexColors) /*[ COLORED_END ]*/ : 0; if(index < leftSize) { node = nodeLeft; continue; } else { index -= leftSize; } // the result is in the centre int size = /*[ NODE_SIZE(node, indexColors) EXAMPLE_START ]*/ node.nodeSize(indexColors) /*[ EXAMPLE_END ]*/; if(index < size) { return node; } else { index -= size; } // the result is on the right node = node.right; } } /** * Add a tree node at the specified index relative to the specified index * colors. The inserted nodes' color, value and size are specified. * *

      Note that nodes with null values will never be * merged together to allow those nodes to be assigned other values later. * * @param size the size of the node to insert. * @param index the location into this tree to insert at * @param indexColors the colors that index is relative to. This should be * all colors in the tree ORed together for the entire tree. * @param value the node value. If non-null, the node may be * combined with other nodes of the same color and value. null * valued nodes will never be combined with each other. * @return the element the specified value was inserted into. This is non-null * unless the size parameter is 0, in which case the result is always * null. */ public Element add(int index, /*[ COLORED_START ]*/ byte indexColors, byte color, /*[ COLORED_END ]*/ T0 value, int size) { assert(index >= 0); assert(index <= size(/*[ COLORED_START ]*/ indexColors /*[ COLORED_END ]*/)); assert(size >= 0); if(this.root == null) { if(index != 0) throw new IndexOutOfBoundsException(); this.root = new /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/(/*[ COLORED_START ]*/ color, /*[ COLORED_END ]*/ size, value, null); assert(valid()); return this.root; } else { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ inserted = insertIntoSubtree(root, index, /*[ COLORED_START ]*/ indexColors, color, /*[ COLORED_END ]*/ value, size); assert(valid()); return inserted; } } /** * @param parent the subtree to insert into, must not be null. * @param index the color index to insert at * @param indexColors a bitmask of all colors that the index is defined in * terms of. For example, if this is determined in terms of colors 4, 8 * and 32, then the value here should be 44 (32 + 8 + 4). * @param color a bitmask value such as 1, 2, 4, 8, 16, 32, 64 or 128. * @param value the object to hold in the inserted node. * @param size the size of the inserted node, with respect to indices. * @return the inserted node, or the modified node if this insert simply * increased the size of an existing node. */ private /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ insertIntoSubtree(/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ parent, int index, /*[ COLORED_START ]*/ byte indexColors, byte color, /*[ COLORED_END ]*/ T0 value, int size) { while(true) { assert(parent != null); assert(index >= 0); // figure out the layout of this node /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ parentLeft = parent.left; int parentLeftSize = parentLeft != null ? parentLeft./*[ COLORED_START(count1) ]*/ size(indexColors) /*[ COLORED_END ]*/ : 0; int parentRightStartIndex = parentLeftSize + /*[ NODE_SIZE(parent, indexColors) EXAMPLE_START ]*/ parent.nodeSize(indexColors) /*[ EXAMPLE_END ]*/; // the first thing we want to try is to merge this value into the // current node, since that's the cheapest thing to do: if(/*[ WIDE_NODES_START(false &&) WIDE_NODES_END ]*/ /*[ COLORED_START ]*/ color == parent.color && /*[ COLORED_END ]*/ value == parent.t0 && value != null) { if(index >= parentLeftSize && index <= parentRightStartIndex) { /*[ WIDE_NODES_START ]*/ parent.size += size; /*[ WIDE_NODES_END ]*/ fixCountsThruRoot(parent, /*[ COLORED_START ]*/ color, /*[ COLORED_END ]*/ size); return parent; } } // we can insert on the left if(index <= parentLeftSize) { // as a new left child if(parentLeft == null) { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ inserted = new /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/(/*[ COLORED_START ]*/ color, /*[ COLORED_END ]*/ size, value, parent); parent.left = inserted; fixCountsThruRoot(parent, /*[ COLORED_START ]*/ color, /*[ COLORED_END ]*/ size); fixHeightPostChange(parent, false); return inserted; // recurse on the left } else { parent = parentLeft; continue; } } // we need to insert in the centre. This works by splitting in the // centre, and inserting the value if(index < parentRightStartIndex) { int parentRightHalfSize = parentRightStartIndex - index; /*[ WIDE_NODES_START ]*/ parent.size -= parentRightHalfSize; /*[ WIDE_NODES_END ]*/ fixCountsThruRoot(parent, /*[ COLORED_START ]*/ parent.color, /*[ COLORED_END ]*/ -parentRightHalfSize); // insert as null first to make sure this doesn't get merged back Element inserted = insertIntoSubtree(parent, index, /*[ COLORED_START ]*/ indexColors, parent.color, /*[ COLORED_END ]*/ null, parentRightHalfSize); inserted.set(parent.t0); // recalculate parentRightStartIndex, since that should have // changed by now. this will then go on to insert on the right parentRightStartIndex = parentLeftSize + /*[ NODE_SIZE(parent, indexColors) EXAMPLE_START ]*/ parent.nodeSize(indexColors) /*[ EXAMPLE_END ]*/; } // on the right right: { int parentSize = parent./*[ COLORED_START(count1) ]*/ size(indexColors) /*[ COLORED_END ]*/; assert(index <= parentSize); /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ parentRight = parent.right; // as a right child if(parentRight == null) { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ inserted = new /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/(/*[ COLORED_START ]*/ color, /*[ COLORED_END ]*/ size, value, parent); parent.right = inserted; fixCountsThruRoot(parent, /*[ COLORED_START ]*/ color, /*[ COLORED_END ]*/ size); fixHeightPostChange(parent, false); return inserted; // recurse on the right } else { parent = parentRight; index -= parentRightStartIndex; } } } } /** * Add a tree node in sorted order. * * @param size the size of the node to insert. * @param value the node value. If non-null, the node may be * combined with other nodes of the same color and value. null * valued nodes will never be combined with each other. * @return the element the specified value was inserted into. This is non-null * unless the size parameter is 0, in which case the result is always * null. */ public Element addInSortedOrder(byte color, T0 value, int size) { assert(size >= 0); if(this.root == null) { this.root = new /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/(/*[ COLORED_START ]*/ color, /*[ COLORED_END ]*/ size, value, null); assert(valid()); return this.root; } else { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ inserted = insertIntoSubtreeInSortedOrder(root, /*[ COLORED_START ]*/ color, /*[ COLORED_END ]*/ value, size); assert(valid()); return inserted; } } /** * @param parent the subtree to insert into, must not be null. * @param color a bitmask value such as 1, 2, 4, 8, 16, 32, 64 or 128. * @param value the object to hold in the inserted node. * @param size the size of the inserted node, with respect to indices. * @return the inserted node, or the modified node if this insert simply * increased the size of an existing node. */ private /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ insertIntoSubtreeInSortedOrder(/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ parent, /*[ COLORED_START ]*/ byte color, /*[ COLORED_END ]*/ T0 value, int size) { while(true) { assert(parent != null); // calculating the sort side is a little tricky since we can have // unsorted nodes in the tree. we just look for a neighbour (ie next) // that is sorted, and compare with that int sortSide; for(/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ currentFollower = parent; true; currentFollower = next(currentFollower)) { // we've hit the end of the list, assume the element is on the left side if(currentFollower == null) { sortSide = -1; break; // we've found a comparable node, use it } else if(currentFollower.sorted == Element.SORTED) { sortSide = comparator.compare(value, currentFollower.t0); break; } } // the first thing we want to try is to merge this value into the // current node, since that's the cheapest thing to do: if(/*[ WIDE_NODES_START(false &&) WIDE_NODES_END ]*/ sortSide == 0 && /*[ COLORED_START ]*/ color == parent.color && /*[ COLORED_END ]*/ value == parent.t0 && value != null) { /*[ WIDE_NODES_START ]*/ parent.size += size; /*[ WIDE_NODES_END ]*/ fixCountsThruRoot(parent, /*[ COLORED_START ]*/ color, /*[ COLORED_END ]*/ size); return parent; } // insert on the left... boolean insertOnLeft = false; insertOnLeft = insertOnLeft || sortSide < 0; insertOnLeft = insertOnLeft || sortSide == 0 && parent.left == null; insertOnLeft = insertOnLeft || sortSide == 0 && parent.right != null && parent.left.height < parent.right.height; if(insertOnLeft) { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ parentLeft = parent.left; // as a new left child if(parentLeft == null) { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ inserted = new /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/(/*[ COLORED_START ]*/ color, /*[ COLORED_END ]*/ size, value, parent); parent.left = inserted; fixCountsThruRoot(parent, /*[ COLORED_START ]*/ color, /*[ COLORED_END ]*/ size); fixHeightPostChange(parent, false); return inserted; // recurse on the left } else { parent = parentLeft; continue; } // ...or on the right } else { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ parentRight = parent.right; // as a right child if(parentRight == null) { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ inserted = new /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/(/*[ COLORED_START ]*/ color, /*[ COLORED_END ]*/ size, value, parent); parent.right = inserted; fixCountsThruRoot(parent, /*[ COLORED_START ]*/ color, /*[ COLORED_END ]*/ size); fixHeightPostChange(parent, false); return inserted; // recurse on the right } else { parent = parentRight; } } } } /** * Adjust counts for all nodes (including the specified node) up the tree * to the root. The counts of the specified color are adjusted by delta * (which may be positive or negative). */ private final void fixCountsThruRoot(/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ node, /*[ COLORED_START ]*/ byte color, /*[ COLORED_END ]*/ int delta) { /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `m4_ifelse(VAR_COLOUR_COUNT,`1',`for( ; node != null; node = node.parent) node.'counti(i)` += delta; ', `if(color == 'indexToBit(i)`) { for( ; node != null; node = node.parent) node.'counti(i)` += delta; } ')') GENERATED_CODE_END EXAMPLE_START ]*/ if(color == 1) { for( ; node != null; node = node.parent) node.count1 += delta; } if(color == 2) { for( ; node != null; node = node.parent) node.count2 += delta; } if(color == 4) { for( ; node != null; node = node.parent) node.count4 += delta; } /*[ EXAMPLE_END ]*/ } /*[ COLORED_START ]*/ /** * Change the color of the specified element. */ public final void setColor(Element element, byte color) { BciiNode node = (BciiNode)element; byte oldColor = node.getColor(); if(oldColor == color) return; fixCountsThruRoot(node, oldColor, -node.size); node.color = color; fixCountsThruRoot(node, color, node.size); } /*[ COLORED_END ]*/ /** * Fix the height of the specified ancestor after inserting a child node. * This method short circuits when it finds the first node where the size * has not changed. * * @param node the root of a changed subtree. This shouldn't be called * on inserted nodes, but rather their parent nodes, since only * the parent nodes sizes will be changing. * @param allTheWayToRoot true to walk up the tree all the way * to the tree's root, or false to walk up until the height * is unchanged. We go to the root on a delete, since the rotate is on * the opposite side of the tree, whereas on an insert we only delete * as far as necessary. */ private final void fixHeightPostChange(/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ node, boolean allTheWayToRoot) { // update the height for(; node != null; node = node.parent) { byte leftHeight = node.left != null ? node.left.height : 0; byte rightHeight = node.right != null ? node.right.height : 0; // rotate left? if(leftHeight > rightHeight && (leftHeight - rightHeight == 2)) { // do we need to rotate the left child first? int leftLeftHeight = node.left.left != null ? node.left.left.height : 0; int leftRightHeight = node.left.right != null ? node.left.right.height : 0; if(leftRightHeight > leftLeftHeight) { rotateRight(node.left); } // finally rotate right node = rotateLeft(node); // rotate right? } else if(rightHeight > leftHeight && (rightHeight - leftHeight == 2)) { // do we need to rotate the right child first? int rightLeftHeight = node.right.left != null ? node.right.left.height : 0; int rightRightHeight = node.right.right != null ? node.right.right.height : 0; if(rightLeftHeight > rightRightHeight) { rotateLeft(node.right); } // finally rotate left node = rotateRight(node); } // update the node height leftHeight = node.left != null ? node.left.height : 0; rightHeight = node.right != null ? node.right.height : 0; byte newNodeHeight = (byte) (Math.max(leftHeight, rightHeight) + 1); // if the height doesn't need updating, we might just be done! if(!allTheWayToRoot && node.height == newNodeHeight) return; // otherwise change the height and keep rotating node.height = newNodeHeight; } } /** * Perform an AVL rotation of the tree. * * D B * / \ ROTATE / \ * B E LEFT AT A D * / \ NODE D / \ * A C C E * * @return the new root of the subtree */ private final /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ rotateLeft(/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ subtreeRoot) { assert(subtreeRoot.left != null); // subtreeRoot is D // newSubtreeRoot is B /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ newSubtreeRoot = subtreeRoot.left; // modify the links between nodes // attach C as a child of to D subtreeRoot.left = newSubtreeRoot.right; if(newSubtreeRoot.right != null) newSubtreeRoot.right.parent = subtreeRoot; // link b as the new root node for this subtree newSubtreeRoot.parent = subtreeRoot.parent; if(newSubtreeRoot.parent != null) { if(newSubtreeRoot.parent.left == subtreeRoot) newSubtreeRoot.parent.left = newSubtreeRoot; else if(newSubtreeRoot.parent.right == subtreeRoot) newSubtreeRoot.parent.right = newSubtreeRoot; else throw new IllegalStateException(); } else { root = newSubtreeRoot; } // attach D as a child of B newSubtreeRoot.right = subtreeRoot; subtreeRoot.parent = newSubtreeRoot; // update height and counts of the old subtree root byte subtreeRootLeftHeight = subtreeRoot.left != null ? subtreeRoot.left.height : 0; byte subtreeRootRightHeight = subtreeRoot.right != null ? subtreeRoot.right.height : 0; subtreeRoot.height = (byte)(Math.max(subtreeRootLeftHeight, subtreeRootRightHeight) + 1); /*[ REFRESH_COUNTS(subtreeRoot) ]*/ subtreeRoot.refreshCounts(); /*[ EXAMPLE_END ]*/ // update height and counts of the new subtree root byte newSubtreeRootLeftHeight = newSubtreeRoot.left != null ? newSubtreeRoot.left.height : 0; byte newSubtreeRootRightHeight = newSubtreeRoot.right != null ? newSubtreeRoot.right.height : 0; newSubtreeRoot.height = (byte)(Math.max(newSubtreeRootLeftHeight, newSubtreeRootRightHeight) + 1); /*[ REFRESH_COUNTS(newSubtreeRoot) ]*/ newSubtreeRoot.refreshCounts(); /*[ EXAMPLE_END ]*/ return newSubtreeRoot; } private final /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ rotateRight(/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ subtreeRoot) { assert(subtreeRoot.right != null); // subtreeRoot is D // newSubtreeRoot is B /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ newSubtreeRoot = subtreeRoot.right; // modify the links between nodes // attach C as a child of to D subtreeRoot.right = newSubtreeRoot.left; if(newSubtreeRoot.left != null) newSubtreeRoot.left.parent = subtreeRoot; // link b as the new root node for this subtree newSubtreeRoot.parent = subtreeRoot.parent; if(newSubtreeRoot.parent != null) { if(newSubtreeRoot.parent.left == subtreeRoot) newSubtreeRoot.parent.left = newSubtreeRoot; else if(newSubtreeRoot.parent.right == subtreeRoot) newSubtreeRoot.parent.right = newSubtreeRoot; else throw new IllegalStateException(); } else { root = newSubtreeRoot; } // attach D as a child of B newSubtreeRoot.left = subtreeRoot; subtreeRoot.parent = newSubtreeRoot; // update height and counts of the old subtree root byte subtreeRootLeftHeight = subtreeRoot.left != null ? subtreeRoot.left.height : 0; byte subtreeRootRightHeight = subtreeRoot.right != null ? subtreeRoot.right.height : 0; subtreeRoot.height = (byte)(Math.max(subtreeRootLeftHeight, subtreeRootRightHeight) + 1); /*[ REFRESH_COUNTS(subtreeRoot) ]*/ subtreeRoot.refreshCounts(); /*[ EXAMPLE_END ]*/ // update height and counts of the new subtree root byte newSubtreeRootLeftHeight = newSubtreeRoot.left != null ? newSubtreeRoot.left.height : 0; byte newSubtreeRootRightHeight = newSubtreeRoot.right != null ? newSubtreeRoot.right.height : 0; newSubtreeRoot.height = (byte)(Math.max(newSubtreeRootLeftHeight, newSubtreeRootRightHeight) + 1); /*[ REFRESH_COUNTS(newSubtreeRoot) ]*/ newSubtreeRoot.refreshCounts(); /*[ EXAMPLE_END ]*/ return newSubtreeRoot; } /** * Remove the specified element from the tree outright. */ public void remove(Element element) { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ node = (/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/)element; /*[ WIDE_NODES_START ]*/ assert(node.size > 0); /*[ WIDE_NODES_END ]*/ assert(root != null); // delete the node by adding to the zero queue fixCountsThruRoot(node, /*[ COLORED_START ]*/ node.color, /*[ COLORED_END ]*/ /*[ WIDE_NODES_START(-1) ]*/ -node.size /*[ WIDE_NODES_END ]*/); /*[ WIDE_NODES_START ]*/ node.size = 0; /*[ WIDE_NODES_END ]*/ zeroQueue.add(node); drainZeroQueue(); assert(valid()); } /** * Remove size values at the specified index. Only values of the type * specified in indexColors will be removed. * *

      Note that if the two nodes on either side of the removed node could * be merged, they probably will not be merged by this implementation. This * is to simplify the implementation, but it means that when iterating a * tree, sometimes multiple nodes of the same color and value will be * encountered in sequence. */ public void remove(int index, /*[ COLORED_START ]*/ byte indexColors, /*[ COLORED_END ]*/ int size) { if(size == 0) return; assert(index >= 0); assert(index + size <= size(/*[ COLORED_START ]*/ indexColors /*[ COLORED_END ]*/)); assert(root != null); // remove values from the tree removeFromSubtree(root, index, /*[ COLORED_START ]*/ indexColors, /*[ COLORED_END ]*/ size); // clean up any nodes that got deleted drainZeroQueue(); assert(valid()); } /** * Prune all nodes scheduled for deletion. */ private void drainZeroQueue() { for(int i = 0, size = zeroQueue.size(); i < size; i++) { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ node = zeroQueue.get(i); /*[ WIDE_NODES_START ]*/ assert(node.size == 0); /*[ WIDE_NODES_END ]*/ if(node.right == null) { replaceChild(node, node.left); } else if(node.left == null) { replaceChild(node, node.right); } else { node = replaceEmptyNodeWithChild(node); } } zeroQueue.clear(); } /** * Remove at the specified index in the specified subtree. This doesn't ever * remove any nodes of size zero, that's up to the caller to do after by * removing all nodes in the zeroQueue from the tree. */ private void removeFromSubtree(/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ node, int index, /*[ COLORED_START ]*/ byte indexColors, /*[ COLORED_END ]*/ int size) { while(size > 0) { assert(node != null); assert(index >= 0); // figure out the layout of this node /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ nodeLeft = node.left; int leftSize = nodeLeft != null ? nodeLeft./*[ COLORED_START(count1) ]*/ size(indexColors) /*[ COLORED_END ]*/ : 0; // delete on the left first if(index < leftSize) { // we can only remove part of our requirement on the left, so do // that part recursively if(index + size > leftSize) { int toRemove = leftSize - index; removeFromSubtree(nodeLeft, index, /*[ COLORED_START ]*/ indexColors, /*[ COLORED_END ]*/ toRemove); size -= toRemove; leftSize -= toRemove; // we can do our full delete on the left side } else { node = nodeLeft; continue; } } assert(index >= leftSize); // delete in the centre int rightStartIndex = leftSize + /*[ NODE_SIZE(node, indexColors) EXAMPLE_START ]*/ node.nodeSize(indexColors) /*[ EXAMPLE_END ]*/; if(index < rightStartIndex) { int toRemove = Math.min(rightStartIndex - index, size); // decrement the appropriate counts all the way up /*[ WIDE_NODES_START ]*/ node.size -= toRemove; /*[ WIDE_NODES_END ]*/ size -= toRemove; rightStartIndex -= toRemove; fixCountsThruRoot(node, /*[ COLORED_START ]*/ node.color, /*[ COLORED_END ]*/ -toRemove); if(/*[ WIDE_NODES_START(true) ]*/ node.size == 0 /*[ WIDE_NODES_END ]*/) { zeroQueue.add(node); } if(size == 0) return; } assert(index >= rightStartIndex); // delete on the right last index -= rightStartIndex; node = node.right; } } /** * Replace the specified node with the specified replacement. This does the * replacement, then walks up the tree to ensure heights are correct, so * the replacement node should have its height set first before this method * is called. */ private void replaceChild(/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ node, /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ replacement) { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ nodeParent = node.parent; // replace the root if(nodeParent == null) { assert(node == root); root = replacement; // replace on the left } else if(nodeParent.left == node) { nodeParent.left = replacement; // replace on the right } else if(nodeParent.right == node) { nodeParent.right = replacement; } // update the replacement's parent if(replacement != null) { replacement.parent = nodeParent; } // the height has changed, update that up the tree fixHeightPostChange(nodeParent, true); } /** * Replace the specified node with another node deeper in the tree. This * is necessary to maintain treeness through deletes. * *

      This implementation finds the largest node in the left subtree, * removes it, and puts it in the specified node's place. * * @return the replacement node */ private /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ replaceEmptyNodeWithChild(/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ toReplace) { /*[ WIDE_NODES_START ]*/ assert(toReplace.size == 0); /*[ WIDE_NODES_END ]*/ assert(toReplace.left != null); assert(toReplace.right != null); // find the rightmost child on the leftside /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ replacement = toReplace.left; while(replacement.right != null) { replacement = replacement.right; } assert(replacement.right == null); // remove that node from the tree fixCountsThruRoot(replacement, /*[ COLORED_START ]*/ replacement.color, /*[ COLORED_END ]*/ /*[ WIDE_NODES_START(-1) ]*/ -replacement.size /*[ WIDE_NODES_END ]*/); replaceChild(replacement, replacement.left); // update the tree structure to point to the replacement replacement.left = toReplace.left; if(replacement.left != null) replacement.left.parent = replacement; replacement.right = toReplace.right; if(replacement.right != null) replacement.right.parent = replacement; replacement.height = toReplace.height; /*[ REFRESH_COUNTS(replacement) ]*/ replacement.refreshCounts(); /*[ EXAMPLE_END ]*/ replaceChild(toReplace, replacement); fixCountsThruRoot(replacement.parent, /*[ COLORED_START ]*/ replacement.color, /*[ COLORED_END ]*/ /*[ WIDE_NODES_START(1) ]*/ replacement.size /*[ WIDE_NODES_END ]*/); return replacement; } /** * Replace all values at the specified index with the specified new value. * *

      Currently this uses a naive implementation of remove then add. If * it proves desirable, it may be worthwhile to optimize this implementation * with one that performs the remove and insert simultaneously, to save * on tree navigation. * * @return the element that was updated. This is non-null unless the size * parameter is 0, in which case the result is always null. */ public Element set(int index, /*[ COLORED_START ]*/ byte indexColors, byte color, /*[ COLORED_END ]*/ T0 value, int size) { remove(index, /*[ COLORED_START ]*/ indexColors, /*[ COLORED_END ]*/ size); return add(index, /*[ COLORED_START ]*/ indexColors, color, /*[ COLORED_END ]*/ value, size); } /** * Remove all nodes from the tree. Note that this is much faster than calling * remove on all elements, since the structure can be discarded instead of * managed during the removal. */ public void clear() { root = null; } /** * Get the index of the specified element, counting only the colors * specified. * *

      This method is an hotspot, so its crucial that it run as efficiently * as possible. */ public int indexOfNode(Element element, byte colorsOut) { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ node = (/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/)element; // count all elements left of this node int index = node.left != null ? node.left./*[ COLORED_START(count1) ]*/ size(colorsOut) /*[ COLORED_END ]*/ : 0; // add all elements on the left, all the way to the root for( ; node.parent != null; node = node.parent) { if(node.parent.right == node) { index += node.parent.left != null ? node.parent.left./*[ COLORED_START(count1) ]*/ size(colorsOut) /*[ COLORED_END ]*/ : 0; index += /*[ NODE_SIZE(node.parent, colorsOut) EXAMPLE_START ]*/ node.parent.nodeSize(colorsOut) /*[ EXAMPLE_END ]*/; } } return index; } /** * Find the index of the specified element * * @param firstIndex true to return the index of the first occurrence of the * specified element, or false for the last index. * @param simulated true to return an index value even if the element is not * found. Otherwise -1 is returned. * @return an index, or -1 if simulated is false and there exists no * element x in this tree such that * BciiTree.getComparator().compare(x, element) == 0. */ public int indexOfValue(T0 element, boolean firstIndex, boolean simulated, byte colorsOut) { int result = 0; boolean found = false; // go deep, looking for our node of interest /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ node = root; while(true) { if(node == null) { if(found && !firstIndex) result--; if(found || simulated) return result; else return -1; } // figure out if the value is left, center or right int comparison = comparator.compare(element, node.get()); // recurse on the left if(comparison < 0) { node = node.left; continue; } /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ nodeLeft = node.left; // the result is in the centre if(comparison == 0) { found = true; // recurse deeper on the left, looking for the first left match if(firstIndex) { node = nodeLeft; continue; } } // recurse on the right, increment result by left size and center size result += nodeLeft != null ? nodeLeft./*[ COLORED_START(count1) ]*/ size(colorsOut) /*[ COLORED_END ]*/ : 0; result += /*[ NODE_SIZE(node, colorsOut) EXAMPLE_START ]*/ node.nodeSize(colorsOut) /*[ EXAMPLE_END ]*/; node = node.right; } } /** * Convert one index into another. */ public int convertIndexColor(int index, byte indexColors, byte colorsOut) { if(root == null) { if(index == 0) return 0; else throw new IndexOutOfBoundsException(); } int result = 0; // go deep, looking for our node of interest /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ node = root; while(true) { assert(node != null); assert(index >= 0); // figure out the layout of this node /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ nodeLeft = node.left; int leftSize = nodeLeft != null ? nodeLeft./*[ COLORED_START(count1) ]*/ size(indexColors) /*[ COLORED_END ]*/ : 0; // recurse on the left if(index < leftSize) { node = nodeLeft; continue; // increment by the count on the left } else { if(nodeLeft != null) result += nodeLeft./*[ COLORED_START(count1) ]*/ size(colorsOut) /*[ COLORED_END ]*/; index -= leftSize; } // the result is in the centre int size = /*[ NODE_SIZE(node, indexColors) EXAMPLE_START ]*/ node.nodeSize(indexColors) /*[ EXAMPLE_END ]*/; if(index < size) { // we're on a node of the same color, return the adjusted index if(/*[ COLORED_START(true) ]*/ (colorsOut & node.color) > 0 /*[ COLORED_END ]*/) { result += index; // we're on a node of a different color, return the previous node of the requested color } else { result -= 1; } return result; // increment by the count in the centre } else { result += /*[ NODE_SIZE(node, colorsOut) EXAMPLE_START ]*/ node.nodeSize(colorsOut) /*[ EXAMPLE_END ]*/; index -= size; } // the result is on the right node = node.right; } } /** * The size of the tree for the specified colors. */ public int size(/*[ COLORED_START ]*/ byte colors /*[ COLORED_END ]*/) { if(root == null) return 0; else return root./*[ COLORED_START(count1) ]*/ size(colors) /*[ COLORED_END ]*/; } /** * Print this tree as a list of values. */ @Override public String toString() { if(root == null) return ""; return root.toString(/*[ COLORED_START ]*/ coder.getColors() /*[ COLORED_END ]*/); } /** * Print this tree as a list of colors, removing all hierarchy. */ /*[ COLORED_START ]*/ public String asSequenceOfColors() { if(root == null) return ""; // print it flattened, like a list of colors StringBuffer result = new StringBuffer(); for(BciiNode n = firstNode(); n != null; n = next(n)) { Object color = coder.getColors().get(colorAsIndex(n.color)); for(/*[ WIDE_NODES_START(true) ]*/ int i = 0; i < n.size; i++/*[ WIDE_NODES_END ]*/) { result.append(color); } } return result.toString(); } /*[ COLORED_END ]*/ /** * Find the next node in the tree, working from left to right. */ public static /*[ TYPELIST_START ]*/ /*[ TYPELIST_END ]*/ /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ next(/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ node) { // if this node has a right subtree, it's the leftmost node in that subtree if(node.right != null) { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ child = node.right; while(child.left != null) { child = child.left; } return child; // otherwise its the nearest ancestor where I'm in the left subtree } else { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ ancestor = node; while(ancestor.parent != null && ancestor.parent.right == ancestor) { ancestor = ancestor.parent; } return ancestor.parent; } } /** * Find the previous node in the tree, working from right to left. */ public static /*[ TYPELIST_START ]*/ /*[ TYPELIST_END ]*/ /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ previous(/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ node) { // if this node has a left subtree, it's the rightmost node in that subtree if(node.left != null) { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ child = node.left; while(child.right != null) { child = child.right; } return child; // otherwise its the nearest ancestor where I'm in the right subtree } else { /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ ancestor = node; while(ancestor.parent != null && ancestor.parent.left == ancestor) { ancestor = ancestor.parent; } return ancestor.parent; } } /** * Find the leftmost child in this subtree. */ /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ firstNode() { if(root == null) return null; /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ result = root; while(result.left != null) { result = result.left; } return result; } /** * @return true if this tree is structurally valid */ private boolean valid() { // walk through all nodes in the tree, looking for something invalid for(/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ node = firstNode(); node != null; node = next(node)) { // sizes (counts) are valid /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `int originalCounti(i) = node.counti(i); ') GENERATED_CODE_END EXAMPLE_START ]*/ int originalCount1 = node.count1; int originalCount2 = node.count2; int originalCount4 = node.count4; /*[ EXAMPLE_END ]*/ /*[ REFRESH_COUNTS(node) ]*/ node.refreshCounts(); /*[ EXAMPLE_END ]*/ /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `assert(originalCounti(i) == node.counti(i)) : "Incorrect count i on node: \n" + node + "\n Expected " + node.counti(i) + " but was " + originalCounti(i); ') GENERATED_CODE_END EXAMPLE_START ]*/ assert(originalCount1 == node.count1) : "Incorrect count 1 on node: \n" + node + "\n Expected " + node.count1 + " but was " + originalCount1; assert(originalCount2 == node.count2) : "Incorrect count 2 on node: \n" + node + "\n Expected " + node.count2 + " but was " + originalCount2; assert(originalCount4 == node.count4) : "Incorrect count 4 on node: \n" + node + "\n Expected " + node.count4 + " but was " + originalCount4; /*[ EXAMPLE_END ]*/ // heights are valid int leftHeight = node.left != null ? node.left.height : 0; int rightHeight = node.right != null ? node.right.height : 0; assert(Math.max(leftHeight, rightHeight) + 1 == node.height); // left child's parent is this assert(node.left == null || node.left.parent == node); // right child's parent is this assert(node.right == null || node.right.parent == node); // tree is AVL assert(Math.abs(leftHeight - rightHeight) < 2) : "Subtree is not AVL: \n" + node; } // we're valid return true; } /** * Convert the specified color value (such as 1, 2, 4, 8, 16 etc.) into an * index value (such as 0, 1, 2, 3, 4 etc. ). */ static final int colorAsIndex(byte color) { switch(color) { case 1: return 0; case 2: return 1; case 4: return 2; case 8: return 3; case 16: return 4; case 32: return 5; case 64: return 6; } throw new IllegalArgumentException(); } } /*[ END_M4_JAVA ]*/ ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/FourColorTreeIterator.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/FourColorTreeItera0000644000175000017500000002266412106516370033002 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt.barcode2; import java.util.NoSuchElementException; /* # some M4 Macros that make it easy to use m4 with Java M4 Macros # define a function NODE_WIDTH(boolean) to get the node's size for this color # define a function NODE_SIZE(node, colors) to no node.nodeSize() # define a function to refresh counts # multiple values */ /*[ BEGIN_M4_JAVA ]*/ /** * Iterate through a {@link FourColorTree}, one element at a time. * *

      We should consider adding the following enhancements to this class: *

    • writing methods, such as set() and remove(). *
    • a default color, specified at construction time, that shall always be * used as the implicit parameter to overloaded versions of {@link #hasNext} * and {@link #next}. * * @author Jesse Wilson */ public class FourColorTreeIterator < T0> { int count1; int count2; int count4; int count8; private FourColorTree < T0> tree; private FourColorNode < T0> node; private int index; public FourColorTreeIterator/**/(FourColorTree < T0> tree) { this(tree, 0, (byte)0); } /** * Create an iterator starting at the specified index. * * @param tree the tree to iterate * @param nextIndex the index to be returned after calling {@link #next next()}. * @param nextIndexColors the colors to interpret nextIndex in terms of */ public FourColorTreeIterator/**/(FourColorTree < T0> tree, int nextIndex, byte nextIndexColors) { this.tree = tree; // if the start is, we need to find the node in the tree if(nextIndex != 0) { int currentIndex = nextIndex - 1; this.node = ( FourColorNode < T0> )tree.get(currentIndex , nextIndexColors ); // find the counts count1 = tree.convertIndexColor(currentIndex, nextIndexColors, (byte)1) + (node.color == 1 ? 0 : 1); count2 = tree.convertIndexColor(currentIndex, nextIndexColors, (byte)2) + (node.color == 2 ? 0 : 1); count4 = tree.convertIndexColor(currentIndex, nextIndexColors, (byte)4) + (node.color == 4 ? 0 : 1); count8 = tree.convertIndexColor(currentIndex, nextIndexColors, (byte)8) + (node.color == 8 ? 0 : 1); // find out the index in the node if(node.color == 1) this.index = count1 - tree.indexOfNode(this.node, (byte)1); if(node.color == 2) this.index = count2 - tree.indexOfNode(this.node, (byte)2); if(node.color == 4) this.index = count4 - tree.indexOfNode(this.node, (byte)4); if(node.color == 8) this.index = count8 - tree.indexOfNode(this.node, (byte)8); // just start before the beginning of the tree } else { this.node = null; this.index = 0; } } /** * Create a {@link FourColorTreeIterator} exactly the same as this one. * The iterators will be backed by the same tree but maintain * separate cursors into the tree. */ public FourColorTreeIterator < T0> copy() { FourColorTreeIterator < T0> result = new FourColorTreeIterator < T0> (tree); result.count1 = this.count1; result.count2 = this.count2; result.count4 = this.count4; result.count8 = this.count8; result.node = node; result.index = index; return result; } /** * @return true if there's an element of the specified color in * this tree following the current element. */ public boolean hasNext( byte colors ) { if(node == null) { return tree.size( colors ) > 0; } else if( (colors & node.color) != 0 ) { return index( colors ) < tree.size( colors ) - 1; } else { return index( colors ) < tree.size( colors ); } } /** * @return true if there's a node of the specified color in this * tree following the current node. */ public boolean hasNextNode( byte colors ) { if(node == null) { return tree.size( colors ) > 0; } else { return nodeEndIndex( colors ) < tree.size( colors ); } } /** * Step to the next element. */ public void next( byte colors ) { if(!hasNext( colors )) { throw new NoSuchElementException(); } // start at the first node in the tree if(node == null) { node = tree.firstNode(); index = 0; if((node.color & colors) != 0) return; // increment within the current node } else if( (node.color & colors) != 0 && index < node.size - 1) { if(node.color == 1) count1++; if(node.color == 2) count2++; if(node.color == 4) count4++; if(node.color == 8) count8++; index++; return; } // scan through the nodes, looking for the first one of the right color while(true) { if(node.color == 1) count1 += node.size - index; if(node.color == 2) count2 += node.size - index; if(node.color == 4) count4 += node.size - index; if(node.color == 8) count8 += node.size - index; node = FourColorTree.next(node); index = 0; // we've found a node that meet our requirements, so return if((node.color & colors) != 0) break; } } /** * Step to the next node. */ public void nextNode( byte colors ) { if(!hasNextNode( colors )) { throw new NoSuchElementException(); } // start at the first node in the tree if(node == null) { node = tree.firstNode(); index = 0; if((node.color & colors) != 0) return; } // scan through the nodes, looking for the first one of the right color while(true) { if(node.color == 1) count1 += node.size - index; if(node.color == 2) count2 += node.size - index; if(node.color == 4) count4 += node.size - index; if(node.color == 8) count8 += node.size - index; node = FourColorTree.next(node); index = 0; // we've found a node that meet our requirements, so return if((node.color & colors) != 0) break; } } /** * Get the size of the current node, or 0 if it's color doesn't match those * specified. */ public int nodeSize( byte colors ) { if( (node.color & colors) != 0 ) { return node.size ; } else { return 0; } } /** * The color of the current element. */ public byte color() { if(node == null) throw new IllegalStateException(); return node.color; } /** * Expected values for index should be in the range ( 0, size() - 1 ) */ public int index( byte colors ) { if(node == null) throw new NoSuchElementException(); // total the values of the specified array for the specified colors. int result = 0; if((colors & 1) != 0) result += count1; if((colors & 2) != 0) result += count2; if((colors & 4) != 0) result += count4; if((colors & 8) != 0) result += count8; return result; } /** * Get the index of the current node's start. */ public int nodeStartIndex( byte colors ) { if(node == null) throw new NoSuchElementException(); // the count of all nodes prior to this one int result = 0; // this should merely be the sum of each count if((colors & 1) != 0) result += count1; if((colors & 2) != 0) result += count2; if((colors & 4) != 0) result += count4; if((colors & 8) != 0) result += count8; // subtract the count of anything in the current node which we may // have included inadvertently if( (node.color & colors) != 0 ) { result -= index; } return result; } /** * Get the index of the node immediately following the current. Expected * values are in the range ( 1, size() ) */ public int nodeEndIndex( byte colors ) { if(node == null) throw new NoSuchElementException(); // the count of all nodes previous return nodeStartIndex( colors ) + nodeSize( colors ); } public T0 value() { if(node == null) throw new IllegalStateException(); return node.get(); } public Element node() { if(node == null) throw new IllegalStateException(); return node; } } /*[ END_M4_JAVA ]*/ libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/BciiNode.java0000644000175000017500000002107212106516370031647 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt.barcode2; import java.util.Arrays; import java.util.List; /* m4_include(source/ca/odell/glazedlists/impl/adt/barcode2/JavaMacros.m4) m4_include(source/ca/odell/glazedlists/impl/adt/barcode2/TreeMacros.m4) */ /*[ BEGIN_M4_JAVA ]*/ /** * A node in a tree which supports both a value and compressed nodes that * contain a size, useful for index offsetting. * *

      Note that the counts summary member is created lazily when * this node is given children. This causes the code to be less easy to read, * but it means we can put off about a huge number of object allocations since * 50% of the nodes in an arbitrary tree are leaf nodes, and these leaf nodes * now don't have counts. * * @author Jesse Wilson */ class /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ implements Element { /** the number of elements of each color in this subtree */ /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `int counti(i); ') GENERATED_CODE_END EXAMPLE_START ]*/ int count1; int count2; int count4; /*[ EXAMPLE_END ]*/ /*[ COLORED_START ]*/ /** the node's color */ byte color; /*[ COLORED_END ]*/ /** the node's value */ /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_TYPE_INDEX, `Ti(i) ti(i); ') GENERATED_CODE_END EXAMPLE_START ]*/ T0 t0; T1 t1; /*[ EXAMPLE_END ]*/ /*[ WIDE_NODES_START ]*/ /** the size of this node */ int size; /*[ WIDE_NODES_END ]*/ /** values for managing the node within the tree */ byte height; /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ left, right, parent; /** whether this node is consistent in the sorting order */ int sorted = SORTED; /** * Create a new node. * * @param color a bitmask value such as 1, 2, 4, 8 or 16. * @param size the size of the node * @param value the value of the node * @param parent the parent node in the tree, or null for the * root node. */ public BciiNode/**/(/*[ COLORED_START ]*/ byte color, /*[ COLORED_END ]*/ int size, T0 value, /*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/ parent) { /*[ COLORED_START ]*/ assert(BciiTree.colorAsIndex(color) >= 0 && BciiTree.colorAsIndex(color) < 7); this.color = color; /*[ COLORED_END ]*/ /*[ WIDE_NODES_START(assert(size == 1);) ]*/ this.size = size; /*[ WIDE_NODES_END ]*/ this.t0 = value; this.height = 1; this.parent = parent; /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `m4_ifelse(VAR_COLOUR_COUNT,`1',`count1 += size; ', `if(color == indexToBit(i)) counti(i) += size; ')') GENERATED_CODE_END EXAMPLE_START ]*/ if(color == 1) count1 += size; if(color == 2) count2 += size; if(color == 4) count4 += size; /*[ EXAMPLE_END ]*/ } /** * Get the value of this element. */ public T0 get() { return t0; } /** * Set the value of this element. */ public void set(T0 value) { this.t0 = value; } /** access the node's values */ /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_TYPE_INDEX, ``public T''i ``get''i``() { return t''i``; } public void set''i``(T''i`` value) { this.t''i ``= value; } '') GENERATED_CODE_END EXAMPLE_START ]*/ /*[ EXAMPLE_END ]*/ /** * Get the color of this element. */ public byte getColor() { return /*[ COLORED_START(1) ]*/ color /*[ COLORED_END ]*/; } /** * The size of this entire node, including the left child, this node * and the right child. */ final int size(byte colors) { // total the values of the specified array for the specified colors. int result = 0; /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `if((colors & indexToBit(i)) != 0) result += counti(i); ') GENERATED_CODE_END EXAMPLE_START ]*/ if((colors & 1) != 0) result += count1; if((colors & 2) != 0) result += count2; if((colors & 4) != 0) result += count4; /*[ EXAMPLE_END ]*/ return result; } /*[ COLORED_START ]*/ /** * The size of the node for the specified colors. */ final int nodeSize(byte colors) { return (colors & color) > 0 ? size : 0; } /*[ COLORED_END ]*/ /** * Update the counts member variable by examining the counts of * the child nodes and the size member variable. */ final void refreshCounts(/*[ WIDE_NODES_START(boolean countSelf) WIDE_NODES_END ]*/) { /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `counti(i) = 0; ') GENERATED_CODE_END EXAMPLE_START ]*/ count1 = 0; count2 = 0; count4 = 0; /*[ EXAMPLE_END ]*/ // left child if(left != null) { /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `counti(i) += left.counti(i); ') GENERATED_CODE_END EXAMPLE_START ]*/ count1 += left.count1; count2 += left.count2; count4 += left.count4; /*[ EXAMPLE_END ]*/ } // right child if(right != null) { /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `counti(i) += right.counti(i); ') GENERATED_CODE_END EXAMPLE_START ]*/ count1 += right.count1; count2 += right.count2; count4 += right.count4; /*[ EXAMPLE_END ]*/ } // this node /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `m4_ifelse(VAR_COLOUR_COUNT,`1',counti(i)` += 'NODE_WIDTH(countSelf)`; ', `if(color == 'indexToBit(i)`) 'counti(i)` += 'NODE_WIDTH(countSelf)`; ')') GENERATED_CODE_END EXAMPLE_START ]*/ if(color == 1) count1 += size; if(color == 2) count2 += size; if(color == 4) count4 += size; /*[ EXAMPLE_END ]*/ } /** {@inheritDoc} */ @Override public String toString() { return toString(Arrays.asList(new String[] { "A", "B", "C", "D", "E", "F", "G", "H"})); } /** * Write this node out as a String, using the specified colors to write * each of the node values. */ String toString(List colors) { StringBuffer result = new StringBuffer(); asTree(0, result, colors); return result.toString(); } /** * Dump this node as a String for diagnostic and debugging purposes. */ void asTree(int indentation, StringBuffer out, List colors) { // write the left subtree if(left != null) left.asTree(indentation + 1, out, colors); // write this node for(int i = 0; i < indentation; i++) { out.append(" "); } /*[ COLORED_START ]*/ out.append(colors.get(BciiTree.colorAsIndex(color))); /*[ COLORED_END ]*/ /*[ WIDE_NODES_START ]*/ out.append(" [").append(size).append("]"); /*[ WIDE_NODES_END ]*/ if(t0 != null) { out.append(": "); if(t0 instanceof BciiNode) { out.append(""); } else { out.append(t0); } } out.append("\n"); // write the right subtree if(right != null) right.asTree(indentation + 1, out, colors); } /** * Toggle whether this node is sorted. */ public void setSorted(int sorted) { this.sorted = sorted; } /** * Get whether the value of this node is greater than the previous node * and less than the next node. This is useful to have occasional unsorted * elements in an otherwise sorted collection, such as what happens when the * user expects order to be both sorted and stable during edits which would * otherwise change the sorting order. */ public int getSorted() { return sorted; } /** {@inheritDoc} */ public Element next() { return BciiTree.next(this); } /** {@inheritDoc} */ public Element previous() { return BciiTree.previous(this); } } /*[ END_M4_JAVA ]*/ libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/SimpleTree.java0000644000175000017500000011204312106516370032243 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt.barcode2; import ca.odell.glazedlists.GlazedLists; import java.util.ArrayList; import java.util.Comparator; import java.util.List; /* # some M4 Macros that make it easy to use m4 with Java M4 Macros # define a function NODE_WIDTH(boolean) to get the node's size for this color # define a function NODE_SIZE(node, colors) to no node.nodeSize() # define a function to refresh counts # multiple values */ /*[ BEGIN_M4_JAVA ]*/ /** * Our second generation tree class. * *

      Currently the API for this class is fairly low-level, particularly the * use of bytes as color values. This is an implementation detail, * exposed to maximize performance. Wherever necessary, consider creating a * facade around this Tree class that provides methods more appropriate * for your particular application. * *

      This is a prototype replacement for the Barcode class that adds support * for up to seven different colors. As well, this supports values in the node. * It will hopefully also replace our IndexedTree class. This class * is designed after those two classes and hopefully improves upon them in * a few interesting ways: *

    • Avoid recursion wherever possible to increase performance *
    • Be generic to simplify handling of black/white values. These can be * handled in one case rather than one case for each. *
    • Make the node class a dataholder only and put most of the logic in * the tree class. This allows us to share different Node classes with * different memory requirements, while using the same logic class * *

      This class came into being so we could use a tree to replace * ListEventBlocks, which has only mediocre performance, particularly * due to having to sort elements. As well, we might be able to keep a moved * value in the tree, to support moved elements in ListEvents. * * @author Jesse Wilson */ public class SimpleTree < T0> { /** the tree's root, or null for an empty tree */ private SimpleNode < T0> root = null; /** * a list to add all nodes to that must be removed from * the tree. The nodes are removed only after the tree has been modified, * which allows us a chance to do rotations without losing our position * in the tree. */ private final List< SimpleNode < T0> > zeroQueue = new ArrayList< SimpleNode < T0> >(); /** * The comparator to use when performing ordering operations on the tree. * Sometimes this tree will not be sorted, so in such situations this * comparator will not be used. */ private final Comparator comparator; /** * @param coder specifies the node colors * @param comparator the comparator to use when ordering values within the * tree. If this tree is unsorted, use the one-argument constructor. */ public SimpleTree/**/( Comparator comparator) { if(comparator == null) throw new NullPointerException("Comparator cannot be null."); this.comparator = comparator; } /** * @param coder specifies the node colors */ public SimpleTree/**/( ) { this( (Comparator)GlazedLists.comparableComparator()); } public Comparator getComparator() { return comparator; } /** * Get the tree element at the specified index relative to the specified index * colors. * *

      This method is an hotspot, so its crucial that it run as efficiently * as possible. */ public Element get(int index ) { if(root == null) throw new IndexOutOfBoundsException(); // go deep, looking for our node of interest SimpleNode < T0> node = root; while(true) { assert(node != null); assert(index >= 0); // recurse on the left SimpleNode < T0> nodeLeft = node.left; int leftSize = nodeLeft != null ? nodeLeft. count1 : 0; if(index < leftSize) { node = nodeLeft; continue; } else { index -= leftSize; } // the result is in the centre int size = 1 ; if(index < size) { return node; } else { index -= size; } // the result is on the right node = node.right; } } /** * Add a tree node at the specified index relative to the specified index * colors. The inserted nodes' color, value and size are specified. * *

      Note that nodes with null values will never be * merged together to allow those nodes to be assigned other values later. * * @param size the size of the node to insert. * @param index the location into this tree to insert at * @param indexColors the colors that index is relative to. This should be * all colors in the tree ORed together for the entire tree. * @param value the node value. If non-null, the node may be * combined with other nodes of the same color and value. null * valued nodes will never be combined with each other. * @return the element the specified value was inserted into. This is non-null * unless the size parameter is 0, in which case the result is always * null. */ public Element add(int index, T0 value, int size) { assert(index >= 0); assert(index <= size( )); assert(size >= 0); if(this.root == null) { if(index != 0) throw new IndexOutOfBoundsException(); this.root = new SimpleNode < T0> ( size, value, null); assert(valid()); return this.root; } else { SimpleNode < T0> inserted = insertIntoSubtree(root, index, value, size); assert(valid()); return inserted; } } /** * @param parent the subtree to insert into, must not be null. * @param index the color index to insert at * @param indexColors a bitmask of all colors that the index is defined in * terms of. For example, if this is determined in terms of colors 4, 8 * and 32, then the value here should be 44 (32 + 8 + 4). * @param color a bitmask value such as 1, 2, 4, 8, 16, 32, 64 or 128. * @param value the object to hold in the inserted node. * @param size the size of the inserted node, with respect to indices. * @return the inserted node, or the modified node if this insert simply * increased the size of an existing node. */ private SimpleNode < T0> insertIntoSubtree( SimpleNode < T0> parent, int index, T0 value, int size) { while(true) { assert(parent != null); assert(index >= 0); // figure out the layout of this node SimpleNode < T0> parentLeft = parent.left; int parentLeftSize = parentLeft != null ? parentLeft. count1 : 0; int parentRightStartIndex = parentLeftSize + 1 ; // the first thing we want to try is to merge this value into the // current node, since that's the cheapest thing to do: if( false && value == parent.t0 && value != null) { if(index >= parentLeftSize && index <= parentRightStartIndex) { fixCountsThruRoot(parent, size); return parent; } } // we can insert on the left if(index <= parentLeftSize) { // as a new left child if(parentLeft == null) { SimpleNode < T0> inserted = new SimpleNode < T0> ( size, value, parent); parent.left = inserted; fixCountsThruRoot(parent, size); fixHeightPostChange(parent, false); return inserted; // recurse on the left } else { parent = parentLeft; continue; } } // we need to insert in the centre. This works by splitting in the // centre, and inserting the value if(index < parentRightStartIndex) { int parentRightHalfSize = parentRightStartIndex - index; fixCountsThruRoot(parent, -parentRightHalfSize); // insert as null first to make sure this doesn't get merged back Element inserted = insertIntoSubtree(parent, index, null, parentRightHalfSize); inserted.set(parent.t0); // recalculate parentRightStartIndex, since that should have // changed by now. this will then go on to insert on the right parentRightStartIndex = parentLeftSize + 1 ; } // on the right right: { int parentSize = parent. count1 ; assert(index <= parentSize); SimpleNode < T0> parentRight = parent.right; // as a right child if(parentRight == null) { SimpleNode < T0> inserted = new SimpleNode < T0> ( size, value, parent); parent.right = inserted; fixCountsThruRoot(parent, size); fixHeightPostChange(parent, false); return inserted; // recurse on the right } else { parent = parentRight; index -= parentRightStartIndex; } } } } /** * Add a tree node in sorted order. * * @param size the size of the node to insert. * @param value the node value. If non-null, the node may be * combined with other nodes of the same color and value. null * valued nodes will never be combined with each other. * @return the element the specified value was inserted into. This is non-null * unless the size parameter is 0, in which case the result is always * null. */ public Element addInSortedOrder(byte color, T0 value, int size) { assert(size >= 0); if(this.root == null) { this.root = new SimpleNode < T0> ( size, value, null); assert(valid()); return this.root; } else { SimpleNode < T0> inserted = insertIntoSubtreeInSortedOrder(root, value, size); assert(valid()); return inserted; } } /** * @param parent the subtree to insert into, must not be null. * @param color a bitmask value such as 1, 2, 4, 8, 16, 32, 64 or 128. * @param value the object to hold in the inserted node. * @param size the size of the inserted node, with respect to indices. * @return the inserted node, or the modified node if this insert simply * increased the size of an existing node. */ private SimpleNode < T0> insertIntoSubtreeInSortedOrder( SimpleNode < T0> parent, T0 value, int size) { while(true) { assert(parent != null); // calculating the sort side is a little tricky since we can have // unsorted nodes in the tree. we just look for a neighbour (ie next) // that is sorted, and compare with that int sortSide; for( SimpleNode < T0> currentFollower = parent; true; currentFollower = next(currentFollower)) { // we've hit the end of the list, assume the element is on the left side if(currentFollower == null) { sortSide = -1; break; // we've found a comparable node, use it } else if(currentFollower.sorted == Element.SORTED) { sortSide = comparator.compare(value, currentFollower.t0); break; } } // the first thing we want to try is to merge this value into the // current node, since that's the cheapest thing to do: if( false && sortSide == 0 && value == parent.t0 && value != null) { fixCountsThruRoot(parent, size); return parent; } // insert on the left... boolean insertOnLeft = false; insertOnLeft = insertOnLeft || sortSide < 0; insertOnLeft = insertOnLeft || sortSide == 0 && parent.left == null; insertOnLeft = insertOnLeft || sortSide == 0 && parent.right != null && parent.left.height < parent.right.height; if(insertOnLeft) { SimpleNode < T0> parentLeft = parent.left; // as a new left child if(parentLeft == null) { SimpleNode < T0> inserted = new SimpleNode < T0> ( size, value, parent); parent.left = inserted; fixCountsThruRoot(parent, size); fixHeightPostChange(parent, false); return inserted; // recurse on the left } else { parent = parentLeft; continue; } // ...or on the right } else { SimpleNode < T0> parentRight = parent.right; // as a right child if(parentRight == null) { SimpleNode < T0> inserted = new SimpleNode < T0> ( size, value, parent); parent.right = inserted; fixCountsThruRoot(parent, size); fixHeightPostChange(parent, false); return inserted; // recurse on the right } else { parent = parentRight; } } } } /** * Adjust counts for all nodes (including the specified node) up the tree * to the root. The counts of the specified color are adjusted by delta * (which may be positive or negative). */ private final void fixCountsThruRoot( SimpleNode < T0> node, int delta) { for( ; node != null; node = node.parent) node.count1 += delta; } /** * Fix the height of the specified ancestor after inserting a child node. * This method short circuits when it finds the first node where the size * has not changed. * * @param node the root of a changed subtree. This shouldn't be called * on inserted nodes, but rather their parent nodes, since only * the parent nodes sizes will be changing. * @param allTheWayToRoot true to walk up the tree all the way * to the tree's root, or false to walk up until the height * is unchanged. We go to the root on a delete, since the rotate is on * the opposite side of the tree, whereas on an insert we only delete * as far as necessary. */ private final void fixHeightPostChange( SimpleNode < T0> node, boolean allTheWayToRoot) { // update the height for(; node != null; node = node.parent) { byte leftHeight = node.left != null ? node.left.height : 0; byte rightHeight = node.right != null ? node.right.height : 0; // rotate left? if(leftHeight > rightHeight && (leftHeight - rightHeight == 2)) { // do we need to rotate the left child first? int leftLeftHeight = node.left.left != null ? node.left.left.height : 0; int leftRightHeight = node.left.right != null ? node.left.right.height : 0; if(leftRightHeight > leftLeftHeight) { rotateRight(node.left); } // finally rotate right node = rotateLeft(node); // rotate right? } else if(rightHeight > leftHeight && (rightHeight - leftHeight == 2)) { // do we need to rotate the right child first? int rightLeftHeight = node.right.left != null ? node.right.left.height : 0; int rightRightHeight = node.right.right != null ? node.right.right.height : 0; if(rightLeftHeight > rightRightHeight) { rotateLeft(node.right); } // finally rotate left node = rotateRight(node); } // update the node height leftHeight = node.left != null ? node.left.height : 0; rightHeight = node.right != null ? node.right.height : 0; byte newNodeHeight = (byte) (Math.max(leftHeight, rightHeight) + 1); // if the height doesn't need updating, we might just be done! if(!allTheWayToRoot && node.height == newNodeHeight) return; // otherwise change the height and keep rotating node.height = newNodeHeight; } } /** * Perform an AVL rotation of the tree. * * D B * / \ ROTATE / \ * B E LEFT AT A D * / \ NODE D / \ * A C C E * * @return the new root of the subtree */ private final SimpleNode < T0> rotateLeft( SimpleNode < T0> subtreeRoot) { assert(subtreeRoot.left != null); // subtreeRoot is D // newSubtreeRoot is B SimpleNode < T0> newSubtreeRoot = subtreeRoot.left; // modify the links between nodes // attach C as a child of to D subtreeRoot.left = newSubtreeRoot.right; if(newSubtreeRoot.right != null) newSubtreeRoot.right.parent = subtreeRoot; // link b as the new root node for this subtree newSubtreeRoot.parent = subtreeRoot.parent; if(newSubtreeRoot.parent != null) { if(newSubtreeRoot.parent.left == subtreeRoot) newSubtreeRoot.parent.left = newSubtreeRoot; else if(newSubtreeRoot.parent.right == subtreeRoot) newSubtreeRoot.parent.right = newSubtreeRoot; else throw new IllegalStateException(); } else { root = newSubtreeRoot; } // attach D as a child of B newSubtreeRoot.right = subtreeRoot; subtreeRoot.parent = newSubtreeRoot; // update height and counts of the old subtree root byte subtreeRootLeftHeight = subtreeRoot.left != null ? subtreeRoot.left.height : 0; byte subtreeRootRightHeight = subtreeRoot.right != null ? subtreeRoot.right.height : 0; subtreeRoot.height = (byte)(Math.max(subtreeRootLeftHeight, subtreeRootRightHeight) + 1); subtreeRoot.refreshCounts(!zeroQueue.contains(subtreeRoot)); // update height and counts of the new subtree root byte newSubtreeRootLeftHeight = newSubtreeRoot.left != null ? newSubtreeRoot.left.height : 0; byte newSubtreeRootRightHeight = newSubtreeRoot.right != null ? newSubtreeRoot.right.height : 0; newSubtreeRoot.height = (byte)(Math.max(newSubtreeRootLeftHeight, newSubtreeRootRightHeight) + 1); newSubtreeRoot.refreshCounts(!zeroQueue.contains(newSubtreeRoot)); return newSubtreeRoot; } private final SimpleNode < T0> rotateRight( SimpleNode < T0> subtreeRoot) { assert(subtreeRoot.right != null); // subtreeRoot is D // newSubtreeRoot is B SimpleNode < T0> newSubtreeRoot = subtreeRoot.right; // modify the links between nodes // attach C as a child of to D subtreeRoot.right = newSubtreeRoot.left; if(newSubtreeRoot.left != null) newSubtreeRoot.left.parent = subtreeRoot; // link b as the new root node for this subtree newSubtreeRoot.parent = subtreeRoot.parent; if(newSubtreeRoot.parent != null) { if(newSubtreeRoot.parent.left == subtreeRoot) newSubtreeRoot.parent.left = newSubtreeRoot; else if(newSubtreeRoot.parent.right == subtreeRoot) newSubtreeRoot.parent.right = newSubtreeRoot; else throw new IllegalStateException(); } else { root = newSubtreeRoot; } // attach D as a child of B newSubtreeRoot.left = subtreeRoot; subtreeRoot.parent = newSubtreeRoot; // update height and counts of the old subtree root byte subtreeRootLeftHeight = subtreeRoot.left != null ? subtreeRoot.left.height : 0; byte subtreeRootRightHeight = subtreeRoot.right != null ? subtreeRoot.right.height : 0; subtreeRoot.height = (byte)(Math.max(subtreeRootLeftHeight, subtreeRootRightHeight) + 1); subtreeRoot.refreshCounts(!zeroQueue.contains(subtreeRoot)); // update height and counts of the new subtree root byte newSubtreeRootLeftHeight = newSubtreeRoot.left != null ? newSubtreeRoot.left.height : 0; byte newSubtreeRootRightHeight = newSubtreeRoot.right != null ? newSubtreeRoot.right.height : 0; newSubtreeRoot.height = (byte)(Math.max(newSubtreeRootLeftHeight, newSubtreeRootRightHeight) + 1); newSubtreeRoot.refreshCounts(!zeroQueue.contains(newSubtreeRoot)); return newSubtreeRoot; } /** * Remove the specified element from the tree outright. */ public void remove(Element element) { SimpleNode < T0> node = ( SimpleNode < T0> )element; assert(root != null); // delete the node by adding to the zero queue fixCountsThruRoot(node, -1 ); zeroQueue.add(node); drainZeroQueue(); assert(valid()); } /** * Remove size values at the specified index. Only values of the type * specified in indexColors will be removed. * *

      Note that if the two nodes on either side of the removed node could * be merged, they probably will not be merged by this implementation. This * is to simplify the implementation, but it means that when iterating a * tree, sometimes multiple nodes of the same color and value will be * encountered in sequence. */ public void remove(int index, int size) { if(size == 0) return; assert(index >= 0); assert(index + size <= size( )); assert(root != null); // remove values from the tree removeFromSubtree(root, index, size); // clean up any nodes that got deleted drainZeroQueue(); assert(valid()); } /** * Prune all nodes scheduled for deletion. */ private void drainZeroQueue() { for(int i = 0, size = zeroQueue.size(); i < size; i++) { SimpleNode < T0> node = zeroQueue.get(i); if(node.right == null) { replaceChild(node, node.left); } else if(node.left == null) { replaceChild(node, node.right); } else { node = replaceEmptyNodeWithChild(node); } } zeroQueue.clear(); } /** * Remove at the specified index in the specified subtree. This doesn't ever * remove any nodes of size zero, that's up to the caller to do after by * removing all nodes in the zeroQueue from the tree. */ private void removeFromSubtree( SimpleNode < T0> node, int index, int size) { while(size > 0) { assert(node != null); assert(index >= 0); // figure out the layout of this node SimpleNode < T0> nodeLeft = node.left; int leftSize = nodeLeft != null ? nodeLeft. count1 : 0; // delete on the left first if(index < leftSize) { // we can only remove part of our requirement on the left, so do // that part recursively if(index + size > leftSize) { int toRemove = leftSize - index; removeFromSubtree(nodeLeft, index, toRemove); size -= toRemove; leftSize -= toRemove; // we can do our full delete on the left side } else { node = nodeLeft; continue; } } assert(index >= leftSize); // delete in the centre int rightStartIndex = leftSize + 1 ; if(index < rightStartIndex) { int toRemove = Math.min(rightStartIndex - index, size); // decrement the appropriate counts all the way up size -= toRemove; rightStartIndex -= toRemove; fixCountsThruRoot(node, -toRemove); if( true ) { zeroQueue.add(node); } if(size == 0) return; } assert(index >= rightStartIndex); // delete on the right last index -= rightStartIndex; node = node.right; } } /** * Replace the specified node with the specified replacement. This does the * replacement, then walks up the tree to ensure heights are correct, so * the replacement node should have its height set first before this method * is called. */ private void replaceChild( SimpleNode < T0> node, SimpleNode < T0> replacement) { SimpleNode < T0> nodeParent = node.parent; // replace the root if(nodeParent == null) { assert(node == root); root = replacement; // replace on the left } else if(nodeParent.left == node) { nodeParent.left = replacement; // replace on the right } else if(nodeParent.right == node) { nodeParent.right = replacement; } // update the replacement's parent if(replacement != null) { replacement.parent = nodeParent; } // the height has changed, update that up the tree fixHeightPostChange(nodeParent, true); } /** * Replace the specified node with another node deeper in the tree. This * is necessary to maintain treeness through deletes. * *

      This implementation finds the largest node in the left subtree, * removes it, and puts it in the specified node's place. * * @return the replacement node */ private SimpleNode < T0> replaceEmptyNodeWithChild( SimpleNode < T0> toReplace) { assert(toReplace.left != null); assert(toReplace.right != null); // find the rightmost child on the leftside SimpleNode < T0> replacement = toReplace.left; while(replacement.right != null) { replacement = replacement.right; } assert(replacement.right == null); // remove that node from the tree fixCountsThruRoot(replacement, -1 ); replaceChild(replacement, replacement.left); // update the tree structure to point to the replacement replacement.left = toReplace.left; if(replacement.left != null) replacement.left.parent = replacement; replacement.right = toReplace.right; if(replacement.right != null) replacement.right.parent = replacement; replacement.height = toReplace.height; replacement.refreshCounts(!zeroQueue.contains(replacement)); replaceChild(toReplace, replacement); fixCountsThruRoot(replacement.parent, 1 ); return replacement; } /** * Replace all values at the specified index with the specified new value. * *

      Currently this uses a naive implementation of remove then add. If * it proves desirable, it may be worthwhile to optimize this implementation * with one that performs the remove and insert simultaneously, to save * on tree navigation. * * @return the element that was updated. This is non-null unless the size * parameter is 0, in which case the result is always null. */ public Element set(int index, T0 value, int size) { remove(index, size); return add(index, value, size); } /** * Remove all nodes from the tree. Note that this is much faster than calling * remove on all elements, since the structure can be discarded instead of * managed during the removal. */ public void clear() { root = null; } /** * Get the index of the specified element, counting only the colors * specified. * *

      This method is an hotspot, so its crucial that it run as efficiently * as possible. */ public int indexOfNode(Element element, byte colorsOut) { SimpleNode < T0> node = ( SimpleNode < T0> )element; // count all elements left of this node int index = node.left != null ? node.left. count1 : 0; // add all elements on the left, all the way to the root for( ; node.parent != null; node = node.parent) { if(node.parent.right == node) { index += node.parent.left != null ? node.parent.left. count1 : 0; index += 1 ; } } return index; } /** * Find the index of the specified element * * @param firstIndex true to return the index of the first occurrence of the * specified element, or false for the last index. * @param simulated true to return an index value even if the element is not * found. Otherwise -1 is returned. * @return an index, or -1 if simulated is false and there exists no * element x in this tree such that * SimpleTree.getComparator().compare(x, element) == 0. */ public int indexOfValue(T0 element, boolean firstIndex, boolean simulated, byte colorsOut) { int result = 0; boolean found = false; // go deep, looking for our node of interest SimpleNode < T0> node = root; while(true) { if(node == null) { if(found && !firstIndex) result--; if(found || simulated) return result; else return -1; } // figure out if the value is left, center or right int comparison = comparator.compare(element, node.get()); // recurse on the left if(comparison < 0) { node = node.left; continue; } SimpleNode < T0> nodeLeft = node.left; // the result is in the centre if(comparison == 0) { found = true; // recurse deeper on the left, looking for the first left match if(firstIndex) { node = nodeLeft; continue; } } // recurse on the right, increment result by left size and center size result += nodeLeft != null ? nodeLeft. count1 : 0; result += 1 ; node = node.right; } } /** * Convert one index into another. */ public int convertIndexColor(int index, byte indexColors, byte colorsOut) { if(root == null) { if(index == 0) return 0; else throw new IndexOutOfBoundsException(); } int result = 0; // go deep, looking for our node of interest SimpleNode < T0> node = root; while(true) { assert(node != null); assert(index >= 0); // figure out the layout of this node SimpleNode < T0> nodeLeft = node.left; int leftSize = nodeLeft != null ? nodeLeft. count1 : 0; // recurse on the left if(index < leftSize) { node = nodeLeft; continue; // increment by the count on the left } else { if(nodeLeft != null) result += nodeLeft. count1 ; index -= leftSize; } // the result is in the centre int size = 1 ; if(index < size) { // we're on a node of the same color, return the adjusted index if( true ) { result += index; // we're on a node of a different color, return the previous node of the requested color } else { result -= 1; } return result; // increment by the count in the centre } else { result += 1 ; index -= size; } // the result is on the right node = node.right; } } /** * The size of the tree for the specified colors. */ public int size( ) { if(root == null) return 0; else return root. count1 ; } /** * Print this tree as a list of values. */ @Override public String toString() { if(root == null) return ""; return root.toString( ); } /** * Print this tree as a list of colors, removing all hierarchy. */ /** * Find the next node in the tree, working from left to right. */ public static < T0> SimpleNode < T0> next( SimpleNode < T0> node) { // if this node has a right subtree, it's the leftmost node in that subtree if(node.right != null) { SimpleNode < T0> child = node.right; while(child.left != null) { child = child.left; } return child; // otherwise its the nearest ancestor where I'm in the left subtree } else { SimpleNode < T0> ancestor = node; while(ancestor.parent != null && ancestor.parent.right == ancestor) { ancestor = ancestor.parent; } return ancestor.parent; } } /** * Find the previous node in the tree, working from right to left. */ public static < T0> SimpleNode < T0> previous( SimpleNode < T0> node) { // if this node has a left subtree, it's the rightmost node in that subtree if(node.left != null) { SimpleNode < T0> child = node.left; while(child.right != null) { child = child.right; } return child; // otherwise its the nearest ancestor where I'm in the right subtree } else { SimpleNode < T0> ancestor = node; while(ancestor.parent != null && ancestor.parent.left == ancestor) { ancestor = ancestor.parent; } return ancestor.parent; } } /** * Find the leftmost child in this subtree. */ SimpleNode < T0> firstNode() { if(root == null) return null; SimpleNode < T0> result = root; while(result.left != null) { result = result.left; } return result; } /** * @return true if this tree is structurally valid */ private boolean valid() { // walk through all nodes in the tree, looking for something invalid for( SimpleNode < T0> node = firstNode(); node != null; node = next(node)) { // sizes (counts) are valid int originalCount1 = node.count1; node.refreshCounts(!zeroQueue.contains(node)); assert(originalCount1 == node.count1) : "Incorrect count 0 on node: \n" + node + "\n Expected " + node.count1 + " but was " + originalCount1; // heights are valid int leftHeight = node.left != null ? node.left.height : 0; int rightHeight = node.right != null ? node.right.height : 0; assert(Math.max(leftHeight, rightHeight) + 1 == node.height); // left child's parent is this assert(node.left == null || node.left.parent == node); // right child's parent is this assert(node.right == null || node.right.parent == node); // tree is AVL assert(Math.abs(leftHeight - rightHeight) < 2) : "Subtree is not AVL: \n" + node; } // we're valid return true; } /** * Convert the specified color value (such as 1, 2, 4, 8, 16 etc.) into an * index value (such as 0, 1, 2, 3, 4 etc. ). */ static final int colorAsIndex(byte color) { switch(color) { case 1: return 0; case 2: return 1; case 4: return 2; case 8: return 3; case 16: return 4; case 32: return 5; case 64: return 6; } throw new IllegalArgumentException(); } } /*[ END_M4_JAVA ]*/ ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/SimpleTreeIterator.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/SimpleTreeIterator0000644000175000017500000001574512106516370033050 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt.barcode2; import java.util.NoSuchElementException; /* # some M4 Macros that make it easy to use m4 with Java M4 Macros # define a function NODE_WIDTH(boolean) to get the node's size for this color # define a function NODE_SIZE(node, colors) to no node.nodeSize() # define a function to refresh counts # multiple values */ /*[ BEGIN_M4_JAVA ]*/ /** * Iterate through a {@link SimpleTree}, one element at a time. * *

      We should consider adding the following enhancements to this class: *

    • writing methods, such as set() and remove(). *
    • a default color, specified at construction time, that shall always be * used as the implicit parameter to overloaded versions of {@link #hasNext} * and {@link #next}. * * @author Jesse Wilson */ public class SimpleTreeIterator < T0> { int count1; private SimpleTree < T0> tree; private SimpleNode < T0> node; private int index; public SimpleTreeIterator/**/(SimpleTree < T0> tree) { this(tree, 0, (byte)0); } /** * Create an iterator starting at the specified index. * * @param tree the tree to iterate * @param nextIndex the index to be returned after calling {@link #next next()}. * @param nextIndexColors the colors to interpret nextIndex in terms of */ public SimpleTreeIterator/**/(SimpleTree < T0> tree, int nextIndex, byte nextIndexColors) { this.tree = tree; // if the start is, we need to find the node in the tree if(nextIndex != 0) { int currentIndex = nextIndex - 1; this.node = ( SimpleNode < T0> )tree.get(currentIndex ); // find the counts count1 = currentIndex; // find out the index in the node this.index = count1 - tree.indexOfNode(this.node, (byte)1); // just start before the beginning of the tree } else { this.node = null; this.index = 0; } } /** * Create a {@link SimpleTreeIterator} exactly the same as this one. * The iterators will be backed by the same tree but maintain * separate cursors into the tree. */ public SimpleTreeIterator < T0> copy() { SimpleTreeIterator < T0> result = new SimpleTreeIterator < T0> (tree); result.count1 = this.count1; result.node = node; result.index = index; return result; } /** * @return true if there's an element of the specified color in * this tree following the current element. */ public boolean hasNext( ) { if(node == null) { return tree.size( ) > 0; } else if( true ) { return index( ) < tree.size( ) - 1; } else { return index( ) < tree.size( ); } } /** * @return true if there's a node of the specified color in this * tree following the current node. */ public boolean hasNextNode( ) { if(node == null) { return tree.size( ) > 0; } else { return nodeEndIndex( ) < tree.size( ); } } /** * Step to the next element. */ public void next( ) { if(!hasNext( )) { throw new NoSuchElementException(); } // start at the first node in the tree if(node == null) { node = tree.firstNode(); index = 0; return; // increment within the current node } else if( index < 1 - 1) { count1++; index++; return; } // scan through the nodes, looking for the first one of the right color while(true) { count1 += 1 - index; node = SimpleTree.next(node); index = 0; // we've found a node that meet our requirements, so return break; } } /** * Step to the next node. */ public void nextNode( ) { if(!hasNextNode( )) { throw new NoSuchElementException(); } // start at the first node in the tree if(node == null) { node = tree.firstNode(); index = 0; return; } // scan through the nodes, looking for the first one of the right color while(true) { count1 += 1 - index; node = SimpleTree.next(node); index = 0; // we've found a node that meet our requirements, so return break; } } /** * Get the size of the current node, or 0 if it's color doesn't match those * specified. */ public int nodeSize( ) { if( true ) { return 1 ; } else { return 0; } } /** * Expected values for index should be in the range ( 0, size() - 1 ) */ public int index( ) { if(node == null) throw new NoSuchElementException(); // total the values of the specified array for the specified colors. int result = 0; result += count1; return result; } /** * Get the index of the current node's start. */ public int nodeStartIndex( ) { if(node == null) throw new NoSuchElementException(); // the count of all nodes prior to this one int result = 0; // this should merely be the sum of each count result += count1; // subtract the count of anything in the current node which we may // have included inadvertently if( true ) { result -= index; } return result; } /** * Get the index of the node immediately following the current. Expected * values are in the range ( 1, size() ) */ public int nodeEndIndex( ) { if(node == null) throw new NoSuchElementException(); // the count of all nodes previous return nodeStartIndex( ) + nodeSize( ); } public T0 value() { if(node == null) throw new IllegalStateException(); return node.get(); } public Element node() { if(node == null) throw new IllegalStateException(); return node; } } /*[ END_M4_JAVA ]*/ ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/FourColorTreeAsList.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/FourColorTreeAsLis0000644000175000017500000000477712106516370032756 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt.barcode2; import java.util.AbstractList; import java.util.List; /* # some M4 Macros that make it easy to use m4 with Java M4 Macros # define a function NODE_WIDTH(boolean) to get the node's size for this color # define a function NODE_SIZE(node, colors) to no node.nodeSize() # define a function to refresh counts # multiple values */ /*[ BEGIN_M4_JAVA ]*/ /** * Adapt a {@link FourColorTree} for use as a {@link List}. * * @author Jesse Wilson */ public class FourColorTreeAsList < T0> extends AbstractList { private final FourColorTree < T0> tree; private final byte colors; /** the color of inserted or added elements */ private final byte color; /** * Create a new {@link FourColorTreeAsList} adapting the specified tree. */ public FourColorTreeAsList/**/(FourColorTree tree) { this(tree, tree.getCoder().colorsToByte(tree.getCoder().getColors()), (byte)1); } /** * Create a new {@link FourColorTreeAsList}, adapting the specified colors subset * of the specified tree. Inserted elements via {@link #add} will be of the * specified color. */ public FourColorTreeAsList/**/(FourColorTree < T0> tree , byte colors, byte color ) { this.tree = tree; this.colors = colors; this.color = color; } /** {@inheritDoc} */ @Override public T0 get(int index) { return tree.get(index , colors ).get(); } /** {@inheritDoc} */ @Override public void add(int index, T0 element) { tree.add(index, colors, color, element, 1); } /** {@inheritDoc} */ @Override public T0 set(int index, T0 element) { T0 replaced = get(index); tree.set(index, colors, color, element, 1); return replaced; } /** {@inheritDoc} */ @Override public T0 remove(int index) { T0 removed = get(index); tree.remove(index, colors, 1); return removed; } /** {@inheritDoc} */ @Override public int size() { return tree.size( colors ); } } /*[ END_M4_JAVA ]*/ ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/BciiTreeIterator.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/BciiTreeIterator.j0000644000175000017500000003104012106516370032677 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt.barcode2; import java.util.NoSuchElementException; /* m4_include(source/ca/odell/glazedlists/impl/adt/barcode2/JavaMacros.m4) m4_include(source/ca/odell/glazedlists/impl/adt/barcode2/TreeMacros.m4) */ /*[ BEGIN_M4_JAVA ]*/ /** * Iterate through a {@link BciiTree}, one element at a time. * *

      We should consider adding the following enhancements to this class: *

    • writing methods, such as set() and remove(). *
    • a default color, specified at construction time, that shall always be * used as the implicit parameter to overloaded versions of {@link #hasNext} * and {@link #next}. * * @author Jesse Wilson */ public class BciiTreeIterator/*[ TYPELIST_START ]*/ /*[ TYPELIST_END ]*/ { /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `int counti(i); ') GENERATED_CODE_END EXAMPLE_START ]*/ int count1; int count2; int count4; /*[ EXAMPLE_END ]*/ private BciiTree/*[ TYPELIST_START ]*/ /*[ TYPELIST_END ]*/ tree; private BciiNode/*[ TYPELIST_START ]*/ /*[ TYPELIST_END ]*/ node; private int index; public BciiTreeIterator/**/(BciiTree/*[ TYPELIST_START ]*/ /*[ TYPELIST_END ]*/ tree) { this(tree, 0, (byte)0); } /** * Create an iterator starting at the specified index. * * @param tree the tree to iterate * @param nextIndex the index to be returned after calling {@link #next next()}. * @param nextIndexColors the colors to interpret nextIndex in terms of */ public BciiTreeIterator/**/(BciiTree/*[ TYPELIST_START ]*/ /*[ TYPELIST_END ]*/ tree, int nextIndex, byte nextIndexColors) { this.tree = tree; // if the start is, we need to find the node in the tree if(nextIndex != 0) { int currentIndex = nextIndex - 1; this.node = (/*[ NODENAME_START ]*/ BciiNode /*[ NODENAME_END ]*/)tree.get(currentIndex /*[ COLORED_START ]*/, nextIndexColors /*[ COLORED_END ]*/); // find the counts /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `m4_ifelse(VAR_COLOUR_COUNT,`1',`count1 = currentIndex; ', counti(i)` = tree.convertIndexColor(currentIndex, nextIndexColors, (byte)'indexToBit(i)`) + (node.color == 'indexToBit(i)` ? 0 : 1); ')') GENERATED_CODE_END EXAMPLE_START ]*/ count1 = tree.convertIndexColor(currentIndex, nextIndexColors, (byte)1) + (node.color == 1 ? 0 : 1); count2 = tree.convertIndexColor(currentIndex, nextIndexColors, (byte)2) + (node.color == 2 ? 0 : 1); count4 = tree.convertIndexColor(currentIndex, nextIndexColors, (byte)4) + (node.color == 4 ? 0 : 1); /*[ EXAMPLE_END ]*/ // find out the index in the node /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `m4_ifelse(VAR_COLOUR_COUNT,`1',`this.index = count1 - tree.indexOfNode(this.node, (byte)1); ',`if(node.color == 'indexToBit(i)`) this.index = 'counti(i)` - tree.indexOfNode(this.node, '(byte)indexToBit(i)`); ')') GENERATED_CODE_END EXAMPLE_START ]*/ if(node.color == 1) this.index = count1 - tree.indexOfNode(this.node, (byte)1); if(node.color == 2) this.index = count2 - tree.indexOfNode(this.node, (byte)2); if(node.color == 4) this.index = count4 - tree.indexOfNode(this.node, (byte)4); /*[ EXAMPLE_END ]*/ // just start before the beginning of the tree } else { this.node = null; this.index = 0; } } /** * Create a {@link BciiTreeIterator} exactly the same as this one. * The iterators will be backed by the same tree but maintain * separate cursors into the tree. */ public BciiTreeIterator/*[ TYPELIST_START ]*/ /*[ TYPELIST_END ]*/ copy() { BciiTreeIterator/*[ TYPELIST_START ]*/ /*[ TYPELIST_END ]*/ result = new BciiTreeIterator/*[ TYPELIST_START ]*/ /*[ TYPELIST_END ]*/(tree); /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `result.counti(i) = this.counti(i); ') GENERATED_CODE_END EXAMPLE_START ]*/ result.count1 = this.count1; result.count2 = this.count2; result.count4 = this.count4; /*[ EXAMPLE_END ]*/ result.node = node; result.index = index; return result; } /** * @return true if there's an element of the specified color in * this tree following the current element. */ public boolean hasNext(/*[ COLORED_START ]*/ byte colors /*[ COLORED_END ]*/) { if(node == null) { return tree.size(/*[ COLORED_START ]*/ colors /*[ COLORED_END ]*/) > 0; } else if(/*[ COLORED_START(true) ]*/ (colors & node.color) != 0 /*[ COLORED_END ]*/) { return index(/*[ COLORED_START ]*/ colors /*[ COLORED_END ]*/) < tree.size(/*[ COLORED_START ]*/ colors /*[ COLORED_END ]*/) - 1; } else { return index(/*[ COLORED_START ]*/ colors /*[ COLORED_END ]*/) < tree.size(/*[ COLORED_START ]*/ colors /*[ COLORED_END ]*/); } } /** * @return true if there's a node of the specified color in this * tree following the current node. */ public boolean hasNextNode(/*[ COLORED_START ]*/ byte colors /*[ COLORED_END ]*/) { if(node == null) { return tree.size(/*[ COLORED_START ]*/ colors /*[ COLORED_END ]*/) > 0; } else { return nodeEndIndex(/*[ COLORED_START ]*/ colors /*[ COLORED_END ]*/) < tree.size(/*[ COLORED_START ]*/ colors /*[ COLORED_END ]*/); } } /** * Step to the next element. */ public void next(/*[ COLORED_START ]*/ byte colors /*[ COLORED_END ]*/) { if(!hasNext(/*[ COLORED_START ]*/ colors /*[ COLORED_END ]*/)) { throw new NoSuchElementException(); } // start at the first node in the tree if(node == null) { node = tree.firstNode(); index = 0; /*[ COLORED_START ]*/if((node.color & colors) != 0) /*[ COLORED_END ]*/ return; // increment within the current node } else if(/*[ COLORED_START ]*/ (node.color & colors) != 0 && /*[ COLORED_END ]*/ index < /*[ WIDE_NODES_START(1) ]*/ node.size /*[ WIDE_NODES_END ]*/ - 1) { /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `m4_ifelse(VAR_COLOUR_COUNT,`1',`count1++; ', `if(node.color == indexToBit(i)) counti(i)++; ')') GENERATED_CODE_END EXAMPLE_START ]*/ if(node.color == 1) count1++; if(node.color == 2) count2++; if(node.color == 4) count4++; /*[ EXAMPLE_END ]*/ index++; return; } // scan through the nodes, looking for the first one of the right color while(true) { /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `m4_ifelse(VAR_COLOUR_COUNT,`1',`count1 += NODE_SIZE(node, colors) - index; ', `if(node.color == indexToBit(i)) counti(i) += node.size - index; ')') GENERATED_CODE_END EXAMPLE_START ]*/ if(node.color == 1) count1 += node.size - index; if(node.color == 2) count2 += node.size - index; if(node.color == 4) count4 += node.size - index; /*[ EXAMPLE_END ]*/ node = BciiTree.next(node); index = 0; // we've found a node that meet our requirements, so return /*[ COLORED_START ]*/ if((node.color & colors) != 0) /*[ COLORED_END ]*/ break; } } /** * Step to the next node. */ public void nextNode(/*[ COLORED_START ]*/ byte colors /*[ COLORED_END ]*/) { if(!hasNextNode(/*[ COLORED_START ]*/ colors /*[ COLORED_END ]*/)) { throw new NoSuchElementException(); } // start at the first node in the tree if(node == null) { node = tree.firstNode(); index = 0; /*[ COLORED_START ]*/if((node.color & colors) != 0) /*[ COLORED_END ]*/ return; } // scan through the nodes, looking for the first one of the right color while(true) { /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `m4_ifelse(VAR_COLOUR_COUNT,`1',`count1 += NODE_SIZE(node, colors) - index; ', `if(node.color == indexToBit(i)) counti(i) += node.size - index; ')') GENERATED_CODE_END EXAMPLE_START ]*/ if(node.color == 1) count1 += node.size - index; if(node.color == 2) count2 += node.size - index; if(node.color == 4) count4 += node.size - index; /*[ EXAMPLE_END ]*/ node = BciiTree.next(node); index = 0; // we've found a node that meet our requirements, so return /*[ COLORED_START ]*/ if((node.color & colors) != 0) /*[ COLORED_END ]*/ break; } } /** * Get the size of the current node, or 0 if it's color doesn't match those * specified. */ public int nodeSize(/*[ COLORED_START ]*/ byte colors /*[ COLORED_END ]*/) { if(/*[ COLORED_START(true) ]*/ (node.color & colors) != 0 /*[ COLORED_END ]*/) { return /*[ WIDE_NODES_START(1) ]*/ node.size /*[ WIDE_NODES_END ]*/; } else { return 0; } } /*[ COLORED_START ]*/ /** * The color of the current element. */ public byte color() { if(node == null) throw new IllegalStateException(); return node.color; } /*[ COLORED_END ]*/ /** * Expected values for index should be in the range ( 0, size() - 1 ) */ public int index(/*[ COLORED_START ]*/ byte colors /*[ COLORED_END ]*/) { if(node == null) throw new NoSuchElementException(); // total the values of the specified array for the specified colors. int result = 0; /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `m4_ifelse(VAR_COLOUR_COUNT,`1',`result += count1; ', `if((colors & indexToBit(i)) != 0) result += counti(i); ')') GENERATED_CODE_END EXAMPLE_START ]*/ if((colors & 1) != 0) result += count1; if((colors & 2) != 0) result += count2; if((colors & 4) != 0) result += count4; /*[ EXAMPLE_END ]*/ return result; } /** * Get the index of the current node's start. */ public int nodeStartIndex(/*[ COLORED_START ]*/ byte colors /*[ COLORED_END ]*/) { if(node == null) throw new NoSuchElementException(); // the count of all nodes prior to this one int result = 0; // this should merely be the sum of each count /*[ GENERATED_CODE_START forloop(`i', 0, VAR_LAST_COLOR_INDEX, `m4_ifelse(VAR_COLOUR_COUNT,`1',`result += count1; ', `if((colors & indexToBit(i)) != 0) result += counti(i); ')') GENERATED_CODE_END EXAMPLE_START ]*/ if((colors & 1) != 0) result += count1; if((colors & 2) != 0) result += count2; if((colors & 4) != 0) result += count4; /*[ EXAMPLE_END ]*/ // subtract the count of anything in the current node which we may // have included inadvertently if(/*[ COLORED_START(true) ]*/ (node.color & colors) != 0 /*[ COLORED_END ]*/) { result -= index; } return result; } /** * Get the index of the node immediately following the current. Expected * values are in the range ( 1, size() ) */ public int nodeEndIndex(/*[ COLORED_START ]*/ byte colors /*[ COLORED_END ]*/) { if(node == null) throw new NoSuchElementException(); // the count of all nodes previous return nodeStartIndex(/*[ COLORED_START ]*/ colors /*[ COLORED_END ]*/) + nodeSize(/*[ COLORED_START ]*/ colors /*[ COLORED_END ]*/); } public T0 value() { if(node == null) throw new IllegalStateException(); return node.get(); } public Element node() { if(node == null) throw new IllegalStateException(); return node; } } /*[ END_M4_JAVA ]*/ libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/Element.java0000644000175000017500000000174112106516370031565 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt.barcode2; /** * The exposed interface of a node. * * @author Jesse Wilson */ public interface Element { /** a node that's greater than its predecessor and less than its successor */ public static final int SORTED = 0; /** a node whose value is unrelated to those of its predecessor or successor */ public static final int UNSORTED = 1; /** a node that's in-work, no inserts or deletes should be performed when nodes are in this state */ public static final int PENDING = 2; V get(); void set(V value); byte getColor(); void setSorted(int sorted); int getSorted(); Element next(); Element previous(); }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/SimpleNode.java0000644000175000017500000001317412106516370032236 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt.barcode2; import java.util.Arrays; import java.util.List; /* # some M4 Macros that make it easy to use m4 with Java M4 Macros # define a function NODE_WIDTH(boolean) to get the node's size for this color # define a function NODE_SIZE(node, colors) to no node.nodeSize() # define a function to refresh counts # multiple values */ /*[ BEGIN_M4_JAVA ]*/ /** * A node in a tree which supports both a value and compressed nodes that * contain a size, useful for index offsetting. * *

      Note that the counts summary member is created lazily when * this node is given children. This causes the code to be less easy to read, * but it means we can put off about a huge number of object allocations since * 50% of the nodes in an arbitrary tree are leaf nodes, and these leaf nodes * now don't have counts. * * @author Jesse Wilson */ class SimpleNode < T0> implements Element { /** the number of elements of each color in this subtree */ int count1; /** the node's value */ T0 t0; /** values for managing the node within the tree */ byte height; SimpleNode < T0> left, right, parent; /** whether this node is consistent in the sorting order */ int sorted = SORTED; /** * Create a new node. * * @param color a bitmask value such as 1, 2, 4, 8 or 16. * @param size the size of the node * @param value the value of the node * @param parent the parent node in the tree, or null for the * root node. */ public SimpleNode/**/( int size, T0 value, SimpleNode < T0> parent) { assert(size == 1); this.t0 = value; this.height = 1; this.parent = parent; count1 += size; } /** * Get the value of this element. */ public T0 get() { return t0; } /** * Set the value of this element. */ public void set(T0 value) { this.t0 = value; } /** access the node's values */ public T0 get0() { return t0; } public void set0(T0 value) { this.t0 = value; } /** * Get the color of this element. */ public byte getColor() { return 1 ; } /** * The size of this entire node, including the left child, this node * and the right child. */ final int size(byte colors) { // total the values of the specified array for the specified colors. int result = 0; if((colors & 1) != 0) result += count1; return result; } /** * Update the counts member variable by examining the counts of * the child nodes and the size member variable. */ final void refreshCounts( boolean countSelf ) { count1 = 0; // left child if(left != null) { count1 += left.count1; } // right child if(right != null) { count1 += right.count1; } // this node count1 += countSelf ? 1 : 0; } /** {@inheritDoc} */ @Override public String toString() { return toString(Arrays.asList(new String[] { "A", "B", "C", "D", "E", "F", "G", "H"})); } /** * Write this node out as a String, using the specified colors to write * each of the node values. */ String toString(List colors) { StringBuffer result = new StringBuffer(); asTree(0, result, colors); return result.toString(); } /** * Dump this node as a String for diagnostic and debugging purposes. */ void asTree(int indentation, StringBuffer out, List colors) { // write the left subtree if(left != null) left.asTree(indentation + 1, out, colors); // write this node for(int i = 0; i < indentation; i++) { out.append(" "); } if(t0 != null) { out.append(": "); if(t0 instanceof SimpleNode) { out.append(""); } else { out.append(t0); } } out.append("\n"); // write the right subtree if(right != null) right.asTree(indentation + 1, out, colors); } /** * Toggle whether this node is sorted. */ public void setSorted(int sorted) { this.sorted = sorted; } /** * Get whether the value of this node is greater than the previous node * and less than the next node. This is useful to have occasional unsorted * elements in an otherwise sorted collection, such as what happens when the * user expects order to be both sorted and stable during edits which would * otherwise change the sorting order. */ public int getSorted() { return sorted; } /** {@inheritDoc} */ public Element next() { return SimpleTree.next(this); } /** {@inheritDoc} */ public Element previous() { return SimpleTree.previous(this); } } /*[ END_M4_JAVA ]*/ libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/FourColorNode.java0000644000175000017500000001560112106516370032714 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt.barcode2; import java.util.Arrays; import java.util.List; /* # some M4 Macros that make it easy to use m4 with Java M4 Macros # define a function NODE_WIDTH(boolean) to get the node's size for this color # define a function NODE_SIZE(node, colors) to no node.nodeSize() # define a function to refresh counts # multiple values */ /*[ BEGIN_M4_JAVA ]*/ /** * A node in a tree which supports both a value and compressed nodes that * contain a size, useful for index offsetting. * *

      Note that the counts summary member is created lazily when * this node is given children. This causes the code to be less easy to read, * but it means we can put off about a huge number of object allocations since * 50% of the nodes in an arbitrary tree are leaf nodes, and these leaf nodes * now don't have counts. * * @author Jesse Wilson */ class FourColorNode < T0> implements Element { /** the number of elements of each color in this subtree */ int count1; int count2; int count4; int count8; /** the node's color */ byte color; /** the node's value */ T0 t0; /** the size of this node */ int size; /** values for managing the node within the tree */ byte height; FourColorNode < T0> left, right, parent; /** whether this node is consistent in the sorting order */ int sorted = SORTED; /** * Create a new node. * * @param color a bitmask value such as 1, 2, 4, 8 or 16. * @param size the size of the node * @param value the value of the node * @param parent the parent node in the tree, or null for the * root node. */ public FourColorNode/**/( byte color, int size, T0 value, FourColorNode < T0> parent) { assert(FourColorTree.colorAsIndex(color) >= 0 && FourColorTree.colorAsIndex(color) < 7); this.color = color; this.size = size; this.t0 = value; this.height = 1; this.parent = parent; if(color == 1) count1 += size; if(color == 2) count2 += size; if(color == 4) count4 += size; if(color == 8) count8 += size; } /** * Get the value of this element. */ public T0 get() { return t0; } /** * Set the value of this element. */ public void set(T0 value) { this.t0 = value; } /** access the node's values */ public T0 get0() { return t0; } public void set0(T0 value) { this.t0 = value; } /** * Get the color of this element. */ public byte getColor() { return color ; } /** * The size of this entire node, including the left child, this node * and the right child. */ final int size(byte colors) { // total the values of the specified array for the specified colors. int result = 0; if((colors & 1) != 0) result += count1; if((colors & 2) != 0) result += count2; if((colors & 4) != 0) result += count4; if((colors & 8) != 0) result += count8; return result; } /** * The size of the node for the specified colors. */ final int nodeSize(byte colors) { return (colors & color) > 0 ? size : 0; } /** * Update the counts member variable by examining the counts of * the child nodes and the size member variable. */ final void refreshCounts( ) { count1 = 0; count2 = 0; count4 = 0; count8 = 0; // left child if(left != null) { count1 += left.count1; count2 += left.count2; count4 += left.count4; count8 += left.count8; } // right child if(right != null) { count1 += right.count1; count2 += right.count2; count4 += right.count4; count8 += right.count8; } // this node if(color == 1) count1 += size; if(color == 2) count2 += size; if(color == 4) count4 += size; if(color == 8) count8 += size; } /** {@inheritDoc} */ @Override public String toString() { return toString(Arrays.asList(new String[] { "A", "B", "C", "D", "E", "F", "G", "H"})); } /** * Write this node out as a String, using the specified colors to write * each of the node values. */ String toString(List colors) { StringBuffer result = new StringBuffer(); asTree(0, result, colors); return result.toString(); } /** * Dump this node as a String for diagnostic and debugging purposes. */ void asTree(int indentation, StringBuffer out, List colors) { // write the left subtree if(left != null) left.asTree(indentation + 1, out, colors); // write this node for(int i = 0; i < indentation; i++) { out.append(" "); } out.append(colors.get(FourColorTree.colorAsIndex(color))); out.append(" [").append(size).append("]"); if(t0 != null) { out.append(": "); if(t0 instanceof FourColorNode) { out.append(""); } else { out.append(t0); } } out.append("\n"); // write the right subtree if(right != null) right.asTree(indentation + 1, out, colors); } /** * Toggle whether this node is sorted. */ public void setSorted(int sorted) { this.sorted = sorted; } /** * Get whether the value of this node is greater than the previous node * and less than the next node. This is useful to have occasional unsorted * elements in an otherwise sorted collection, such as what happens when the * user expects order to be both sorted and stable during edits which would * otherwise change the sorting order. */ public int getSorted() { return sorted; } /** {@inheritDoc} */ public Element next() { return FourColorTree.next(this); } /** {@inheritDoc} */ public Element previous() { return FourColorTree.previous(this); } } /*[ END_M4_JAVA ]*/ ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/BciiTreeAsList.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/BciiTreeAsList.jav0000644000175000017500000000533112106516370032640 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt.barcode2; import java.util.AbstractList; import java.util.List; /* m4_include(source/ca/odell/glazedlists/impl/adt/barcode2/JavaMacros.m4) m4_include(source/ca/odell/glazedlists/impl/adt/barcode2/TreeMacros.m4) */ /*[ BEGIN_M4_JAVA ]*/ /** * Adapt a {@link BciiTree} for use as a {@link List}. * * @author Jesse Wilson */ public class BciiTreeAsList/*[ TYPELIST_START ]*/ /*[ TYPELIST_END ]*/ extends AbstractList { private final BciiTree/*[ TYPELIST_START ]*/ /*[ TYPELIST_END ]*/ tree; /*[ COLORED_START ]*/ private final byte colors; /** the color of inserted or added elements */ private final byte color; /*[ COLORED_END ]*/ /*[ COLORED_START ]*/ /** * Create a new {@link BciiTreeAsList} adapting the specified tree. */ public BciiTreeAsList/**/(BciiTree tree) { this(tree, tree.getCoder().colorsToByte(tree.getCoder().getColors()), (byte)1); } /*[ COLORED_END ]*/ /** * Create a new {@link BciiTreeAsList}, adapting the specified colors subset * of the specified tree. Inserted elements via {@link #add} will be of the * specified color. */ public BciiTreeAsList/**/(BciiTree/*[ TYPELIST_START ]*/ /*[ TYPELIST_END ]*/ tree /*[ COLORED_START ]*/ , byte colors, byte color /*[ COLORED_END ]*/) { this.tree = tree; /*[ COLORED_START ]*/ this.colors = colors; this.color = color; /*[ COLORED_END ]*/ } /** {@inheritDoc} */ @Override public T0 get(int index) { return tree.get(index /*[ COLORED_START ]*/, colors /*[ COLORED_END ]*/).get(); } /** {@inheritDoc} */ @Override public void add(int index, T0 element) { tree.add(index, /*[ COLORED_START ]*/ colors, color, /*[ COLORED_END ]*/ element, 1); } /** {@inheritDoc} */ @Override public T0 set(int index, T0 element) { T0 replaced = get(index); tree.set(index, /*[ COLORED_START ]*/ colors, color, /*[ COLORED_END ]*/ element, 1); return replaced; } /** {@inheritDoc} */ @Override public T0 remove(int index) { T0 removed = get(index); tree.remove(index, /*[ COLORED_START ]*/ colors, /*[ COLORED_END ]*/ 1); return removed; } /** {@inheritDoc} */ @Override public int size() { return tree.size(/*[ COLORED_START ]*/ colors /*[ COLORED_END ]*/); } } /*[ END_M4_JAVA ]*/ ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/ListToByteCoder.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/ListToByteCoder.ja0000644000175000017500000000447412106516370032672 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt.barcode2; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Try to make conversions and color operations as efficient as possible * by using bytes as values rather than full-size objects. This exploits * a limitation that there's at most 8 possible values in the list of colors. * * @author Jesse Wilson */ public class ListToByteCoder { private final List allColors; private final int colorCount; public ListToByteCoder(List allColors) { if(allColors.size() > 7) throw new IllegalArgumentException("Max 7 colors!"); this.allColors = Collections.unmodifiableList(new ArrayList(allColors)); this.colorCount = this.allColors.size(); } /** * List the colors encoded by this coder. */ public List getColors() { return allColors; } /** * Encode the specified list of colors into a byte. */ public byte colorsToByte(List colors) { int result = 0; for(int i = 0; i < colors.size(); i++) { C color = colors.get(i); int index = allColors.indexOf(color); result = result | (1 << index); } return (byte)result; } /** * Encode the specified color into a byte. */ public byte colorToByte(C color) { int index = allColors.indexOf(color); int result = (1 << index); return (byte)result; } /** * Decode the specified byte into a color. */ public C byteToColor(byte encoded) { for(int i = 0; i < colorCount; i++) { if(((1 << i) & encoded) > 0) return allColors.get(i); } throw new IllegalStateException(); } /** * Decode the specified bytes into colors. */ public List byteToColors(byte encoded) { List result = new ArrayList(colorCount); for(int i = 0; i < colorCount; i++) { if(((1 << i) & encoded) > 0) result.add(allColors.get(i)); } return result; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/JavaMacros.m40000644000175000017500000000075412106516370031624 0ustar gregoagregoa# some M4 Macros that make it easy to use m4 with Java m4_define(QUOTE_STOP, `/*[') m4_define(QUOTE_START, `]*'`/') m4_define(EXAMPLE_START, `m4_ifelse(') m4_define(EXAMPLE_END, `)') m4_define(BEGIN_M4_JAVA, ``BEGIN_M4_JAVA' QUOTE_START m4_changequote(QUOTE_START, QUOTE_STOP) ') m4_define(END_M4_JAVA, `m4_changequote QUOTE_STOP `END_M4_JAVA'') m4_define(GENERATED_CODE_START, `m4_changequote') m4_define(GENERATED_CODE_END, `m4_changequote(QUOTE_START, QUOTE_STOP)') m4_define(M4_COMMENT,) libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/FourColorTree.java0000644000175000017500000012065412106516370032733 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt.barcode2; import ca.odell.glazedlists.GlazedLists; import java.util.ArrayList; import java.util.Comparator; import java.util.List; /* # some M4 Macros that make it easy to use m4 with Java M4 Macros # define a function NODE_WIDTH(boolean) to get the node's size for this color # define a function NODE_SIZE(node, colors) to no node.nodeSize() # define a function to refresh counts # multiple values */ /*[ BEGIN_M4_JAVA ]*/ /** * Our second generation tree class. * *

      Currently the API for this class is fairly low-level, particularly the * use of bytes as color values. This is an implementation detail, * exposed to maximize performance. Wherever necessary, consider creating a * facade around this Tree class that provides methods more appropriate * for your particular application. * *

      This is a prototype replacement for the Barcode class that adds support * for up to seven different colors. As well, this supports values in the node. * It will hopefully also replace our IndexedTree class. This class * is designed after those two classes and hopefully improves upon them in * a few interesting ways: *

    • Avoid recursion wherever possible to increase performance *
    • Be generic to simplify handling of black/white values. These can be * handled in one case rather than one case for each. *
    • Make the node class a dataholder only and put most of the logic in * the tree class. This allows us to share different Node classes with * different memory requirements, while using the same logic class * *

      This class came into being so we could use a tree to replace * ListEventBlocks, which has only mediocre performance, particularly * due to having to sort elements. As well, we might be able to keep a moved * value in the tree, to support moved elements in ListEvents. * * @author Jesse Wilson */ public class FourColorTree < T0> { /** the colors in the tree, used for printing purposes only */ private final ListToByteCoder coder; /** the tree's root, or null for an empty tree */ private FourColorNode < T0> root = null; /** * a list to add all nodes to that must be removed from * the tree. The nodes are removed only after the tree has been modified, * which allows us a chance to do rotations without losing our position * in the tree. */ private final List< FourColorNode < T0> > zeroQueue = new ArrayList< FourColorNode < T0> >(); /** * The comparator to use when performing ordering operations on the tree. * Sometimes this tree will not be sorted, so in such situations this * comparator will not be used. */ private final Comparator comparator; /** * @param coder specifies the node colors * @param comparator the comparator to use when ordering values within the * tree. If this tree is unsorted, use the one-argument constructor. */ public FourColorTree/**/( ListToByteCoder coder, Comparator comparator) { if(coder == null) throw new NullPointerException("Coder cannot be null."); if(comparator == null) throw new NullPointerException("Comparator cannot be null."); this.coder = coder; this.comparator = comparator; } /** * @param coder specifies the node colors */ public FourColorTree/**/( ListToByteCoder coder ) { this( coder, (Comparator)GlazedLists.comparableComparator()); } public ListToByteCoder getCoder() { return coder; } public Comparator getComparator() { return comparator; } /** * Get the tree element at the specified index relative to the specified index * colors. * *

      This method is an hotspot, so its crucial that it run as efficiently * as possible. */ public Element get(int index , byte indexColors ) { if(root == null) throw new IndexOutOfBoundsException(); // go deep, looking for our node of interest FourColorNode < T0> node = root; while(true) { assert(node != null); assert(index >= 0); // recurse on the left FourColorNode < T0> nodeLeft = node.left; int leftSize = nodeLeft != null ? nodeLeft. size(indexColors) : 0; if(index < leftSize) { node = nodeLeft; continue; } else { index -= leftSize; } // the result is in the centre int size = node.nodeSize(indexColors) ; if(index < size) { return node; } else { index -= size; } // the result is on the right node = node.right; } } /** * Add a tree node at the specified index relative to the specified index * colors. The inserted nodes' color, value and size are specified. * *

      Note that nodes with null values will never be * merged together to allow those nodes to be assigned other values later. * * @param size the size of the node to insert. * @param index the location into this tree to insert at * @param indexColors the colors that index is relative to. This should be * all colors in the tree ORed together for the entire tree. * @param value the node value. If non-null, the node may be * combined with other nodes of the same color and value. null * valued nodes will never be combined with each other. * @return the element the specified value was inserted into. This is non-null * unless the size parameter is 0, in which case the result is always * null. */ public Element add(int index, byte indexColors, byte color, T0 value, int size) { assert(index >= 0); assert(index <= size( indexColors )); assert(size >= 0); if(this.root == null) { if(index != 0) throw new IndexOutOfBoundsException(); this.root = new FourColorNode < T0> ( color, size, value, null); assert(valid()); return this.root; } else { FourColorNode < T0> inserted = insertIntoSubtree(root, index, indexColors, color, value, size); assert(valid()); return inserted; } } /** * @param parent the subtree to insert into, must not be null. * @param index the color index to insert at * @param indexColors a bitmask of all colors that the index is defined in * terms of. For example, if this is determined in terms of colors 4, 8 * and 32, then the value here should be 44 (32 + 8 + 4). * @param color a bitmask value such as 1, 2, 4, 8, 16, 32, 64 or 128. * @param value the object to hold in the inserted node. * @param size the size of the inserted node, with respect to indices. * @return the inserted node, or the modified node if this insert simply * increased the size of an existing node. */ private FourColorNode < T0> insertIntoSubtree( FourColorNode < T0> parent, int index, byte indexColors, byte color, T0 value, int size) { while(true) { assert(parent != null); assert(index >= 0); // figure out the layout of this node FourColorNode < T0> parentLeft = parent.left; int parentLeftSize = parentLeft != null ? parentLeft. size(indexColors) : 0; int parentRightStartIndex = parentLeftSize + parent.nodeSize(indexColors) ; // the first thing we want to try is to merge this value into the // current node, since that's the cheapest thing to do: if( color == parent.color && value == parent.t0 && value != null) { if(index >= parentLeftSize && index <= parentRightStartIndex) { parent.size += size; fixCountsThruRoot(parent, color, size); return parent; } } // we can insert on the left if(index <= parentLeftSize) { // as a new left child if(parentLeft == null) { FourColorNode < T0> inserted = new FourColorNode < T0> ( color, size, value, parent); parent.left = inserted; fixCountsThruRoot(parent, color, size); fixHeightPostChange(parent, false); return inserted; // recurse on the left } else { parent = parentLeft; continue; } } // we need to insert in the centre. This works by splitting in the // centre, and inserting the value if(index < parentRightStartIndex) { int parentRightHalfSize = parentRightStartIndex - index; parent.size -= parentRightHalfSize; fixCountsThruRoot(parent, parent.color, -parentRightHalfSize); // insert as null first to make sure this doesn't get merged back Element inserted = insertIntoSubtree(parent, index, indexColors, parent.color, null, parentRightHalfSize); inserted.set(parent.t0); // recalculate parentRightStartIndex, since that should have // changed by now. this will then go on to insert on the right parentRightStartIndex = parentLeftSize + parent.nodeSize(indexColors) ; } // on the right right: { int parentSize = parent. size(indexColors) ; assert(index <= parentSize); FourColorNode < T0> parentRight = parent.right; // as a right child if(parentRight == null) { FourColorNode < T0> inserted = new FourColorNode < T0> ( color, size, value, parent); parent.right = inserted; fixCountsThruRoot(parent, color, size); fixHeightPostChange(parent, false); return inserted; // recurse on the right } else { parent = parentRight; index -= parentRightStartIndex; } } } } /** * Add a tree node in sorted order. * * @param size the size of the node to insert. * @param value the node value. If non-null, the node may be * combined with other nodes of the same color and value. null * valued nodes will never be combined with each other. * @return the element the specified value was inserted into. This is non-null * unless the size parameter is 0, in which case the result is always * null. */ public Element addInSortedOrder(byte color, T0 value, int size) { assert(size >= 0); if(this.root == null) { this.root = new FourColorNode < T0> ( color, size, value, null); assert(valid()); return this.root; } else { FourColorNode < T0> inserted = insertIntoSubtreeInSortedOrder(root, color, value, size); assert(valid()); return inserted; } } /** * @param parent the subtree to insert into, must not be null. * @param color a bitmask value such as 1, 2, 4, 8, 16, 32, 64 or 128. * @param value the object to hold in the inserted node. * @param size the size of the inserted node, with respect to indices. * @return the inserted node, or the modified node if this insert simply * increased the size of an existing node. */ private FourColorNode < T0> insertIntoSubtreeInSortedOrder( FourColorNode < T0> parent, byte color, T0 value, int size) { while(true) { assert(parent != null); // calculating the sort side is a little tricky since we can have // unsorted nodes in the tree. we just look for a neighbour (ie next) // that is sorted, and compare with that int sortSide; for( FourColorNode < T0> currentFollower = parent; true; currentFollower = next(currentFollower)) { // we've hit the end of the list, assume the element is on the left side if(currentFollower == null) { sortSide = -1; break; // we've found a comparable node, use it } else if(currentFollower.sorted == Element.SORTED) { sortSide = comparator.compare(value, currentFollower.t0); break; } } // the first thing we want to try is to merge this value into the // current node, since that's the cheapest thing to do: if( sortSide == 0 && color == parent.color && value == parent.t0 && value != null) { parent.size += size; fixCountsThruRoot(parent, color, size); return parent; } // insert on the left... boolean insertOnLeft = false; insertOnLeft = insertOnLeft || sortSide < 0; insertOnLeft = insertOnLeft || sortSide == 0 && parent.left == null; insertOnLeft = insertOnLeft || sortSide == 0 && parent.right != null && parent.left.height < parent.right.height; if(insertOnLeft) { FourColorNode < T0> parentLeft = parent.left; // as a new left child if(parentLeft == null) { FourColorNode < T0> inserted = new FourColorNode < T0> ( color, size, value, parent); parent.left = inserted; fixCountsThruRoot(parent, color, size); fixHeightPostChange(parent, false); return inserted; // recurse on the left } else { parent = parentLeft; continue; } // ...or on the right } else { FourColorNode < T0> parentRight = parent.right; // as a right child if(parentRight == null) { FourColorNode < T0> inserted = new FourColorNode < T0> ( color, size, value, parent); parent.right = inserted; fixCountsThruRoot(parent, color, size); fixHeightPostChange(parent, false); return inserted; // recurse on the right } else { parent = parentRight; } } } } /** * Adjust counts for all nodes (including the specified node) up the tree * to the root. The counts of the specified color are adjusted by delta * (which may be positive or negative). */ private final void fixCountsThruRoot( FourColorNode < T0> node, byte color, int delta) { if(color == 1) { for( ; node != null; node = node.parent) node.count1 += delta; } if(color == 2) { for( ; node != null; node = node.parent) node.count2 += delta; } if(color == 4) { for( ; node != null; node = node.parent) node.count4 += delta; } if(color == 8) { for( ; node != null; node = node.parent) node.count8 += delta; } } /** * Change the color of the specified element. */ public final void setColor(Element element, byte color) { FourColorNode node = (FourColorNode)element; byte oldColor = node.getColor(); if(oldColor == color) return; fixCountsThruRoot(node, oldColor, -node.size); node.color = color; fixCountsThruRoot(node, color, node.size); } /** * Fix the height of the specified ancestor after inserting a child node. * This method short circuits when it finds the first node where the size * has not changed. * * @param node the root of a changed subtree. This shouldn't be called * on inserted nodes, but rather their parent nodes, since only * the parent nodes sizes will be changing. * @param allTheWayToRoot true to walk up the tree all the way * to the tree's root, or false to walk up until the height * is unchanged. We go to the root on a delete, since the rotate is on * the opposite side of the tree, whereas on an insert we only delete * as far as necessary. */ private final void fixHeightPostChange( FourColorNode < T0> node, boolean allTheWayToRoot) { // update the height for(; node != null; node = node.parent) { byte leftHeight = node.left != null ? node.left.height : 0; byte rightHeight = node.right != null ? node.right.height : 0; // rotate left? if(leftHeight > rightHeight && (leftHeight - rightHeight == 2)) { // do we need to rotate the left child first? int leftLeftHeight = node.left.left != null ? node.left.left.height : 0; int leftRightHeight = node.left.right != null ? node.left.right.height : 0; if(leftRightHeight > leftLeftHeight) { rotateRight(node.left); } // finally rotate right node = rotateLeft(node); // rotate right? } else if(rightHeight > leftHeight && (rightHeight - leftHeight == 2)) { // do we need to rotate the right child first? int rightLeftHeight = node.right.left != null ? node.right.left.height : 0; int rightRightHeight = node.right.right != null ? node.right.right.height : 0; if(rightLeftHeight > rightRightHeight) { rotateLeft(node.right); } // finally rotate left node = rotateRight(node); } // update the node height leftHeight = node.left != null ? node.left.height : 0; rightHeight = node.right != null ? node.right.height : 0; byte newNodeHeight = (byte) (Math.max(leftHeight, rightHeight) + 1); // if the height doesn't need updating, we might just be done! if(!allTheWayToRoot && node.height == newNodeHeight) return; // otherwise change the height and keep rotating node.height = newNodeHeight; } } /** * Perform an AVL rotation of the tree. * * D B * / \ ROTATE / \ * B E LEFT AT A D * / \ NODE D / \ * A C C E * * @return the new root of the subtree */ private final FourColorNode < T0> rotateLeft( FourColorNode < T0> subtreeRoot) { assert(subtreeRoot.left != null); // subtreeRoot is D // newSubtreeRoot is B FourColorNode < T0> newSubtreeRoot = subtreeRoot.left; // modify the links between nodes // attach C as a child of to D subtreeRoot.left = newSubtreeRoot.right; if(newSubtreeRoot.right != null) newSubtreeRoot.right.parent = subtreeRoot; // link b as the new root node for this subtree newSubtreeRoot.parent = subtreeRoot.parent; if(newSubtreeRoot.parent != null) { if(newSubtreeRoot.parent.left == subtreeRoot) newSubtreeRoot.parent.left = newSubtreeRoot; else if(newSubtreeRoot.parent.right == subtreeRoot) newSubtreeRoot.parent.right = newSubtreeRoot; else throw new IllegalStateException(); } else { root = newSubtreeRoot; } // attach D as a child of B newSubtreeRoot.right = subtreeRoot; subtreeRoot.parent = newSubtreeRoot; // update height and counts of the old subtree root byte subtreeRootLeftHeight = subtreeRoot.left != null ? subtreeRoot.left.height : 0; byte subtreeRootRightHeight = subtreeRoot.right != null ? subtreeRoot.right.height : 0; subtreeRoot.height = (byte)(Math.max(subtreeRootLeftHeight, subtreeRootRightHeight) + 1); subtreeRoot.refreshCounts(); // update height and counts of the new subtree root byte newSubtreeRootLeftHeight = newSubtreeRoot.left != null ? newSubtreeRoot.left.height : 0; byte newSubtreeRootRightHeight = newSubtreeRoot.right != null ? newSubtreeRoot.right.height : 0; newSubtreeRoot.height = (byte)(Math.max(newSubtreeRootLeftHeight, newSubtreeRootRightHeight) + 1); newSubtreeRoot.refreshCounts(); return newSubtreeRoot; } private final FourColorNode < T0> rotateRight( FourColorNode < T0> subtreeRoot) { assert(subtreeRoot.right != null); // subtreeRoot is D // newSubtreeRoot is B FourColorNode < T0> newSubtreeRoot = subtreeRoot.right; // modify the links between nodes // attach C as a child of to D subtreeRoot.right = newSubtreeRoot.left; if(newSubtreeRoot.left != null) newSubtreeRoot.left.parent = subtreeRoot; // link b as the new root node for this subtree newSubtreeRoot.parent = subtreeRoot.parent; if(newSubtreeRoot.parent != null) { if(newSubtreeRoot.parent.left == subtreeRoot) newSubtreeRoot.parent.left = newSubtreeRoot; else if(newSubtreeRoot.parent.right == subtreeRoot) newSubtreeRoot.parent.right = newSubtreeRoot; else throw new IllegalStateException(); } else { root = newSubtreeRoot; } // attach D as a child of B newSubtreeRoot.left = subtreeRoot; subtreeRoot.parent = newSubtreeRoot; // update height and counts of the old subtree root byte subtreeRootLeftHeight = subtreeRoot.left != null ? subtreeRoot.left.height : 0; byte subtreeRootRightHeight = subtreeRoot.right != null ? subtreeRoot.right.height : 0; subtreeRoot.height = (byte)(Math.max(subtreeRootLeftHeight, subtreeRootRightHeight) + 1); subtreeRoot.refreshCounts(); // update height and counts of the new subtree root byte newSubtreeRootLeftHeight = newSubtreeRoot.left != null ? newSubtreeRoot.left.height : 0; byte newSubtreeRootRightHeight = newSubtreeRoot.right != null ? newSubtreeRoot.right.height : 0; newSubtreeRoot.height = (byte)(Math.max(newSubtreeRootLeftHeight, newSubtreeRootRightHeight) + 1); newSubtreeRoot.refreshCounts(); return newSubtreeRoot; } /** * Remove the specified element from the tree outright. */ public void remove(Element element) { FourColorNode < T0> node = ( FourColorNode < T0> )element; assert(node.size > 0); assert(root != null); // delete the node by adding to the zero queue fixCountsThruRoot(node, node.color, -node.size ); node.size = 0; zeroQueue.add(node); drainZeroQueue(); assert(valid()); } /** * Remove size values at the specified index. Only values of the type * specified in indexColors will be removed. * *

      Note that if the two nodes on either side of the removed node could * be merged, they probably will not be merged by this implementation. This * is to simplify the implementation, but it means that when iterating a * tree, sometimes multiple nodes of the same color and value will be * encountered in sequence. */ public void remove(int index, byte indexColors, int size) { if(size == 0) return; assert(index >= 0); assert(index + size <= size( indexColors )); assert(root != null); // remove values from the tree removeFromSubtree(root, index, indexColors, size); // clean up any nodes that got deleted drainZeroQueue(); assert(valid()); } /** * Prune all nodes scheduled for deletion. */ private void drainZeroQueue() { for(int i = 0, size = zeroQueue.size(); i < size; i++) { FourColorNode < T0> node = zeroQueue.get(i); assert(node.size == 0); if(node.right == null) { replaceChild(node, node.left); } else if(node.left == null) { replaceChild(node, node.right); } else { node = replaceEmptyNodeWithChild(node); } } zeroQueue.clear(); } /** * Remove at the specified index in the specified subtree. This doesn't ever * remove any nodes of size zero, that's up to the caller to do after by * removing all nodes in the zeroQueue from the tree. */ private void removeFromSubtree( FourColorNode < T0> node, int index, byte indexColors, int size) { while(size > 0) { assert(node != null); assert(index >= 0); // figure out the layout of this node FourColorNode < T0> nodeLeft = node.left; int leftSize = nodeLeft != null ? nodeLeft. size(indexColors) : 0; // delete on the left first if(index < leftSize) { // we can only remove part of our requirement on the left, so do // that part recursively if(index + size > leftSize) { int toRemove = leftSize - index; removeFromSubtree(nodeLeft, index, indexColors, toRemove); size -= toRemove; leftSize -= toRemove; // we can do our full delete on the left side } else { node = nodeLeft; continue; } } assert(index >= leftSize); // delete in the centre int rightStartIndex = leftSize + node.nodeSize(indexColors) ; if(index < rightStartIndex) { int toRemove = Math.min(rightStartIndex - index, size); // decrement the appropriate counts all the way up node.size -= toRemove; size -= toRemove; rightStartIndex -= toRemove; fixCountsThruRoot(node, node.color, -toRemove); if( node.size == 0 ) { zeroQueue.add(node); } if(size == 0) return; } assert(index >= rightStartIndex); // delete on the right last index -= rightStartIndex; node = node.right; } } /** * Replace the specified node with the specified replacement. This does the * replacement, then walks up the tree to ensure heights are correct, so * the replacement node should have its height set first before this method * is called. */ private void replaceChild( FourColorNode < T0> node, FourColorNode < T0> replacement) { FourColorNode < T0> nodeParent = node.parent; // replace the root if(nodeParent == null) { assert(node == root); root = replacement; // replace on the left } else if(nodeParent.left == node) { nodeParent.left = replacement; // replace on the right } else if(nodeParent.right == node) { nodeParent.right = replacement; } // update the replacement's parent if(replacement != null) { replacement.parent = nodeParent; } // the height has changed, update that up the tree fixHeightPostChange(nodeParent, true); } /** * Replace the specified node with another node deeper in the tree. This * is necessary to maintain treeness through deletes. * *

      This implementation finds the largest node in the left subtree, * removes it, and puts it in the specified node's place. * * @return the replacement node */ private FourColorNode < T0> replaceEmptyNodeWithChild( FourColorNode < T0> toReplace) { assert(toReplace.size == 0); assert(toReplace.left != null); assert(toReplace.right != null); // find the rightmost child on the leftside FourColorNode < T0> replacement = toReplace.left; while(replacement.right != null) { replacement = replacement.right; } assert(replacement.right == null); // remove that node from the tree fixCountsThruRoot(replacement, replacement.color, -replacement.size ); replaceChild(replacement, replacement.left); // update the tree structure to point to the replacement replacement.left = toReplace.left; if(replacement.left != null) replacement.left.parent = replacement; replacement.right = toReplace.right; if(replacement.right != null) replacement.right.parent = replacement; replacement.height = toReplace.height; replacement.refreshCounts(); replaceChild(toReplace, replacement); fixCountsThruRoot(replacement.parent, replacement.color, replacement.size ); return replacement; } /** * Replace all values at the specified index with the specified new value. * *

      Currently this uses a naive implementation of remove then add. If * it proves desirable, it may be worthwhile to optimize this implementation * with one that performs the remove and insert simultaneously, to save * on tree navigation. * * @return the element that was updated. This is non-null unless the size * parameter is 0, in which case the result is always null. */ public Element set(int index, byte indexColors, byte color, T0 value, int size) { remove(index, indexColors, size); return add(index, indexColors, color, value, size); } /** * Remove all nodes from the tree. Note that this is much faster than calling * remove on all elements, since the structure can be discarded instead of * managed during the removal. */ public void clear() { root = null; } /** * Get the index of the specified element, counting only the colors * specified. * *

      This method is an hotspot, so its crucial that it run as efficiently * as possible. */ public int indexOfNode(Element element, byte colorsOut) { FourColorNode < T0> node = ( FourColorNode < T0> )element; // count all elements left of this node int index = node.left != null ? node.left. size(colorsOut) : 0; // add all elements on the left, all the way to the root for( ; node.parent != null; node = node.parent) { if(node.parent.right == node) { index += node.parent.left != null ? node.parent.left. size(colorsOut) : 0; index += node.parent.nodeSize(colorsOut) ; } } return index; } /** * Find the index of the specified element * * @param firstIndex true to return the index of the first occurrence of the * specified element, or false for the last index. * @param simulated true to return an index value even if the element is not * found. Otherwise -1 is returned. * @return an index, or -1 if simulated is false and there exists no * element x in this tree such that * FourColorTree.getComparator().compare(x, element) == 0. */ public int indexOfValue(T0 element, boolean firstIndex, boolean simulated, byte colorsOut) { int result = 0; boolean found = false; // go deep, looking for our node of interest FourColorNode < T0> node = root; while(true) { if(node == null) { if(found && !firstIndex) result--; if(found || simulated) return result; else return -1; } // figure out if the value is left, center or right int comparison = comparator.compare(element, node.get()); // recurse on the left if(comparison < 0) { node = node.left; continue; } FourColorNode < T0> nodeLeft = node.left; // the result is in the centre if(comparison == 0) { found = true; // recurse deeper on the left, looking for the first left match if(firstIndex) { node = nodeLeft; continue; } } // recurse on the right, increment result by left size and center size result += nodeLeft != null ? nodeLeft. size(colorsOut) : 0; result += node.nodeSize(colorsOut) ; node = node.right; } } /** * Convert one index into another. */ public int convertIndexColor(int index, byte indexColors, byte colorsOut) { if(root == null) { if(index == 0) return 0; else throw new IndexOutOfBoundsException(); } int result = 0; // go deep, looking for our node of interest FourColorNode < T0> node = root; while(true) { assert(node != null); assert(index >= 0); // figure out the layout of this node FourColorNode < T0> nodeLeft = node.left; int leftSize = nodeLeft != null ? nodeLeft. size(indexColors) : 0; // recurse on the left if(index < leftSize) { node = nodeLeft; continue; // increment by the count on the left } else { if(nodeLeft != null) result += nodeLeft. size(colorsOut) ; index -= leftSize; } // the result is in the centre int size = node.nodeSize(indexColors) ; if(index < size) { // we're on a node of the same color, return the adjusted index if( (colorsOut & node.color) > 0 ) { result += index; // we're on a node of a different color, return the previous node of the requested color } else { result -= 1; } return result; // increment by the count in the centre } else { result += node.nodeSize(colorsOut) ; index -= size; } // the result is on the right node = node.right; } } /** * The size of the tree for the specified colors. */ public int size( byte colors ) { if(root == null) return 0; else return root. size(colors) ; } /** * Print this tree as a list of values. */ @Override public String toString() { if(root == null) return ""; return root.toString( coder.getColors() ); } /** * Print this tree as a list of colors, removing all hierarchy. */ public String asSequenceOfColors() { if(root == null) return ""; // print it flattened, like a list of colors StringBuffer result = new StringBuffer(); for(FourColorNode n = firstNode(); n != null; n = next(n)) { Object color = coder.getColors().get(colorAsIndex(n.color)); for( int i = 0; i < n.size; i++ ) { result.append(color); } } return result.toString(); } /** * Find the next node in the tree, working from left to right. */ public static < T0> FourColorNode < T0> next( FourColorNode < T0> node) { // if this node has a right subtree, it's the leftmost node in that subtree if(node.right != null) { FourColorNode < T0> child = node.right; while(child.left != null) { child = child.left; } return child; // otherwise its the nearest ancestor where I'm in the left subtree } else { FourColorNode < T0> ancestor = node; while(ancestor.parent != null && ancestor.parent.right == ancestor) { ancestor = ancestor.parent; } return ancestor.parent; } } /** * Find the previous node in the tree, working from right to left. */ public static < T0> FourColorNode < T0> previous( FourColorNode < T0> node) { // if this node has a left subtree, it's the rightmost node in that subtree if(node.left != null) { FourColorNode < T0> child = node.left; while(child.right != null) { child = child.right; } return child; // otherwise its the nearest ancestor where I'm in the right subtree } else { FourColorNode < T0> ancestor = node; while(ancestor.parent != null && ancestor.parent.left == ancestor) { ancestor = ancestor.parent; } return ancestor.parent; } } /** * Find the leftmost child in this subtree. */ FourColorNode < T0> firstNode() { if(root == null) return null; FourColorNode < T0> result = root; while(result.left != null) { result = result.left; } return result; } /** * @return true if this tree is structurally valid */ private boolean valid() { // walk through all nodes in the tree, looking for something invalid for( FourColorNode < T0> node = firstNode(); node != null; node = next(node)) { // sizes (counts) are valid int originalCount1 = node.count1; int originalCount2 = node.count2; int originalCount4 = node.count4; int originalCount8 = node.count8; node.refreshCounts(); assert(originalCount1 == node.count1) : "Incorrect count 0 on node: \n" + node + "\n Expected " + node.count1 + " but was " + originalCount1; assert(originalCount2 == node.count2) : "Incorrect count 1 on node: \n" + node + "\n Expected " + node.count2 + " but was " + originalCount2; assert(originalCount4 == node.count4) : "Incorrect count 2 on node: \n" + node + "\n Expected " + node.count4 + " but was " + originalCount4; assert(originalCount8 == node.count8) : "Incorrect count 3 on node: \n" + node + "\n Expected " + node.count8 + " but was " + originalCount8; // heights are valid int leftHeight = node.left != null ? node.left.height : 0; int rightHeight = node.right != null ? node.right.height : 0; assert(Math.max(leftHeight, rightHeight) + 1 == node.height); // left child's parent is this assert(node.left == null || node.left.parent == node); // right child's parent is this assert(node.right == null || node.right.parent == node); // tree is AVL assert(Math.abs(leftHeight - rightHeight) < 2) : "Subtree is not AVL: \n" + node; } // we're valid return true; } /** * Convert the specified color value (such as 1, 2, 4, 8, 16 etc.) into an * index value (such as 0, 1, 2, 3, 4 etc. ). */ static final int colorAsIndex(byte color) { switch(color) { case 1: return 0; case 2: return 1; case 4: return 2; case 8: return 3; case 16: return 4; case 32: return 5; case 64: return 6; } throw new IllegalArgumentException(); } } /*[ END_M4_JAVA ]*/ ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/SimpleTreeAsList.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/barcode2/SimpleTreeAsList.j0000644000175000017500000000371712106516370032702 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt.barcode2; import java.util.AbstractList; import java.util.List; /* # some M4 Macros that make it easy to use m4 with Java M4 Macros # define a function NODE_WIDTH(boolean) to get the node's size for this color # define a function NODE_SIZE(node, colors) to no node.nodeSize() # define a function to refresh counts # multiple values */ /*[ BEGIN_M4_JAVA ]*/ /** * Adapt a {@link SimpleTree} for use as a {@link List}. * * @author Jesse Wilson */ public class SimpleTreeAsList < T0> extends AbstractList { private final SimpleTree < T0> tree; /** * Create a new {@link SimpleTreeAsList}, adapting the specified colors subset * of the specified tree. Inserted elements via {@link #add} will be of the * specified color. */ public SimpleTreeAsList/**/(SimpleTree < T0> tree ) { this.tree = tree; } /** {@inheritDoc} */ @Override public T0 get(int index) { return tree.get(index ).get(); } /** {@inheritDoc} */ @Override public void add(int index, T0 element) { tree.add(index, element, 1); } /** {@inheritDoc} */ @Override public T0 set(int index, T0 element) { T0 replaced = get(index); tree.set(index, element, 1); return replaced; } /** {@inheritDoc} */ @Override public T0 remove(int index) { T0 removed = get(index); tree.remove(index, 1); return removed; } /** {@inheritDoc} */ @Override public int size() { return tree.size( ); } } /*[ END_M4_JAVA ]*/ libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/Barcode.java0000644000175000017500000004203212106516372030052 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt; import java.util.Iterator; /** * A Barcode is an ADT to replace the more general CompressableList * ADT. CompressableList provides list compression capabilites that allow * a list to be accessed by both the real index and a compressed index. * The compressed index corresponds to the index of the current value as * though no nulls existed in the list. * *

      This provides a huge performance boost over ArrayList on partially empty * lists. However, the CompressableList is one of the volatile implementation * classes for internal development and isn't the best structure for the current * usage. The GlazedLists use CompressableList to store only three values: * Boolean.TRUE, Boolean.FALSE, and null. As such, it was slower and more * memory intensive than it could be due to its general purpose design. * *

      The Barcode is designed such that a list of n elements of the same * colour will contain at most one node for BLACK and no nodes for WHITE. * This will improve the performance and scalability of the GlazedLists * which currently make use of CompressableList. * *

      Barcode does not support more than two values stored in the list. * Three different values are used by one of the GlazedLists at this time. * Until UniqueList is refactored to make use of only two values, Barcode * cannot completely replace CompressableList and they will exist in parallel. * *

      In an effort to maximize performance this ADT does NOT validate that arguments * passed to methods are valid in any way. While this adds inherent risk to * the use of this code, this is a volatile implementation class. As such, it * should only be used for internal GlazedList development. It is up to the * calling code to do any argument validation which may be necessary. * *

      Every effort has been made to squeeze the highest performance and smallest * footprint out of this data structure. These benefits hopefully don't come at * the cost of code clarity or maintainability. The memory usage of this ADT * is bound to the number of sequences of BLACK elements. WHITE elements * have no memory impact on the data structure. * * * @author Kevin Maltby * */ public final class Barcode { /** barcode colour constants */ public static final Object WHITE = Boolean.FALSE; public static final Object BLACK = Boolean.TRUE; /** the root of the underlying tree */ private BarcodeNode root = null; /** the size of the trailing whitespace */ private int whiteSpace = 0; /** the size of tree */ private int treeSize = 0; /** * Prints internal debug information for this barcode */ public void printDebug() { System.out.println("\nTotal Size: " + size()); System.out.println("Trailing Whitespace : " + whiteSpace); System.out.println("Tree Size: " + treeSize); System.out.println("Tree Structure:\n" + root); } /** * Validates the barcode's internal structure */ public void validate() { if(root != null) root.validate(); } /** * Gets the size of this barcode */ public int size() { return treeSize + whiteSpace; } /** * Whether or not this barcode is empty */ public boolean isEmpty() { return size() == 0; } /** * Gets the size of the white portion of this barcode */ public int whiteSize() { return root == null ? whiteSpace : root.whiteSize() + whiteSpace; } /** * Gets the size of the black portion of this barcode */ public int blackSize() { return root == null ? 0 : root.blackSize(); } /** * Gets the size of the given colour portion of this barcode */ public int colourSize(Object colour) { if(colour == WHITE) return whiteSize(); else return blackSize(); } /** * Inserts a sequence of the specified colour into the barcode */ public void add(int index, Object colour, int length) { if(colour == WHITE) addWhite(index, length); else addBlack(index, length); } /** * Inserts a sequence of white into the list */ public void addWhite(int index, int length) { if(length < 0) throw new IllegalStateException(); if(length == 0) return; // Adding to the trailing whitespace if(root == null || index >= treeSize) { whiteSpace += length; // Adding whitespace to the actual list } else { root.insertWhite(index, length); treeSizeChanged(); } } /** * Inserts a sequence of black into the list */ public void addBlack(int index, int length) { if(length < 0) throw new IllegalArgumentException(); if(length == 0) return; // Make a new root if(root == null) { root = new BarcodeNode(this, null, length, index); treeSize = index + length; whiteSpace -= index; // Add in the trailing whitespace } else if(index >= treeSize) { int movingWhitespace = index - treeSize; whiteSpace -= movingWhitespace; root.insertBlackAtEnd(length, movingWhitespace); treeSizeChanged(); // Add values to the actual list } else { root.insertBlack(index, length); treeSizeChanged(); } } /** * Gets the value in this list at the given index */ public Object get(int index) { if(getBlackIndex(index) == -1) return WHITE; return BLACK; } /** * Sets all of the values between index and index + length to either * WHITE or BLACK depending on the value of colour * * @param colour Determines which colour to set the values in the range * to. Valid values for colour are Barcode.WHITE and * Barcode.BLACK. */ public void set(int index, Object colour, int length) { if(length < 1) throw new IllegalArgumentException(); // The set affects the trailing whitespace int trailingChange = index > treeSize - 1 ? length : index + length - treeSize; if(trailingChange > 0) { if(colour == BLACK) { whiteSpace -= trailingChange; addBlack(index, trailingChange); } length -= trailingChange; if(length == 0) return; } // The set affects the list if(root != null) { root.set(index, colour, length); if(root != null) treeSizeChanged(); } } /** * Sets all of the values between index and index + length to WHITE */ public void setWhite(int index, int length) { set(index, WHITE, length); } /** * Sets all of the values between index and index + length to WHITE */ public void setBlack(int index, int length) { set(index, BLACK, length); } /** * Removes the values from the given index to index + length */ public void remove(int index, int length) { if(length < 1) throw new IllegalArgumentException(); // The remove affects the trailing whitespace int trailingChange = index > treeSize ? length : index + length - treeSize; if(trailingChange > 0) { whiteSpace -= trailingChange; length -= trailingChange; } // The remove occurs in the actual list if(root != null && index < treeSize) { int oldTreeSize = -1; while(length > 0) { oldTreeSize = treeSize; root.remove(index, length); if(root != null) treeSizeChanged(); length -= (oldTreeSize - treeSize); } if(root != null) treeSizeChanged(); } } /** * Clears the list */ public void clear() { treeSize = 0; whiteSpace = 0; root = null; } /** * Gets the root for this Barcode. This method is exposed for * Iterators on Barcode whose set() operations may create a * root node on a Barcode where none existed. */ BarcodeNode getRootNode() { return root; } /** * Sets the root for this list. This method is exposed for the * BarcodeNode in the event that the list's root is involved in * an AVL rotation. */ void setRootNode(BarcodeNode root) { this.root = root; if(root == null) treeSize = 0; } /** * Gets the size of the underlying tree structure for this Barcode. This * method is exposed for Iterators on Barcode who would otherwise have to * maintain the state of treeSize themselves. */ int treeSize() { return treeSize; } /** * Notifies the list that the underlying list size has changed. This method * is exposed for BarcodeNode to propagate size adjustments. */ void treeSizeChanged() { treeSize = root.size(); } /** * Gets the real index of an element given the black index or white index. */ public int getIndex(int colourIndex, Object colour) { // Get the real index of a WHITE element if(colour == WHITE) { // There are no black elements if(root == null) { return colourIndex; // Retrieving from the trailing whitespace with a tree } else if(colourIndex >= root.whiteSize()) { return colourIndex - root.whiteSize() + treeSize; // The index maps to an element in the tree } else { return root.getIndexByWhiteIndex(colourIndex); } // Get the real index of a BLACK element } else { return root.getIndexByBlackIndex(colourIndex); } } /** * Gets the colour-based index of the element with the given real * index. * * @param index the real index. * @param colour the colour to retrieve the colour-based index for. * * @return The colour-based index of the element at index or -1 if that * element does not match the given colour. */ public int getColourIndex(int index, Object colour) { if(colour == WHITE) return getWhiteIndex(index); else return getBlackIndex(index); } /** * Gets the white index of the node with the given real * index. * * @param index specifies the real index. * * @return The white index of the element at index or -1 if that element is BLACK. */ public int getWhiteIndex(int index) { // Get a white index from the list if(root != null && index < treeSize) return root.getWhiteIndex(index); // There are only white indexes in the trailing whitespace else { if(root != null) return index - treeSize + root.whiteSize(); else return index; } } /** * Gets the black index of the node with the given real * index. * * @param index specifies the real index. * * @return The black index of the element at index or -1 if that element is WHITE. */ public int getBlackIndex(int index) { if(root != null && index < treeSize) return root.getBlackIndex(index); else return -1; } /** * Gets the colour-based index of the element with the given real index or * the colour-based index of the previous or next element matching the given * colour if that element is of the opposite colour. * * @param left true for opposite colour elements to return the colour-based * index of the first matching element before it in the list. Such * values will range from -1 through size()-1. * False for opposite colour elements to return the colour-based index * of the first matching element after it in the list. Such values will * range from 0 through size(). */ public int getColourIndex(int index, boolean left, Object colour) { if(colour == WHITE) return getWhiteIndex(index, left); else return getBlackIndex(index, left); } /** * Gets the white index of the element with the given real index or * the white index of the previous or next WHITE element if that element * is BLACK. * * @param left true for BLACK elements to return the white index of the * first WHITE element before it in the list. Such values will range * from -1 through size()-1. False for BLACK * elements to return the white index of the first WHITE element after * it in the list. Such values will range from 0 through * size(). */ public int getWhiteIndex(int index, boolean left) { if(root == null) return index; else if(index >= treeSize) return index - treeSize + root.whiteSize(); else return root.getWhiteIndex(index, left); } /** * Gets the black index of the element with the given real index or * the black index of the previous or next BLACK element if that element * is WHITE. * * @param left true for WHITE elements to return the black index of the * first BLACK element before it in the list. Such values will range * from -1 through size()-1. False for WHITE * elements to return the black index of the first BLACK element after * it in the list. Such values will range from 0 through * size(). */ public int getBlackIndex(int index, boolean left) { // there is no tree if(root == null) { if(left) return -1; else return 0; // if it is beyond the tree } else if(index >= treeSize) { if(left) return root.blackSize() - 1; return root.blackSize(); // get from the tree } else { return root.getBlackIndex(index, left); } } /** * Gets the index of the WHITE element at whiteIndex relative to the WHITE * element after the previous BLACK element or the start of the list if no * BLACK element exists before this node. */ public int getWhiteSequenceIndex(int whiteIndex) { // There is no tree sequence is beyond the tree if(root == null) { return whiteIndex; // The sequence is beyond the tree } else if(whiteIndex >= root.whiteSize()) { return whiteIndex - root.whiteSize(); // lookup the sequence index within the tree } else { return root.getWhiteSequenceIndex(whiteIndex); } } /** * This method exists for CollectionList which needs a way to call * getBlackIndex(index, true) with a white-centric index. */ public int getBlackBeforeWhite(int whiteIndex) { // there is no tree if(root == null) { return -1; // starting from beyond the tree } else if(whiteIndex >= root.whiteSize()) { return root.blackSize() - 1; // the index is from within the tree } else { return root.getBlackBeforeWhite(whiteIndex); } } /** * Finds a sequence of the given colour that is at least size elements * in length. * * @param size the minimum size of a matching sequence. * * @return The natural index of the first element in the sequence or -1 if * no sequences of that length exist. */ public int findSequenceOfMinimumSize(int size, Object colour) { // there is no tree if(root == null) { // There are no black sequences if(colour == BLACK) return -1; // The trailing whitespace matches else if(whiteSpace >= size) return 0; // nothing matches else return -1; // focus only within the tree } else if(colour == BLACK) { return root.findSequenceOfMinimumSize(size, colour); // check the tree first, if it fails check the trailing whitespace } else { int result = root.findSequenceOfMinimumSize(size, colour); if(result == -1 && whiteSpace >= size) result = treeSize; return result; } } /** * Provides a specialized {@link Iterator} that iterates over a * {@link Barcode} to provide high performance access to {@link Barcode} * functionality. */ public BarcodeIterator iterator() { return new BarcodeIterator(this); } @Override public String toString() { StringBuffer result = new StringBuffer(); for(BarcodeIterator bi = iterator(); bi.hasNext(); ) { result.append(bi.next() == Barcode.BLACK ? "X" : "_"); } return result.toString(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/gnutrove/0000755000175000017500000000000012106516372027520 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/SparseListNode.java0000644000175000017500000006421312106516372031417 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt; // for iterators import java.util.Iterator; import java.util.NoSuchElementException; /** * A SparseListNode models a node in an SparseList. This class * does the bulk of the heavy lifting for SparseList. * * @author Kevin Maltby * */ public final class SparseListNode { /** the parent node */ private SparseListNode parent; /** the tree that this node is a member of */ private SparseList host; /** the left and right child nodes */ private SparseListNode left = null; private SparseListNode right = null; /** the size of the left subtree and right subtrees including empty space */ private int totalRightSize = 0; private int totalLeftSize = 0; /** the amount of empty space that preceeds this node */ private int emptySpace = 0; /** the height of this subtree */ private int height = 1; /** the value at this node */ private Object value = null; /** * Creates a new SparseListNode with the specified parent node, host tree and value. */ SparseListNode(SparseList host, SparseListNode parent, Object value) { this.host = host; this.parent = parent; this.value = value; } /** * This is a convienience constructor for creating a new SparseListNode * with a given value and amount of preceeding empty space. */ SparseListNode(SparseList host, SparseListNode parent, Object value, int emptySpace) { this(host, parent, value); this.emptySpace = emptySpace; } /** * Returns the size of the subtree rooted at this node */ int size() { return totalLeftSize + emptySpace + totalRightSize + 1; } /** * Inserts a value into the host tree. */ void insert(int index, Object value) { int localizedIndex = index - totalLeftSize; // Recurse to the Left adjusting sizes as you go if(localizedIndex < 0) { totalLeftSize++; left.insert(index, value); // Recurse to the Right adjusting sizes as you go } else if(localizedIndex > emptySpace) { totalRightSize++; right.insert(localizedIndex - emptySpace - 1, value); // Insert in the middle of the empty space } else if(localizedIndex < emptySpace) { emptySpace -= localizedIndex; totalLeftSize += localizedIndex + 1; if(left == null) { left = new SparseListNode(host, this, value, localizedIndex); ensureAVL(); } else { left.insertAtEnd(value, localizedIndex); } // Insert at the same index as this node } else { insertAtThisNode(value); } } /** * Inserts a value into the host tree at an index where a value already * exists. This will offset the current node's value by 1. */ private void insertAtThisNode(Object value) { SparseListNode replacement = new SparseListNode(host, parent, value, emptySpace); emptySpace = 0; replacement.height = height; height = 1; replacement.totalRightSize = totalRightSize + 1; // Since the left side will be unaffected by this insert, just 'move' it onto the replacement replacement.left = left; if(left != null) { replacement.left.parent = replacement; replacement.totalLeftSize = totalLeftSize; totalLeftSize = 0; left = null; } // Notify the host tree that the root has changed if(parent == null) host.setRootNode(replacement); // Replace this with the new child in the parent else parent.replace(this, replacement); // Move this to the right child of the replacement if(right == null) { parent = replacement; replacement.right = this; replacement.ensureAVL(); // Move this to be the smallest node in the right subtree } else { replacement.right = right; replacement.right.parent = replacement; totalRightSize = 0; right = null; replacement.right.moveToSmallest(this); } } /** * Inserts a value at the end of the tree rooted at this. */ void insertAtEnd(Object value, int leadingNulls) { // Adjust sizes during recursion totalRightSize += leadingNulls + 1; // Recurse to the right if(right != null) right.insertAtEnd(value, leadingNulls); // Insert on the right else { right = new SparseListNode(host, this, value, leadingNulls); ensureAVL(); } } /** * Inserts multiple null values as empty space in the host tree. */ void insertEmptySpace(int index, int length) { int localizedIndex = index - totalLeftSize; // Recurse to the Left if(localizedIndex < 0) { totalLeftSize += length; left.insertEmptySpace(index, length); // Recurse to the Right } else if(localizedIndex > emptySpace) { totalRightSize += length; right.insertEmptySpace(localizedIndex - emptySpace - 1, length); // Insert at this node } else { emptySpace += length; } } /** * Moves a given node to be the smallest node in the subtree rooted at * this. */ private void moveToSmallest(SparseListNode movingNode) { // Adjust sizes during recursion totalLeftSize += movingNode.emptySpace + 1; // Recurse to the left if(left != null) { left.moveToSmallest(movingNode); // Add the node as a left child of this } else { // Add the moving node on the left movingNode.parent = this; left = movingNode; // Adjust heights and rotate if necessary ensureAVL(); } } /** * Gets the index of the value in this node. This is NOT the index of the * first null indexed by this node. */ public int getIndex() { if(parent != null) return parent.getIndex(this) + totalLeftSize + emptySpace; return totalLeftSize + emptySpace; } private int getIndex(SparseListNode child) { // the child is on the left, return the index recursively if(child == left) { if(parent != null) return parent.getIndex(this); return 0; // the child is on the right, return the index recursively } else { if(parent != null) return parent.getIndex(this) + totalLeftSize + emptySpace + 1; return totalLeftSize + emptySpace + 1; } } /** * Gets the node with the given index, or null if that index is empty. */ SparseListNode getNode(int index) { int localizedIndex = index - totalLeftSize; // Recurse to the Left if(localizedIndex < 0) return left.getNode(index); // Recurse to the Right else if(localizedIndex > emptySpace) return right.getNode(localizedIndex - emptySpace - 1); // Get a null from the middle of the empty space else if(localizedIndex < emptySpace) return null; // Get this node else return this; } /** * Gets the value of this node. */ public Object getValue() { return value; } /** * Sets the value of this node and returns the replaced value. * If the value is set to null, this node will be removed from * the tree and clear() will be called. */ public Object setValue(Object value) { // Just a simple set operation if(value != null) { Object oldValue = this.value; this.value = value; return oldValue; // This node must be removed and replaced with empty space } else { emptySpace++; return unlink(); } } /** * Sets the value of the node at a given index. */ Object set(int index, Object value) { int localizedIndex = index - totalLeftSize; // Recurse to the Left if(localizedIndex < 0) { return left.set(index, value); // Recurse to the Right } else if(localizedIndex > emptySpace) { return right.set(localizedIndex - emptySpace - 1, value); // Set a value in the middle of the empty space } else if(localizedIndex < emptySpace) { if(value == null) return null; emptySpace--; insert(index, value); return null; // Set the value in this node } else { return setValue(value); } } /** * Removes and returns the value at the given index. */ Object remove(int index) { int localizedIndex = index - totalLeftSize; // Recurse to the Left if(localizedIndex < 0) { totalLeftSize--; return left.remove(index); // Recurse to the Right } else if(localizedIndex > emptySpace) { totalRightSize--; return right.remove(localizedIndex - emptySpace - 1); // Remove from the middle of the empty space } else if(localizedIndex < emptySpace) { emptySpace--; return null; // Remove from the value in this node } else { return unlink(); } } /** * Unlinks this node from the tree and clears it. */ private Object unlink() { int index = -1; SparseListNode replacement = null; boolean isLeftChild = false; // Two children exist if(right != null && left != null) { return unlinkFromTwoChildren(); // Only a right child exists } else if(right != null) { replacement = right; replacement.parent = parent; replacement.emptySpace += emptySpace; // A left child or no child exists, which are handled almost the same way } else { // Only a left child exists if(left != null) { replacement = left; replacement.parent = parent; // No children exist } else replacement = null; // Parent is null so empty space moves to the trailing nulls iff it is significant if(parent == null) index = emptySpace == 0 ? -1 : host.size(); // This is a left child so empty space goes to the parent else if(parent.left == this) { isLeftChild = true; parent.emptySpace += emptySpace; parent.totalLeftSize -= emptySpace; // Find the index of the empty space to insert it later iff it is significant } else if(emptySpace != 0) index = getIndex() - emptySpace; } // This wasn't the root of the tree if(parent != null) { parent.replace(this, replacement); parent.ensureAVL(); // This was the root so replace the reference in the host } else { host.setRootNode(replacement); } // Empty space needs to be reinserted elsewhere if(index != -1) { if(parent != null) parent.prepareForReinsert(isLeftChild, emptySpace); host.addNulls(index, emptySpace); } return clear(); } /** * Unlinks this node in the special case where this node has both * a left and right child. */ private Object unlinkFromTwoChildren() { // Get the replacement from the right subtree SparseListNode replacement = right.pruneSmallestChild(); SparseListNode repParent = replacement.parent; replacement.emptySpace += emptySpace; replacement.height = height; // left subtree is unaffected so move it and cache sizes replacement.left = left; replacement.left.parent = replacement; replacement.totalLeftSize = totalLeftSize; // adjust replacement's parent link to this.parent replacement.parent = parent; // Notify the host tree that the root has changed if(parent == null) host.setRootNode(replacement); // Replace this with the new child in the parent else parent.replace(this, replacement); // The smallest node is the right child of this if(repParent == this) replacement.ensureAVL(); // The smallest node is a left child in the right subtree else { // linking on the right subtree needs updating repParent.left = replacement.right; if(repParent.left != null) repParent.left.parent = repParent; repParent.totalLeftSize = replacement.totalRightSize; replacement.right = right; replacement.right.parent = replacement; replacement.totalRightSize = replacement.right.size(); repParent.ensureAVL(); } return clear(); } /** * Prunes and returns the smallest child of the subtree rooted at this. * Tree references are maintained out of necessity of the calling method, * but sizes in the subtree are corrected accordingly. */ private SparseListNode pruneSmallestChild() { // Recurse to the left if(left != null) { SparseListNode prunedNode = left.pruneSmallestChild(); totalLeftSize -= prunedNode.emptySpace + 1; return prunedNode; // return this node } else return this; } /** * Prepares this tree to have length nulls reinserted. This method * recurses up the tree altering sizes so that the tree is in a * consistent state for addNulls() to be called on the host tree. */ private void prepareForReinsert(boolean leftChild, int length) { // left subtree is smaller if(leftChild) totalLeftSize -= length; // right subtree is smaller else totalRightSize -= length; // recurse up the tree to the root if(parent != null) parent.prepareForReinsert(parent.left == this, length); // Notify the tree size has changed else host.treeSizeChanged(); } /** * Clears this node and returns the value it had. */ private Object clear() { // clear the children left = null; totalLeftSize = 0; right = null; totalRightSize = 0; // clear this node and return value host = null; parent = null; emptySpace = 0; height = -1; Object thisValue = value; value = null; return thisValue; } /** * Ensures that the tree satisfies the AVL property. It is sufficient to * recurse up the tree only as long as height recalculations are needed. * As such, this method is intended to be called only on a node whose height * may be out of sync due to an insertion or deletion. For example, calling * this method on a leaf node will not guarantee that this tree satisfies the * AVL property as it will not recurse. */ private void ensureAVL() { int oldHeight = height; recalculateHeight(); avlRotate(); // If adjustments were made, recurse up the tree if(height != oldHeight && parent != null) parent.ensureAVL(); } /** * Replaces a given child with the replacement node */ private void replace(SparseListNode child, SparseListNode replacement) { // replacing the left child if(child == left) left = replacement; // Replacing the right child else right = replacement; } /** * Recalculates the cached height at this level. */ private void recalculateHeight() { int leftHeight = left == null ? 0 : left.height; int rightHeight = right == null ? 0 : right.height; height = 1 + Math.max(leftHeight, rightHeight); } /** * Determines if AVL rotations are required and performs them if they are. */ private void avlRotate() { // look up the left and right heights int leftHeight = (left != null ? left.height : 0); int rightHeight = (right != null ? right.height : 0); // rotations will be on the left if(leftHeight - rightHeight >= 2) { // determine if a double rotation is necessary int leftLeftHeight = (left.left != null ? left.left.height : 0); int leftRightHeight = (left.right != null ? left.right.height : 0); // Perform first half of double rotation if necessary if(leftRightHeight > leftLeftHeight) left.rotateRight(); // Do the rotation for this node rotateLeft(); // rotations will be on the right } else if(rightHeight - leftHeight >= 2) { // determine if a double rotation is necessary int rightLeftHeight = (right.left != null ? right.left.height : 0); int rightRightHeight = (right.right != null ? right.right.height : 0); // Perform first half of double rotation if necessary if(rightLeftHeight > rightRightHeight) right.rotateLeft(); // Do the rotation for this node rotateRight(); } } /** * AVL-Rotates this subtree with its left child. */ private void rotateLeft() { // The replacement node is on the left SparseListNode replacement = left; // take the right child of the replacement as my left child left = replacement.right; totalLeftSize = replacement.totalRightSize; if(replacement.right != null) replacement.right.parent = this; // set the right child of the replacement to this replacement.right = this; replacement.totalRightSize = size(); // set the replacement's parent to my parent and mine to the replacement if(parent != null) parent.replace(this, replacement); // set a new tree root else host.setRootNode(replacement); // fix parent links on this and the replacement replacement.parent = parent; parent = replacement; // recalculate height at this node recalculateHeight(); // require height to be recalculated on the replacement node replacement.height = 0; } /** * AVL-Rotates this subtree with its right child. */ private void rotateRight() { // The replacement node is on the right SparseListNode replacement = right; // take the left child of the replacement as my right child right = replacement.left; totalRightSize = replacement.totalLeftSize; if(replacement.left != null) replacement.left.parent = this; // set the left child of the replacement to this replacement.left = this; replacement.totalLeftSize = size(); // set the replacement's parent to my parent and mine to the replacement if(parent != null) parent.replace(this, replacement); // set a new tree root else host.setRootNode(replacement); // fix parent links on this and the replacement replacement.parent = parent; parent = replacement; // recalculate height at this node recalculateHeight(); // require height to be recalculated on the replacement node replacement.height = 0; } /** * For debugging purposes. */ @Override public String toString() { return "[ " + left + " <"+emptySpace+"> " + value +" <"+height+"> " + right + " ]"; } /** * Corrects all the cached sizes up the tree by the given offsets starting * from this so an Iterator can perform a fast remove. */ private void correctSizes(int sizeChange) { if(parent != null) { // left subtree has changed in size if(parent.left == this) totalLeftSize += sizeChange; // right subtree has changed in size else totalRightSize += sizeChange; // recurse up the tree to the root parent.correctSizes(sizeChange); // Notify the host tree that the size has changed } else host.treeSizeChanged(); } /** * A specialized Iterator that will significantly outperform the default * one provided by AbstractList when acting on this ADT. */ final static class SparseListIterator implements Iterator { /** the current SparseListNode being inspected */ private SparseListNode currentNode = null; /** the number of times the current node has been requested */ private int timesRequested = -1; /** a reference to the SparseList for removal of trailing nulls */ private SparseList sparseList = null; /** the size of the actual tree within the SparseList*/ private int treeSize = 0; /** the size of the list */ private int size = 0; /** the current index being inspected */ private int index = -1; /** * Creates a new Iterator that is optimized for SparseLists. */ SparseListIterator(SparseList sparseList, SparseListNode root) { // move the Iterator to the start position. if(root != null) { this.treeSize = root.size(); currentNode = root; while(currentNode.left != null) { currentNode = currentNode.left; } } this.sparseList = sparseList; this.size = sparseList.size(); } /** * Returns whether or not there are more values in the SparseList to * iterate over. */ public boolean hasNext() { if(index >= treeSize - 1 && index == size - 1) { return false; } return true; } /** * Gets the next value in this SparseList. */ public Object next() { // iterate on this node timesRequested++; index++; // handle the empty tree case if(currentNode == null) { // beyond the tree in the trailing nulls if(index < size) { return null; // at the end of the list } else { throw new NoSuchElementException(); } // at the edge of the current node } else if(timesRequested > currentNode.emptySpace) { // move to the next node if(index < treeSize) { findNextNode(); timesRequested = 0; // act on the trailing nulls } else { // beyond the tree in the trailing nulls if(index < size) { return null; // at the end of the list } else { throw new NoSuchElementException(); } } } // next() was a null value if(timesRequested < currentNode.emptySpace) { return null; // next() was the value of this node } else if(timesRequested == currentNode.emptySpace) { return currentNode.value; // the iterator is out of state } else { throw new IllegalStateException(); } } /** * Removes the current value at the Iterator from the SparseList. * * @throws UnsupportedOperationException This feature is not yet implemented. * */ public void remove() { // handle the uninitialized iterator case if(timesRequested == -1) { throw new IllegalStateException("Cannot remove() without a prior call to next()"); // remove from the trailing nulls } else if(currentNode == null || index >= treeSize) { sparseList.remove(index); // remove a null } else if(timesRequested < currentNode.emptySpace) { currentNode.correctSizes(-1); currentNode.emptySpace--; // remove a value } else if(timesRequested == currentNode.emptySpace) { currentNode.correctSizes(-1); SparseListNode nodeToRemove = currentNode; findNextNode(); timesRequested = -1; nodeToRemove.unlink(); // the iterator is out of state } else { throw new IllegalStateException(); } } /** * Finds the next node in the tree. */ private void findNextNode() { // go into the right subtree for the next node if(currentNode.right != null) { currentNode = currentNode.right; while(currentNode.left != null) { currentNode = currentNode.left; } // go to the parent for the next node } else if(currentNode.parent.left == currentNode) { currentNode = currentNode.parent; // get out of the right subtree } else if(currentNode.parent.right == currentNode) { // move to the top of the current subtree while(currentNode.parent.right == currentNode) { currentNode = currentNode.parent; } // Move up one more node to leave the subtree currentNode = currentNode.parent; // the iterator is out of state } else { throw new IllegalStateException(); } } /** * Finds the previous node in the tree. */ private void findPreviousNode() { throw new UnsupportedOperationException("Not implemented yet."); } @Override public String toString() { return "Accessing " + currentNode + " for the " + timesRequested + " time."; } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/AgedNodeComparator.java0000644000175000017500000000157612106516372032221 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt; // to implement the Comparator interface import java.util.Comparator; /** * A Comparator for sorting AgedNodes * * @author Kevin Maltby */ public final class AgedNodeComparator implements Comparator { public final int compare(Object o1, Object o2) { AgedNode node1 = (AgedNode)o1; AgedNode node2 = (AgedNode)o2; long difference = node1.getTimestamp() - node2.getTimestamp(); if(difference < 0) { return -1; } else if(difference > 0) { return 1; } else { return 0; } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/IdentityMultimap.java0000644000175000017500000000225112106516372032014 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt; import java.util.ArrayList; import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; /** * A poor man's multimap, used only to reduce the complexity code that deals * with these otherwise painful structures. * * @author Jesse Wilson */ public class IdentityMultimap extends IdentityHashMap> { public void addValue(K key, V value) { List values = super.get(key); if(values == null) { values = new ArrayList(2); put(key, values); } values.add(value); } @Override public List get(Object key) { List values = super.get(key); return values == null ? Collections.emptyList() : values; } public int count(Object key) { List values = super.get(key); return values == null ? 0 : values.size(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/BarcodeNode.java0000644000175000017500000012740712106516372030672 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt; /** * A BarcodeNode models a node in an Barcode. This class * does the bulk of the heavy lifting for Barcode. * * @author Kevin Maltby * */ final class BarcodeNode { /** the parent node */ BarcodeNode parent; /** the tree that this node is a member of */ private Barcode host; /** the left and right child nodes */ BarcodeNode left = null; BarcodeNode right = null; /** the size of the black portion of the left and right subtrees */ private int blackLeftSize = 0; private int blackRightSize = 0; /** the total size of the left and right subtrees */ private int treeLeftSize = 0; private int treeRightSize = 0; /** the amount of empty space that precedes this node */ int whiteSpace = 0; /** the number of values represented by this node */ int rootSize = 1; /** the height of this subtree */ private int height = 1; /** * Creates a new BarcodeNode with the specified parent node and host tree. */ private BarcodeNode(Barcode host, BarcodeNode parent) { this.host = host; this.parent = parent; } /** * This is a convenience constructor for creating a new BarcodeNode * with a given number of values and amount of preceding empty space. */ BarcodeNode(Barcode host, BarcodeNode parent, int values, int whiteSpace) { this(host, parent); this.whiteSpace = whiteSpace; this.rootSize = values; } /** * Returns the size of the subtree rooted at this node */ int size() { return treeLeftSize + whiteSpace + rootSize + treeRightSize; } /** * Returns the size of the black portion of the subtree rooted at this */ int blackSize() { return blackLeftSize + rootSize + blackRightSize; } /** * Returns the size of the white portion of the subtree rooted at this */ int whiteSize() { return (treeLeftSize - blackLeftSize) + whiteSpace + (treeRightSize - blackRightSize); } /** * Inserts multiple values into the host tree */ void insertBlack(int index, int length) { int localIndex = index - treeLeftSize; // Recurse to the Left adjusting sizes as you go if(localIndex < 0) { blackLeftSize += length; treeLeftSize += length; left.insertBlack(index, length); // Recurse to the Right adjusting sizes as you go } else if(localIndex > whiteSpace + rootSize) { blackRightSize += length; treeRightSize += length; right.insertBlack(localIndex - whiteSpace - rootSize, length); // The new values should be compressed into this node } else if(localIndex == whiteSpace + rootSize) { rootSize += length; // Insert in the middle of the empty space } else if(localIndex < whiteSpace) { whiteSpace -= localIndex; blackLeftSize += length; treeLeftSize += localIndex + length; if(left == null) { left = new BarcodeNode(host, this, length, localIndex); ensureAVL(); } else { left.insertBlackAtEnd(length, localIndex); } // Insert within this node } else { rootSize += length; } } /** * Inserts a value at the end of the tree rooted at this. */ void insertBlackAtEnd(int values, int leadingWhite) { // Recurse to the right if(right != null) { blackRightSize += values; treeRightSize += values + leadingWhite; right.insertBlackAtEnd(values, leadingWhite); // Insert on the right } else { if(leadingWhite == 0) { rootSize += values; } else { blackRightSize += values; treeRightSize += values + leadingWhite; right = new BarcodeNode(host, this, values, leadingWhite); ensureAVL(); } } } /** * Inserts multiple null values as empty space in the host tree. */ void insertWhite(int index, int length) { int localIndex = index - treeLeftSize; // Recurse to the Left if(localIndex < 0) { treeLeftSize += length; left.insertWhite(index, length); // Recurse to the Right } else if(localIndex > whiteSpace + rootSize - 1) { treeRightSize += length; right.insertWhite(localIndex - whiteSpace - rootSize, length); // Insert in the whitespace for this node } else if(localIndex <= whiteSpace) { whiteSpace += length; // Insert within this node } else { localIndex -= whiteSpace; int movingRoot = rootSize - localIndex; rootSize = localIndex; blackRightSize += movingRoot; treeRightSize += movingRoot + length; if(right == null) { right = new BarcodeNode(host, this, movingRoot, length); ensureAVL(); } else { BarcodeNode node = new BarcodeNode(host, null, movingRoot, length); right.moveToSmallest(node); } } } /** * Moves a given node to be the smallest node in the subtree rooted at * this. */ private void moveToSmallest(BarcodeNode movingNode) { // Recurse to the left if(left != null) { blackLeftSize += movingNode.rootSize; treeLeftSize += movingNode.whiteSpace + movingNode.rootSize; left.moveToSmallest(movingNode); // Add the node as a left child of this } else { // This node will be compressed now if(whiteSpace == 0) { rootSize += movingNode.rootSize; whiteSpace += movingNode.whiteSpace; movingNode.clear(); // Add the moving node on the left } else { blackLeftSize += movingNode.rootSize; treeLeftSize += movingNode.whiteSpace + movingNode.rootSize; movingNode.parent = this; left = movingNode; ensureAVL(); } } } /** * Gets the white-centric index from the given list index or returns -1 * if that list index has a value of Barcode.BLACK. */ int getWhiteIndex(int index) { return getWhiteIndex(index, 0); } private int getWhiteIndex(int index, int accumulation) { int localIndex = index - treeLeftSize; // Recurse to the Left if(localIndex < 0) return left.getWhiteIndex(index, accumulation); // Recurse to the Right else if(localIndex > whiteSpace + rootSize - 1) { accumulation += (treeLeftSize - blackLeftSize) + whiteSpace; return right.getWhiteIndex(localIndex - whiteSpace - rootSize, accumulation); // Get the white index from this node } else if(localIndex < whiteSpace) return accumulation + (treeLeftSize - blackLeftSize) + localIndex; // Get the white index from the black portion of this node else return -1; } /** * Gets the black-centric index from the given list index or returns -1 * if that list index has a value of Barcode.WHITE. */ int getBlackIndex(int index) { return getBlackIndex(index, 0); } private int getBlackIndex(int index, int accumulation) { int localIndex = index - treeLeftSize; // Recurse to the Left if(localIndex < 0) return left.getBlackIndex(index, accumulation); // Recurse to the Right else if(localIndex > whiteSpace + rootSize - 1) { return right.getBlackIndex(localIndex - whiteSpace - rootSize, accumulation + blackLeftSize + rootSize); // Get the black index from the white portion of this node } else if(localIndex < whiteSpace) return -1; // Get the black index from this node else return accumulation + blackLeftSize + localIndex - whiteSpace; } /** * Gets the white-centric index from the given list index. * * @param lead true for an index with a value of Barcode.BLACK to return * the white-centric index of the previous white value in the Barcode. * False for an index with a value of Barcode.BLACK to return * the white-centric index of the next white value in the Barcode. */ public int getWhiteIndex(int index, boolean lead) { int localIndex = index - treeLeftSize; // Recurse to the Left if(localIndex < 0) return left.getWhiteIndex(index, lead); // Recurse to the Right else if(localIndex > whiteSpace + rootSize - 1) { return right.getWhiteIndex(localIndex - whiteSpace - rootSize, lead) + treeLeftSize - blackLeftSize + whiteSpace; // Get the white index from within this node } else if(localIndex < whiteSpace) { return treeLeftSize - blackLeftSize + localIndex; // Get the white index based on lead } else { if(lead) return treeLeftSize - blackLeftSize + whiteSpace - 1; return treeLeftSize - blackLeftSize + whiteSpace; } } /** * Gets the black-centric index from the given list index. * * @param lead true for an index with a value of Barcode.WHITE to return * the black-centric index of the previous black value in the Barcode. * False for an index with a value of Barcode.WHITE to return * the black-centric index of the next black value in the Barcode. */ public int getBlackIndex(int index, boolean lead) { int localIndex = index - treeLeftSize; // Recurse to the Left if(localIndex < 0) return left.getBlackIndex(index, lead); // Recurse to the Right else if(localIndex > whiteSpace + rootSize - 1) { return right.getBlackIndex(localIndex - whiteSpace - rootSize, lead) + blackLeftSize + rootSize; // Get the black index based on lead } else if(localIndex < whiteSpace) { if(lead) return blackLeftSize - 1; return blackLeftSize; // Get the black index at this node } else return blackLeftSize + localIndex - whiteSpace; } /** * Gets the list index from a given white-centric index. */ public int getIndexByWhiteIndex(int whiteIndex) { int localIndex = whiteIndex - (treeLeftSize - blackLeftSize); // Recurse to the Left if(localIndex < 0) return left.getIndexByWhiteIndex(whiteIndex); // Recurse to the Right else if(localIndex >= whiteSpace) { return right.getIndexByWhiteIndex(localIndex - whiteSpace) + treeLeftSize + whiteSpace + rootSize; // Get the list index from this node } else return treeLeftSize + localIndex; } /** * Gets the list index from a given black-centric index. */ public int getIndexByBlackIndex(int blackIndex) { int localIndex = blackIndex - blackLeftSize; // Recurse to the Left if(localIndex < 0) return left.getIndexByBlackIndex(blackIndex); // Recurse to the Right else if(localIndex >= rootSize) { return right.getIndexByBlackIndex(localIndex - rootSize) + treeLeftSize + whiteSpace + rootSize; // Get the list index from this node } else return treeLeftSize + whiteSpace + localIndex; } /** * Gets the sequence relative index given a white-centric index. */ public int getWhiteSequenceIndex(int whiteIndex) { int localIndex = whiteIndex - (treeLeftSize - blackLeftSize); // Recurse to the Left if(localIndex < 0) return left.getWhiteSequenceIndex(whiteIndex); // Recurse to the Right else if(localIndex >= whiteSpace) { return right.getWhiteSequenceIndex(localIndex - whiteSpace); // once the recursion is done you have the relative index } else return localIndex; } /** * This method exists for CollectionList which needs a way to call * getBlackIndex(index, true) with a white-centric index. */ public int getBlackBeforeWhite(int whiteIndex) { int localIndex = whiteIndex - (treeLeftSize - blackLeftSize); // Recurse to the Left if(localIndex < 0) return left.getBlackBeforeWhite(whiteIndex); // Recurse to the Right else if(localIndex >= whiteSpace) { return right.getBlackBeforeWhite(localIndex - whiteSpace) + blackLeftSize + rootSize; // Get the black index before this node } else { return blackLeftSize - 1; } } /** * Finds a sequence of the given colour that is at least size elements * in length. * * @param size the minimum size of a matching sequence. * * @return The natural index of the first element in the sequence or -1 if * no sequences of that length exist. */ public int findSequenceOfMinimumSize(int size, Object colour) { return findFirstFitSequence(size, colour, 0); } /** * The depth-first, FIRST FIT implementation. */ private int findFirstFitSequence(int size, Object colour, int accumulation) { int result = -1; // Recurse to the Left if(left != null) { result = left.findFirstFitSequence(size, colour, accumulation); } // Inspect this node if(result == -1) { // Looking for a WHITE sequence if(colour == Barcode.WHITE && size <= whiteSpace) { return accumulation + treeLeftSize; // Looking for a BLACK sequence } else if(colour == Barcode.BLACK && size <= rootSize) { return accumulation + treeLeftSize + whiteSpace; } } // Recurse to the Right if(result == -1 && right != null) { result = right.findFirstFitSequence(size, colour, accumulation + treeLeftSize + whiteSpace + rootSize); } return result; } /** * Sets the values from index to index + length. */ void set(int index, Object value, int length) { if(length == 1) setBaseCase(index, index, value); else set(index, index, value, length); } private void set(int absoluteIndex, int localIndex, Object value, int length) { int localizedIndex = localIndex - treeLeftSize; // Recurse to the Left if(localizedIndex < 0) { left.set(absoluteIndex, localIndex, value, length); // Recurse to the Right } else if(localizedIndex > whiteSpace + rootSize - 1) { right.set(absoluteIndex, localizedIndex - whiteSpace - rootSize, value, length); // Set values on this node to white } else if(value == Barcode.WHITE) { setWhite(absoluteIndex, localizedIndex, length); // Set values on this node to black } else { setBlack(absoluteIndex, localizedIndex, length); } } void setWhite(int absoluteIndex, int localIndex, int length) { int endIndex = localIndex + length - 1; // Set only whitespace so no change at all if(endIndex < whiteSpace) { // Do Nothing // Set only within the black } else if(localIndex > whiteSpace - 1) { int rootChange = Math.min(length, whiteSpace + rootSize - localIndex); // This node will be removed if(rootSize == rootChange) { whiteSpace += rootChange; rootSize = 0; correctSizes(-rootChange, 0); unlink(absoluteIndex - localIndex); // Update root and add white space } else { rootSize -= rootChange; if(localIndex < whiteSpace + rootSize) { correctSizes(-rootChange, 0); insertWhite(localIndex + treeLeftSize, rootChange); } else { correctSizes(-rootChange, -rootChange); host.addWhite(absoluteIndex, rootChange); } } // Set is larger than just this node if(rootChange != length) { host.remove(absoluteIndex + rootChange, length - rootChange); host.addWhite(absoluteIndex + rootChange, length - rootChange); } // Set both black and white } else if(localIndex < whiteSpace + 1 && endIndex < whiteSpace + rootSize) { int rootChange = Math.min(length, whiteSpace + rootSize - localIndex) + (localIndex - whiteSpace); rootSize -= rootChange; whiteSpace += rootChange; correctSizes(-rootChange, 0); // Set this entire node to white } else { whiteSpace += rootSize; int localLength = whiteSpace - localIndex; unlink(absoluteIndex - localIndex); if(localLength != length) { host.remove(absoluteIndex + localLength, length - localLength); host.addWhite(absoluteIndex + localLength, length - localLength); } } } void setBlack(int absoluteIndex, int localIndex, int length) { int endIndex = localIndex + length - 1; int localLength = Math.min(length, whiteSpace + rootSize - localIndex); // Set only black so no change at this node if(localIndex > whiteSpace - 1) { // Do Nothing // Set some or all white to black } else if(endIndex > whiteSpace - 1) { int whiteChange = whiteSpace - localIndex; rootSize += whiteChange; whiteSpace -= whiteChange; correctSizes(whiteChange, 0); compressNode(absoluteIndex - localIndex); // Set within the whitespace } else { whiteSpace -= length; correctSizes(0, -length); host.addBlack(absoluteIndex, length); compressNode(absoluteIndex - localIndex); } // Remove/Add if the length spills over to another node if(localLength != length) { host.remove(absoluteIndex + localLength, length - localLength); host.addBlack(absoluteIndex + localLength, length - localLength); } } /** * Sets the value of the element at a given index. */ private void setBaseCase(int absoluteIndex, int index, Object value) { int localIndex = index - treeLeftSize; // Recurse to the Left if(localIndex < 0) { left.setBaseCase(absoluteIndex, index, value); // Recurse to the Right } else if(localIndex > whiteSpace + rootSize) { right.setBaseCase(absoluteIndex, localIndex - whiteSpace - rootSize, value); // Edge case where leading white moves to this } else if(localIndex == whiteSpace + rootSize) { // Add the new value to this root if(value != Barcode.WHITE) { rootSize++; treeRightSize--; correctSizes(1, 0); right.setFirstNullToTrue(absoluteIndex, localIndex - whiteSpace - rootSize + 1); } // Set a value in the middle of the white space } else if(localIndex < whiteSpace) { if(value == Barcode.WHITE) return; whiteSpace--; correctSizes(1, 0); insertBlack(index, 1); compressNode(absoluteIndex); // Set a value at the leading edge of this node } else if(localIndex == whiteSpace) { if(value == Barcode.WHITE) { whiteSpace++; rootSize--; correctSizes(-1, 0); if(rootSize == 0) unlink(absoluteIndex - localIndex); } // Set a value at the trailing edge of this node } else if(localIndex == whiteSpace + rootSize - 1) { if(value == Barcode.WHITE) { rootSize--; if(right != null) { treeRightSize++; right.insertWhite(localIndex - whiteSpace - rootSize, 1); correctSizes(-1, 0); } else if(parent != null && parent.left == this) { parent.whiteSpace++; parent.treeLeftSize--; parent.correctSizes(true, -1, 0); } else { correctSizes(-1, -1); host.addWhite(absoluteIndex, 1); } } // Set the value in this node } else { if(value == Barcode.WHITE) { rootSize--; correctSizes(-1, 0); insertWhite(index, 1); } } } /** * A helper method for a base-case condition where the first null on a node * is set to a value. This value is moved to the node that it is compressed * into before this method is called. This method may result in further * compression. */ private void setFirstNullToTrue(int absoluteIndex, int index) { int localIndex = index - treeLeftSize; // Recurse to the Left if(localIndex < 0) { treeLeftSize--; left.setFirstNullToTrue(absoluteIndex, index); // Recurse to the Right } else if(localIndex > whiteSpace + rootSize - 1) { treeRightSize--; right.setFirstNullToTrue(absoluteIndex, localIndex - whiteSpace - rootSize); // Affect this node } else { whiteSpace--; compressNode(absoluteIndex); } } /** * Removes the values from the given index to index + length */ void remove(int index, int length) { if(length == 1) removeBaseCase(index, index); else remove(index, index, length); } private void remove(int absoluteIndex, int index, int length) { int localIndex = index - treeLeftSize; // Recurse to the Left if(localIndex < 0) { left.remove(absoluteIndex, index, length); // Recurse to the Right } else if(localIndex > whiteSpace + rootSize - 1) { right.remove(absoluteIndex, localIndex - whiteSpace - rootSize, length); } else { // Trim the length to only affect this node length = Math.min(localIndex + length, whiteSpace + rootSize) - localIndex; int endIndex = localIndex + length - 1; // Remove white and possibly some, but not all, black if(localIndex < whiteSpace && endIndex < whiteSpace + rootSize) { int whiteChange = Math.min(whiteSpace - localIndex, length); int blackChange = Math.max(endIndex - whiteSpace + 1, 0); whiteSpace -= whiteChange; rootSize -= blackChange; correctSizes(-blackChange, -(whiteChange + blackChange)); compressNode(absoluteIndex - localIndex); // Remove only black } else if(localIndex > whiteSpace - 1) { // Remove all black so unlink this node if(length == rootSize) { unlink(absoluteIndex - localIndex); // Only remove some of the black } else { rootSize -= length; correctSizes(-length, -length); } // Remove this entire node } else { int whiteChange = whiteSpace; int blackChange = rootSize; whiteSpace = 0; rootSize = 0; correctSizes(-blackChange, -(whiteChange + blackChange)); unlink(absoluteIndex - localIndex); } } } /** * Removes the single value at a given index. */ void removeBaseCase(int absoluteIndex, int index) { int localIndex = index - treeLeftSize; // Recurse to the Left if(localIndex < 0) { treeLeftSize--; left.removeBaseCase(absoluteIndex, index); // Recurse to the Right } else if(localIndex > whiteSpace + rootSize - 1) { treeRightSize--; right.removeBaseCase(absoluteIndex, localIndex - whiteSpace - rootSize); // Remove from the middle of the white space } else if(localIndex < whiteSpace) { whiteSpace--; compressNode(absoluteIndex); // Remove from the black portion of this node } else { rootSize--; if(rootSize == 0) { rootSize = 1; unlink(absoluteIndex - localIndex, false); } else correctSizes(-1, 0); } } /** * Unlinks this node from the tree and clears it. */ private void unlink(int absoluteIndex) { unlink(absoluteIndex, true); } private void unlink(int absoluteIndex, boolean consistent) { // Two children exist if(right != null && left != null) { if(rootSize != 0) correctSizes(-rootSize, -rootSize, consistent); unlinkWithTwoChildren(); // Only a right child exists } else if(right != null) { unlinkWithRightChild(consistent); // A left child or no child exists, which are handled almost the same way } else { BarcodeNode replacement = null; // Only a left child exists if(left != null) { replacement = left; replacement.parent = parent; // No children exist } else replacement = null; // Parent is null so significant empty space moves to the trailing nulls if(parent == null) { host.setRootNode(replacement); if(whiteSpace != 0) host.addWhite(host.size() + 1, whiteSpace); // This is a left child so empty space goes to the parent } else if(parent.left == this) { parent.whiteSpace += whiteSpace; parent.treeLeftSize -= whiteSpace; parent.left = replacement; parent.ensureAVL(); if(rootSize != 0) parent.correctSizes(true, -rootSize, -rootSize, consistent); clear(); // This is a right child so significant empty space must be reinserted } else { parent.right = replacement; parent.ensureAVL(); if(whiteSpace != 0) { parent.correctSizes(false, -rootSize, -(whiteSpace + rootSize), consistent); host.addWhite(absoluteIndex, whiteSpace); } else if(rootSize != 0) { parent.correctSizes(false, -rootSize, -rootSize, consistent); } clear(); } } } /** * Unlinks this node in the special case where this node has both * a left and right child. */ private void unlinkWithTwoChildren() { // Get the replacement from the right subtree BarcodeNode replacement = right.pruneSmallestChild(); BarcodeNode repParent = replacement.parent; // Adjust sizes on this node whiteSpace += replacement.whiteSpace; rootSize = replacement.rootSize; treeRightSize -= replacement.whiteSpace + replacement.rootSize; blackRightSize -= replacement.rootSize; // The smallest node is the right child of this if(repParent == this) { right = replacement.right; if(right != null) right.parent = this; ensureAVL(); // The smallest node is a left child in the right subtree } else { // linking on the right subtree needs updating repParent.left = replacement.right; if(repParent.left != null) repParent.left.parent = repParent; repParent.ensureAVL(); } replacement.clear(); } /** * Unlinks a node that has only a right child */ private void unlinkWithRightChild(boolean consistent) { whiteSpace += right.whiteSpace; int oldSize = rootSize; rootSize = right.rootSize; right.clear(); right = null; blackRightSize = 0; treeRightSize = 0; height = 1; if(parent != null) { if(oldSize != 0) parent.correctSizes(parent.left == this, -oldSize, -oldSize, consistent); parent.ensureAVL(); } } /** * Prunes and returns the smallest child of the subtree rooted at this. * Tree references are maintained out of necessity of the calling method, * but sizes in the subtree are corrected accordingly. */ private BarcodeNode pruneSmallestChild() { // Recurse to the left if(left != null) { BarcodeNode prunedNode = left.pruneSmallestChild(); blackLeftSize -= prunedNode.rootSize; treeLeftSize -= prunedNode.whiteSpace + prunedNode.rootSize; return prunedNode; // return this node } else return this; } /** * A method to corrects sizes taking into account that the state of the * cached tree sizes may be inconsistent from base-case set or remove. */ private void correctSizes(int blackOffset, int totalOffset, boolean consistent) { if(consistent) correctSizes(blackOffset, totalOffset); else correctSizes(-1, totalOffset - blackOffset); } /** * A method to corrects sizes taking into account that the state of the * cached tree sizes may be inconsistent from base-case set or remove. */ private void correctSizes(boolean leftChild, int blackOffset, int totalOffset, boolean consistent) { if(consistent) correctSizes(leftChild, blackOffset, totalOffset); else correctSizes(leftChild, -1, totalOffset - blackOffset); } /** * Corrects all of the cached sizes up the tree by the given offsets starting * at the parent if it exists. */ private void correctSizes(int blackOffset, int totalOffset) { if(parent != null) parent.correctSizes(parent.left == this, blackOffset, totalOffset); else host.treeSizeChanged(); } /** * Corrects all of the cached sizes up the tree by the given offsets starting * from this. */ private void correctSizes(boolean leftChild, int blackOffset, int totalOffset) { // left subtree is smaller if(leftChild) { blackLeftSize += blackOffset; treeLeftSize += totalOffset; // right subtree is smaller } else { blackRightSize += blackOffset; treeRightSize += totalOffset; } // recurse up the tree to the root if(parent != null) parent.correctSizes(parent.left == this, blackOffset, totalOffset); // Notify the tree size has changed else host.treeSizeChanged(); } /** * Clears this node and returns the value it had. */ private void clear() { // clear the children left = null; blackLeftSize = 0; treeLeftSize = 0; right = null; blackRightSize = 0; treeRightSize = 0; // clear this node host = null; parent = null; whiteSpace = 0; rootSize = 0; height = -1; } /** * Replaces a given child with the replacement node */ private void replace(BarcodeNode child, BarcodeNode replacement) { // replacing the left child if(child == left) left = replacement; // Replacing the right child else right = replacement; } /** * Attempts to compress the current node out of the tree if possible */ private void compressNode(int absoluteIndex) { // Fast fail if this node cannot be compressed if(whiteSpace != 0) return; // This is the root if(parent == null) compressRoot(absoluteIndex); // This is a left child else if(parent.left == this) compressLeftChild(absoluteIndex); // This is a right child else compressRightChild(absoluteIndex); } /** * Compresses the root node */ private void compressRoot(int absoluteIndex) { // Compress to the left if(left != null) { // special case that's really fast if(right == null) { left.rootSize += rootSize; left.parent = null; host.setRootNode(left); clear(); } else { left.compressToTheRight(rootSize); blackLeftSize += rootSize; treeLeftSize += rootSize; rootSize = 0; unlink(absoluteIndex); } // The node is as compressed as possible } else { // Do Nothing } } /** * Compresses a node that is a left child */ private void compressLeftChild(int absoluteIndex) { // Compress to the left if(left != null) { left.compressToTheRight(rootSize); blackLeftSize += rootSize; treeLeftSize += rootSize; rootSize = 0; unlink(absoluteIndex); // Painful re-addition case } else { // This is the first value, can't compress it if(absoluteIndex == 0) return; // move the right child onto the parent parent.left = right; if(right != null) parent.left.parent = parent; // fix tree state and re-add these values parent.correctSizes(true, -rootSize, -rootSize); parent.ensureAVL(); host.addBlack(absoluteIndex - 1, rootSize); clear(); } } /** * Compresses a node that is a right child */ private void compressRightChild(int absoluteIndex) { // Compress to the parent if(left == null) { parent.blackRightSize -= rootSize; parent.treeRightSize -= rootSize; parent.rootSize += rootSize; rootSize = 0; unlink(absoluteIndex); // Compress to the left } else { left.compressToTheRight(rootSize); blackLeftSize += rootSize; treeLeftSize += rootSize; rootSize = 0; unlink(absoluteIndex); } } /** * Compresses the given values into the largest node in this subtree. */ private void compressToTheRight(int values) { if(right != null) { blackRightSize += values; treeRightSize += values; right.compressToTheRight(values); } else { rootSize += values; } } /** * Ensures that the tree satisfies the AVL property. It is sufficient to * recurse up the tree only as long as height recalculations are needed. * As such, this method is intended to be called only on a node whose height * may be out of sync due to an insertion or deletion. For example, calling * this method on a leaf node will not guarantee that this tree satisfies the * AVL property as it will not recurse. */ private void ensureAVL() { int oldHeight = height; recalculateHeight(); avlRotate(); // If adjustments were made, recurse up the tree if(height != oldHeight && parent != null) parent.ensureAVL(); } /** * Recalculates the cached height at this level. */ private void recalculateHeight() { int leftHeight = left == null ? 0 : left.height; int rightHeight = right == null ? 0 : right.height; height = 1 + Math.max(leftHeight, rightHeight); } /** * Determines if AVL rotations are required and performs them if they are. */ private void avlRotate() { // look up the left and right heights int leftHeight = (left != null ? left.height : 0); int rightHeight = (right != null ? right.height : 0); // rotations will be on the left if(leftHeight - rightHeight >= 2) { // determine if a double rotation is necessary int leftLeftHeight = (left.left != null ? left.left.height : 0); int leftRightHeight = (left.right != null ? left.right.height : 0); // Perform first half of double rotation if necessary if(leftRightHeight > leftLeftHeight) left.rotateRight(); // Do the rotation for this node rotateLeft(); // rotations will be on the right } else if(rightHeight - leftHeight >= 2) { // determine if a double rotation is necessary int rightLeftHeight = (right.left != null ? right.left.height : 0); int rightRightHeight = (right.right != null ? right.right.height : 0); // Perform first half of double rotation if necessary if(rightLeftHeight > rightRightHeight) right.rotateLeft(); // Do the rotation for this node rotateRight(); } } /** * AVL-Rotates this subtree with its left child. */ private void rotateLeft() { // The replacement node is on the left BarcodeNode replacement = left; // take the right child of the replacement as my left child left = replacement.right; blackLeftSize = replacement.blackRightSize; treeLeftSize = replacement.treeRightSize; if(replacement.right != null) replacement.right.parent = this; // set the right child of the replacement to this replacement.right = this; replacement.blackRightSize = blackSize(); replacement.treeRightSize = size(); // set the replacement's parent to my parent and mine to the replacement if(parent != null) parent.replace(this, replacement); // set a new tree root else host.setRootNode(replacement); // fix parent links on this and the replacement replacement.parent = parent; parent = replacement; // recalculate height at this node recalculateHeight(); // require height to be recalculated on the replacement node replacement.height = 0; } /** * AVL-Rotates this subtree with its right child. */ private void rotateRight() { // The replacement node is on the right BarcodeNode replacement = right; // take the left child of the replacement as my right child right = replacement.left; blackRightSize = replacement.blackLeftSize; treeRightSize = replacement.treeLeftSize; if(replacement.left != null) replacement.left.parent = this; // set the left child of the replacement to this replacement.left = this; replacement.blackLeftSize = blackSize(); replacement.treeLeftSize = size(); // set the replacement's parent to my parent and mine to the replacement if(parent != null) parent.replace(this, replacement); // set a new tree root else host.setRootNode(replacement); // fix parent links on this and the replacement replacement.parent = parent; parent = replacement; // recalculate height at this node recalculateHeight(); // require height to be recalculated on the replacement node replacement.height = 0; } @Override public String toString() { return "[ " + left + " ("+ blackLeftSize +", " +treeLeftSize+")" +" <"+whiteSpace+"> " + rootSize +" <"+height+"> " +"(" + blackRightSize +", " +treeRightSize+") " + right + " ]"; } /** * Validates this node's state */ public void validate() { validateLineage(); validateHeight(); validateTreeSize(); validateBlackSize(); validateCompression(); validateRootSize(); } private int validateBlackSize() { int leftTreeSize = left == null ? 0 : left.validateBlackSize(); int rightTreeSize = right == null ? 0 : right.validateBlackSize(); if(leftTreeSize != blackLeftSize) throw new IllegalStateException("Black Size Validation Failure in Left Subtree\n" + "Expected: " + leftTreeSize + "\nActual: " + blackLeftSize + "\n" + this); if(rightTreeSize != blackRightSize) throw new IllegalStateException("Black Size Validation Failure in Right Subtree\n" + "Expected: " + rightTreeSize + "\nActual: " + blackRightSize + "\n" + this); return leftTreeSize + rightTreeSize + rootSize; } private int validateHeight() { int leftHeight = left == null ? 0 : left.validateHeight(); int rightHeight = right == null ? 0 : right.validateHeight(); // Validate that height is accurate at all if(height != 1 + Math.max(leftHeight, rightHeight)) throw new IllegalStateException("Height Validation Failure\n" + "Expected: " + (1 + Math.max(leftHeight, rightHeight)) + "\nActual: " + height + "\n" + this); // Validate that height meets the AVL property if(Math.abs(leftHeight - rightHeight) > 1) throw new IllegalStateException("AVL Property Validation Failure\n" + this); return 1 + Math.max(leftHeight, rightHeight); } private void validateLineage() { if(left != null) { if(left.parent != this) throw new IllegalStateException("Lineage Validation Failure\n" + "Left child is orphaned :\n" + left); left.validateLineage(); } if(right != null) { if(right.parent != this) throw new IllegalStateException("Lineage Validation Failure\n" + "Right child is orphaned :\n" + right); right.validateLineage(); } } private void validateCompression() { if(left != null) left.validateCompression(); if(right != null) right.validateCompression(); if(whiteSpace == 0 && getIndexForValidation() != 0) throw new IllegalStateException("Compression Validation Failure\n" + "The following node was found that could be compressed: \n" + this); } private int validateTreeSize() { int leftTreeSize = left == null ? 0 : left.validateTreeSize(); int rightTreeSize = right == null ? 0 : right.validateTreeSize(); if(treeLeftSize != leftTreeSize) throw new IllegalStateException("Tree Size Validation Failure\n" + "The following node was found that had a tree size failure on the left subtree: \n" + this); if(treeRightSize != rightTreeSize) throw new IllegalStateException("Tree Size Validation Failure\n" + "The following node was found that had a tree size failure on the right subtree: \n" + this); return treeLeftSize + whiteSpace + rootSize + treeRightSize; } /** * Gets the index of the first element on this node. This is the index of * the first WHITE element (or first BLACK if there is no whitespace on this node) * indexed by this node. */ private int getIndexForValidation() { if(parent != null) return parent.getIndexForValidation(this) + treeLeftSize; return treeLeftSize; } private int getIndexForValidation(BarcodeNode child) { // the child is on the left, return the index recursively if(child == left) { if(parent != null) return parent.getIndexForValidation(this); return 0; // the child is on the right, return the index recursively } else { if(parent != null) return parent.getIndexForValidation(this) + treeLeftSize + whiteSpace + rootSize; return treeLeftSize + whiteSpace + rootSize; } } private void validateRootSize() { if(left != null) left.validateRootSize(); if(right != null) right.validateRootSize(); if(rootSize == 0) throw new IllegalStateException("Root Size Validation Failure\n" + "A node was found with a root size of zero."); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/IntArrayList.java0000644000175000017500000000605512106516372031105 0ustar gregoagregoa/* Glazed Lists (c) 2012 */ /* http://glazedlists.com/ */ package ca.odell.glazedlists.impl.adt; /** * Simple implementation of an array list suitable for storage of primitive integers. * * @author Rob Eden */ public class IntArrayList { private int[] data; private int size = 0; /** * Create a list with an initial capacity of ten. */ public IntArrayList() { this( 10 ); } /** * Create a list with the given initial capacity. * * @param initial_capacity The capacity to initially allow in the list without * requiring additional array space to be allocated. */ public IntArrayList( int initial_capacity ) { data = new int[ initial_capacity ]; } /** * Returns the number of entries in the list. */ public int size() { return size; } /** * Indicates whether or not the list is empty. */ public boolean isEmpty() { return size == 0; } /** * Clear the existing data in the list. */ public void clear() { size = 0; } /** * Return the value at the given index. If the index is outside the current bounds * of the list, an exception will be thrown. * * @param index The index from which to get the value. */ public int get( int index ) { checkAccess( index ); return data[ index ]; } /** * Add the given value to the end of the list. */ public void add( int value ) { checkGrow( 1 ); data[ size ] = value; size++; } /** * Set the value of the given index. If the index is outside the existing bounds of * the list, an exception will be thrown. * * @param index The index at which to set the value. * @param value The new value to be set. */ public void set( int index, int value ) { checkAccess( index ); data[ index ] = value; } /** * Determine if there is sufficient data in the list to make access to the given index * make sense. If it is outside the current bounds of the list, an exception will * be thrown. * * @param index The index to be accessed. * * @throws IndexOutOfBoundsException If the index is outside the bounds of the list. */ private void checkAccess( int index ) { if ( size <= index ) { throw new IndexOutOfBoundsException( "Index " + index + " is outside list bounds (size=" + size + ")" ); } } /** * Determines if the existing array has enough space left to add the given amount of * data. If it doesn't, a new array of sufficient size will be created and existing * data will be copied to it. * * @param amount The amount of entries we would like to add to the list. */ private void checkGrow( int amount ) { if ( size + amount <= data.length ) return; int new_length = data.length * 2; while( new_length < ( size + amount ) ) { new_length = data.length * 2; } int[] new_data = new int[ new_length ]; System.arraycopy( data, 0, new_data, 0, size ); data = new_data; } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/KeyedCollection.java0000644000175000017500000000630312106516372031571 0ustar gregoagregoapackage ca.odell.glazedlists.impl.adt; import java.util.*; /** * A Collection that stores keys in a map, with positions as the values. * * @author jplemieux * @author Jesse Wilson */ public final class KeyedCollection { private final Map values; private final Comparator

      positionComparator; private P first, last; public KeyedCollection(Comparator

      positionComparator, Map mapWithValuesAsKeys) { if (!mapWithValuesAsKeys.isEmpty()) throw new IllegalArgumentException("mapWithValuesAsKeys must be empty"); this.positionComparator = positionComparator; this.values = mapWithValuesAsKeys; } /** * Inserts the specified value at the specified position. */ public void insert(P position, V value) { Object previousPositions = values.get(value); if (previousPositions == null) { values.put(value, position); } else if (previousPositions instanceof SortedSet) { SortedSet

      allPositions = (SortedSet)previousPositions; allPositions.add(position); } else { SortedSet

      allPositions = new TreeSet

      (positionComparator); allPositions.add((P)previousPositions); allPositions.add(position); values.put(value, allPositions); } if (first == null || lessThan(position, first)) { first = position; } if (last == null || greaterThan(position, last)) { last = position; } } /** * Returns the first position of the specified value. * * @param min the returned value will be greater than or equal to this. * @param max the returned position will be less than this. * @return the position, or null if no such value exists in * the requested range. */ public P find(P min, P max, V value) { if (positionComparator.compare(min, max) > 0) throw new IllegalArgumentException("min " + min + " > max " + max); Object positionsAsSingleOrSet = values.get(value); if (positionsAsSingleOrSet == null) { return null; } else if (positionsAsSingleOrSet instanceof SortedSet) { SortedSet

      positions = (SortedSet

      )positionsAsSingleOrSet; SortedSet

      positionsInRange = positions.subSet(min, max); return positionsInRange.isEmpty() ? null : positionsInRange.iterator().next(); } else { P position = (P)positionsAsSingleOrSet; if (!lessThan(position, min) && lessThan(position, max)) { return position; } } return null; } /** * Returns the largest position that has been inserted into this * collection. */ public P last() { return last; } /** * Returns the smallest position that has been inserted into this * collection. */ public P first() { return first; } private boolean lessThan(P a, P b) { return positionComparator.compare(a, b) < 0; } private boolean greaterThan(P a, P b) { return positionComparator.compare(a, b) > 0; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/BarcodeIterator.java0000644000175000017500000004753412106516372031600 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt; import java.util.Iterator; import java.util.NoSuchElementException; /** * A BarcodeIterator is a specialized {@link Iterator} implementation for moving * over a Barcode efficiently. * * @author Kevin Maltby */ public class BarcodeIterator implements Iterator { /** keep a reference for removes in the trailing whitespace */ private Barcode barcode = null; /** the current node being inspected */ private BarcodeNode currentNode = null; /** the number of requests on the current node */ private int localIndex = -1; /** the number of black elements before this node */ private int blackSoFar = 0; /** the number of white elements before this node */ private int whiteSoFar = 0; /** * Creates a new Iterator for the given Barcode. */ BarcodeIterator(Barcode barcode) { BarcodeNode root = barcode.getRootNode(); // move the Iterator to the start position. if(root != null) { currentNode = root; while(currentNode.left != null) { currentNode = currentNode.left; } } this.barcode = barcode; } /** * Returns whether or not there are more values in the {@link Barcode} to * iterate over. */ public boolean hasNext() { if(getIndex() == barcode.size() - 1) { return false; } return true; } /** * Returns true if there are more BLACK elements in the {@link Barcode} to * move the {@link Iterator} to. */ public boolean hasNextBlack() { if(getIndex() >= barcode.treeSize() - 1) return false; else if(currentNode == null) return false; else return true; } /** * Returns true if there are more WHITE elements in the {@link Barcode} to * move the {@link Iterator} to. */ public boolean hasNextWhite() { if(barcode.size() != barcode.treeSize()) return hasNext(); else if(currentNode == null) return false; else if(localIndex < currentNode.whiteSpace - 1 || whiteSoFar + currentNode.whiteSpace < barcode.whiteSize()) return true; else return false; } /** * Returns true if there are more elements in the {@link Barcode} to * move the {@link Iterator} to that match the provided colour. */ public boolean hasNextColour(Object colour) { if(colour == Barcode.BLACK) return hasNextBlack(); return hasNextWhite(); } /** * Gets the next value in this SparseList. */ public Object next() { // iterate on this node localIndex++; // handle the empty tree case if(currentNode == null) { // beyond the tree in the trailing whitespace if(getIndex() < barcode.size()) { return Barcode.WHITE; // at the end of the list } else { throw new NoSuchElementException(); } // at the edge of the current node } else if(localIndex >= currentNode.whiteSpace + currentNode.rootSize) { // move to the next node if(getIndex() < barcode.treeSize()) { blackSoFar += currentNode.rootSize; whiteSoFar += currentNode.whiteSpace; findNextNode(); localIndex = 0; // act on the trailing whitespace } else { // beyond the tree in the trailing whitespace if(getIndex() < barcode.size()) { return Barcode.WHITE; // at the end of the list } else { throw new NoSuchElementException(); } } } // next() was a WHITE value if(localIndex < currentNode.whiteSpace) { return Barcode.WHITE; // next() was a BLACK value } else { return Barcode.BLACK; } } /** * Moves this {@link Iterator} to the next element in the {@link Barcode} * that is BLACK. * * @throws NoSuchElementException if hasNextBlack() returns false. */ public Object nextBlack() { // iterate on this node localIndex++; // handle the empty tree case if(currentNode == null) { throw new NoSuchElementException(); // currently in the whitespace of this node } else if(localIndex < currentNode.whiteSpace) { localIndex = currentNode.whiteSpace; // at the edge of the current node } else if(localIndex >= currentNode.whiteSpace + currentNode.rootSize) { // move to the next node if(getIndex() < barcode.treeSize()) { whiteSoFar += currentNode.whiteSpace; blackSoFar += currentNode.rootSize; findNextNode(); localIndex = currentNode.whiteSpace; // act on the trailing whitespace } else { throw new NoSuchElementException(); } } if(localIndex < currentNode.whiteSpace) throw new IllegalStateException(); return Barcode.BLACK; } /** * Moves this {@link Iterator} to the next element in the {@link Barcode} * that is WHITE. * * @throws NoSuchElementException if hasNextWhite() returns false. */ public Object nextWhite() { // iterate on this node localIndex++; // handle the empty tree case if(currentNode == null) { // beyond the tree in the trailing whitespace if(getIndex() < barcode.size()) { return Barcode.WHITE; // at the end of the list } else { throw new NoSuchElementException(); } // at the edge of the current node } else if(localIndex >= currentNode.whiteSpace) { // move to the trailing whitespace if(getIndex() < barcode.treeSize() && getIndex() + currentNode.rootSize >= barcode.treeSize()) { localIndex = currentNode.whiteSpace; localIndex += currentNode.rootSize; } // move to the next node if(getIndex() < barcode.treeSize()) { blackSoFar += currentNode.rootSize; whiteSoFar += currentNode.whiteSpace; findNextNode(); localIndex = 0; // act on the trailing whitespace } else { // beyond the tree in the trailing whitespace if(getIndex() < barcode.size()) { return Barcode.WHITE; // at the end of the list } else { throw new NoSuchElementException(); } } } if(localIndex >= currentNode.whiteSpace) throw new IllegalStateException(); return Barcode.WHITE; } /** * Moves this {@link Iterator} to the next element in the {@link Barcode} * that matches the provided colour. * * @throws NoSuchElementException if hasNextColour(colour) returns false. */ public Object nextColour(Object colour) { if(colour == Barcode.BLACK) return nextBlack(); return nextWhite(); } /** * Removes the current value at the Iterator from the {@link Barcode}. */ public void remove() { // Fast fail if the Iterator isn't set up right yet if(localIndex == -1) { throw new NoSuchElementException("Cannot call remove() before next() is called."); // Removing from the trailing whitespace } else if(currentNode == null || getIndex() >= barcode.treeSize()) { barcode.remove(getIndex(), 1); localIndex--; // Removing from the tree } else { BarcodeNode affectedNode = currentNode; // The currentNode gets compressed to the left if(localIndex == 0 && currentNode.whiteSpace == 1 && getIndex() != 0) { findPreviousNode(); blackSoFar -= currentNode.rootSize; whiteSoFar -= currentNode.whiteSpace; localIndex += currentNode.whiteSpace + currentNode.rootSize; // The currentNode gets compressed to the right } else if(localIndex == currentNode.whiteSpace && currentNode.rootSize == 1) { // The only node in the tree is going to be unlinked if(localIndex == barcode.treeSize() - 1) { currentNode = null; // This was the end of the tree go to trailing whitespace } else if(getIndex() == barcode.treeSize() - 1) { findPreviousNode(); blackSoFar -= currentNode.rootSize; whiteSoFar -= currentNode.whiteSpace; localIndex += currentNode.whiteSpace + currentNode.rootSize; // There is a node after this one so go to it } else { findNextNode(); } } // Remove the value affectedNode.removeBaseCase(getIndex(), localIndex); localIndex--; } } /** * Sets the most recently viewed element to WHITE and returns the white-centric * index of the element after the set is complete. */ public int setWhite() { // Fast fail for a non-existant element if(localIndex == -1) { throw new NoSuchElementException("Cannot call setWhite() before next() is called."); // No-op for a WHITE element } else if(currentNode == null || getIndex() >= barcode.treeSize() || localIndex < currentNode.whiteSpace) { return getWhiteIndex(); // Not at the end of the tree } else if(getIndex() != barcode.treeSize() - 1) { // This whole node gets compressed right if(currentNode.rootSize == 1) { BarcodeNode affectedNode = currentNode; findNextNode(); affectedNode.setWhite(getIndex(), localIndex, 1); if(barcode.getRootNode() != null) barcode.treeSizeChanged(); if(currentNode.whiteSpace == 0 && currentNode.rootSize == 0) currentNode = affectedNode; return whiteSoFar + localIndex; // Special case where set just changes values on this node } else if(localIndex == currentNode.whiteSpace) { currentNode.setWhite(getIndex(), localIndex, 1); if(barcode.getRootNode() != null) barcode.treeSizeChanged(); return whiteSoFar + localIndex; // Create a new node or part of this node will compress right } else { currentNode.setWhite(getIndex(), localIndex, 1); if(barcode.getRootNode() != null) barcode.treeSizeChanged(); blackSoFar += currentNode.rootSize; whiteSoFar += currentNode.whiteSpace; findNextNode(); localIndex = 0; return whiteSoFar; } // Set causes multiple values to move to the trailing whitespace } else if(currentNode.rootSize == 1) { BarcodeNode affectedNode = currentNode; // This was the last node in the tree and now it's gone if(currentNode.whiteSpace + 1 == barcode.treeSize()) { currentNode = null; affectedNode.setWhite(getIndex(), localIndex, 1); if(barcode.getRootNode() != null) barcode.treeSizeChanged(); return localIndex; // There are more nodes before this one } else { findPreviousNode(); int currentLocalIndex = localIndex; blackSoFar -= currentNode.rootSize; whiteSoFar -= currentNode.whiteSpace; localIndex += currentNode.whiteSpace + currentNode.rootSize; affectedNode.setWhite(getIndex(), currentLocalIndex, 1); if(barcode.getRootNode() != null) barcode.treeSizeChanged(); if(currentNode.whiteSpace == 0 && currentNode.rootSize == 0) currentNode = affectedNode; return whiteSoFar + localIndex - currentNode.rootSize; } // Setting only one element into the trailing whitespace } else { currentNode.setWhite(getIndex(), localIndex, 1); if(barcode.getRootNode() != null) barcode.treeSizeChanged(); return whiteSoFar + localIndex - currentNode.rootSize; } } /** * Sets the most recently viewed element to BLACK and returns the white-centric * index of the element after the set is complete. */ public int setBlack() { // Fast fail for a non-existant element if(localIndex == -1) { throw new NoSuchElementException("Cannot call setBlack() before next() is called."); // Set in the trailing whitespace without a tree } else if(currentNode == null) { barcode.setBlack(getIndex(), 1); currentNode = barcode.getRootNode(); return 0; // Set at the edge of the trailing whitespace } else if(getIndex() == barcode.treeSize()) { barcode.setBlack(getIndex(), 1); if(barcode.getRootNode() != null) barcode.treeSizeChanged(); // Set within the trailing whitespace } else if(getIndex() > barcode.treeSize()) { barcode.setBlack(getIndex(), 1); if(barcode.getRootNode() != null) barcode.treeSizeChanged(); whiteSoFar += currentNode.whiteSpace; blackSoFar += currentNode.rootSize; localIndex -= currentNode.whiteSpace + currentNode.rootSize; findNextNode(); // Node gets compressed to the left } else if(localIndex < currentNode.whiteSpace && currentNode.whiteSpace == 1) { // At the start of the tree, no compression if(getIndex() == 0) { currentNode.setBlack(getIndex(), localIndex, 1); if(barcode.getRootNode() != null) barcode.treeSizeChanged(); // Either this node or the node before it is about to disappear } else { BarcodeNode affectedNode = currentNode; findPreviousNode(); int currentLocalIndex = localIndex; blackSoFar -= currentNode.rootSize; whiteSoFar -= currentNode.whiteSpace; localIndex += currentNode.whiteSpace + currentNode.rootSize; affectedNode.setBlack(getIndex(), currentLocalIndex, 1); if(barcode.getRootNode() != null) barcode.treeSizeChanged(); if(currentNode.whiteSpace == 0 && currentNode.rootSize == 0) currentNode = affectedNode; } // Any other WHITE element set to BLACK } else if(localIndex < currentNode.whiteSpace) { // Element gets compressed left if(localIndex == 0) { currentNode.setBlack(getIndex(), localIndex, 1); if(barcode.getRootNode() != null) barcode.treeSizeChanged(); blackSoFar++; localIndex--; return blackSoFar - 1; // Element is at the edge of the existing BLACK } else if(localIndex == currentNode.whiteSpace - 1) { currentNode.setBlack(getIndex(), localIndex, 1); if(barcode.getRootNode() != null) barcode.treeSizeChanged(); return blackSoFar; // Created a new node to the left } else { currentNode.setBlack(getIndex(), localIndex, 1); if(barcode.getRootNode() != null) barcode.treeSizeChanged(); whiteSoFar += localIndex; blackSoFar++; localIndex = -1; return blackSoFar - 1; } } return getBlackIndex(); } /** * Sets the most recently viewed element to the value of colour and returns * the colour specific index of the element after the set is complete. */ public int set(Object colour) { if(colour == Barcode.BLACK) return setBlack(); return setWhite(); } /** * Gets the index of the last element visited. */ public int getIndex() { return blackSoFar + whiteSoFar + localIndex; } /** * Gets the black-centric index of the last element visited or -1 if that * element is white. */ public int getBlackIndex() { if(localIndex == -1) { return blackSoFar - 1; } else if(currentNode == null || localIndex < currentNode.whiteSpace || localIndex >= currentNode.whiteSpace + currentNode.rootSize) { return -1; } return blackSoFar + localIndex - currentNode.whiteSpace; } /** * Gets the white-centric index of the last element visited or -1 if that * element is black. */ public int getWhiteIndex() { if(currentNode == null) { if(localIndex == -1 && whiteSoFar != 0) return whiteSoFar - 1; else return localIndex; } else if(localIndex >= currentNode.whiteSpace && localIndex < currentNode.whiteSpace + currentNode.rootSize) return -1; else if(localIndex >= currentNode.whiteSpace + currentNode.rootSize) return whiteSoFar + localIndex - currentNode.rootSize; return whiteSoFar + localIndex; } /** * Gets the colour-centric index of the last element based on the value of * colour. If the element didn't match the colour specified, this method * returns -1. */ public int getColourIndex(Object colour) { if(colour == Barcode.WHITE) return getWhiteIndex(); return getBlackIndex(); } /** * Finds the next node in the tree. */ private void findNextNode() { // go into the right subtree for the next node if(currentNode.right != null) { currentNode = currentNode.right; while(currentNode.left != null) { currentNode = currentNode.left; } // go to the parent for the next node } else if(currentNode.parent.left == currentNode) { currentNode = currentNode.parent; // get out of the right subtree } else if(currentNode.parent.right == currentNode) { // move to the top of the current subtree while(currentNode.parent.right == currentNode) { currentNode = currentNode.parent; } // Move up one more node to leave the subtree currentNode = currentNode.parent; // the iterator is out of state } else { throw new IllegalStateException(); } } /** * Finds the previous node in the tree. */ private void findPreviousNode() { // go into the left subtree for the previous node if(currentNode.left != null) { currentNode = currentNode.left; while(currentNode.right != null) { currentNode = currentNode.right; } // go to the parent for the next node } else if(currentNode.parent.right == currentNode) { currentNode = currentNode.parent; // get out of the left subtree } else if(currentNode.parent.left == currentNode) { // move to the top of the current subtree while(currentNode.parent.left == currentNode) { currentNode = currentNode.parent; } // Move up one more node to leave the subtree currentNode = currentNode.parent; // the iterator is out of state } else { throw new IllegalStateException(); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/SparseList.java0000644000175000017500000001743212106516372030612 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.adt; // For Lists and Iterators import java.util.AbstractList; import java.util.Collections; import java.util.Iterator; import java.util.List; /** * A SparseList is an ADT to complement the CompressableList and IndexedTree * ADTs. IndexedTree provides accessible nodes that can recalculate their index * on the fly so users can avoid dealing with index offsetting. CompressableList * provides list compression capabilites that allow a list to be accessed * by both the real index and a compressed index. The compressed index * corresponds to the index of the current value as though no nulls exist * in the list. While this is a powerful feature, the larger benefits of the * compression of nulls are a significant performance boost and smaller footprint * for lists that tend towards containing a significant number of nulls. * *

      The SparseList was created to provide the indexed accessible nodes * as found in IndexedTree while reaping the performance and memory enhancements * of CompressableList. These optimizations have been taken several steps further * to gain significantly better performance over the current implementation of * CompressableList. * *

      In an effort to maximize performance, this ADT does NOT validate that * arguments passed to methods are valid in any way. While this adds inherent * risk to the use of this code, this is a volatile implementation class. As * such, it should only be used for internal GlazedList development. It is up * to the calling code to do any argument validation which may be necessary. If * you are still concerned, consider the benefits. Being in a tree structure * means the methods on this ADT are often recursive. Recursively validating * arguments makes no sense, and has a real-world impact on performance, while * not a Big-Oh impact. * *

      Every effort has been made to squeeze the highest performance and smallest * footprint out of this data structure. These benefits hopefully don't come at * the cost of code clarity or maintainability. The memory usage of this ADT * is bound to the number of non-null elements. Null elements have no additional * memory impact on the data structure. * *

      The intent of this high-performance, low-cost data structure is for * improving the scalability of some of the GlazedLists. It is technically * possible to scale this ADT above the Integer.MAX_SIZE barrier imposed by * integer-based indexing. However, doing so requires particular care in the * structuring of the list and should be avoided if possible. It is advised * that users do their best to operate within the bounds of the Integer.MAX_SIZE * size limit. * * @author Kevin Maltby * */ public final class SparseList extends AbstractList { /** the root of the tree */ private SparseListNode root = null; /** the total size of this data structure */ private int size = 0; /** the size of tree */ private int treeSize = 0; /** * Gets the size of this {@link List}. */ @Override public int size() { return size; } /** * Inserts a value into this tree at the given index */ @Override public void add(int index, Object value) { // Let nulls be inserted by the method created for that purpose if(value == null) { addNulls(index, 1); // The tree already has a root } else if(root != null) { // Insert a real node into the trailing nulls if(index >= treeSize) { int movingNulls = index - treeSize; size -= movingNulls; // Insert at the end of the tree root.insertAtEnd(value, movingNulls); treeSizeChanged(); // Insert into the tree } else { root.insert(index, value); treeSizeChanged(); } // Create a root for this tree } else { root = new SparseListNode(this, null, value, index); treeSize = index + 1; size++; } } /** * Inserts a sequence of nulls into the tree */ public void addNulls(int index, int length) { // Increase the total tree size size += length; // The nulls are to be added to the actual tree if(root != null && index < treeSize) { root.insertEmptySpace(index, length); treeSize += length; } } /** * Gets the value in this tree at the given index */ @Override public Object get(int index) { SparseListNode node = getNode(index); // The value at that index is a null if(node == null) return null; return node.getValue(); } /** * Gets the node for an element in this tree at the given index * or null if the value at that index is null */ public SparseListNode getNode(int index) { if(root != null && index < treeSize) return root.getNode(index); return null; } /** * Sets the value of the node at the given index */ @Override public Object set(int index, Object value) { // The set occurs in the actual tree if(root != null && index < treeSize) { Object returnValue = root.set(index, value); // This is not intutive as the tree size should 'never' change on a // set call. However, since sets can result in inserts and deletes // this is currently necessary. But I'm working on it. treeSizeChanged(); return returnValue; // The set occurs in the trailing nulls } else { // no-op to set a null to a null if(value == null) { return null; // this is equivalent to adding at this index } else { size--; add(index, value); return null; } } } /** * Removes the nodex at the given index */ @Override public Object remove(int index) { // The remove occurs in the actual tree if(root != null && index < treeSize) { Object returnValue = root.remove(index); treeSizeChanged(); return returnValue; // The remove occurs in the trailing nulls } else { size--; return null; } } /** * Clears the tree */ @Override public void clear() { size = 0; root = null; } /** * Sets the root for this tree. This method is exposed for the * SparseListNode in the event that the tree's root is involved in * an AVL rotation. */ void setRootNode(SparseListNode root) { this.root = root; if(root == null) { size -= treeSize; treeSize = 0; } } /** * Notifies the tree that the underlying tree size has changed. This method * is exposed for SparseListNode to make size adjustments. */ void treeSizeChanged() { size -= treeSize; treeSize = root != null ? root.size() : 0; size += treeSize; } /** * Obtains an {@link Iterator} for this {@link List}. */ @Override public Iterator iterator() { if(size == 0) return Collections.EMPTY_LIST.iterator(); return new SparseListNode.SparseListIterator(this, root); } /** * Prints out the structure of the tree for debug purposes. */ public void printDebug() { System.out.println(root.toString()); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/adt/CircularArrayList.java0000644000175000017500000001300112106516372032104 0ustar gregoagregoapackage ca.odell.glazedlists.impl.adt; import java.util.AbstractList; /** * @author jessewilson */ public class CircularArrayList extends AbstractList { // [ 3 4 _ _ _ 0 1 2 ] // head = 5 // tail = 2 // size = 5 // arrayLength = 8 // [ _ _ 0 1 2 3 4 _ ] // head = 2 // tail = 7 // size = 5 // arrayLength = 8 int head = 0; int size = 0; Object[] values = new Object[10]; int arrayLength = values.length; @Override public T get(int index) { return (T)values[toCircularIndex(index)]; } @Override public void add(int index, T element) { growIfNecessary(); int indexToAdd = toCircularIndex(index); int distToHead = distanceToHead(indexToAdd); int distToTail = distanceToTail(indexToAdd); // shift values to the right, that's less work if (distToTail <= distToHead) { int tail = tail(); shift(indexToAdd, tail, 1); values[indexToAdd] = element; // shift values to the left, that's less work } else { shift(head, indexToAdd, -1); values[modIndex(indexToAdd - 1)] = element; head = modIndex(head - 1); } size++; } int tail() { return modIndex(head + size); } void growIfNecessary() { int size = size(); if (size < arrayLength) { return; } Object[] biggerValues = new Object[values.length * 2]; int tail = tail(); // [ _ _ 0 1 2 3 4 _ ] ==> [ 0 1 2 3 4 _ _ _ _ _ _ _ _ _ _ _ ] if (head < tail) { System.arraycopy(values, head, biggerValues, 0, tail - head); // [ 2 3 4 _ _ _ 0 1 ] ==> [ 0 1 2 3 4 _ _ _ _ _ _ _ _ _ _ _ ] } else { System.arraycopy(values, head, biggerValues, 0, arrayLength - head); System.arraycopy(values, 0, biggerValues, arrayLength - head, tail); } values = biggerValues; arrayLength = biggerValues.length; head = 0; } @Override public T remove(int index) { int indexToRemove = toCircularIndex(index); int distToHead = distanceToHead(indexToRemove); int distToTail = distanceToTail(indexToRemove); T removed = get(index); // shift values to the left, that's less work if (distToTail < distToHead) { int tail = tail(); shift(indexToRemove + 1, tail, -1); values[modIndex(tail - 1)] = null; // shift values to the right, that's less work } else { shift(head, indexToRemove, 1); values[head] = null; head = modIndex(head + 1); } size--; return removed; } /** * Shift the values at the specified indices in the specified direction. * *

      The ultimate implementation of a shift left would break the array * into 4 parts: *

    • stuff that isn't moved *
    • stuff at the end that needs to be moved to to the left *
    • stuff at the very beginning that needs to be moved to the end *
    • stuff near the beginning that needs to be moved to the beginning *

      This implementation isn't ultimate. * * @param first inclusive * @param last exclusive * @param distance either -1 or 1 */ void shift(int first, int last, int distance) { if (distance != 1 && distance != -1) { throw new IllegalArgumentException(); } if (first == last) { return; } if (last == 0) { last = arrayLength; } // a split shift if (first > last && last != 0) { // [ 3 4 _ _ _ 0 1 2 ] ==> [ 2 3 4 _ _ _ 0 1 ] if (distance == 1) { System.arraycopy(values, 0, values, 1, last); values[0] = values[arrayLength - 1]; System.arraycopy(values, first, values, first + 1, arrayLength - first - 1); values[first] = null; // [ 1 2 3 4 _ _ _ 0 ] ==> [ 2 3 4 _ _ _ 0 1 ] } else if(distance == -1) { System.arraycopy(values, first, values, first - 1, arrayLength - first); values[arrayLength - 1] = values[0]; System.arraycopy(values, 1, values, 0, last - 1); values[last - 1] = null; } // a single shift } else { // [ 0 1 2 3 4 _ _ _ ] ==> [ 1 2 3 4 _ _ _ 0 ] if (distance == -1 && first == 0) { values[arrayLength - 1] = values[0]; System.arraycopy(values, 1, values, 0, last - 1); values[last - 1] = null; // [ _ _ _ 0 1 2 3 4 ] ==> [ 4 _ _ _ 0 1 2 3 ] } else if (distance == 1 && last == arrayLength) { values[0] = values[arrayLength - 1]; System.arraycopy(values, first, values, first + 1, last - first - 1); values[first] = null; // [ _ _ 0 1 2 3 4 _ ] ==> [ _ _ _ 0 1 2 3 4 ] } else if(distance == 1) { System.arraycopy(values, first, values, first + 1, last - first); values[first] = null; // [ _ _ _ 0 1 2 3 4 ] ==> [ _ _ 0 1 2 3 4 _ ] } else if(distance == -1) { System.arraycopy(values, first, values, first - 1, last - first); values[last - 1] = null; } } } private int distanceToTail(int index) { int tail = tail(); return index <= tail ? tail - index : tail + arrayLength - index; } private int distanceToHead(int index) { return index >= head ? index - head : index + arrayLength - head; } @Override public int size() { return size; } int toCircularIndex(int index) { // validate bounds if (index < 0 || index > size()) { throw new IndexOutOfBoundsException("Index " + index + " on List of size: " + size); } return modIndex(index + head); } /** * Returns the index modded within the values array. */ int modIndex(int value) { return (value + arrayLength) % arrayLength; } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/nio/0000755000175000017500000000000012106516362025663 5ustar gregoagregoa././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/nio/ServerShutdownException.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/nio/ServerShutdownException.jav0000644000175000017500000000110312106516362033241 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.nio; // NIO is used for CTP /** * The reason a connection is closed when the server is shutdown. */ class ServerShutdownException extends Exception { /** * Creates a new ServerShutdownException. */ public ServerShutdownException() { super("Server shutting down"); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/nio/Shutdown.java0000644000175000017500000000431112106516362030340 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.nio; // NIO is used for CTP import java.io.IOException; import java.nio.channels.SelectionKey; import java.nio.channels.ServerSocketChannel; import java.util.Iterator; import java.util.logging.Logger; /** * A task that gracefully shuts down the NIO daemon. */ class Shutdown implements Runnable { /** logging */ private static Logger logger = Logger.getLogger(Shutdown.class.toString()); /** the I/O event queue daemon */ private NIODaemon nioDaemon = null; /** * Create a new NIOShutdown that shuts down a server using the specified * NIODaemon. */ public Shutdown(NIODaemon nioDaemon) { this.nioDaemon = nioDaemon; } /** * Runs the specified task. */ public void run() { logger.info("Cleaning up listening socket and closing " + (nioDaemon.getSelector().keys().size()-1) + " connections"); // kill all connections for(Iterator k = nioDaemon.getSelector().keys().iterator(); k.hasNext(); ) { SelectionKey key = (SelectionKey)k.next(); // close an invalid connection if(!key.isValid()) { NIOAttachment attachment = (NIOAttachment)key.attachment(); attachment.close(new IOException("Connection closed")); // close the server socket } else if((key.interestOps() & SelectionKey.OP_ACCEPT) != 0) { try { ServerSocketChannel server = (ServerSocketChannel)key.channel(); server.close(); key.cancel(); } catch(IOException e) { logger.warning("Error closing server socket, " + e.getMessage()); } // close a connection socket } else { NIOAttachment attachment = (NIOAttachment)key.attachment(); attachment.close(new ServerShutdownException()); } } } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/nio/NIOAttachment.java0000644000175000017500000000152012106516362031162 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.nio; // NIO is used for CTP /** * Handles all sorts of incoming NIO events. An object implementing this interface * must be set as the attachment for all SelectionKeys used by the NIODaemon. */ public interface NIOAttachment { /** * Handle a connect-ready key. */ public void handleConnect(); /** * Handle a read-ready key. */ public void handleRead(); /** * Handle a write-ready key. */ public void handleWrite(); /** * Handle a close-ready key. */ public void close(Exception reason); } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/nio/SelectAndHandle.java0000644000175000017500000000560612106516362031513 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.nio; // NIO is used for CTP import java.io.IOException; import java.nio.channels.SelectionKey; import java.util.Iterator; import java.util.logging.Level; import java.util.logging.Logger; /** * The SelectAndHandle selects ready keys and handles them. */ class SelectAndHandle implements Runnable { /** logging */ private static Logger logger = Logger.getLogger(SelectAndHandle.class.toString()); /** the I/O event queue daemon */ private NIODaemon nioDaemon = null; /** * Create a new SelectorHandler for the specified NIO Daemon. */ public SelectAndHandle(NIODaemon nioDaemon) { this.nioDaemon = nioDaemon; } /** * Select and handle. */ public void run() { select(); handle(); } /** * Selects keys which are ready to be processed. */ void select() { // This may block for a long time. Upon returning, the // selected set contains keys of the ready channels try { nioDaemon.getSelector().select(); } catch(IOException e) { logger.log(Level.WARNING, e.getMessage(), e); } } /** * Handles all keys which are ready to be processed. */ void handle() { // Iterate over the selected keys for(Iterator i = nioDaemon.getSelector().selectedKeys().iterator(); i.hasNext(); ) { SelectionKey key = (SelectionKey)i.next(); i.remove(); // Is a new connection coming in? if(key.isValid() && key.isAcceptable()) { nioDaemon.getServer().handleAccept(key, nioDaemon.getSelector()); } // an outgoing connection has been established if(key.isValid() && key.isConnectable()) { NIOAttachment attachment = (NIOAttachment)key.attachment(); attachment.handleConnect(); } // incoming data can be read if(key.isValid() && key.isReadable()) { NIOAttachment attachment = (NIOAttachment)key.attachment(); attachment.handleRead(); } // outgoing data can be written if(key.isValid() && key.isWritable()) { NIOAttachment attachment = (NIOAttachment)key.attachment(); attachment.handleWrite(); } // clean up broken connections if(!key.isValid()) { NIOAttachment attachment = (NIOAttachment)key.attachment(); attachment.close(new IOException("Connection closed")); } } } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/nio/NIODaemon.java0000644000175000017500000001445012106516362030303 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.nio; // NIO is used for CTP import java.io.IOException; import java.nio.channels.Selector; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; /** * An event queue of I/O events and a thread to run them on. */ public final class NIODaemon implements Runnable { /** logging */ private static Logger logger = Logger.getLogger(NIODaemon.class.toString()); /** asynch queue of tasks to execute */ private List pendingRunnables = new ArrayList(); /** the only thread that shall access the network resources of this manager */ private Thread ioThread = null; /** the selector to awaken when necessary */ private Selector selector; /** whether the connection manager shall shut down */ private boolean keepRunning = false; /** whom to handle incoming connections */ private NIOServer server = null; /** * Starts the NIODaemon. */ public synchronized void start() throws IOException { // verify we haven't already started if(ioThread != null) throw new IllegalStateException(); // prepare for non-blocking, selectable IO selector = Selector.open(); // start handling connections keepRunning = true; ioThread = new Thread(this, "GlazedLists nio"); ioThread.start(); } /** * Continuously selects a connection which needs servicing and services it. */ public void run() { // the list of runnables to run this iteration List toExecute = new ArrayList(); // always run the selector handler SelectAndHandle selectAndHandle = new SelectAndHandle(this); // continuously select a socket and action on it while(keepRunning) { // get the list of runnables to run synchronized(this) { toExecute.addAll(pendingRunnables); toExecute.add(selectAndHandle); pendingRunnables.clear(); } // run the runnables for(Iterator i = toExecute.iterator(); keepRunning && i.hasNext(); ) { Runnable runnable = (Runnable)i.next(); i.remove(); try { runnable.run(); } catch(RuntimeException e) { logger.log(Level.SEVERE, "Failure processing I/O, continuing", e); } } } // do final clean up of state synchronized(this) { pendingRunnables.clear(); selector = null; ioThread = null; keepRunning = false; } } /** * Tests whether this connection manager has started. */ public synchronized boolean isRunning() { return (ioThread != null); } /** * Tests whether the current thread is the network thread. */ public synchronized boolean isNetworkThread() { return Thread.currentThread() == ioThread; } /** * Wake up the CTP thread so that it may process pending events. */ private void wakeUp() { selector.wakeup(); } /** * Runs the specified task on the NIODaemon thread. */ public void invokeAndWait(Runnable runnable) { // if the server has not yet been started if(!isRunning()) throw new IllegalStateException(); // invoke immediately if possible if(isNetworkThread()) { runnable.run(); // run on the network thread while waiting on the current thread } else { BlockingRunnable blockingRunnable = new BlockingRunnable(runnable); synchronized(blockingRunnable) { // start the event synchronized(this) { pendingRunnables.add(blockingRunnable); } wakeUp(); // wait for it to be completed try { blockingRunnable.wait(); } catch(InterruptedException e) { throw new RuntimeException("Wait interrupted " + e.getMessage()); } // propagate any RuntimeExceptions RuntimeException problem = blockingRunnable.getInvocationTargetException(); if(problem != null) throw problem; } } } /** * Runs the specified task the next time the NIODaemon thread has a chance. */ public void invokeLater(Runnable runnable) { synchronized(this) { // if the server has not yet been started if(!isRunning()) throw new IllegalStateException(); pendingRunnables.add(runnable); wakeUp(); } } /** * Stops the NIODaemon. */ public void stop() { // shutdown the server invokeAndWait(new Shutdown(this)); // stop the server invokeAndWait(new Stop()); } /** * Stops the server after it has been shut down. */ private class Stop implements Runnable { public void run() { // warn if unsatisfied keys remain if(selector.keys().size() != 0) { logger.warning("Server stopping with " + selector.keys().size() + " active connections"); } else { logger.info("Server stopping with " + selector.keys().size() + " active connections"); } // break out of the server dispatch loop keepRunning = false; } } /** * Gets the selector that this NIODaemon manages. */ public Selector getSelector() { return selector; } /** * Configure this NIODaemon to use the specified server handler for acceptable * selection keys. */ public void setServer(NIOServer server) { this.server = server; } public NIOServer getServer() { return server; } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/nio/BlockingRunnable.java0000644000175000017500000000262712106516362031754 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.nio; // NIO is used for CTP /** * A Runnable that unblocks the calling thread when it finishes executing. * *

      If this Runnable throws any {@link RuntimeException}s, they can be accessed * via this API. They will not be propagated up. */ class BlockingRunnable implements Runnable { /** the target runnable */ private Runnable target; /** any exception thrown during invocation */ private RuntimeException problem = null; /** * Creates a BlockingRunnable that runs the specified target while the calling * thread waits. */ public BlockingRunnable(Runnable target) { this.target = target; } /** * Runs the specified task. */ public void run() { // run the target runnable try { target.run(); } catch(RuntimeException e) { this.problem = e; } // wake up the waiting thread synchronized(this) { notify(); } } /** * Get any exception that was thrown during invocation. */ public RuntimeException getInvocationTargetException() { return problem; } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/nio/NIOServer.java0000644000175000017500000000113012106516362030335 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.nio; // NIO is used for CTP import java.nio.channels.SelectionKey; import java.nio.channels.Selector; /** * Handles incoming NIO connections. */ public interface NIOServer { /** * Handle an accept-ready selection key. */ public void handleAccept(SelectionKey selectionKey, Selector selector); } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/EventListIterator.java0000644000175000017500000001470612106516372031401 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; // the core Glazed Lists package import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import java.util.ListIterator; import java.util.NoSuchElementException; /** * The EventListIterator is an iterator that allows the user to iterate * on a list that may be changed while it is iterated. This is * possible because the iterator is a listener for change events to the * source list. * *

      This iterator simply keeps an index of where it is and what it last * saw. It knows nothing about the underlying storage performance of the List * that it iterates, and therefore provides generic, possibly slow access * to its elements. * * @author Jesse Wilson */ public class EventListIterator implements ListIterator, ListEventListener { /** the list being iterated */ private EventList source; /** the index of the next element to view */ private int nextIndex; /** the most recently accessed element */ private int lastIndex = -1; /** * Create a new event list iterator that iterates over the specified * source list. * * @param source the list to iterate */ public EventListIterator(EventList source) { this(source, 0, true); } /** * Creates a new iterator that starts at the specified index. * * @param source the list to iterate * @param nextIndex the starting point within the list */ public EventListIterator(EventList source, int nextIndex) { this(source, nextIndex, true); } /** * Creates a new iterator that starts at the specified index. * * @param source the list to iterate * @param nextIndex the starting point within the list * @param automaticallyRemove true if this SubList should deregister itself * from the ListEventListener list of the source list once it is * otherwise out of scope. * * @see GlazedLists#weakReferenceProxy(EventList, ListEventListener) */ public EventListIterator(EventList source, int nextIndex, boolean automaticallyRemove) { this.source = source; this.nextIndex = nextIndex; // listen directly or via a proxy that will do garbage collection if(automaticallyRemove) { ListEventListener gcProxy = new WeakReferenceProxy(source, this); source.addListEventListener(gcProxy); // do not manage dependencies for iterators, they never have multiple sources source.getPublisher().removeDependency(source, gcProxy); } else { source.addListEventListener(this); // do not manage dependencies for iterators, they never have multiple sources source.getPublisher().removeDependency(source, this); } } /** * Returns true if this list iterator has more elements when traversing the * list in the forward direction. */ public boolean hasNext() { return nextIndex < source.size(); } /** * Returns the next element in the list. */ public E next() { // next shouldn't have been called. if(nextIndex == source.size()) { throw new NoSuchElementException("Cannot retrieve element " + nextIndex + " on a list of size " + source.size()); // when next has not been removed } else { lastIndex = nextIndex; nextIndex++; return source.get(lastIndex); } } /** * Returns the index of the element that would be returned by a subsequent call to next. */ public int nextIndex() { return nextIndex; } /** * Returns true if this list iterator has more elements when traversing the * list in the reverse direction. */ public boolean hasPrevious() { return nextIndex > 0; } /** * Returns the previous element in the list. */ public E previous() { // previous shouldn't have been called if(nextIndex == 0) { throw new NoSuchElementException("Cannot retrieve element " + nextIndex + " on a list of size " + source.size()); // when previous has not been removed } else { nextIndex--; lastIndex = nextIndex; return source.get(nextIndex); } } /** * Returns the index of the element that would be returned by a subsequent call to previous. */ public int previousIndex() { return nextIndex - 1; } /** * Inserts the specified element into the list (optional operation). */ public void add(E o) { source.add(nextIndex, o); } /** * Removes from the list the last element that was returned by next * or previous (optional operation). */ public void remove() { if(lastIndex == -1) throw new IllegalStateException("Cannot remove() without a prior call to next() or previous()"); source.remove(lastIndex); } /** * Replaces the last element returned by next or previous with the * specified element (optional operation). */ public void set(E o) { if(lastIndex == -1) throw new IllegalStateException("Cannot set() without a prior call to next() or previous()"); source.set(lastIndex, o); } /** * When the list is changed, the iterator adjusts its index. */ public void listChanged(ListEvent listChanges) { while(listChanges.next()) { int changeIndex = listChanges.getIndex(); int changeType = listChanges.getType(); // if it is an insert if(changeType == ListEvent.INSERT) { if(changeIndex <= nextIndex) nextIndex++; if(lastIndex != -1 && changeIndex <= lastIndex) lastIndex++; // if it is a delete } else if(changeType == ListEvent.DELETE) { if(changeIndex < nextIndex) nextIndex--; if(lastIndex != -1 && changeIndex < lastIndex) lastIndex--; else if(lastIndex != -1 && changeIndex == lastIndex) lastIndex = -1; } } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/pmap/0000755000175000017500000000000012106516360026031 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/pmap/AddChunk.java0000644000175000017500000000426312106516360030362 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.pmap; // NIO is used for CTP import java.io.IOException; import java.util.logging.Logger; /** * Adds a chunk to the persistent map. * * @author Jesse Wilson */ class AddChunk implements Runnable { /** logging */ private static Logger logger = Logger.getLogger(AddChunk.class.toString()); /** the host map */ private final PersistentMap persistentMap; /** the value to write out */ private final Chunk newValue; /** the value to erase */ private final Chunk oldValue; /** * Create a new AddChunk. */ public AddChunk(PersistentMap persistentMap, Chunk newValue, Chunk oldValue) { this.persistentMap = persistentMap; this.newValue = newValue; this.oldValue = oldValue; } /** * Write the chunk to disk. * *

      This is a multiple stage procedure: *

        *
      1. Figure out how many bytes are needed for this chunk: *
      2. Allocate that many bytes in the file for the new section *
      3. Clean up the new section: mark it as empty and of the new size (flush 1) *
      4. Fill the new section (flush 2) *
      5. Mark the new section as not empty any more (flush 3) *
      */ public void run() { try { // allocate persistentMap.allocate(newValue); // get a sequence id newValue.setSequenceId(persistentMap.nextSequenceId()); // write out the data newValue.writeData(); // clear the old value if(oldValue != null) { oldValue.delete(); } logger.info("Successfully wrote value for key \"" + newValue.getKey() + "\""); } catch(IOException e) { persistentMap.fail(e, "Failed to write to file " + persistentMap.getFile().getPath()); } } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/pmap/BlockingValueCallback.java0000644000175000017500000000275512106516360033047 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.pmap; // NIO is used for CTP import ca.odell.glazedlists.impl.io.Bufferlo; /** * A ValueCallback that simply blocks until the value is ready. * * @author Jesse Wilson */ class BlockingValueCallback implements ValueCallback { /** the value returned */ private Bufferlo value = null; /** * Gets the value for the specified Chunk. */ public static Bufferlo get(Chunk member) { // queue the get BlockingValueCallback callback = new BlockingValueCallback(); member.fetchValue(callback); // wait till its ready synchronized(callback) { if(callback.value == null) { try { callback.wait(); } catch(InterruptedException e) { throw new RuntimeException(e); } } } // return the result return callback.value; } /** * Handles a value being completely loaded into memory and ready to read. */ public void valueLoaded(Chunk member, Bufferlo value) { synchronized(this) { this.value = value; notify(); } } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/pmap/PersistentMap.java0000644000175000017500000002070712106516360031500 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.pmap; // NIO is used for CTP import ca.odell.glazedlists.impl.nio.NIODaemon; import ca.odell.glazedlists.io.ByteCoder; import ca.odell.glazedlists.io.GlazedListsIO; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.io.Serializable; import java.nio.channels.FileChannel; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; /** * A Map whose entries are persisted to disk transactionally. * *

      This class uses an extra thread to do all persistence. This means that all * operations will be immediate, but will return without having taken effect on disk. * To flush the disk, call {@link #flush()}. * * @author Jesse Wilson */ public final class PersistentMap implements Map { // Implementation Notes // // Each entry is stored as a chunk in a file. // The chunks are allocated, modified and then turned on // All access is serialized /** logging */ private static Logger logger = Logger.getLogger(PersistentMap.class.toString()); /** read and write without ever flushing meta-data such as file-mod date */ private static final String FILE_ACCESS_MODE = "rwd"; /** the file where all the data is stored */ private File file = null; private FileChannel fileChannel = null; /** the I/O event queue daemon */ private NIODaemon nioDaemon = null; /** the underlying map stores the objects in RAM */ private Map map = new HashMap(); /** converts the key from an Object to bytes and back */ private ByteCoder keyCoder = null; /** the next sequence id to return */ private int nextSequenceId = 1500; /** just allocate bytes in order */ private int nextAvailableByte = 8; /** * Creates a new PersistentMap for the specified file that uses the {@link Serializable} * interface to convert keys to bytes. */ public PersistentMap(File file) throws IOException { this(file, GlazedListsIO.serializableByteCoder()); } /** * Creates a new PersistentMap for the specified file that uses the specified * {@link ByteCoder} to convert keys to bytes. */ public PersistentMap(File file, ByteCoder keyCoder) throws IOException { this.file = file; this.keyCoder = keyCoder; // set up file access fileChannel = new RandomAccessFile(file, FILE_ACCESS_MODE).getChannel(); // start the nio daemon nioDaemon = new NIODaemon(); nioDaemon.start(); // load the initial file nioDaemon.invokeAndWait(new OpenFile(this)); } /** * Closes the file used by this PersistentMap. */ public void close() { // close the file nioDaemon.invokeAndWait(new CloseFile(this)); // invalidate the local state map = null; } /** * Blocks until all pending writes to disk have completed. */ public void flush() { // ensure all pending changes have been written nioDaemon.invokeAndWait(NoOp.instance()); } /** * Removes all mappings from this map. */ public void clear() { // This must merge all chunks into one massive chunk. throw new UnsupportedOperationException(); } /** * Returns true if this map contains a mapping for the specified key. */ public boolean containsKey(Object key) { return map.containsKey(key); } /** * Returns true if this map maps one or more keys to the specified value. */ public boolean containsValue(Object value) { return map.containsValue(value); } /** * Returns a collection view of the mappings contained in this map. */ public Set entrySet() { throw new UnsupportedOperationException(); } /** * Associates the specified value with the specified key in this map. * * @throws IllegalArgumentException if value is not a chunk. */ public Object put(Object key, Object value) { if(!(value instanceof Chunk)) throw new IllegalArgumentException("value must be a chunk"); Chunk newValue = (Chunk)value; // prepare the chunk for persistence newValue.initializeForPersistence(this, key); // save the new chunk and update the memory-data Chunk oldValue = (Chunk)map.put(key, newValue); // write the chunk nioDaemon.invokeLater(new AddChunk(this, newValue, oldValue)); // return the previous value return oldValue; } /** * Returns the value to which the specified key is mapped in this weak hash map, or null if the map contains no mapping for this key. */ public Object get(Object key) { return map.get(key); } /** * Removes the mapping for this key from this map if present. */ public Object remove(Object key) { // remove from the memory-map Chunk removed = (Chunk)map.remove(key); // if there was nothing to remove if(removed == null) return null; // remove from disk nioDaemon.invokeLater(new RemoveChunk(this, removed)); // return the removed value return removed; } /** * Returns a set view of the keys contained in this map. */ public Set keySet() { return Collections.unmodifiableSet(map.keySet()); } /** * Returns a collection view of the values contained in this map. */ public Collection values() { return Collections.unmodifiableCollection(map.values()); } /** * Returns true if this map contains no key-value mappings. */ public boolean isEmpty() { return map.isEmpty(); } /** * Copies all of the mappings from the specified map to this map These mappings will replace any mappings that this map had for any of the keys currently in the specified map. */ public void putAll(Map m) { // this allocates a big chunk that is off and puts all the little chunks inside // then the big chunk is split into small chunks that are all on throw new UnsupportedOperationException(); } /** * Returns the number of key-value mappings in this map. */ public int size() { return map.size(); } /** * Gets the next logical sequence ID for a new chunk and increments the value * for the next caller. */ int nextSequenceId() { return nextSequenceId++; } /** * Get the daemon that does all the threading and selection. */ NIODaemon getNIODaemon() { return nioDaemon; } /** * Gets the file being written. */ File getFile() { return file; } /** * Gets the channel for writing to this file. */ FileChannel getFileChannel() { return fileChannel; } /** * Handles a write failure. */ void fail(IOException e, String message) { logger.log(Level.SEVERE, message, e); } /** * Handles the specified chunk having been loaded from file. */ void loadedChunk(Chunk chunk) { // if this chunk contains active data if(chunk.isOn()) { map.put(chunk.getKey(), chunk); nextSequenceId = Math.max(nextSequenceId, chunk.getSequenceId() + 1); } // update allocation nextAvailableByte = Math.max(nextAvailableByte, chunk.getOffset() + chunk.size()); } /** * Get the {@link ByteCoder} used to convert the key Objects to bytes and back. */ ByteCoder getKeyCoder() { return keyCoder; } /** * Allocate some space for this chunk. The returned value is an array of two * integers. Result[0] == offset into file, Result[1] == number of bytes allocated. * Result[2] == the size index being used for this chunk, either 1 or 2, or 0 * for new space. * *

      More bytes may be allocated than necessary, and it is absolutely mandatory * that chunks consume the full number of bytes allocated to them. */ void allocate(Chunk value) throws IOException { int offset = nextAvailableByte; int size = value.bytesRequired(); nextAvailableByte += size; value.allocateAsNew(offset, size); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/pmap/OpenFile.java0000644000175000017500000000574412106516360030407 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.pmap; // NIO is used for CTP import ca.odell.glazedlists.impl.io.Bufferlo; import java.io.IOException; import java.nio.channels.FileChannel; import java.text.ParseException; import java.util.logging.Logger; /** * Opens a file for reading and writing a persistent map. * * @author Jesse Wilson */ class OpenFile implements Runnable { /** logging */ private static Logger logger = Logger.getLogger(OpenFile.class.toString()); /** the host map */ private PersistentMap persistentMap = null; /** * Create a new OpenFile. */ public OpenFile(PersistentMap persistentMap) { this.persistentMap = persistentMap; } /** * Open the file, read any data if it exists, and prepare everything. */ public void run() { FileChannel fileChannel = persistentMap.getFileChannel(); try { // if the file doesn't already exist if(fileChannel.size() == 0) { createFile(); return; } // first read the header readHeader(); // now read the data while(true) { Chunk chunk = Chunk.readChunk(persistentMap); if(chunk == null) break; persistentMap.loadedChunk(chunk); if(chunk.isOn()) { logger.info("Successfully loaded key \"" + chunk.getKey() + "\""); } } } catch(IOException e) { persistentMap.fail(e, "Failed to access file " + persistentMap.getFile().getPath()); } } /** * Reads the file header, or creates a new file if the header is broken or * doesn't exist. */ private void readHeader() throws IOException { try { // process the file header Bufferlo fileHeader = new Bufferlo(); fileHeader.readFromChannel(persistentMap.getFileChannel(), 8); fileHeader.consume("GLAZED\n\n"); } catch(ParseException e) { // the file header is broken, bail throw new IOException("The file cannot be read because it is not of the expected type"); } logger.info("Successfully read file header"); } /** * Creates the PersistentMap file. */ private void createFile() throws IOException { // write the file header Bufferlo fileHeader = new Bufferlo(); fileHeader.write("GLAZED\n\n"); persistentMap.getFileChannel().position(0); fileHeader.writeToChannel(persistentMap.getFileChannel()); logger.info("Successfully created file"); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/pmap/ValueCallback.java0000644000175000017500000000121412106516360031363 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.pmap; // NIO is used for CTP import ca.odell.glazedlists.impl.io.Bufferlo; /** * Listens for the loading of a value. * * @author Jesse Wilson */ public interface ValueCallback { /** * Handles a value being completely loaded into memory and ready to read. */ public void valueLoaded(Chunk member, Bufferlo value); }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/pmap/LoadValue.java0000644000175000017500000000266412106516360030560 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.pmap; // NIO is used for CTP import java.io.IOException; import java.util.logging.Logger; /** * Loads the value for a chunk from disk. * * @author Jesse Wilson */ class LoadValue implements Runnable { /** logging */ private static Logger logger = Logger.getLogger(LoadValue.class.toString()); /** the chunk with the data of interest */ private final Chunk chunk; /** the interested party */ private final ValueCallback valueCallback; /** * Create a new LoadValue. */ public LoadValue(Chunk chunk, ValueCallback valueCallback) { this.chunk = chunk; this.valueCallback = valueCallback; } /** * Read a chunk's value from disk. */ public void run() { try { if(!chunk.isOn()) throw new IOException("Chunk has been destroyed"); valueCallback.valueLoaded(chunk, chunk.readValue()); } catch(IOException e) { chunk.getPersistentMap().fail(e, "Failed to read value from file " + chunk.getPersistentMap().getFile().getPath()); valueCallback.valueLoaded(chunk, null); } } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/pmap/CloseFile.java0000644000175000017500000000217012106516360030541 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.pmap; // NIO is used for CTP import java.io.IOException; import java.util.logging.Logger; /** * Closes the file for reading and writing a persistent map. * * @author Jesse Wilson */ class CloseFile implements Runnable { /** logging */ private static Logger logger = Logger.getLogger(CloseFile.class.toString()); /** the host map */ private PersistentMap persistentMap = null; /** * Create a new CloseFile. */ public CloseFile(PersistentMap persistentMap) { this.persistentMap = persistentMap; } /** * Close the file. */ public void run() { try { persistentMap.getFileChannel().close(); } catch(IOException e) { persistentMap.fail(e, "Failed to close file " + persistentMap.getFile().getPath()); } } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/pmap/RemoveChunk.java0000644000175000017500000000251112106516360031121 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.pmap; // NIO is used for CTP import java.io.IOException; import java.util.logging.Logger; /** * Removes a chunk from the persistent map. * * @author Jesse Wilson */ class RemoveChunk implements Runnable { /** logging */ private static Logger logger = Logger.getLogger(RemoveChunk.class.toString()); /** the host map */ private final PersistentMap persistentMap; /** the value to erase */ private final Chunk chunk; /** * Create a new RemoveChunk. */ public RemoveChunk(PersistentMap persistentMap, Chunk chunk) { this.persistentMap = persistentMap; this.chunk = chunk; } /** * Removes the chunk by marking it off. */ public void run() { try { chunk.delete(); logger.info("Successfully removed value for key \"" + chunk.getKey() + "\""); } catch(IOException e) { persistentMap.fail(e, "Failed to write to file " + persistentMap.getFile().getPath()); } } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/pmap/NoOp.java0000644000175000017500000000165412106516360027555 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.pmap; // NIO is used for CTP /** * This doesn't do anything! It's useful for flushing pending events with invokeAndWait(). */ class NoOp implements Runnable { /** singleton */ private static NoOp instance = new NoOp(); /** * Private constructor blocks users from not using the singleton. */ private NoOp() { // nothing } /** * Get an instance of NoOp. This is used instead of a conventional constructor * because NoOp is a singleton. */ public static NoOp instance() { return instance; } /** * Doesn't do anything! */ public void run() { // nothing } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/pmap/Chunk.java0000644000175000017500000002711012106516360027745 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.pmap; // NIO is used for CTP import ca.odell.glazedlists.impl.io.Bufferlo; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.nio.channels.FileChannel; /** * A chunk of a file. * * @author Jesse Wilson */ public final class Chunk { /** the host PersistentMap */ private PersistentMap persistentMap = null; /** the offset of this chunk into the file */ private int offset = -1; /** the current size of this chunk and previous/next size */ private int[] size = new int[] { -1, -1 }; /** whether the size 0 or size 1 */ private int sizeToUse = -1; /** whether this chunk is on or off */ private boolean on = false; /** the order in which this chunk was written to file */ private int sequenceId = -1; /** the key for this chunk, should be immutable or treated as such */ private Object key = null; private Bufferlo keyBytes = null; private int keyBytesLength = -1; /** the value for this chunk */ private int valueBytesLength = -1; private Bufferlo valueBytes = null; /** * Creates a {@link Chunk} with the specified value. */ public Chunk(Bufferlo value) { valueBytes = value.duplicate(); valueBytesLength = valueBytes.length(); } /** * Creates a {@link Chunk} from the specified data from disk. */ private Chunk(PersistentMap persistentMap, int offset, boolean on, int sizeToUse, int[] size) { this.persistentMap = persistentMap; this.offset = offset; this.on = on; this.sizeToUse = sizeToUse; this.size = size; } /** * Fetches the value for this chunk and sends it to the specified ValueCallback. */ public void fetchValue(ValueCallback valueCallback) { persistentMap.getNIODaemon().invokeLater(new LoadValue(this, valueCallback)); } /** * Gets the value for this Chunk. Since the value may not be available immediately, * this method will block until it is available. For non-blocking access, use * the {@link #fetchValue(ValueCallback)} method. */ public Bufferlo getValue() { return BlockingValueCallback.get(this); } /** * Gets the map that hosts this chunk. */ PersistentMap getPersistentMap() { return persistentMap; } /** * Get the current size of this chunk. */ int size() { return size[sizeToUse]; } /** * Gets whether this Chunk contains active data. */ boolean isOn() { return on; } /** * Get the offset of this chunk. */ public int getOffset() { return offset; } /** * Gets the key that indexes this chunk. */ Object getKey() { return key; } /** * Sets the ID in which this chunk was created. */ void setSequenceId(int sequenceId) { this.sequenceId = sequenceId; } /** * Get the sequence of this chunk. */ int getSequenceId() { return sequenceId; } /** * Sets up how this chunk will be persisted. */ void initializeForPersistence(PersistentMap persistentMap, Object key) { // we can't reuse chunks (yet) if(this.persistentMap != null) throw new IllegalStateException("doubly-initialized chunk"); // save the host this.persistentMap = persistentMap; // save the key and take a binary snapshot of it this.key = key; try { keyBytes = new Bufferlo(); persistentMap.getKeyCoder().encode(key, keyBytes.getOutputStream()); keyBytesLength = keyBytes.length(); } catch(IOException e) { persistentMap.fail(e, "Unexpected encoding exception"); } } /** * Get the number of bytes required to write out this chunk. */ int bytesRequired() { int required = 0; // header required += 4; // on/off required += 4; // sizeToUse required += 4; // size one required += 4; // size two // body required += 4; // sequence ID required += 4; // key size required += 4; // value size required += keyBytesLength; // key required += valueBytesLength; // value return required; } /** * Set the size of this Chunk. This writes the new size to disk. This requires * 1 flush to disk. */ void allocateAsNew(int offset, int size) throws IOException { this.offset = offset; this.sizeToUse = 0; this.size[0] = size; this.size[1] = size; this.on = false; // write the size FileChannel fileChannel = persistentMap.getFileChannel(); Bufferlo sizeData = new Bufferlo(); DataOutputStream sizeDataOut = new DataOutputStream(sizeData.getOutputStream()); sizeDataOut.writeInt(0); // on == false sizeDataOut.writeInt(sizeToUse); sizeDataOut.writeInt(this.size[0]); sizeDataOut.writeInt(this.size[1]); sizeData.writeToChannel(fileChannel.position(offset)); fileChannel.force(false); } /** * Deletes this Chunk. This simply marks the Chunk's on value to off. */ void delete() throws IOException { assert(offset != -1); if(!on) return; // turn the chunk off this.on = false; // write that to disk FileChannel fileChannel = persistentMap.getFileChannel(); Bufferlo sizeData = new Bufferlo(); DataOutputStream sizeDataOut = new DataOutputStream(sizeData.getOutputStream()); sizeDataOut.writeInt(0); // on == false sizeData.writeToChannel(fileChannel.position(offset)); fileChannel.force(false); } /** * Reads the chunk into memory. */ static Chunk readChunk(PersistentMap persistentMap) throws IOException { // prepare to read FileChannel fileChannel = persistentMap.getFileChannel(); Bufferlo sizeData = new Bufferlo(); DataInputStream dataIn = new DataInputStream(sizeData.getInputStream()); // read the header int offset = (int)fileChannel.position(); int bytesRequired = 16; int read = sizeData.readFromChannel(fileChannel, bytesRequired); if(read < 0) return null; else if(read < bytesRequired) throw new IOException("Insufficent bytes available"); // parse the header int on = dataIn.readInt(); int sizeToUse = dataIn.readInt(); int size[] = new int[] { -1 , -1 }; size[0] = dataIn.readInt(); size[1] = dataIn.readInt(); // validate the header data if(on != 0 && on != 1) throw new IOException("Unexpected on value: " + on); if(sizeToUse != 0 && sizeToUse != 1) throw new IOException("Unexpected size to use value " + sizeToUse); if(size[sizeToUse] < 0) throw new IOException("Unexpected size: " + size[sizeToUse]); // header success Chunk chunk = new Chunk(persistentMap, offset, (on == 1), sizeToUse, size); // read the data if(chunk.on) { chunk.readHeader(); } // adjust the position to after this chunk fileChannel.position(offset + size[sizeToUse]); // success return chunk; } /** * Writes the data of this chunk to file. This requires 2 flushes to disk. */ void writeData() throws IOException { assert(offset != -1); assert(!on); // prepare to write FileChannel fileChannel = persistentMap.getFileChannel(); Bufferlo chunkData = new Bufferlo(); DataOutputStream chunkDataOut = new DataOutputStream(chunkData.getOutputStream()); // write the data chunkDataOut.writeInt(sequenceId); chunkDataOut.writeInt(keyBytesLength); chunkDataOut.writeInt(valueBytesLength); chunkData.append(keyBytes); chunkData.append(valueBytes); chunkData.writeToChannel(fileChannel.position(offset + 16)); fileChannel.force(false); // turn the written data on on = true; chunkDataOut.writeInt(1); // on == true chunkData.writeToChannel(fileChannel.position(offset)); fileChannel.force(false); // clean up stuff we don't need no more keyBytes = null; valueBytes = null; } /** * Reads the sequence ID, key and value size for this chunk. */ private void readHeader() throws IOException { assert(offset != -1); assert(size() != -1); // prepare to read FileChannel fileChannel = persistentMap.getFileChannel(); Bufferlo chunkAsBytes = new Bufferlo(); DataInputStream dataIn = new DataInputStream(chunkAsBytes.getInputStream()); // read the whole chunk int bytesRequired = size(); int read = chunkAsBytes.readFromChannel(fileChannel.position(offset), bytesRequired); if(read < bytesRequired) throw new IOException("Expected " + bytesRequired + " but found " + read + " bytes"); // skip the chunk header dataIn.readInt(); // on/off dataIn.readInt(); // size to use dataIn.readInt(); // size 1 dataIn.readInt(); // size 2 // read the data sequenceId = dataIn.readInt(); keyBytesLength = dataIn.readInt(); valueBytesLength = dataIn.readInt(); keyBytes = chunkAsBytes.consume(keyBytesLength); // skip any excess chunkAsBytes.clear(); // process the read data key = persistentMap.getKeyCoder().decode(keyBytes.getInputStream()); } /** * Reads the value for this chunk. */ Bufferlo readValue() throws IOException { assert(offset != -1); assert(size() != -1); assert(valueBytesLength != -1); // prepare to read FileChannel fileChannel = persistentMap.getFileChannel(); Bufferlo valueBytes = new Bufferlo(); int valueLocation = offset; // adjust the value location: header valueLocation += 4; // on/off valueLocation += 4; // sizeToUse valueLocation += 4; // size one valueLocation += 4; // size two // adjust the value location: body valueLocation += 4; // sequence ID valueLocation += 4; // key size valueLocation += 4; // value size valueLocation += keyBytesLength; // key // read int read = valueBytes.readFromChannel(fileChannel.position(valueLocation), valueBytesLength); if(read < valueBytesLength) throw new IOException("Expected " + valueBytesLength + " but found " + read + " bytes"); // done return valueBytes; } @Override public String toString() { StringBuffer result = new StringBuffer(); result.append(""); return result.toString(); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/PLAFDetector.java0000644000175000017500000000777712106516372030200 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; // for inspecting themes import javax.swing.*; import javax.swing.plaf.metal.MetalLookAndFeel; import javax.swing.plaf.metal.MetalTheme; import java.lang.reflect.Method; import java.security.AccessControlException; /** * A PLAFDetector provides a means to discover which versions of themes * are available on the host system. * * @author Jesse Wilson */ public final class PLAFDetector { /** * Workaround method to get the metal theme, either "Steel" or "Ocean". This is because the * Metal look and feel's getName() property does not distinguish between versions correctly. * A bug has been submitted to the Sun Java bug database and will be reviewed. */ public static String getMetalTheme() { try { MetalLookAndFeel metalLNF = (MetalLookAndFeel)UIManager.getLookAndFeel(); Method getCurrentTheme = metalLNF.getClass().getMethod("getCurrentTheme", new Class[0]); MetalTheme currentTheme = (MetalTheme)getCurrentTheme.invoke(metalLNF, new Object[0]); return "Metal/" + currentTheme.getName(); } catch(NoSuchMethodException e) { // must be Java 1.4 because getCurrentTheme() method does not exist // therefore the theme of interest is "Steel" return "Metal/Steel"; } catch(Exception e) { e.printStackTrace(); return "Metal/Steel"; } } /** * Workaround method to get the Windows theme, either "Windows Classic", * "Windows XP" or "Windows Vista". * *

      The test for the Windows style is also an ugly hack because Swing's * pluggable look-and-feel provides no alternative means for determining if * the current theme is XP. For compatibility, the algorithm to determine if * the style is XP is derived from similar code in the XPStyle class. */ public static String getWindowsTheme() { String classic = "Classic Windows"; String xp = "Windows XP"; String vista = "Windows Vista"; // theme active property must be "Boolean.TRUE"; String themeActiveKey = "win.xpstyle.themeActive"; Boolean themeActive = (Boolean)java.awt.Toolkit.getDefaultToolkit().getDesktopProperty(themeActiveKey); if(themeActive == null) return classic; if(!themeActive.booleanValue()) return classic; // no "swing.noxp" system property String noXPProperty = "swing.noxp"; try { if(System.getProperty(noXPProperty) != null) return classic; } catch (AccessControlException e) { // in WebStart, it is possible to receive this exception when // querying for the swing.noxp property - in which case we don't // want to acknowledge the security violation } // l&f class must not be "WindowsClassicLookAndFeel" String classicLnF = "com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel"; if(UIManager.getLookAndFeel().getClass().getName().equals(classicLnF)) return "Classic Windows"; // breakdown by major engineering version // Windows 2000 os.version is 5.0 // Windows XP os.version is 5.1 // Windows Vista os.version is 6 try { double osVersion = Double.parseDouble(System.getProperty("os.version")); if(osVersion >= 6.0) return vista; if(osVersion >= 5.1) return xp; return classic; } catch (AccessControlException e) { // return a reasonable default if we're not allowed access to the OS version return xp; } catch (NumberFormatException e) { // return a reasonable default if the version wasn't in the expected format return xp; } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/ReadOnlyList.java0000644000175000017500000001326212106516372030317 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; // standard collections import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.TransformedList; import ca.odell.glazedlists.event.ListEvent; import java.util.Collection; import java.util.List; /** * An {@link EventList} that does not allow writing operations. * *

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

      The {@link ReadOnlyList} 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 any {@link List}. * *

      {@link ReadOnlyList} does not alter the runtime performance * characteristics of the source {@link EventList}. * *

      Warning: This class is * thread ready but not thread safe. See {@link EventList} for an example * of thread safe code. * * @see TransformedList * * @author Jesse Wilson */ public final class ReadOnlyList extends TransformedList { /** * Creates a {@link ReadOnlyList} to provide a view of an {@link EventList} * that does not allow write operations. */ public ReadOnlyList(EventList source) { super(source); source.addListEventListener(this); } /** * @return false; ReadOnlyList is... ahem... readonly */ @Override protected boolean isWritable() { return false; } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { // just pass on the changes updates.forwardEvent(listChanges); } // // All accessor methods must call to the source list so as not to // disturb the performance characteristics of the source list algorithms. // /** {@inheritDoc} */ @Override public boolean contains(Object object) { return source.contains(object); } /** {@inheritDoc} */ @Override public Object[] toArray() { return source.toArray(); } /** {@inheritDoc} */ @Override public T[] toArray(T[] array) { return source.toArray(array); } /** {@inheritDoc} */ @Override public boolean containsAll(Collection values) { return source.containsAll(values); } /** {@inheritDoc} */ @Override public int indexOf(Object object) { return source.indexOf(object); } /** {@inheritDoc} */ @Override public int lastIndexOf(Object object) { return source.lastIndexOf(object); } /** {@inheritDoc} */ @Override public boolean equals(Object object) { return source.equals(object); } /** {@inheritDoc} */ @Override public int hashCode() { return source.hashCode(); } // // All mutator methods should throw an UnsupportedOperationException with // a descriptive message explaining why mutations are disallowed. // /** @throws UnsupportedOperationException since ReadOnlyList cannot be modified */ @Override public boolean add(E value) { throw new UnsupportedOperationException("ReadOnlyList cannot be modified"); } /** @throws UnsupportedOperationException since ReadOnlyList cannot be modified */ @Override public void add(int index, E value) { throw new UnsupportedOperationException("ReadOnlyList cannot be modified"); } /** @throws UnsupportedOperationException since ReadOnlyList cannot be modified */ @Override public boolean addAll(Collection values) { throw new UnsupportedOperationException("ReadOnlyList cannot be modified"); } /** @throws UnsupportedOperationException since ReadOnlyList cannot be modified */ @Override public boolean addAll(int index, Collection values) { throw new UnsupportedOperationException("ReadOnlyList cannot be modified"); } /** @throws UnsupportedOperationException since ReadOnlyList cannot be modified */ @Override public void clear() { throw new UnsupportedOperationException("ReadOnlyList cannot be modified"); } /** @throws UnsupportedOperationException since ReadOnlyList cannot be modified */ @Override public boolean remove(Object toRemove) { throw new UnsupportedOperationException("ReadOnlyList cannot be modified"); } /** @throws UnsupportedOperationException since ReadOnlyList cannot be modified */ @Override public E remove(int index) { throw new UnsupportedOperationException("ReadOnlyList cannot be modified"); } /** @throws UnsupportedOperationException since ReadOnlyList cannot be modified */ @Override public boolean removeAll(Collection collection) { throw new UnsupportedOperationException("ReadOnlyList cannot be modified"); } /** @throws UnsupportedOperationException since ReadOnlyList cannot be modified */ @Override public boolean retainAll(Collection values) { throw new UnsupportedOperationException("ReadOnlyList cannot be modified"); } /** @throws UnsupportedOperationException since ReadOnlyList cannot be modified */ @Override public E set(int index, E value) { throw new UnsupportedOperationException("ReadOnlyList cannot be modified"); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/gui/0000755000175000017500000000000012106516362025662 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/gui/SortingStrategy.java0000644000175000017500000000350712106516362031702 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.gui; /** * Behaviour that defines how to interpret a mouse click on a table's header. * *

      This interface is intentionally stateless so that a single * instance can be shared between multiple TableComparatorChoosers. * *

      This interface is not designed to be implemented by * users of TableComparatorChooser and is not guaranteed to be * backward compatible between releases. Users are encouraged to use the * predefined constant values in * {@link ca.odell.glazedlists.gui.AbstractTableComparatorChooser}. * * @author Jesse Wilson */ public interface SortingStrategy { /** * This method is called each time a user attempts to adjust the sort order * enforced by a TableComparatorChooser by clicking in a table * header. The implementation is expected to adjust the * sortingState as necessary and call * {@link SortingState#fireSortingChanged()} afterward to honour the new * sorting state. * * @param sortingState an object which models the details regarding which * columns are enforcing sort order * @param column the column that was clicked on * @param clicks the number of recorded mouse clicks * @param shift true if the shift key was down at the time of the click * @param control true if the control key was down at the time of the click */ public void columnClicked(SortingState sortingState, int column, int clicks, boolean shift, boolean control); }././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/gui/MouseOnlySortingStrategyWithUndo.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/gui/MouseOnlySortingStrategyWit0000644000175000017500000000417012106516362033316 0ustar gregoagregoapackage ca.odell.glazedlists.impl.gui; import java.util.List; /** * @see ca.odell.glazedlists.gui.AbstractTableComparatorChooser#MULTIPLE_COLUMN_MOUSE_WITH_UNDO * * @author James Lemieux */ public class MouseOnlySortingStrategyWithUndo implements SortingStrategy { /** * The normal MouseOnlySortingStrategy that is decorated with the ability * to clear the sort from the table. */ private final SortingStrategy decorated = new MouseOnlySortingStrategy(true); public void columnClicked(SortingState sortingState, int column, int clicks, boolean shift, boolean control) { final SortingState.SortingColumn clickedColumn = sortingState.getColumns().get(column); // if the column defines no Comparators, we don't need to alter the table's Comparator if (clickedColumn.getComparators().isEmpty()) return; final List recentlyClickedColumns = sortingState.getRecentlyClickedColumns(); final boolean wasPrimarySortColumnClicked = !recentlyClickedColumns.isEmpty() && clickedColumn == recentlyClickedColumns.get(0); final boolean isPrimarySortColumnReversed = clickedColumn.isReverse(); final boolean isLastComparatorForPrimarySortColumn = clickedColumn.getComparatorIndex() == clickedColumn.getComparators().size() - 1; // if the following conditions exist on this click: // a) the column that was clicked was the column providing the primary sort // b) the primary sort column is currently reversed // c) the primary sort column is currently sorting using its last Comparator // // then deviate from the normal behaviour of MouseOnlySortingStrategy by // removing all sorting from the table if (wasPrimarySortColumnClicked && isPrimarySortColumnReversed && isLastComparatorForPrimarySortColumn) { sortingState.clearComparators(); sortingState.fireSortingChanged(); return; } // otherwise, proceed with the normal MouseOnlySortingStrategy decorated.columnClicked(sortingState, column, clicks, shift, control); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/gui/SortingState.java0000644000175000017500000003430712106516362031162 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.gui; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.gui.AbstractTableComparatorChooser; import ca.odell.glazedlists.gui.AdvancedTableFormat; import ca.odell.glazedlists.gui.TableFormat; import ca.odell.glazedlists.impl.sort.ComparatorChain; import ca.odell.glazedlists.impl.sort.ReverseComparator; import ca.odell.glazedlists.impl.sort.TableColumnComparator; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Keep track of which columns are sorted and how. This is * largely independent of how that state is applied to a * SortedList, which is managed independently by * TableComparatorChooser. * *

      Users must explicity call {@link #fireSortingChanged()} in order * to prepare a new Comparator for the target table. * * @author Jesse Wilson */ public class SortingState { /** this regular expression for parsing the string representation of a column */ private static final Pattern FROM_STRING_PATTERN = Pattern.compile("^\\s*column\\s+(\\d+)(\\s+comparator\\s+(\\d+))?(\\s+(reversed))?\\s*$", Pattern.CASE_INSENSITIVE); /** the sorting style on a column is used for icon choosing */ protected static final int COLUMN_UNSORTED = 0; protected static final int COLUMN_PRIMARY_SORTED = 1; protected static final int COLUMN_PRIMARY_SORTED_REVERSE = 2; protected static final int COLUMN_PRIMARY_SORTED_ALTERNATE = 3; protected static final int COLUMN_PRIMARY_SORTED_ALTERNATE_REVERSE = 4; protected static final int COLUMN_SECONDARY_SORTED = 5; protected static final int COLUMN_SECONDARY_SORTED_REVERSE = 6; protected static final int COLUMN_SECONDARY_SORTED_ALTERNATE = 7; protected static final int COLUMN_SECONDARY_SORTED_ALTERNATE_REVERSE = 8; /** the columns and their click counts in indexed order */ protected List sortingColumns; /** a list that contains all ColumnClickTrackers with non-zero click counts in their visitation order */ protected List recentlyClickedColumns = new ArrayList(2); private final AbstractTableComparatorChooser tableComparatorChooser; /** whom to notify when the sorting state is chaged */ private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this); public SortingState(AbstractTableComparatorChooser tableComparatorChooser) { this.tableComparatorChooser = tableComparatorChooser; } public AbstractTableComparatorChooser getTableComparatorChooser() { return tableComparatorChooser; } public void fireSortingChanged() { changeSupport.firePropertyChange("comparator", null, null); } public void addPropertyChangeListener(PropertyChangeListener listener) { changeSupport.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { changeSupport.removePropertyChangeListener(listener); } public Comparator buildComparator() { // build a new comparator if(recentlyClickedColumns.isEmpty()) { return null; } else { List> comparators = new ArrayList>(recentlyClickedColumns.size()); for(Iterator i = recentlyClickedColumns.iterator(); i.hasNext(); ) { SortingColumn sortingColumn = i.next(); Comparator comparator = sortingColumn.getComparator(); if(comparator == null) throw new IllegalStateException(); comparators.add(comparator); } return GlazedLists.chainComparators(comparators); } } /** * @return the indices of the columns currently being sorted. */ public List getSortingColumnIndexes() { final List sortingColumns = new ArrayList(); final List recentlyClickedColumns = getRecentlyClickedColumns(); for(int c = 0; c < recentlyClickedColumns.size(); c++) { SortingState.SortingColumn clickedColumn = recentlyClickedColumns.get(c); sortingColumns.add(new Integer(clickedColumn.getColumn())); } return sortingColumns; } public void appendComparator(int column, int comparatorIndex, boolean reverse) { if(column > getColumns().size()) throw new IllegalArgumentException("invalid column " + column + ", must be in range 0, " + sortingColumns.size()); if(comparatorIndex >= sortingColumns.get(column).getComparators().size()) throw new IllegalArgumentException("invalid comparator index " + comparatorIndex + ", must be in range 0, " + sortingColumns.get(column).getComparators().size()); if(recentlyClickedColumns.contains(getColumns().get(column))) return; // add clicks to the specified column SortingColumn sortingColumn = sortingColumns.get(column); sortingColumn.setComparatorIndex(comparatorIndex); sortingColumn.setReverse(reverse); // rebuild the clicked column list recentlyClickedColumns.add(sortingColumn); } public void detectStateFromComparator(Comparator foreignComparator) { // Clear the current click counts clearComparators(); // Populate a list of Comparators final List comparatorsList; if(foreignComparator == null) { comparatorsList = Collections.emptyList(); } else if(foreignComparator instanceof ComparatorChain) { ComparatorChain chain = (ComparatorChain)foreignComparator; comparatorsList = Arrays.asList(chain.getComparators()); } else { comparatorsList = Collections.singletonList(foreignComparator); } // walk through the list of Comparators and assign click counts for(Iterator i = comparatorsList.iterator(); i.hasNext(); ) { // get the current comparator Comparator comparator = i.next(); boolean reverse = false; if(comparator instanceof ReverseComparator) { reverse = true; comparator = ((ReverseComparator)comparator).getSourceComparator(); } // discover where to add clicks for this comparator for(int c = 0; c < sortingColumns.size(); c++) { if(recentlyClickedColumns.contains(sortingColumns.get(c))) { continue; } int comparatorIndex = sortingColumns.get(c).getComparators().indexOf(comparator); if(comparatorIndex != -1) { final SortingColumn columnClickTracker = sortingColumns.get(c); columnClickTracker.setComparatorIndex(comparatorIndex); columnClickTracker.setReverse(reverse); recentlyClickedColumns.add(columnClickTracker); } } } } public void clearComparators() { // clear the click counts for(Iterator i = recentlyClickedColumns.iterator(); i.hasNext(); ) { SortingColumn sortingColumn = i.next(); sortingColumn.clear(); } recentlyClickedColumns.clear(); } /** * When the column model is changed, this resets the column clicks and * comparator list for each column. */ public void rebuildColumns(TableFormat tableFormat) { // build the column click trackers final int columnCount = tableFormat.getColumnCount(); sortingColumns = new ArrayList(columnCount); for(int i = 0; i < columnCount; i++) { sortingColumns.add(createSortingColumn(tableFormat, i)); } recentlyClickedColumns.clear(); } protected SortingColumn createSortingColumn(TableFormat tableFormat, int columnIndex) { return new SortingColumn(tableFormat, columnIndex); } public List getColumns() { return sortingColumns; } public List getRecentlyClickedColumns() { return recentlyClickedColumns; } @Override public String toString() { final StringBuffer result = new StringBuffer(); for(Iterator i = getSortingColumnIndexes().iterator(); i.hasNext();) { final int columnIndex = i.next().intValue(); final SortingState.SortingColumn sortingColumn = getColumns().get(columnIndex); // write the column index result.append("column "); result.append(columnIndex); // write the comparator index final int comparatorIndex = sortingColumn.getComparatorIndex(); if(comparatorIndex != 0) { result.append(" comparator "); result.append(comparatorIndex); } // write reversed if(sortingColumn.isReverse()) { result.append(" reversed"); } // add a comma if more columns exist if (i.hasNext()) { result.append(", "); } } return result.toString(); } public void fromString(String stringEncoded) { clearComparators(); // parse each column part in sequence using regex groups String[] parts = stringEncoded.split(","); for(int p = 0; p < parts.length; p++) { // skip empty strings if(parts[p].trim().length() == 0) continue; Matcher matcher = FROM_STRING_PATTERN.matcher(parts[p]); if(!matcher.find()) throw new IllegalArgumentException("Failed to parse column spec, \"" + parts[p] + "\""); int columnIndex = Integer.parseInt(matcher.group(1)); int comparatorIndex = matcher.group(3) == null ? 0 : Integer.parseInt(matcher.group(3)); boolean reversedComparator = matcher.group(5) != null; // bail on invalid data if(columnIndex >= sortingColumns.size()) continue; if(comparatorIndex >= sortingColumns.get(columnIndex).getComparators().size()) continue; // add this comparator in sequence appendComparator(columnIndex, comparatorIndex, reversedComparator); } } public class SortingColumn { /** the column whose sorting state is being managed */ private final int column; /** the sequence of comparators for this column */ private final List comparators = new ArrayList(1); /** whether this column is sorted in reverse order */ private boolean reverse = false; /** the comparator in the comparator list to sort by */ private int comparatorIndex = -1; public SortingColumn(TableFormat tableFormat, int column) { this.column = column; // add the preferred comparator for AdvancedTableFormat if(tableFormat instanceof AdvancedTableFormat) { AdvancedTableFormat advancedTableFormat = (AdvancedTableFormat)tableFormat; Comparator columnComparator = advancedTableFormat.getColumnComparator(column); if(columnComparator != null) comparators.add(new TableColumnComparator(tableFormat, column, columnComparator)); // otherwise just add the default comparator } else { comparators.add(new TableColumnComparator(tableFormat, column)); } } public void clear() { this.reverse = false; this.comparatorIndex = -1; } public int getColumn() { return column; } /** * Gets the index of the comparator to use for this column. */ public void setComparatorIndex(int comparatorIndex) { assert(comparatorIndex < comparators.size()); this.comparatorIndex = comparatorIndex; } public int getComparatorIndex() { return comparatorIndex; } /** * Gets the list of comparators for this column. */ public List getComparators() { return comparators; } /** * Gets the current best comparator to sort this column. */ public Comparator getComparator() { if(comparatorIndex == -1) return null; Comparator comparator = comparators.get(getComparatorIndex()); if(isReverse()) comparator = GlazedLists.reverseComparator(comparator); return comparator; } /** * Get whether this column is in reverse order. */ public boolean isReverse() { return reverse; } public void setReverse(boolean reverse) { this.reverse = reverse; } /** * Gets the sorting style for this column. */ public int getSortingStyle() { if(comparatorIndex == -1) return COLUMN_UNSORTED; boolean primaryColumn = !recentlyClickedColumns.isEmpty() && recentlyClickedColumns.get(0) == this; boolean primaryComparator = getComparatorIndex() == 0; if(primaryColumn) { if(!isReverse()) { if(primaryComparator) return COLUMN_PRIMARY_SORTED; else return COLUMN_PRIMARY_SORTED_ALTERNATE; } else { if(primaryComparator) return COLUMN_PRIMARY_SORTED_REVERSE; else return COLUMN_PRIMARY_SORTED_ALTERNATE_REVERSE; } } else { if(!isReverse()) { if(primaryComparator) return COLUMN_SECONDARY_SORTED; else return COLUMN_SECONDARY_SORTED_ALTERNATE; } else { if(primaryComparator) return COLUMN_SECONDARY_SORTED_REVERSE; else return COLUMN_SECONDARY_SORTED_ALTERNATE_REVERSE; } } } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/gui/ThreadProxyEventList.java0000644000175000017500000002244712106516362032645 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.gui; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.SortedList; import ca.odell.glazedlists.TransformedList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventAssembler; import ca.odell.glazedlists.event.ListEventListener; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.RandomAccess; /** * An {@link EventList} that only forwards its events on a proxy thread, * regardless of the thread of their origin. * *

      While the proxy thread is not executing, any number of events may arrive. * This means that multiple changes may have occurred before the proxy can * notify its listeners. One problem created by this scenario is that some of * these changes may contradict one another. For example, an inserted item may * be later removed before either event is processed. To overcome this problem, * this class uses the change-contradiction resolving logic of * {@link ListEventAssembler}. * *

      The flow of events is as follows: *

        *
      1. Any thread makes a change to the source list and calls * {@link ThreadProxyEventList#listChanged(ListEvent)}. *
      2. The event is enqueued and the proxy thread is notified that it has a * task to process in the future. *
      * *

      At some point in the future, after one or more events have been enqueued, * the proxy thread executes and processes its queue: *

        *
      1. First it acquires a lock to prevent further concurrent changes *
      2. All enqueued changes are combined into a single change. Currently this * implementation does a best effort on conflict resolution. It is * limited in its handling of reordering events, as caused by changing a * {@link SortedList} {@link Comparator}. *
      3. The single, combined event is fired. *
      4. The first listener is the {@link ThreadProxyEventList} itself. It listens * to its own event because this event will be free of conflicts. It applies * the changes to its own private copy of the data. *
      5. All other listeners are notified of the change. *
      6. The lock is released. *
      * *

      The {@link ThreadProxyEventList} keeps a private copy of the elements of the * source {@link EventList}. This enables interested classes to read a consistent * (albeit potentially out of date) view of the data at all times. * *

      Important: ThreadProxyEventList * relies heavily on its ability to pause changes to its source EventList * while it is updating its private copy of the source data. It does this by * acquiring the writeLock for the list pipeline. This implies that * ALL code which accesses the pipeline must be thread-safe * (or the acquisition of the writeLock will be meaningless). See * {@link EventList} for an example of thread safe code. * * @author Jesse Wilson */ public abstract class ThreadProxyEventList extends TransformedList implements RandomAccess { /** a local cache of the source list */ private List localCache = new ArrayList(); /** propagates events on the proxy thread */ private UpdateRunner updateRunner = new UpdateRunner(); /** propagates events immediately. The source events might be fired later */ private final ListEventAssembler cacheUpdates = new ListEventAssembler(this, ListEventAssembler.createListEventPublisher()); /** whether the proxy thread has been scheduled */ private volatile boolean scheduled = false; /** * Create a {@link ThreadProxyEventList} which delivers changes to the * given source on a particular {@link Thread}, called the * proxy {@link Thread} of a subclasses choosing. The {@link Thread} used * depends on the implementation of {@link #schedule(Runnable)}. * * @param source the {@link EventList} for which to proxy events */ public ThreadProxyEventList(EventList source) { super(source); // populate the initial cache value localCache.addAll(source); // handle my own events to update the internal state cacheUpdates.addListEventListener(updateRunner); // handle changes in the source event list source.addListEventListener(this); } /** {@inheritDoc} */ @Override public final void listChanged(ListEvent listChanges) { // if we've haven't scheduled a commit, we need to begin a new event if(!scheduled) { updates.beginEvent(true); cacheUpdates.beginEvent(true); } // add the changes for this event to our queue updates.forwardEvent(listChanges); cacheUpdates.forwardEvent(listChanges); // commit the event on the appropriate thread if(!scheduled) { scheduled = true; schedule(updateRunner); } } /** * Schedule the specified runnable to be executed on the proxy thread. * * @param runnable a unit of work to be executed on the proxy thread */ protected abstract void schedule(Runnable runnable); /** {@inheritDoc} */ @Override public final int size() { return localCache.size(); } /** {@inheritDoc} */ @Override public final E get(int index) { return localCache.get(index); } /** {@inheritDoc} */ @Override protected final boolean isWritable() { return true; } /** * Apply the {@link ListEvent} to the {@link List}. * * @param source the EventList whose changes are being proxied to another thread * @param listChanges the list of changes from the source to be applied * @param localCache a private snapshot of the source which * is now out of sync with that source list and will be repaired * @return a new List to serve as the up-to-date local cache */ private List applyChangeToCache(EventList source, ListEvent listChanges, List localCache) { List result = new ArrayList(source.size()); // cacheOffset is the running index delta between localCache and result int resultIndex = 0; int cacheOffset = 0; while(true) { // find the next change (or the end of the list) int changeIndex; int changeType; if(listChanges.next()) { changeIndex = listChanges.getIndex(); changeType = listChanges.getType(); } else { changeIndex = source.size(); changeType = -1; } // perform all the updates before this change for(; resultIndex < changeIndex; resultIndex++) { result.add(resultIndex, localCache.get(resultIndex + cacheOffset)); } // perform this change if(changeType == ListEvent.DELETE) { cacheOffset++; } else if(changeType == ListEvent.UPDATE) { result.add(resultIndex, source.get(changeIndex)); resultIndex++; } else if(changeType == ListEvent.INSERT) { result.add(resultIndex, source.get(changeIndex)); resultIndex++; cacheOffset--; } else if(changeType == -1) { break; } } return result; } /** {@inheritDoc} */ @Override public void dispose() { super.dispose(); cacheUpdates.removeListEventListener(updateRunner); } /** * Updates the internal data using the proxy thread. * * @author Jesse Wilson */ private class UpdateRunner implements Runnable, ListEventListener { /** * When run, this combines all events thus far and forwards them. * *

      If a reordering event is being forwarded, the reordering may be lost * if it arrives simultaneously with another event. This is somewhat of a * hack for the time being. Hopefully later we can refine this so that a * new event is created with these changes properly. */ public void run() { getReadWriteLock().writeLock().lock(); try { // We need to apply the changes to the local cache immediately, // before forwarding the event downstream to other listeners. // This is necessary so that intermediate states in this list // are visible to larger list changes (such as clearing tables, // see bug 447) cacheUpdates.commitEvent(); updates.commitEvent(); } finally { scheduled = false; getReadWriteLock().writeLock().unlock(); } } /** * Update local state as a consequence of the change event. */ public void listChanged(ListEvent listChanges) { localCache = applyChangeToCache(source, listChanges, localCache); } } }././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/gui/MouseKeyboardSortingStrategy.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/gui/MouseKeyboardSortingStrateg0000644000175000017500000000621412106516362033261 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.gui; import java.util.List; /** * @see ca.odell.glazedlists.gui.AbstractTableComparatorChooser#MULTIPLE_COLUMN_KEYBOARD * * @author Jesse Wilson */ public class MouseKeyboardSortingStrategy implements SortingStrategy { /** a column is sorted in forward, reverse or not at all */ private static final int NONE = 0; private static final int FORWARD = 1; private static final int REVERSE = 2; /** * Adjust the sorting state based on receiving the specified click event. */ public void columnClicked(SortingState sortingState, int column, int clicks, boolean shift, boolean control) { SortingState.SortingColumn sortingColumn = sortingState.getColumns().get(column); if(sortingColumn.getComparators().isEmpty()) return; List recentlyClickedColumns = sortingState.getRecentlyClickedColumns(); // figure out which comparator and reverse state we were on before int comparatorIndexBefore = sortingColumn.getComparatorIndex(); final int forwardReverseNoneBefore; if(comparatorIndexBefore == -1) forwardReverseNoneBefore = NONE; else forwardReverseNoneBefore = sortingColumn.isReverse() ? REVERSE : FORWARD; // figure out which comparator and reverse state we shall go to int forwardReverseNoneAfter; int comparatorIndexAfter; boolean moreComparators = comparatorIndexBefore + 1 < sortingColumn.getComparators().size(); boolean lastDirective = shift ? forwardReverseNoneBefore == FORWARD : forwardReverseNoneBefore == REVERSE; // if we're on the last mode of this comparator, go to the next comparator if(moreComparators && lastDirective) { comparatorIndexAfter = (comparatorIndexBefore + 1) % sortingColumn.getComparators().size(); forwardReverseNoneAfter = forwardReverseNoneBefore == FORWARD ? REVERSE : FORWARD; // otherwise merely toggle forward/reverse/none } else { comparatorIndexAfter = comparatorIndexBefore != -1 ? comparatorIndexBefore : 0; forwardReverseNoneAfter = (shift ? forwardReverseNoneBefore + 2 : forwardReverseNoneBefore + 1) % 3; } // clean up if necessary if(!control) { sortingState.clearComparators(); } // prepare the latest column if(forwardReverseNoneAfter == NONE) { sortingColumn.clear(); recentlyClickedColumns.remove(sortingColumn); } else { sortingColumn.setComparatorIndex(comparatorIndexAfter); sortingColumn.setReverse(forwardReverseNoneAfter == REVERSE); if(!recentlyClickedColumns.contains(sortingColumn)) { recentlyClickedColumns.add(sortingColumn); } } // rebuild the sorting state sortingState.fireSortingChanged(); } }././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/gui/MouseOnlySortingStrategy.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/gui/MouseOnlySortingStrategy.ja0000644000175000017500000000553112106516362033225 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.gui; import java.util.Iterator; import java.util.List; /** * @see ca.odell.glazedlists.gui.AbstractTableComparatorChooser#SINGLE_COLUMN * @see ca.odell.glazedlists.gui.AbstractTableComparatorChooser#MULTIPLE_COLUMN_MOUSE * * @author Jesse Wilson */ public final class MouseOnlySortingStrategy implements SortingStrategy { /** if false, other sorting columns will be cleared before a click takes effect */ private final boolean multipleColumnSort; /** * Create a new {@link ca.odell.glazedlists.impl.gui.MouseOnlySortingStrategy}, sorting multiple * columns or not as specified. */ public MouseOnlySortingStrategy(boolean multipleColumnSort) { this.multipleColumnSort = multipleColumnSort; } /** * Adjust the sorting state based on receiving the specified clicks. */ public void columnClicked(SortingState sortingState, int column, int clicks, boolean shift, boolean control) { SortingState.SortingColumn clickedColumn = sortingState.getColumns().get(column); if(clickedColumn.getComparators().isEmpty()) return; List recentlyClickedColumns = sortingState.getRecentlyClickedColumns(); // on a double click, clear all click counts if(clicks == 2) { for(Iterator i = recentlyClickedColumns.iterator(); i.hasNext(); ) { SortingState.SortingColumn sortingColumn = i.next(); sortingColumn.clear(); } recentlyClickedColumns.clear(); // if we're only sorting one column at a time, clear other columns } else if(!multipleColumnSort) { for(Iterator i = recentlyClickedColumns.iterator(); i.hasNext(); ) { SortingState.SortingColumn sortingColumn = i.next(); if(sortingColumn != clickedColumn) { sortingColumn.clear(); } } recentlyClickedColumns.clear(); } // add a click to the newly clicked column if it has any comparators int netClicks = 1 + clickedColumn.getComparatorIndex() * 2 + (clickedColumn.isReverse() ? 1 : 0); clickedColumn.setComparatorIndex((netClicks / 2) % clickedColumn.getComparators().size()); clickedColumn.setReverse(netClicks % 2 == 1); if(!recentlyClickedColumns.contains(clickedColumn)) { recentlyClickedColumns.add(clickedColumn); } // rebuild the sorting state sortingState.fireSortingChanged(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/rbp/0000755000175000017500000000000012106516356025664 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/rbp/ResourceListener.java0000644000175000017500000000137712106516356032034 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.rbp; // NIO is used for BRP import ca.odell.glazedlists.impl.io.Bufferlo; /** * A resource listener subscribes to the deltas published by a resource. * * @author Jesse Wilson */ public interface ResourceListener { /** * Handles a change in a resource contained by the specified delta. This method * will be called while holding the Resource's write lock. */ public void resourceUpdated(Resource resource, Bufferlo delta); } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/rbp/PeerConnection.java0000644000175000017500000002033012106516356031440 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.rbp; // NIO is used for BRP import ca.odell.glazedlists.impl.ctp.CTPConnection; import ca.odell.glazedlists.impl.ctp.CTPHandler; import ca.odell.glazedlists.impl.io.Bufferlo; import java.text.ParseException; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; /** * Models a connection to a local peer. * *

      A connection is multiplexed and serves multiple resources. It contains a map * of resources being published and resources being subscribed to. * * @author Jesse Wilson */ class PeerConnection implements CTPHandler { /** logging */ private static Logger logger = Logger.getLogger(PeerConnection.class.toString()); /** the peer that owns all connections */ private Peer peer; /** the lower level connection to this peer */ private CTPConnection connection = null; /** the state of this connection */ private static final int AWAITING_CONNECT = 0; private static final int READY = 1; private static final int AWAITING_CLOSE = 2; private static final int CLOSED = 3; private int state = AWAITING_CONNECT; /** the incoming bytes pending a full block */ private Bufferlo currentBlock = new Bufferlo(); /** the outgoing bytes pending a connection */ private Bufferlo pendingConnect = new Bufferlo(); /** locally subscribed resources by resource name */ Map incomingSubscriptions = new TreeMap(); /** locally published resources by resource name */ Map outgoingPublications = new TreeMap(); /** * Creates a new PeerConnection for the specified peer. */ public PeerConnection(Peer peer) { this.peer = peer; } /** * Handles the connection being ready for chunks to be sent. */ public void connectionReady(CTPConnection connection) { // know where we were before int priorState = state; // now that we're connected this.connection = connection; this.state = READY; // handle any pending operations: data if(pendingConnect.length() > 0) { connection.sendChunk(pendingConnect); } // handle any pending operations: close if(priorState == AWAITING_CLOSE) { close(); } } /** * Handles the connection being closed by the remote client. This will also * be called if there is a connection error, which is the case when a remote * host sends data that cannot be interpretted by CTPConnection. * * @param reason An exception if the connection was closed as the result of * a failure. This may be null. */ public void connectionClosed(CTPConnection source, Exception reason) { this.connection = null; this.state = CLOSED; peer.connections.remove(this); // notify resources of the close List resourcesToNotify = new ArrayList(); resourcesToNotify.addAll(incomingSubscriptions.values()); resourcesToNotify.addAll(outgoingPublications.values()); for(Iterator r = resourcesToNotify.iterator(); r.hasNext(); ) { ResourceConnection resource = (ResourceConnection)r.next(); resource.getResource().connectionClosed(resource, reason); } } /** * Handles reception of the specified chunk of data. This chunk should be able * to be cleanly concatenated with the previous and following chunks without * problem by the reader. * * @param data A non-empty ByteBuffer containing the bytes for this chunk. The * relevant bytes start at data.position() and end at data.limit(). This * buffer is only valid for the duration of this method call. */ public void receiveChunk(CTPConnection source, Bufferlo data) { // get all the data in the working block currentBlock.append(data); // handle all blocks try { PeerBlock block = null; while((block = PeerBlock.fromBytes(currentBlock, source.getLocalHost(), source.getLocalPort())) != null) { ResourceUri resourceUri = block.getResourceUri(); // get the resource for this connection ResourceConnection resource = null; if(block.isSubscribe()) { resource = new ResourceConnection(this, peer.getPublishedResource(resourceUri)); } else if(block.isUnsubscribe()) { resource = (ResourceConnection)outgoingPublications.get(resourceUri); } else if(block.isSubscribeConfirm() || block.isUpdate() || block.isUnpublish()) { resource = (ResourceConnection)incomingSubscriptions.get(resourceUri); } else { throw new UnsupportedOperationException(); } // handle an unknown resource name if(resource == null) { logger.warning("Unknown resource: \"" + resourceUri + "\""); close(); return; } // handle the block resource.getResource().incomingBlock(resource, block); } // if the data is corrupted, close the connection } catch(ParseException e) { source.close(e); // if any other error happened, close the connection } catch(RuntimeException e) { logger.log(Level.WARNING, "Unexpected error handling block", e.getMessage()); source.close(e); } } /** * Test whether this connection is being used by incoming subscriptions or * outgoing publications. */ boolean isIdle() { return (incomingSubscriptions.isEmpty() && outgoingPublications.isEmpty()); } /** * Close this peer connection. */ public void close() { // if we're already done if(state == CLOSED) { logger.warning("Closing a closed connection"); return; } // close now state = AWAITING_CLOSE; if(connection != null) { connection.close(); peer.connections.remove(this); } } /** * Writes the specified block to this peer. */ public void writeBlock(PeerResource resource, PeerBlock block) { if(state == AWAITING_CONNECT) { pendingConnect.append(block.toBytes(null, -1)); } else if(state == READY) { connection.sendChunk(block.toBytes(connection.getLocalHost(), connection.getLocalPort())); } else if(state == CLOSED || state == AWAITING_CLOSE) { logger.warning("Write block to closed connection: " + this); } else { throw new IllegalStateException(); } } /** * Gets this connection as a String. */ @Override public String toString() { if(state == AWAITING_CONNECT) return "pending"; else if(state == READY) return connection.toString(); else if(state == CLOSED) return "closed"; else if(state == AWAITING_CLOSE) return "closing"; else throw new IllegalStateException(); } /** * Prints the current state of this connection. */ void print() { System.out.print(this); System.out.print(": "); System.out.print("Incoming {"); for(Iterator s = incomingSubscriptions.keySet().iterator(); s.hasNext(); ) { ResourceUri resourceUri = (ResourceUri)s.next(); System.out.print(resourceUri); if(s.hasNext()) System.out.print(", "); } System.out.print("}, "); System.out.print("Outgoing {"); for(Iterator s = outgoingPublications.keySet().iterator(); s.hasNext(); ) { ResourceUri resourceUri = (ResourceUri)s.next(); System.out.print(resourceUri); if(s.hasNext()) System.out.print(", "); } System.out.println("}"); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/rbp/Peer.java0000644000175000017500000001221612106516356027424 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.rbp; // NIO is used for BRP import ca.odell.glazedlists.impl.ctp.CTPConnectionManager; import ca.odell.glazedlists.impl.ctp.CTPHandler; import ca.odell.glazedlists.impl.ctp.CTPHandlerFactory; import java.io.IOException; import java.util.*; import java.util.logging.Logger; /** * A peer manages publishing and subscribing to resources. * * @author Jesse Wilson */ public class Peer implements CTPHandlerFactory { /** logging */ private static Logger logger = Logger.getLogger(Peer.class.toString()); /** the resources being subscribed to */ Map subscribed = new TreeMap(); /** the resources being published */ Map published = new TreeMap(); /** the active connections to peers */ List connections = new ArrayList(); /** the connection management */ private CTPConnectionManager connectionManager; /** * Creates a new peer that binds to the specified port. */ public Peer(int listenPort) { this.connectionManager = new CTPConnectionManager(this, listenPort); } /** * Upon a connect, a CTPHandler is required to handle the data of this connection. * The returned CTPHandler will be delegated to handle the connection's data. */ public CTPHandler constructHandler() { PeerConnection incoming = new PeerConnection(this); connections.add(incoming); return incoming; } /** * Starts the peer. */ public void start() throws IOException { connectionManager.start(); } /** * Stops the peer. */ public void stop() { connectionManager.getNIODaemon().invokeAndWait(new StopRunnable()); } private class StopRunnable implements Runnable { public void run() { // unsubscribe from everything for(Iterator s = subscribed.values().iterator(); s.hasNext(); ) { PeerResource resource = (PeerResource)s.next(); resource.status().disconnect(); } subscribed.clear(); // unpublish everything logger.warning("Closing with published entries"); // close all connections List connectionsToClose = new ArrayList(); connectionsToClose.addAll(connections); for(Iterator c = connectionsToClose.iterator(); c.hasNext(); ) { PeerConnection connection = (PeerConnection)c.next(); connection.close(); } // stop the connection manager connectionManager.stop(); } } /** * Prints the current state of this peer. */ public void print() { System.out.println(" -------- -------- -------- -------- -------- -------- -------- -------- "); System.out.println("Subscribed Resources:"); for(Iterator s = subscribed.values().iterator(); s.hasNext(); ) { PeerResource resource = (PeerResource)s.next(); resource.print(); } System.out.println(""); System.out.println("Published Resources:"); for(Iterator s = published.values().iterator(); s.hasNext(); ) { PeerResource resource = (PeerResource)s.next(); resource.print(); } System.out.println(""); System.out.println("Connections:"); for(Iterator s = connections.iterator(); s.hasNext(); ) { PeerConnection connection = (PeerConnection)s.next(); connection.print(); } System.out.println(""); } /** * Subscribe to the specified resource. */ public ResourceStatus subscribe(Resource resource, String host, int port, String path) { PeerResource peerResource = new PeerResource(this, resource, ResourceUri.remote(host, port, path)); return peerResource.status(); } /** * Publish the specified resource. */ public ResourceStatus publish(Resource resource, String path) { PeerResource peerResource = new PeerResource(this, resource, ResourceUri.local(path)); return peerResource.status(); } /** * Gets the specified published resource. */ PeerResource getPublishedResource(ResourceUri resourceUri) { return (PeerResource)published.get(resourceUri); } /** * Gets the specified connection, or creates it if necessary. */ PeerConnection getConnection(String host, int port) { PeerConnection peerConnection = new PeerConnection(this); connectionManager.connect(peerConnection, host, port); connections.add(peerConnection); return peerConnection; } /** * Runs the specified task on the network thread. */ void invokeLater(Runnable runnable) { connectionManager.getNIODaemon().invokeLater(runnable); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/rbp/ResourceStatusListener.java0000644000175000017500000000165212106516356033234 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.rbp; // NIO is used for BRP import java.util.EventListener; /** * Listens to the current status of a resource with respect to the network. * * @author Jesse Wilson */ public interface ResourceStatusListener extends EventListener { /** * Called each time a resource becomes connected. */ public void resourceConnected(ResourceStatus resource); /** * Called each time a resource's disconnected status changes. This method may * be called for each attempt it makes to reconnect to the network. */ public void resourceDisconnected(ResourceStatus resource, Exception cause); } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/rbp/PeerBlock.java0000644000175000017500000002033312106516356030376 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.rbp; // NIO is used for BRP import ca.odell.glazedlists.impl.io.Bufferlo; import java.text.ParseException; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; /** * A binary message between peers. * * @author Jesse Wilson */ class PeerBlock { /** constants used in the protocol */ private static final String RESOURCE_URI = "Resource-Uri"; private static final String SESSION_ID = "Session-Id"; private static final String UPDATE_ID = "Update-Id"; private static final String ACTION = "Action"; private static final String ACTION_SUBSCRIBE = "Subscribe"; private static final String ACTION_SUBSCRIBE_CONFIRM = "Subscribe-Confirm"; private static final String ACTION_UPDATE = "Update"; private static final String ACTION_UNSUBSCRIBE = "Unsubscribe"; private static final String ACTION_UNPUBLISH = "Unpublish"; /** the resource this block is concerned with */ private ResourceUri resourceUri = null; /** a check id */ private int sessionId = -1; /** the action of this block */ private String action = null; /** the sequence ID of this block */ private int updateId = -1; /** the binary data of this block */ private Bufferlo payload = null; /** * Create a new PeerBlock. */ private PeerBlock(ResourceUri resourceUri, int sessionId, String action, int updateId, Bufferlo payload) { this.resourceUri = resourceUri; this.sessionId = sessionId; this.action = action; this.updateId = updateId; this.payload = payload; } /** * Create a new subscribe block. */ public static PeerBlock subscribeConfirm(ResourceUri resourceUri, int sessionId, int updateId, Bufferlo snapshot) { return new PeerBlock(resourceUri, sessionId, PeerBlock.ACTION_SUBSCRIBE_CONFIRM, updateId, snapshot); } /** * Create a new subscribe block. */ public static PeerBlock update(ResourceUri resourceUri, int sessionId, int updateId, Bufferlo delta) { return new PeerBlock(resourceUri, sessionId, PeerBlock.ACTION_UPDATE, updateId, delta); } /** * Create a new subscribe block. */ public static PeerBlock subscribe(ResourceUri resourceUri) { return new PeerBlock(resourceUri, -1, PeerBlock.ACTION_SUBSCRIBE, -1, null); } /** * Create a new subscribe block. */ public static PeerBlock unsubscribe(ResourceUri resourceUri) { return new PeerBlock(resourceUri, -1, PeerBlock.ACTION_UNSUBSCRIBE, -1, null); } /** * Create a new subscribe block. */ public static PeerBlock unpublish(ResourceUri resourceUri) { return new PeerBlock(resourceUri, -1, PeerBlock.ACTION_UNPUBLISH, -1, null); } /** * Whether this is a subscribe-confirm block. */ public boolean isSubscribeConfirm() { return ACTION_SUBSCRIBE_CONFIRM.equals(action); } /** * Whether this is an update block. */ public boolean isUpdate() { return ACTION_UPDATE.equals(action); } /** * Whether this is a subscribe block. */ public boolean isSubscribe() { return ACTION_SUBSCRIBE.equals(action); } /** * Whether this is an unsubscribe block. */ public boolean isUnsubscribe() { return ACTION_UNSUBSCRIBE.equals(action); } /** * Whether this is an unpublish block. */ public boolean isUnpublish() { return ACTION_UNPUBLISH.equals(action); } /** * Get the PeerBlock from the specified bytes. * * @return the first PeerBlock from the specified bytes, it it exists. The * result may be null indicating an incomplete PeerBlock. There may be * multiple PeerBlocks available in the specified list of bytes. */ public static PeerBlock fromBytes(Bufferlo bytes, String localHost, int localPort) throws ParseException { // if a full block is not loaded int blockEndIndex = bytes.indexOf("\\r\\n"); if(blockEndIndex == -1) return null; // read the bytes of the first block String blockSizeInDecimal = bytes.readUntil("\\r\\n", false); int blockSizeWithHeaders = Integer.parseInt(blockSizeInDecimal); // if the full block is not loaded, give up int bytesRequired = blockEndIndex + 2 + blockSizeWithHeaders + 2; if(bytes.length() < bytesRequired) { return null; } // consume the size bytes.consume("[0-9]*\\r\\n"); // load the headers int lengthBeforeHeaders = bytes.length(); Map headers = new TreeMap(); while(true) { if(bytes.indexOf("\\r\\n") == 0) break; String key = bytes.readUntil("\\:( )*"); String value = bytes.readUntil("\\r\\n"); headers.put(key, value); } bytes.consume("\\r\\n"); int lengthAfterHeaders = bytes.length(); int headersLength = lengthBeforeHeaders - lengthAfterHeaders; // load the data int payloadLength = blockSizeWithHeaders - headersLength; Bufferlo payload = bytes.consume(payloadLength); bytes.consume("\\r\\n"); // parse the headers String resourceUriString = (String)headers.get(RESOURCE_URI); ResourceUri resourceUri = ResourceUri.localOrRemote(resourceUriString, localHost, localPort); String sessionIdString = (String)headers.get(SESSION_ID); String action = (String)headers.get(ACTION); String updateIdString = (String)headers.get(UPDATE_ID); int sessionId = (sessionIdString != null) ? Integer.parseInt(sessionIdString) : -1; int updateId = (updateIdString != null) ? Integer.parseInt(updateIdString) : -1; // return the result return new PeerBlock(resourceUri, sessionId, action, updateId, payload); } /** * Get the bytes for this block. */ public Bufferlo toBytes(String localHost, int localPort) { // the writer with no size info Bufferlo writer = new Bufferlo(); // populate the map of headers Map headers = new TreeMap(); if(resourceUri != null) headers.put(RESOURCE_URI, resourceUri.toString(localHost, localPort)); if(sessionId != -1) headers.put(SESSION_ID, new Integer(sessionId)); if(action != null) headers.put(ACTION, action); if(updateId != -1) headers.put(UPDATE_ID, new Integer(updateId)); // write the header values for(Iterator i = headers.entrySet().iterator(); i.hasNext(); ) { Map.Entry mapEntry = (Map.Entry)i.next(); writer.write(mapEntry.getKey().toString()); writer.write(": "); writer.write(mapEntry.getValue().toString()); writer.write("\r\n"); } writer.write("\r\n"); // write the payload if(payload != null) { writer.append(payload.duplicate()); } // wrap the size Bufferlo writerWithSize = new Bufferlo(); writerWithSize.write("" + writer.length()); writerWithSize.write("\r\n"); writerWithSize.append(writer); writerWithSize.write("\r\n"); // all done return writerWithSize; } /** * Gets the action of this block. */ public String getAction() { return action; } /** * Gets the binary data of this block. */ public Bufferlo getPayload() { return payload; } /** * Gets the update ID of this block. */ public int getUpdateId() { return updateId; } /** * Gets the session ID of this block. */ public int getSessionId() { return sessionId; } /** * Gets the resource that this block is for. */ public ResourceUri getResourceUri() { return resourceUri; } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/rbp/ResourceConnection.java0000644000175000017500000000273512106516356032345 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.rbp; // NIO is used for BRP /** * Maintains the state for a particular resource on a particular connection. * * @author Jesse Wilson */ class ResourceConnection { /** the connection */ private PeerConnection connection; /** the resource */ private PeerResource resource; /** the resource's current update */ private int updateId = -1; /** * Create a new {@link ResourceConnection} to manage the state of the specified * connection and resource. */ public ResourceConnection(PeerConnection connection, PeerResource resource) { this.connection = connection; this.resource = resource; } /** * The current update of this resource connection. */ public void setUpdateId(int updateId) { this.updateId = updateId; } public int getUpdateId() { return updateId; } /** * Gets the connection that is interested in this resource. */ public PeerConnection getConnection() { return connection; } /** * Gets the resource that is attached to this connection. */ public PeerResource getResource() { return resource; } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/rbp/Resource.java0000644000175000017500000000334612106516356030324 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.rbp; // NIO is used for BRP import ca.odell.glazedlists.impl.io.Bufferlo; import ca.odell.glazedlists.util.concurrent.ReadWriteLock; /** * A resource is a dynamic Object that can publish its changes as a series of deltas. * It is also possible to construct a resource using a shapshot. * * @author Jesse Wilson */ public interface Resource { /** * Get a binary snapshot of this resource in its current state. */ public Bufferlo toSnapshot(); /** * Populate this resource with the data from the specified snapshot. */ public void fromSnapshot(Bufferlo snapshot); /** * Apply the specified delta to the binary image of this resource. After the * update has been applied, all {@link ResourceListener}s must be notified. */ public void update(Bufferlo delta); /** * Register the {@link ResourceListener} to receive notification when this * resource is modified. */ public void addResourceListener(ResourceListener listener); /** * Degregister the {@link ResourceListener} from receiving update events. */ public void removeResourceListener(ResourceListener listener); /** * Gets the lock required to share this resource between multiple threads. * * @return a re-entrant {@link ReadWriteLock} that guarantees thread safe * access to this list. */ public ReadWriteLock getReadWriteLock(); } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/rbp/ResourceUri.java0000644000175000017500000001120312106516356030773 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.rbp; // NIO is used for BRP import ca.odell.glazedlists.impl.io.Bufferlo; import java.text.ParseException; /** * The address of a resource. This will be either local ("/hello") or remote * ("glazedlists://host:port/hello"). * * @author Jesse Wilson */ class ResourceUri implements Comparable { /** the resource host */ private String host; private int port; /** the resource path */ private String path; /** true if this resource is sourced locally */ private boolean local; /** * Creates a new {@link ResourceUri}. */ private ResourceUri(String host, int port, String path, boolean local) { this.host = host; this.port = port; this.path = path; this.local = local; } /** * Creates a new returns a {@link ResourceUri} for the specified local path. */ public static ResourceUri local(String path) { return new ResourceUri(null, -1, path, true); } /** * Creates and returns a {@link ResourceUri} for the specified uri. This resource * will be considered local if the host specified matches the host in the uri. * Otherwise the uri will be considered remote. */ public static ResourceUri localOrRemote(String uri, String localHost, int localPort) { try { Bufferlo parser = new Bufferlo(); parser.write(uri); parser.consume("glazedlists\\:\\/\\/"); String host = parser.readUntil("\\:"); String portString = parser.readUntil("\\/"); int port = Integer.parseInt(portString); String path = "/" + parser.toString(); boolean local = (localHost.equals(host)) && (localPort == port); return new ResourceUri(host, port, path, local); } catch(ParseException e) { throw new IllegalStateException(e.getMessage()); } catch(NumberFormatException e) { throw new IllegalStateException(e.getMessage()); } } /** * Creates a new remote {@link ResourceUri}. */ public static ResourceUri remote(String host, int port, String path) { return new ResourceUri(host, port, path, false); } /** * Test if this URI is local. */ public boolean isLocal() { return local; } /** * Test if this URI is remote. */ public boolean isRemote() { return !local; } /** * Get the URI host. */ public String getHost() { return host; } /** * Get the URI port. */ public int getPort() { return port; } /** * Gets this resource as a String, substituting the specified local host and * local port as necessary. */ public String toString(String localHost, int localPort) { if(local) { return "glazedlists://" + localHost + ":" + localPort + path; } else { return "glazedlists://" + host + ":" + port + path; } } @Override public String toString() { return "Resource URI [local=" + local + ", host=" + host + ", port=" + port + ", path=" + path + "]"; } /** * Computes a hash of this resource uri. */ @Override public int hashCode() { int result = path.hashCode(); if(!local) { result = 37 * result + host.hashCode(); result = 37 * result + port; } return result; } /** * Compares two resources. */ public int compareTo(Object other) { if(other == null) throw new NullPointerException(); ResourceUri otherUri = (ResourceUri)other; if(otherUri.local != this.local) throw new IllegalStateException("Cannot compare local URI with remote URI: " + other + " vs. " + this); int result = path.compareTo(otherUri.path); if(result != 0) return result; if(!local) { result = host.compareTo(otherUri.host); if(result != 0) return result; result = port - otherUri.port; if(result != 0) return result; } return 0; } /** * Tests if this ResourceUri equals that ResourceUri. */ @Override public boolean equals(Object other) { return (compareTo(other) == 0); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/rbp/ResourceStatus.java0000644000175000017500000000250312106516356031522 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.rbp; // NIO is used for BRP /** * Tracks the current status of a resource with respect to the network. * * @author Jesse Wilson */ public interface ResourceStatus { /** * Returns true if this resource is actively being updated by the network. */ public boolean isConnected(); /** * Forces this resource to attempt to connect. The results from the attempt * will not be visible immediately. */ public void connect(); /** * Forces this resource to attempt to disconnect. This will prevent the resource * from consuming network resources. */ public void disconnect(); /** * Registers the specified listener to receive events about the status of this * resource. */ public void addResourceStatusListener(ResourceStatusListener listener); /** * Deregisters the specified listener from receiving events about the status of * this resource. */ public void removeResourceStatusListener(ResourceStatusListener listener); } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/rbp/PeerResource.java0000644000175000017500000003347312106516356031144 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.rbp; // NIO is used for BRP import ca.odell.glazedlists.impl.io.Bufferlo; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Random; /** * A resource that is being published on the network. * * @author Jesse Wilson */ class PeerResource { /** the peer that owns all connections */ private Peer peer; /** the resource being managed */ private Resource resource = null; /** the update ID of the resource */ private int resourceUpdateId = 0; /** the address that this resource is being published as */ private ResourceUri resourceUri; /** the publisher of this resource */ private PeerConnection publisher = null; /** subscribers interested in this resource */ private List subscribers = new ArrayList(); /** the session ID is a simple validation */ private int sessionId = -1; /** listens to changes in the resource */ private PrivateResourceListener resourceListener = new PrivateResourceListener(); /** provides information about the status of this resource */ private PrivateResourceStatus resourceStatus = new PrivateResourceStatus(); /** * Create a new PeerResource for an incoming or outgoing resource. */ public PeerResource(Peer peer, Resource resource, ResourceUri resourceUri) { this.peer = peer; this.resource = resource; this.resourceUri = resourceUri; // create a random session ID as a check if(resourceUri.isLocal()) { this.sessionId = new Random(System.currentTimeMillis()).nextInt(); } // subscribe to the resource resourceStatus.connect(); } /** * Gets the address of this resource. */ public ResourceUri getResourceUri() { return resourceUri; } /** * Handle the state of the specified connection changing. */ public void connectionClosed(ResourceConnection connection, Exception reason) { if(publisher == connection.getConnection()) { publisher = null; resourceStatus.setConnected(false, reason); connection.getConnection().incomingSubscriptions.remove(resourceUri); } else { subscribers.remove(connection); connection.getConnection().outgoingPublications.remove(resourceUri); } } /** * Listens to changes in the resource, so they can be broadcast to subscribers. */ private class PrivateResourceListener implements ResourceListener { /** * Handles a change in a resource contained by the specified delta. This method * will be called while holding the Resource's write lock. */ public void resourceUpdated(Resource resource, Bufferlo delta) { resourceUpdateId++; peer.invokeLater(new UpdatedRunnable(delta, resourceUpdateId)); } private class UpdatedRunnable implements Runnable { private Bufferlo delta = null; private int updateId = -1; public UpdatedRunnable(Bufferlo delta, int updateId) { this.delta = delta; this.updateId = updateId; } public void run() { // if nobody's listening, we're done if(subscribers.isEmpty()) return; // forward the event to listeners PeerBlock block = PeerBlock.update(resourceUri, sessionId, updateId, delta); // send the block to interested subscribers for(int s = 0; s < subscribers.size(); s++) { ResourceConnection subscriber = (ResourceConnection)subscribers.get(s); if(subscriber.getUpdateId() >= updateId) continue; subscriber.getConnection().writeBlock(PeerResource.this, block); subscriber.setUpdateId(updateId); } } } } public ResourceListener resourceListener() { return resourceListener; } /** * Provides information about the status of this resource. */ private class PrivateResourceStatus implements ResourceStatus { /** listeners interested in status changes */ private List statusListeners = new ArrayList(); /** connected if the current subscription has been confirmed */ private boolean connected = false; /** {@inheritDoc} */ public synchronized boolean isConnected() { return connected; } /** {@inheritDoc} */ public void connect() { peer.invokeLater(new ConnectRunnable()); } private class ConnectRunnable implements Runnable { public void run() { // if this is remote, subscribe to this resource if(resourceUri.isRemote()) { publisher = peer.getConnection(resourceUri.getHost(), resourceUri.getPort()); peer.subscribed.put(resourceUri, PeerResource.this); publisher.incomingSubscriptions.put(resourceUri, new ResourceConnection(publisher, PeerResource.this)); PeerBlock subscribe = PeerBlock.subscribe(resourceUri); publisher.writeBlock(PeerResource.this, subscribe); // if this is local, we're immediately connected } else if(resourceUri.isLocal()) { resource.addResourceListener(resourceListener); resourceStatus.setConnected(true, null); if(peer.published.get(resourceUri) != null) throw new IllegalStateException(); peer.published.put(resourceUri, PeerResource.this); } } } /** {@inheritDoc} */ public void disconnect() { peer.invokeLater(new DisconnectRunnable()); } private class DisconnectRunnable implements Runnable { public void run() { // we're immediately disconnected resourceStatus.setConnected(false, null); // if this is remote, unsubscribe if(resourceUri.isRemote()) { peer.subscribed.remove(resourceUri); // clean up the publisher if(publisher != null) { publisher.writeBlock(PeerResource.this, PeerBlock.unsubscribe(resourceUri)); publisher.incomingSubscriptions.remove(resourceUri); if(publisher.isIdle()) publisher.close(); publisher = null; } // if this is local, unpublish everyone } else if(resourceUri.isLocal()) { resource.removeResourceListener(resourceListener); if(peer.published.get(resourceUri) == null) throw new IllegalStateException(); peer.published.remove(resourceUri); // unpublish the subscribers for(Iterator s = subscribers.iterator(); s.hasNext(); ) { ResourceConnection subscriber = (ResourceConnection)s.next(); subscriber.getConnection().writeBlock(PeerResource.this, PeerBlock.unpublish(resourceUri)); subscriber.getConnection().outgoingPublications.remove(resourceUri); if(subscriber.getConnection().isIdle()) subscriber.getConnection().close(); s.remove(); } } } } /** {@inheritDoc} */ public synchronized void addResourceStatusListener(ResourceStatusListener listener) { statusListeners.add(listener); } /** {@inheritDoc} */ public synchronized void removeResourceStatusListener(ResourceStatusListener listener) { statusListeners.remove(listener); } /** * Update the status of this PeerResource and notify everyone who's interested. */ private void setConnected(boolean connected, Exception reason) { List listenersToNotify = new ArrayList(); synchronized(PrivateResourceStatus.this) { this.connected = connected; listenersToNotify.addAll(statusListeners); } for(Iterator i = listenersToNotify.iterator(); i.hasNext(); ) { ResourceStatusListener statusListener = (ResourceStatusListener)i.next(); if(connected) statusListener.resourceConnected(this); else statusListener.resourceDisconnected(this, reason); } } } public ResourceStatus status() { return resourceStatus; } /** * Handles a block of incoming data. */ void incomingBlock(ResourceConnection source, PeerBlock block) { if(block.isSubscribe()) remoteSubscribe(source, block); else if(block.isSubscribeConfirm()) remoteSubscribeConfirm(source, block); else if(block.isUpdate()) remoteUpdate(source, block); else if(block.isUnsubscribe()) remoteUnsubscribe(source, block); else if(block.isUnpublish()) remoteUnpublish(source, block); else throw new IllegalStateException(); } private void remoteUpdate(ResourceConnection publisher, PeerBlock block) { // confirm the update is consistent with the session if(block.getSessionId() != sessionId) throw new IllegalStateException(); // handle the update resource.getReadWriteLock().writeLock().lock(); try { // confirm this update is consistent with the update ID if(block.getUpdateId() != (resourceUpdateId+1)) throw new IllegalStateException("Expected update id " + (resourceUpdateId+1) + " but found " + block.getUpdateId()); // apply locally resource.update(block.getPayload()); // update state and propagate resourceListener.resourceUpdated(resource, block.getPayload()); } finally { resource.getReadWriteLock().writeLock().unlock(); } } private void remoteSubscribe(ResourceConnection subscriber, PeerBlock block) { // we're accepting connections if(resourceStatus.isConnected()) { // save the update id and a snapshot int updateId = -1; Bufferlo snapshot = null; resource.getReadWriteLock().writeLock().lock(); try { updateId = resourceUpdateId; snapshot = resource.toSnapshot(); } finally { resource.getReadWriteLock().writeLock().unlock(); } // create the subscription subscriber.setUpdateId(updateId); subscriber.getConnection().outgoingPublications.put(resourceUri, subscriber); subscribers.add(subscriber); // now send the snapshot to this subscriber PeerBlock subscribeConfirm = PeerBlock.subscribeConfirm(resourceUri, sessionId, updateId, snapshot); subscriber.getConnection().writeBlock(this, subscribeConfirm); // we're not accepting connections for now } else { PeerBlock unpublish = PeerBlock.unpublish(resourceUri); subscriber.getConnection().writeBlock(this, unpublish); if(subscriber.getConnection().isIdle()) subscriber.getConnection().close(); } } private void remoteSubscribeConfirm(ResourceConnection publisher, PeerBlock block) { // handle the confirm resource.getReadWriteLock().writeLock().lock(); try { // apply locally resource.fromSnapshot(block.getPayload()); // update state and propagate resourceUpdateId = block.getUpdateId(); //resourceListener.resourceUpdated(resource, block.getPayload()); } finally { resource.getReadWriteLock().writeLock().unlock(); } // save a session cookie to verify this is the same source sessionId = block.getSessionId(); // finally we're connected resourceStatus.setConnected(true, null); } private void remoteUnsubscribe(ResourceConnection subscriber, PeerBlock block) { // remove the subscription subscribers.remove(subscriber); subscriber.getConnection().outgoingPublications.remove(resourceUri); } private void remoteUnpublish(ResourceConnection subscriber, PeerBlock block) { // immediately disconnected resourceStatus.setConnected(false, new Exception("Resource became unavailable")); // clean up the publisher if(publisher != null) { publisher.incomingSubscriptions.remove(resourceUri); if(publisher.isIdle()) publisher.close(); publisher = null; } } /** * Gets this resource as a String for debugging. */ public void print() { System.out.print(resourceUri); System.out.print(" from: "); System.out.print(publisher); System.out.print(" to: "); for(Iterator s = subscribers.iterator(); s.hasNext(); ) { ResourceConnection subscriber = (ResourceConnection)s.next(); System.out.print(subscriber.getConnection().toString()); if(s.hasNext()) System.out.print(", "); } System.out.println(""); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/io/0000755000175000017500000000000012106516366025511 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/io/BeanXMLByteCoder.java0000644000175000017500000000225512106516366031407 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.io; import ca.odell.glazedlists.io.ByteCoder; import java.beans.XMLDecoder; import java.beans.XMLEncoder; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * A {@link ByteCoder} that uses the {@link java.beans.XMLEncoder XMLEncoder} and * {@link java.beans.XMLDecoder XMLDecoder} classes from java.beans. Encoded Objects * must be JavaBeans. * * @author Jesse Wilson */ public class BeanXMLByteCoder implements ByteCoder { /** {@inheritDoc} */ public void encode(Object source, OutputStream target) throws IOException { XMLEncoder xmlOut = new XMLEncoder(target); xmlOut.writeObject(source); xmlOut.close(); } /** {@inheritDoc} */ public Object decode(InputStream source) throws IOException { XMLDecoder xmlIn = new XMLDecoder(source); return xmlIn.readObject(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/io/SerializableByteCoder.java0000644000175000017500000000214312106516366032563 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.io; import ca.odell.glazedlists.io.ByteCoder; import java.io.*; /** * A {@link ByteCoder} that uses {@link Serializable}. * * @author Jesse Wilson */ public class SerializableByteCoder implements ByteCoder { /** {@inheritDoc} */ public void encode(Object source, OutputStream target) throws IOException { ObjectOutputStream objectOut = new ObjectOutputStream(target); objectOut.writeObject(source); objectOut.close(); } /** {@inheritDoc} */ public Object decode(InputStream source) throws IOException { try { ObjectInputStream objectIn = new ObjectInputStream(source); return objectIn.readObject(); } catch(ClassNotFoundException e) { throw new IllegalStateException(e.getMessage()); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/io/ListEventToBytes.java0000644000175000017500000001670212106516366031611 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.io; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.io.ByteCoder; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * An utility interface for converting Objects to bytes for storage or network * transport. * * @author Jesse Wilson */ public class ListEventToBytes { /** the virtual event type */ private static final int CLEAR = -1; /** * Convert the specified ListEvent to bytes. */ public static Bufferlo toBytes(ListEvent listEvent, ByteCoder byteCoder) throws IOException { // populate the list of parts List parts = new ArrayList(); while(listEvent.next()) { int index = listEvent.getIndex(); int type = listEvent.getType(); Object value = null; if(type == ListEvent.INSERT || type == ListEvent.UPDATE) value = listEvent.getSourceList().get(index); parts.add(new ListEventPart(index, type, value)); } return partsToBytes(parts, byteCoder); } /** * Convert the List to a ListEvent. This is for snapshots or compressions. */ public static Bufferlo toBytes(EventList list, ByteCoder byteCoder) throws IOException { List parts = new ArrayList(); // start with a clear parts.add(new ListEventPart(-1, ListEventToBytes.CLEAR, null)); // add all values as adds for(int i = 0; i < list.size(); i++) { parts.add(new ListEventPart(i, ListEvent.INSERT, list.get(i))); } // get the whole list return partsToBytes(parts, byteCoder); } /** * Apply the specified list event to the specified target list. The write lock * for this list must already be acquired if the list is shared between threads. */ public static void toListEvent(Bufferlo listEvent, EventList target, ByteCoder byteCoder) throws IOException { List parts = bytesToParts(listEvent, byteCoder); for(Iterator i = parts.iterator(); i.hasNext(); ) { ListEventPart part = (ListEventPart)i.next(); if(part.isDelete()) { target.remove(part.getIndex()); } else if(part.isUpdate()) { target.set(part.getIndex(), part.getValue()); } else if(part.isInsert()) { target.add(part.getIndex(), part.getValue()); } else if(part.isClear()) { target.clear(); } } } /** * Encode the parts into bytes. */ private static Bufferlo partsToBytes(List parts, ByteCoder delegate) throws IOException { // prepare the result Bufferlo partsAsBytes = new Bufferlo(); DataOutputStream dataOut = new DataOutputStream(partsAsBytes.getOutputStream()); // convert each part in sequence for(int i = 0; i < parts.size(); i++) { ListEventPart part = (ListEventPart)parts.get(i); // write the index of this part dataOut.writeInt(i); // write the type dataOut.writeInt(part.getType()); // write the index of the change if(part.hasIndex()) dataOut.writeInt(part.getIndex()); // write the value if(part.hasValue()) { Bufferlo valueBuffer = new Bufferlo(); delegate.encode(part.getValue(), valueBuffer.getOutputStream()); dataOut.writeInt(valueBuffer.length()); dataOut.flush(); partsAsBytes.append(valueBuffer); } } // that was easy return partsAsBytes; } /** * Decode the bytes into parts. */ private static List bytesToParts(Bufferlo partsAsBytes, ByteCoder delegate) throws IOException { // prepare the result List parts = new ArrayList(); // read till the end of the file DataInputStream dataIn = new DataInputStream(partsAsBytes.getInputStream()); // convert each part in sequence while(partsAsBytes.length() > 0) { ListEventPart currentPart = new ListEventPart(); // read the index of this part int expectedPartIndex = parts.size(); int partIndex = dataIn.readInt(); if(partIndex != expectedPartIndex) throw new IOException("Expected " + expectedPartIndex + " but found " + partIndex); // read in the type of this part currentPart.setType(dataIn.readInt()); // read in the index of this change if(currentPart.hasIndex()) { currentPart.setIndex(dataIn.readInt()); } // read the value if(currentPart.hasValue()) { int valueLength = dataIn.readInt(); Bufferlo valueBuffer = partsAsBytes.consume(valueLength); Object value = delegate.decode(valueBuffer.getInputStream()); currentPart.setValue(value); } // we've completed one part parts.add(currentPart); } // that was easy return parts; } /** * A part of a ListEvent that is byte codable. */ static class ListEventPart { /** the changed index */ private int index = -1; /** the type of change, INSERT, UPDATE, DELETE or CLEAR */ private int type = ListEventToBytes.CLEAR; /** the inserted or updated value */ private Object value = null; /** * Create a new ByteCodableListEventBlock. */ public ListEventPart(int index, int type, Object value) { this.index = index; this.type = type; this.value = value; } /** * Create a new empty ByteCodableListEventBlock. */ public ListEventPart() { } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } public int getType() { return type; } public void setType(int type) { this.type = type; } public boolean isDelete() { return (type == ListEvent.DELETE); } public boolean isUpdate() { return (type == ListEvent.UPDATE); } public boolean isInsert() { return (type == ListEvent.INSERT); } public boolean isClear() { return (type == ListEventToBytes.CLEAR); } public boolean hasIndex() { return (isUpdate() || isInsert() || isDelete()); } public boolean hasValue() { return (isUpdate() || isInsert()); } } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/io/Bufferlo.java0000644000175000017500000004054712106516366030132 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.channels.GatheringByteChannel; import java.nio.channels.ReadableByteChannel; import java.nio.channels.SelectionKey; import java.text.ParseException; import java.util.Iterator; import java.util.LinkedList; import java.util.ListIterator; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * A high-level class for moving data and parsing protocols. * * @author Jesse Wilson */ public class Bufferlo implements CharSequence { /** the buffers managed by this Bufferlo */ private LinkedList buffers = new LinkedList(); /** write to this bufferlo */ private BufferloOutputStream out = new BufferloOutputStream(); /** read from this bufferlo */ private BufferloInputStream in = new BufferloInputStream(); /** * Clears the contents of this bufferlo. */ public void clear() { buffers.clear(); } /** * Populate this Bufferlo with the data from the specified channel. */ public int readFromChannel(ReadableByteChannel source) throws IOException { int totalRead = 0; while(true) { // we need a new place to write into ByteBuffer writeInto = getWriteIntoBuffer(); // read in int bytesRead = source.read(writeInto); doneWriting(); // figure out what to do next if(bytesRead < 0 && totalRead == 0) return bytesRead; else if(bytesRead <= 0) return totalRead; else totalRead += bytesRead; } } /** * Populate this Bufferlo with the data from the specified channel, up to * * @param bytesRequested the maximum number of bytes to read. */ public int readFromChannel(ReadableByteChannel source, int bytesRequested) throws IOException { int totalRead = 0; while(totalRead < bytesRequested) { // we need a new place to write into ByteBuffer writeInto = getWriteIntoBuffer(); int bytesRemaining = bytesRequested - totalRead; int maxBytesToRead = Math.min(bytesRemaining, writeInto.remaining()); writeInto.limit(writeInto.position() + maxBytesToRead); // read in int bytesRead = source.read(writeInto); doneWriting(); // figure out what to do next if(bytesRead < 0 && totalRead == 0) return bytesRead; else if(bytesRead <= 0) return totalRead; else totalRead += bytesRead; } // we've read all we want return totalRead; } /** * Write the content of this Bufferlo to the specified channel. */ public long writeToChannel(GatheringByteChannel target) throws IOException { // nothing to write if(length() == 0) return 0; // make all buffers readable ByteBuffer[] toWrite = new ByteBuffer[buffers.size()]; for(int b = 0; b < buffers.size(); b++) { ByteBuffer buffer = (ByteBuffer)buffers.get(b); buffer.flip(); toWrite[b] = buffer; } // write them all out long totalWritten = target.write(toWrite); // restore the state on all buffers for(ListIterator b = buffers.listIterator(); b.hasNext(); ) { ByteBuffer buffer = (ByteBuffer)b.next(); if(!buffer.hasRemaining()) { b.remove(); } else if(buffer.position() > 0) { int bytesLeftToRead = buffer.remaining(); buffer.limit(buffer.capacity()); ByteBuffer noneRead = buffer.slice(); noneRead.position(bytesLeftToRead); noneRead.limit(bytesLeftToRead); b.set(noneRead); } else { buffer.position(buffer.limit()); } } // return the count of bytes written return totalWritten; } /** * Write the content of this Bufferlo to the specified channel, updating the * specified {@link SelectionKey} as necessary. */ public long writeToChannel(GatheringByteChannel target, SelectionKey selectionKey) throws IOException { // verify we can still write if(!selectionKey.isValid()) throw new IOException("Key cancelled"); // do the write long totalWritten = writeToChannel(target); // adjust the key based on whether we have leftovers if(length() > 0) { selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_WRITE); } else { selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_WRITE); } // return the count of bytes written return totalWritten; } /** * Gets the bytes of this Bufferlo. */ public byte[] consumeBytes(int bytes) { try { byte[] result = new byte[bytes]; int totalRead = 0; while(totalRead < bytes) { int read = getInputStream().read(result, totalRead, (bytes - totalRead)); totalRead += read; } return result; } catch(IOException e) { throw new IllegalStateException(e.getMessage()); } } /** * Gets an InputStream that reads this buffer. */ public BufferloInputStream getInputStream() { return in; } /** * Gets an OutputStream that writes this buffer. */ public BufferloOutputStream getOutputStream() { return out; } /** * Duplicates the exact state of this buffer. The returned buffer is read-only. */ public Bufferlo duplicate() { Bufferlo result = new Bufferlo(); for(int i = 0; i < buffers.size(); i++) { ByteBuffer buffer = (ByteBuffer)buffers.get(i); result.buffers.add(removeTrailingSpace(buffer)); } return result; } /** * Read the specified bytes into a new Bufferlo. The returned buffer is read-only. */ public Bufferlo consume(int bytes) { assert(bytes >= 0 && bytes <= length()); Bufferlo result = duplicate(); result.limit(bytes); skip(bytes); return result; } /** * Writes the specified String to this Bufferlo. */ public void write(String data) { append(stringToBytes(data)); } /** * Converts the specified String to bytes, one byte per character. The input * String must contain US-ASCII one-byte characters or the result of this * method is not specified. */ public static final ByteBuffer stringToBytes(String in) { try { return ByteBuffer.wrap(in.getBytes("US-ASCII")); } catch(UnsupportedEncodingException e) { throw new IllegalStateException(e.getMessage()); } } /** * Writes the specified data to this bufferlo. This shortens the last ByteBuffer * in the added set so that it will not be written to. This allows a read-only * buffer to be added without it ever being modified. * * This will consume the specified Bufferlo. */ public Bufferlo append(Bufferlo data) { buffers.addAll(data.buffers); data.buffers.clear(); return this; } /** * Appends a the specified data. The data will be consumed so it should be * a duplicate if it is to be reused. */ public Bufferlo append(ByteBuffer data) { ByteBuffer myCopy = data.slice(); myCopy.position(myCopy.limit()); buffers.add(myCopy); data.position(data.limit()); return this; } /** * Limits this Bufferlo to the specified size. */ public void limit(int bytes) { int bytesLeft = bytes; for(ListIterator b = buffers.listIterator(); b.hasNext(); ) { ByteBuffer current = (ByteBuffer)b.next(); if(bytesLeft <= 0) { b.remove(); } else if(current.capacity() >= bytesLeft) { current.position(bytesLeft); current.limit(bytesLeft); } bytesLeft -= current.capacity(); } } /** * Skips the specified number of bytes. */ public void skip(int bytes) { assert(bytes >= 0 && bytes <= length()); int bytesLeft = bytes; for(ListIterator b = buffers.listIterator(); b.hasNext(); ) { ByteBuffer current = (ByteBuffer)b.next(); if(bytesLeft >= current.limit()) { bytesLeft -= current.limit(); b.remove(); } else { current.position(bytesLeft); ByteBuffer smaller = current.slice(); smaller.position(smaller.limit()); b.set(smaller); break; } } } /** * Creates a new ByteBuffer identical to the parameter but without space trailing * after the limit. This allows a buffer to be added to the Bufferlo without * that buffer ever being written to. * * This assumes that position == limit. */ private ByteBuffer removeTrailingSpace(ByteBuffer buffer) { ByteBuffer clone = buffer.duplicate(); clone.position(0); ByteBuffer noTrailingSpace = clone.slice(); noTrailingSpace.position(noTrailingSpace.limit()); return noTrailingSpace; } /** * Write to the Bufferlo as a Stream. */ class BufferloOutputStream extends OutputStream { @Override public void write(int b) { ByteBuffer writeBuffer = getWriteIntoBuffer(); writeBuffer.put((byte)b); doneWriting(); } } /** * Read from the Bufferlo as a Stream. */ class BufferloInputStream extends InputStream { @Override public int read() { ByteBuffer readBuffer = getReadFromBuffer(); if(readBuffer == null) return -1; int result = 0xFF & readBuffer.get(); doneReading(); return result; } } /** * Gets a buffer that we can read from. This buffer must be flipped back into * write mode when this is complete by calling doneReading(). */ private ByteBuffer getReadFromBuffer() { if(buffers.isEmpty()) return null; ByteBuffer readFrom = (ByteBuffer)buffers.getFirst(); readFrom.flip(); return readFrom; } /** * Finishes reading the current read from buffer. */ private void doneReading() { ByteBuffer readFrom = (ByteBuffer)buffers.getFirst(); // if we've exhaused this buffer if(!readFrom.hasRemaining()) { buffers.removeFirst(); // we still have more to read from this buffer } else { int bytesLeftToRead = readFrom.remaining(); readFrom.limit(readFrom.capacity()); ByteBuffer noneRead = readFrom.slice(); noneRead.position(bytesLeftToRead); noneRead.limit(bytesLeftToRead); buffers.set(0, noneRead); } } /** * Gets a buffer that we can write data into. */ private ByteBuffer getWriteIntoBuffer() { // we have a buffer with space remaining if(!buffers.isEmpty()) { ByteBuffer last = (ByteBuffer)buffers.getLast(); if(last.position() < last.capacity()) { last.limit(last.capacity()); return last; } } // we need to create a new buffer ByteBuffer writeInto = getNewBuffer(); buffers.addLast(writeInto); return writeInto; } /** * Finishes writing the current buffer. */ private void doneWriting() { ByteBuffer writeInto = (ByteBuffer)buffers.getLast(); writeInto.limit(writeInto.position()); } /** * Get the number of bytes available. */ public int length() { int bytesAvailable = 0; for(Iterator b = buffers.iterator(); b.hasNext(); ) { ByteBuffer buffer = (ByteBuffer)b.next(); bytesAvailable += buffer.position(); } return bytesAvailable; } /** * Gets the character at the specified index. */ public char charAt(int index) { int bytesLeft = index; for(Iterator b = buffers.iterator(); b.hasNext(); ) { ByteBuffer buffer = (ByteBuffer)b.next(); if(bytesLeft < buffer.position()) { return (char)buffer.get(bytesLeft); } else { bytesLeft -= buffer.position(); } } throw new IndexOutOfBoundsException(); } /** * Returns a new character sequence that is a subsequence of this. */ public CharSequence subSequence(int start, int end) { Bufferlo clone = duplicate(); clone.skip(start); clone.limit(end - start); return clone; } /** * Gets this Bufferlo as a String. */ @Override public String toString() { StringBuffer result = new StringBuffer(); for(int c = 0; c < length(); c++) { result.append(charAt(c)); } return result.toString(); } public String toDebugString() { return "BUFFERLO {" + buffers + "}"; } /** * Finds the first index of the specified regular expression. * * @return the index of the specified regular expression, or -1 if that * regular expression does not currently exist in the ByteBuffer. This * index is volatile because further operations on this ByteBufferParser * may influence the location of the specified regular expression. */ public int indexOf(String regex) { Matcher matcher = Pattern.compile(regex).matcher(this); if(!matcher.find()) return -1; return matcher.start(); } /** * Consumes the specified regular expression. This simply advances the buffer's * position to the end of the regular expression. * * @throws ParseException if the specified expression is not in the input buffer * @return the number of bytes consumed. */ public int consume(String regex) throws ParseException { Matcher matcher = Pattern.compile(regex).matcher(this); if(!matcher.find()) throw new ParseException(regex + " is not in current buffer", 0); if(matcher.start() != 0) throw new ParseException(regex + " is not a prefix of " + this, 0); skip(matcher.end()); return matcher.end(); } /** * Reads the String up until the specified regular expression and returns it. * This advances the buffer's position to the end of the regular expression. * * @throws ParseException if the specified expression is not in the input buffer */ public String readUntil(String regex) throws ParseException { return readUntil(regex, true); } /** * Reads the String up until the specified regular expression and returns it. * * @param consume true to advance the buffer's position to the end of the * regular expression, or false to not modify the buffer * @throws ParseException if the specified expression is not in the input buffer */ public String readUntil(String regex, boolean consume) throws ParseException { Matcher matcher = Pattern.compile(regex).matcher(this); if(!matcher.find()) throw new ParseException(regex + " is not in current buffer", 0); String result = subSequence(0, matcher.start()).toString(); if(consume) skip(matcher.end()); return result; } /** * Gets a new buffer by creating it or removing it from the pool. */ private ByteBuffer getNewBuffer() { int BUFFER_SIZE = 8196; return ByteBuffer.allocateDirect(BUFFER_SIZE); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/HttpClient.java0000644000175000017500000001204412106516372030021 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Overcome flaws in {@link HttpURLConnection} to download files from http://java.net, * which requires cookies to be passed between redirects for downloads to work. * * @author Jesse Wilson */ public class HttpClient { public static final int MAX_REDIRECTS = 20; public static void main(String[] args) throws Exception { // expect a single argument starting with "http" or "https" if(args.length != 2 || !args[0].startsWith("http")) { System.out.println("Usage: HttpClient "); System.out.println(); System.out.println("This program demonstrates how to download from java.net's"); System.out.println("Documents & Files area using URLConnection"); return; } // fast fail if the file already exists File targetFile = new File(args[1]); if(targetFile.exists()) { System.out.println("Skipping " + args[0] + ", file already exists"); return; } // read from the webserver specified InputStream httpIn = getInputStream(args[0]); // write from the stream to our target file System.out.println("Downloading " + args[0]); OutputStream fileOut = new BufferedOutputStream(new FileOutputStream(args[1])); pushStreams(httpIn, fileOut); fileOut.close(); } /** * Move bytes from the specified input stream to the specified output * stream until the input stream is exhaused. * *

      We could optimize this to use byte buffers if it ever became a bottleneck. */ private static void pushStreams(InputStream source, OutputStream target) throws IOException { while(true) { int aByte = source.read(); if(aByte < 0) break; target.write(aByte); } } /** * Follow redirects as necessary to get an InputStream from the specified URL. */ private static InputStream getInputStream(String url) throws IOException { List cookies = new ArrayList(); for(int i = 0; i < MAX_REDIRECTS; i++) { // prepare the connection HttpURLConnection urlConnection = (HttpURLConnection)new URL(url).openConnection(); urlConnection.setInstanceFollowRedirects(false); writeCookies(cookies, urlConnection); urlConnection.connect(); // if this is a redirect, keep the cookies // and load the next location if(urlConnection.getResponseCode() == HttpURLConnection.HTTP_MOVED_TEMP || urlConnection.getResponseCode() == HttpURLConnection.HTTP_MOVED_PERM) { url = urlConnection.getHeaderField("Location"); acceptCookies(cookies, urlConnection); // success! } else { return urlConnection.getInputStream(); } } throw new IOException("Max redirects " + MAX_REDIRECTS + " exceeded!"); } /** * Accept all cookies provided by the HTTP response. * *

      This method fails to perform some important aspects of cookie management, * and is potentially dangerous for all but the most basic of cookie problems. * This method fails at: *

    • Ensuring cookie names are unique *
    • Handling cookie expiry dates *
    • Keeping cookies private to the hosts that provide them *
    • Verifying the hosts specified match the hosts providing the cookies */ private static void acceptCookies(List cookies, HttpURLConnection urlConnection) { for(Iterator i = urlConnection.getHeaderFields().entrySet().iterator(); i.hasNext(); ) { Map.Entry entry = (Map.Entry)i.next(); String key = (String)entry.getKey(); if(!"Set-Cookie".equalsIgnoreCase(key)) continue; List values = (List)entry.getValue(); for(Iterator v = values.iterator(); v.hasNext(); ) { String value = (String)v.next(); String cookie = value.split(";")[0]; cookies.add(cookie); } } } /** * Encode the cookie header for the specified connection. */ private static void writeCookies(List cookies, HttpURLConnection urlConnection) { StringBuffer cookiesString = new StringBuffer(); for(Iterator i = cookies.iterator(); i.hasNext(); ) { if(cookiesString.length() > 0) cookiesString.append("; "); cookiesString.append(i.next()); } if(cookiesString.length() > 0) urlConnection.addRequestProperty("Cookie", cookiesString.toString()); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/swing/0000755000175000017500000000000012106516360026223 5ustar gregoagregoa././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/swing/UpperThresholdRangeModel.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/swing/UpperThresholdRangeModel.0000644000175000017500000000623312106516360033136 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.swing; // the core Glazed Lists packages import ca.odell.glazedlists.ThresholdList; import javax.swing.*; /** * A UpperThresholdRangeModel provides an implementation of a bounded-range * model for a slider widget that binds a JSlider to a ThresholdList. This * implementation maps the slider value to the upper threshold of the * ThresholdList. * * @author Kevin Maltby */ public class UpperThresholdRangeModel extends DefaultBoundedRangeModel implements BoundedRangeModel { /** the list to connect a slider widget to */ private ThresholdList target = null; /** * Creates a new range that controls specified ThresholdList. */ public UpperThresholdRangeModel(ThresholdList target) { this.target = target; } /** * Returns the model's minimum. */ @Override public int getMinimum() { target.getReadWriteLock().readLock().lock(); try { return target.getLowerThreshold(); } finally { target.getReadWriteLock().readLock().unlock(); } } /** * Returns the model's current value. */ @Override public int getValue() { target.getReadWriteLock().readLock().lock(); try { return target.getUpperThreshold(); } finally { target.getReadWriteLock().readLock().unlock(); } } /** * Sets all of the properties for this bounded-range. * *

      This is a tweaked version of the setRangeProperties method to be found * in the JDK source for DefaultBoundedRangeModel. Just giving credit where * credit is due. */ @Override public void setRangeProperties(int newValue, int newExtent, int newMin, int newMax, boolean adjusting) { target.getReadWriteLock().writeLock().lock(); try { // Correct invalid values if(newMin > newMax) newMin = newMax; if(newValue > newMax) newMax = newValue; if(newValue < newMin) newMin = newValue; // See if non-threshold model changes are necessary boolean changed = (newExtent != getExtent()) || (newMax != getMaximum()) || (adjusting != getValueIsAdjusting()); // Set the lower threshold if applicable if(newMin != getMinimum()) { target.setLowerThreshold(newMin); changed = true; } // Set the upper threshold if applicable if(newValue != getValue()) { target.setUpperThreshold(newValue); changed = true; } // Update all of the range properties if there was a change if(changed) super.setRangeProperties(newValue, newExtent, newMin, newMax, adjusting); } finally { target.getReadWriteLock().writeLock().unlock(); } } }././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/swing/SwingThreadProxyEventList.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/swing/SwingThreadProxyEventList0000644000175000017500000000241212106516360033264 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.swing; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.impl.gui.ThreadProxyEventList; import java.awt.EventQueue; /** * Proxies events from all threads to the Swing event dispatch thread. This allows * any thread to write a source {@link EventList} that will be updated on the * Swing thread. * * @author Jesse Wilson */ public class SwingThreadProxyEventList extends ThreadProxyEventList { /** * Create a {@link SwingThreadProxyEventList} that mirrors the specified source * {@link EventList} for access on the Swing thread. */ public SwingThreadProxyEventList(EventList source) { super(source); } /** * Schedule the specified runnable to be run on the proxied thread. */ @Override protected void schedule(Runnable runnable) { if (EventQueue.isDispatchThread()) { runnable.run(); } else { EventQueue.invokeLater(runnable); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/swing/ScreenGeometry.java0000644000175000017500000000721412106516360032025 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.swing; import javax.swing.*; import java.awt.*; /** * Figure out the dimensions of our screen. * *

      This code is inspired by similar in * JPopupMenu.adjustPopupLocationToFitScreen(). * * @author Jesse Wilson */ public class ScreenGeometry { final GraphicsConfiguration graphicsConfiguration; final boolean aqua; public ScreenGeometry(JComponent component) { this.aqua = UIManager.getLookAndFeel().getName().indexOf("Aqua") != -1; this.graphicsConfiguration = graphicsConfigurationForComponent(component); } /** * Get the best graphics configuration for the specified point and component. */ private GraphicsConfiguration graphicsConfigurationForComponent(Component component) { Point point = component.getLocationOnScreen(); // try to find the graphics configuration for our point of interest GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice[] gd = ge.getScreenDevices(); for(int i = 0; i < gd.length; i++) { if(gd[i].getType() != GraphicsDevice.TYPE_RASTER_SCREEN) continue; GraphicsConfiguration defaultGraphicsConfiguration = gd[i].getDefaultConfiguration(); if(!defaultGraphicsConfiguration.getBounds().contains(point)) continue; return defaultGraphicsConfiguration; } // we couldn't find a graphics configuration, use the component's return component.getGraphicsConfiguration(); } /** * Get the bounds of where we can put a popup. */ public Rectangle getScreenBounds() { Rectangle screenSize = getScreenSize(); Insets screenInsets = getScreenInsets(); return new Rectangle( screenSize.x + screenInsets.left, screenSize.y + screenInsets.top, screenSize.width - screenInsets.left - screenInsets.right, screenSize.height - screenInsets.top - screenInsets.bottom ); } /** * Get the bounds of the screen currently displaying the component. */ public Rectangle getScreenSize() { // get the screen bounds and insets via the graphics configuration if(graphicsConfiguration != null) { return graphicsConfiguration.getBounds(); } // just use the toolkit bounds, it's less awesome but sufficient return new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); } /** * Fetch the screen insets, the off limits areas around the screen such * as menu bar, dock or start bar. */ public Insets getScreenInsets() { Insets screenInsets; if(graphicsConfiguration != null) { screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(graphicsConfiguration); } else { screenInsets = new Insets(0, 0, 0, 0); } // tweak the insets for aqua, they're reported incorrectly there if(aqua) { int aquaBottomInsets = 21; // unreported insets, shown in screenshot, https://glazedlists.dev.java.net/issues/show_bug.cgi?id=332 int aquaTopInsets = 22; // for Apple menu bar, found via debugger screenInsets.bottom = Math.max(screenInsets.bottom, aquaBottomInsets); screenInsets.top = Math.max(screenInsets.top, aquaTopInsets); } return screenInsets; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/swing/LowerThresholdRangeModel.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/swing/LowerThresholdRangeModel.0000644000175000017500000000623412106516360033134 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.swing; // the core Glazed Lists packages import ca.odell.glazedlists.ThresholdList; import javax.swing.*; /** * A LowerThresholdRangeModel provides an implementation of a bounded-range * model for a slider widget that binds a JSlider to a ThresholdList. This * implementation maps the slider value to the lower threshold of the * ThresholdList. * * @author Kevin Maltby */ public class LowerThresholdRangeModel extends DefaultBoundedRangeModel implements BoundedRangeModel { /** the list to connect a slider widget to */ private ThresholdList target = null; /** * Creates a new range that controls specified ThresholdList. */ public LowerThresholdRangeModel(ThresholdList target) { this.target = target; } /** * Returns the model's maximum. */ @Override public int getMaximum() { target.getReadWriteLock().readLock().lock(); try { return target.getUpperThreshold(); } finally { target.getReadWriteLock().readLock().unlock(); } } /** * Returns the model's current value. */ @Override public int getValue() { target.getReadWriteLock().readLock().lock(); try { return target.getLowerThreshold(); } finally { target.getReadWriteLock().readLock().unlock(); } } /** * Sets all of the properties for this bounded-range. * *

      This is a tweaked version of the setRangeProperties method to be found * in the JDK source for DefaultBoundedRangeModel. Just giving credit where * credit is due. */ @Override public void setRangeProperties(int newValue, int newExtent, int newMin, int newMax, boolean adjusting) { target.getReadWriteLock().writeLock().lock(); try { // Correct invalid values if(newMin > newMax) newMin = newMax; if(newValue > newMax) newMax = newValue; if(newValue < newMin) newMin = newValue; // See if non-threshold model changes are necessary boolean changed = (newExtent != getExtent()) || (newMin != getMinimum()) || (adjusting != getValueIsAdjusting()); // Set the lower threshold if applicable if(newValue != getValue()) { target.setLowerThreshold(newValue); changed = true; } // Set the upper threshold if applicable if(newMax != getMaximum()) { target.setUpperThreshold(newMax); changed = true; } // Update all of the range properties if there was a change if(changed) super.setRangeProperties(newValue, newExtent, newMin, newMax, adjusting); } finally { target.getReadWriteLock().writeLock().unlock(); } } }././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/swing/ComboBoxPopupLocationFix.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/swing/ComboBoxPopupLocationFix.0000644000175000017500000000710212106516360033120 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.swing; import javax.swing.*; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import java.awt.*; /** * Fix a problem where the JComboBox's popup obscures its editor in the Mac OS X * Aqua look and feel. * *

      Installing this fix will resolve the problem for Aqua without having * side-effects for other look-and-feels. It also supports dynamically changed * look and feels. * * @see bug 332 * * @author Jesse Wilson */ public final class ComboBoxPopupLocationFix { /** the components being fixed */ private final JComboBox comboBox; private final JPopupMenu popupMenu; /** the listener provides callbacks as necessary */ private final Listener listener = new Listener(); /** * Private constructor so users use the more action-oriented * {@link #install} method. */ private ComboBoxPopupLocationFix(JComboBox comboBox) { this.comboBox = comboBox; this.popupMenu = (JPopupMenu)comboBox.getUI().getAccessibleChild(comboBox, 0); popupMenu.addPopupMenuListener(listener); } /** * Install the fix for the specified combo box. */ public static ComboBoxPopupLocationFix install(JComboBox comboBox) { if(comboBox == null) throw new IllegalArgumentException(); return new ComboBoxPopupLocationFix(comboBox); } /** * Uninstall the fix. Usually this is unnecessary since letting the combo * box go out of scope is sufficient. */ public void uninstall() { popupMenu.removePopupMenuListener(listener); } /** * Reposition the popup immediately before it is shown. */ private class Listener implements PopupMenuListener { public void popupMenuWillBecomeVisible(PopupMenuEvent e) { final JComponent popupComponent = (JComponent) e.getSource(); fixPopupLocation(popupComponent); } public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { // do nothing } public void popupMenuCanceled(PopupMenuEvent e) { // do nothing } } /** * Do the adjustment on the specified popupComponent immediately before * it is displayed. */ private void fixPopupLocation(JComponent popupComponent) { // we only need to fix Apple's aqua look and feel if(popupComponent.getClass().getName().indexOf("apple.laf") != 0) { return; } // put the popup right under the combo box so it looks like a // normal Aqua combo box Point comboLocationOnScreen = comboBox.getLocationOnScreen(); int comboHeight = comboBox.getHeight(); int popupY = comboLocationOnScreen.y + comboHeight; // ...unless the popup overflows the screen, in which case we put it // above the combobox Rectangle screenBounds = new ScreenGeometry(comboBox).getScreenBounds(); int popupHeight = popupComponent.getPreferredSize().height; if(comboLocationOnScreen.y + comboHeight + popupHeight > screenBounds.x + screenBounds.height) { popupY = comboLocationOnScreen.y - popupHeight; } popupComponent.setLocation(comboLocationOnScreen.x, popupY); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/SubEventList.java0000644000175000017500000001256312106516372030340 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; // the Glazed Lists' change objects import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.TransformedList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; /** * A SubEventList is a view of a sub-range of an EventList. * *

      Although the SubEventList's size is initially fixed, the * SubEventList can change size as a consequence of changes to * the source list that occur within the range covered by the SubEventList. * *

      This {@link EventList} supports all write operations. * *

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

      Warning: This class * breaks the contract required by {@link java.util.List}. See {@link EventList} * for an example. * * @author Jesse Wilson */ public final class SubEventList extends TransformedList { /** the start index of this list, inclusive */ private int startIndex; /** the end index of this list, exclusive */ private int endIndex; /** * Creates a new SubEventList that covers the specified range of indices * in the source list. * * @param startIndex the start index of the source list, inclusive * @param endIndex the end index of the source list, exclusive * @param source the source list to view * @param automaticallyRemove true if this SubEventList should deregister itself * from the ListEventListener list of the source list once it is * otherwise out of scope. * * @see GlazedLists#weakReferenceProxy(EventList, ListEventListener) */ public SubEventList(EventList source, int startIndex, int endIndex, boolean automaticallyRemove) { super(source); // do consistency checking if(startIndex < 0 || endIndex < startIndex || endIndex > source.size()) { throw new IllegalArgumentException("The range " + startIndex + "-" + endIndex + " is not valid over a list of size " + source.size()); } // save the sublist bounds this.startIndex = startIndex; this.endIndex = endIndex; // listen directly or via a proxy that will do garbage collection if(automaticallyRemove) { source.addListEventListener(new WeakReferenceProxy(source, this)); } else { source.addListEventListener(this); } } /** {@inheritDoc} */ @Override public int size() { return endIndex - startIndex; } /** {@inheritDoc} */ @Override protected int getSourceIndex(int mutationIndex) { return mutationIndex + startIndex; } /** {@inheritDoc} */ @Override protected boolean isWritable() { return true; } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { updates.beginEvent(); // if this sublist is size one, just move the start and end index if(listChanges.isReordering() && size() == 1) { int[] reorderMap = listChanges.getReorderMap(); for(int r = 0; r < reorderMap.length; r++) { if(reorderMap[r] == startIndex) { startIndex = r; endIndex = startIndex + 1; break; } } // handle regular change events by shifting indices as necessary } else { while(listChanges.next()) { int changeIndex = listChanges.getIndex(); int changeType = listChanges.getType(); // if it is a change before if(changeIndex < startIndex || (changeType == ListEvent.INSERT && changeIndex == startIndex)) { if(changeType == ListEvent.INSERT) { startIndex++; endIndex++; } else if(changeType == ListEvent.DELETE) { startIndex--; endIndex--; } // if it is a change within } else if(changeIndex < endIndex) { if(changeType == ListEvent.INSERT) { endIndex++; updates.elementInserted(changeIndex - startIndex, listChanges.getNewValue()); } else if(changeType == ListEvent.UPDATE) { updates.elementUpdated(changeIndex - startIndex, listChanges.getOldValue(), listChanges.getNewValue()); } else if(changeType == ListEvent.DELETE) { endIndex--; updates.elementDeleted(changeIndex - startIndex, listChanges.getOldValue()); } // if it is a change after } else { // do nothing } } } if(startIndex > endIndex) throw new IllegalStateException(); updates.commitEvent(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/ObservableConnector.java0000644000175000017500000000454412106516372031710 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; import ca.odell.glazedlists.ObservableElementList; import java.util.EventListener; import java.util.Observable; import java.util.Observer; /** * An {@link ObservableElementList.Connector} for the archaic {@link Observable} * base class which is rarely used in applications, but apparently used within * the Eclipse Trader * framework, which some Glazed Lists users are using. * * @author James Lemieux */ public class ObservableConnector implements ObservableElementList.Connector, Observer, EventListener { /** The list which contains the elements being observed via this {@link ObservableElementList.Connector}. */ private ObservableElementList list; /** * This method is called whenever the observed object is changed. It * responds by notifying the associated ObservableElementList that the given * {@link Observable} has been changed. * * @param o the Observable that has been updated * @param arg an argument passed to observers which is ignored here */ public void update(Observable o, Object arg) { list.elementChanged(o); } /** * Start observing the specified element. * * @param element the element to be observed * @return the listener that was installed on the element * to be used as a parameter to {@link #uninstallListener(Object, EventListener)} */ public EventListener installListener(E element) { element.addObserver(this); return this; } /** * Stop observing the specified element. * * @param element the observed element * @param listener the listener that was installed on the element * in {@link #installListener(Object)} */ public void uninstallListener(E element, EventListener listener) { element.deleteObserver(this); } /** {@inheritDoc} */ public void setObservableElementList(ObservableElementList list) { this.list = list; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/Main.java0000644000175000017500000001232012106516372026624 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; import javax.swing.*; import java.awt.*; import java.io.IOException; import java.net.URL; import java.util.Map; import java.util.jar.Attributes; import java.util.jar.Manifest; /** * Show version information for the current .jar file. * *

      This requires a some special attributes in the manifest to work: *

    • Built-By, the person who made this build *
    • Built-At, the time this build was created *
    • Contributors, a comma-separated list of developers * *

      Plus some standard manifest attributes are used: *

    • Implementation-Title *
    • Implementation-URL *
    • Implementation-Version * * @author Jesse Wilson */ public class Main { public static void main(String[] args) { final String PATH_TO_THIS_CLASS = "/ca/odell/glazedlists/impl/Main.class"; // find the main attributes from the manifest final Map attributes; try { // get a path to the manifest, or die trying String pathToThisClass = Main.class.getResource(PATH_TO_THIS_CLASS).toString(); int jarDelimiterCharacter = pathToThisClass.lastIndexOf("!"); if(jarDelimiterCharacter == -1) return; String manifestPath = pathToThisClass.substring(0, jarDelimiterCharacter + 1) + "/META-INF/MANIFEST.MF"; URL manifestUrl = new URL(manifestPath); // load the manifest and save the attributes of interest Manifest manifest = new Manifest(manifestUrl.openStream()); attributes = manifest.getMainAttributes(); } catch(IOException e) { return; } // the title String title = (String)attributes.get(Attributes.Name.IMPLEMENTATION_TITLE); String titleHtml = "" + title + ""; // the url String urlHtml = (String)attributes.get(Attributes.Name.IMPLEMENTATION_URL); // the version String versionHtml = (String)attributes.get(Attributes.Name.IMPLEMENTATION_VERSION); // when it was built StringBuffer builtHtml = new StringBuffer(); builtHtml.append(attributes.get(new Attributes.Name("Built-By"))); builtHtml.append("
      "); builtHtml.append(attributes.get(new Attributes.Name("Built-At"))); builtHtml.append("
      "); builtHtml.append("Source: ").append(attributes.get(new Attributes.Name("Source-Version"))); // the contributors String contributorsHtml = (String)attributes.get(new Attributes.Name("Contributors")); contributorsHtml = contributorsHtml.replaceAll(",\\s*", "
      "); // lay it all out on a panel JFrame frame = new JFrame(title); frame.getContentPane().setLayout(new GridBagLayout()); frame.getContentPane().add(new JLabel("" + titleHtml + ""), new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); frame.getContentPane().add(new JLabel("" + urlHtml + ""), new GridBagConstraints(0, 1, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( 0, 10, 10, 10), 0, 0)); frame.getContentPane().add(new JLabel("Version:"), new GridBagConstraints(0, 2, 1, 1, 0.5, 0.0, GridBagConstraints.NORTHEAST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); frame.getContentPane().add(new JLabel("" + versionHtml + ""), new GridBagConstraints(1, 2, 1, 1, 0.5, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); frame.getContentPane().add(new JLabel("Built:"), new GridBagConstraints(0, 3, 1, 1, 0.5, 0.0, GridBagConstraints.NORTHEAST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); frame.getContentPane().add(new JLabel("" + builtHtml + ""), new GridBagConstraints(1, 3, 1, 1, 0.5, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); frame.getContentPane().add(new JLabel("Contributors:"), new GridBagConstraints(0, 4, 1, 1, 0.5, 0.0, GridBagConstraints.NORTHEAST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); frame.getContentPane().add(new JLabel("" + contributorsHtml + ""), new GridBagConstraints(1, 4, 1, 1, 0.5, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(10, 10, 10, 10), 0, 0)); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/0000755000175000017500000000000012106516366026710 5ustar gregoagregoa././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/PropertyEventNameMatcher.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/PropertyEventNameMatch0000644000175000017500000000734212106516366033245 0ustar gregoagregoa/* Glazed Lists (c) 2003-2007 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.matchers; import ca.odell.glazedlists.matchers.Matcher; import java.beans.PropertyChangeEvent; import java.util.Collection; import java.util.HashSet; import java.util.Set; /** * PropertyEventNameMatcher matches {@link java.beans.PropertyChangeEvent}s by property name. * One or more property names to match or filter against may be given. The concrete behaviour of * a PropertyEventNameMatcher depends on the {@link #matchPropertyNames} property. If you want to * match property change events against a known set of property names, use a value of true * for the #matchPropertyNames} property. Alternatively, when you specify false, * the specified property names will serve as an exclude list, e.g. if an event matches a specified * property name, it will be filtered out. * * @see #isMatchPropertyNames() * * @author Holger Brands */ public final class PropertyEventNameMatcher implements Matcher { /** Property names to consider. */ private final Set propertyNames = new HashSet(); /** * Specifies how to use the {@link #propertyNames} to match property change events. * * @see #isMatchPropertyNames() */ private boolean matchPropertyNames; /** * Creates a PropertyEventNameMatcher. * * @param matchPropertyNames if true the property names are used to match events * by name, if false they are used to filter events * @param properties the property names to consider * * @see #isMatchPropertyNames() */ public PropertyEventNameMatcher(boolean matchPropertyNames, String... properties) { if (properties == null) throw new IllegalArgumentException("Array of property names may not be null"); this.matchPropertyNames = matchPropertyNames; for (int i = 0, n = properties.length; i < n; i++) { propertyNames.add(properties[i]); } } /** * Creates a PropertyEventNameMatcher. * * @param matchPropertyNames if true the property names are used to match * events by name, if false they are used to filter events * @param properties the property names to consider * * @see #isMatchPropertyNames() */ public PropertyEventNameMatcher(boolean matchPropertyNames, Collection properties) { if (properties == null) throw new IllegalArgumentException("Collection of property names may not be null"); this.matchPropertyNames = matchPropertyNames; propertyNames.addAll(properties); } /** {@inheritDoc} */ public boolean matches(PropertyChangeEvent event) { final boolean containsProperty = propertyNames.contains(event.getPropertyName()); return matchPropertyNames ? containsProperty : !containsProperty; } /** * Determines how to use the {@link #propertyNames} to match the property change events. If * true, the specified property names serve as a positive list, e.g. if the * property name of an event is contained in the {@link #propertyNames}, the event is matched. * If false, the specified property names serve as a negative list, e.g. if the * property name of an event is contained in the {@link #propertyNames}, the event is * not matched. */ public boolean isMatchPropertyNames() { return matchPropertyNames; } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/BeanPropertyMatcher.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/BeanPropertyMatcher.ja0000644000175000017500000000274512106516366033152 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.matchers; import ca.odell.glazedlists.impl.GlazedListsImpl; import ca.odell.glazedlists.impl.beans.BeanProperty; import ca.odell.glazedlists.matchers.Matcher; /** * A {@link Matcher} which uses a {@link BeanProperty} to read a bean property * from a given bean and check it for equality with a given value. * null property values are allowed. * * @author James Lemieux */ public final class BeanPropertyMatcher implements Matcher { /** The BeanProperty containing logic for extracting the property value from an item. */ private final BeanProperty beanProperty; /** The value with which to compare the bean property. */ private final Object value; /** * Create a new {@link Matcher} that matches whenever the given property * equals the given value. */ public BeanPropertyMatcher(Class beanClass, String propertyName, Object value) { this.beanProperty = new BeanProperty(beanClass, propertyName, true, false); this.value = value; } /** {@inheritDoc} */ public boolean matches(E item) { if (item == null) return false; return GlazedListsImpl.equal(this.beanProperty.get(item), this.value); } }././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/WeakReferenceMatcherEditor.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/WeakReferenceMatcherEd0000644000175000017500000001736012106516366033125 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.matchers; import ca.odell.glazedlists.matchers.Matcher; import ca.odell.glazedlists.matchers.MatcherEditor; import javax.swing.event.EventListenerList; import java.lang.ref.WeakReference; /** * This {@link MatcherEditor} exists to aid with garbage collection of * {@link Listener} objects. It is particularly useful when a long-lived * {@link MatcherEditor} exists and many short-lived {@link Listener} objects * must be added and removed. * *

      Rather than attaching each {@link Listener} to the long-lived * {@link MatcherEditor} with hard references and managing the {@link Listener} * registrations manually, it is considerably easier to contruct a * {@link WeakReferenceMatcherEditor} which removes {@link Listener}s after * they are unreachable and have been garbage collected. * *

      Common usage of this class resembles: *

       * MatcherEditor myCustomMatcherEditor = ...
       * MatcherEditor weakRefMatcherEditor = Matchers.weakReferenceProxy(myCustomMatcherEditor);
       *
       * // customMatcherEditorListener will be removed when it is garbage collected
       * MatcherEditor.Listener customMatcherEditorListener = ...
       * weakRefMatcherEditor.addMatcherEditorListener(customMatcherEditorListener);
       * 
      * * @author James Lemieux */ public final class WeakReferenceMatcherEditor implements MatcherEditor, MatcherEditor.Listener { /** The Listeners for this MatcherEditor. */ private final EventListenerList listenerList = new EventListenerList(); /** The last Matcher that was broadcast from this MatcherEditor. */ private MatcherEditor source; /** * Construct a MatcherEditor which acts as a weak proxy for the given * source. That is, it rebroadcasts MatcherEvents it receives * from the source to its own weak listeners until it, itself, * is no longer reachable, at which time it stops listening to the * source. * * @param source the MatcherEditor to decorate with weak proxying */ public WeakReferenceMatcherEditor(MatcherEditor source) { this.source = source; // listen to the source weakly so we clean ourselves up when we're extinct source.addMatcherEditorListener(new WeakMatcherEditorListener(source, this)); } /** * Return the current {@link Matcher} specified by the decorated * {@link MatcherEditor}. * * @return a non-null {@link Matcher} */ public Matcher getMatcher() { return this.source.getMatcher(); } /** * Wrap the given listener in a {@link WeakReference} and * notify it when the decorated {@link MatcherEditor} fires {@link Matcher} * changes. The weak listener will only be notified while it is reachable * via hard references, and will be cleaned up the next time a new * {@link MatcherEditor.Event} is fired. */ public void addMatcherEditorListener(Listener listener) { this.listenerList.add(Listener.class, new WeakMatcherEditorListener(this, listener)); } /** {@inheritDoc} */ public void removeMatcherEditorListener(Listener listener) { final Object[] listeners = this.listenerList.getListenerList(); // we remove the given listener by identity for (int i = listeners.length - 2; i >= 0; i -= 2) { final Object currentObject = listeners[i+1]; if (currentObject == listener) this.listenerList.remove(MatcherEditor.Listener.class, listener); // if the given listener is a WeakMatcherEditorListener, check if // the currentObject is actually its referent if (currentObject instanceof WeakMatcherEditorListener) { final WeakMatcherEditorListener weakMatcherEditorListener = (WeakMatcherEditorListener) currentObject; final Listener currentListener = weakMatcherEditorListener.getDecoratedListener(); if (currentListener == listener) this.listenerList.remove(MatcherEditor.Listener.class, weakMatcherEditorListener); } } } /** * Indicates a changes has occurred in the delegate Matcher produced by the * MatcherEditor. * * @param matcherEvent a MatcherEditor.Event describing the change in the * delegate Matcher produced by the MatcherEditor */ public void changedMatcher(Event matcherEvent) { final Object[] listeners = this.listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) ((Listener) listeners[i+1]).changedMatcher(matcherEvent); } /** * This is the crux of this MatcherEditor. It wraps a {@link Listener} in a * {@link WeakReference} so that its garbage collection is not affected by * being registered with a {@link MatcherEditor}. Instead, each time it is * notified that the {@link Matcher} changed it must test the availability * of the underlying listener. If it is available, it is notified. If it is * unavailable, it removes itself from listening. */ private class WeakMatcherEditorListener implements Listener { /** The WeakReference housing the true MatcherEditor.Listener. */ private final WeakReference> weakListener; /** The editor that this Listener is listening to. */ private final MatcherEditor editor; /** * Construct a WeakMatcherEditorListener which wraps the given * listener, which is assumed to listen to the given * editor, in a {@link WeakReference}. * * @param editor the {@link MatcherEditor} from which to remove the * listener after it has been garbage collected * @param listener the {@link Listener} containing the true logic for * reacting to matcher changes */ public WeakMatcherEditorListener(MatcherEditor editor, Listener listener) { this.weakListener = new WeakReference>(listener); this.editor = editor; } /** * Return the underlying {@link Listener} from the {@link WeakReference}. */ public Listener getDecoratedListener() { return this.weakListener.get(); } /** * This method tests for the existence of the underlying {@link Listener} * and if it still exists (i.e. has not been garbage collected) it is * notified of the matcherEvent. Otherwise, it is removed * from the {@link MatcherEditor} and will never be notified again. * * @param matcherEvent a MatcherEditor.Event describing the change in the * Matcher produced by the MatcherEditor */ public void changedMatcher(Event matcherEvent) { // fetch the underlying MatcherEditor.Listener final Listener matcherEditorListener = this.weakListener.get(); // if it has been garbage collected, stop listening to the MatcherEditor if (matcherEditorListener == null) { this.editor.removeMatcherEditorListener(this); } else { // otherwise fire the event as though it originated from this WeakReferenceMatcherEditor matcherEvent = new MatcherEditor.Event(WeakReferenceMatcherEditor.this, matcherEvent.getType(), matcherEvent.getMatcher()); matcherEditorListener.changedMatcher(matcherEvent); } } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/NotMatcher.java0000644000175000017500000000175512106516366031627 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.matchers; import ca.odell.glazedlists.matchers.Matcher; /** * A simple {@link Matcher} implementation that inverts the result of another * {@link Matcher Matcher's} {@link Matcher#matches(Object)} method. * * @author Rob Eden */ public class NotMatcher implements Matcher { private Matcher parent; public NotMatcher(Matcher parent) { if (parent == null ) throw new IllegalArgumentException("parent cannot be null"); this.parent = parent; } /** {@inheritDoc} */ public boolean matches(E item) { return !parent.matches(item); } /** {@inheritDoc} */ @Override public String toString() { return "[NotMatcher parent:" + parent + "]"; } }././@LongLink0000000000000000000000000000016300000000000011565 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/NonNullAndNonEmptyStringMatcher.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/NonNullAndNonEmptyStri0000644000175000017500000000177312106516366033207 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.matchers; import ca.odell.glazedlists.matchers.Matcher; /** * A {@link Matcher} implementation that matches String objects if they are * non-null and non-empty. Use {@link #getInstance()} to obtain a singleton * instance. * * James Lemieux */ public class NonNullAndNonEmptyStringMatcher implements Matcher { /** Singleton instance of NonNullAndNonEmptyStringMatcher. */ private static final Matcher INSTANCE = new NonNullAndNonEmptyStringMatcher(); /** * Return a singleton instance. */ public static Matcher getInstance() { return INSTANCE; } /** {@inheritDoc} */ public boolean matches(String item) { return item != null && item.length() > 0; } }././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/FixedMatcherEditor.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/FixedMatcherEditor.jav0000644000175000017500000000152012106516366033122 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.matchers; import ca.odell.glazedlists.matchers.AbstractMatcherEditor; import ca.odell.glazedlists.matchers.Matcher; import ca.odell.glazedlists.matchers.MatcherEditor; /** * A {@link MatcherEditor} whose {@link Matcher} never changes. * * @author Jesse Wilson */ public final class FixedMatcherEditor extends AbstractMatcherEditor { /** * Create a {@link FixedMatcherEditor} for the specified {@link Matcher}. */ public FixedMatcherEditor(Matcher matcher) { super.fireChanged(matcher); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/TypeMatcher.java0000644000175000017500000000120512106516366031776 0ustar gregoagregoapackage ca.odell.glazedlists.impl.matchers; import ca.odell.glazedlists.matchers.Matcher; /** * Matches only items that are one of the given class types. * * @author James Lemieux */ public class TypeMatcher implements Matcher { private final Class[] classes; public TypeMatcher(Class... classes) { this.classes = classes; } public boolean matches(E item) { if (item == null) return false; final Class target = item.getClass(); for (int i = 0; i < classes.length; i++) if (classes[i].isAssignableFrom(target)) return true; return false; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/TrueMatcher.java0000644000175000017500000000162712106516366032004 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.matchers; import ca.odell.glazedlists.matchers.Matcher; /** * A {@link Matcher} implementation that always matches. Use {@link #getInstance()} to * obtain a singleton instance. * * @author Rob Eden */ public final class TrueMatcher implements Matcher { /** Singleton instance of TrueMatcher. */ private static final Matcher INSTANCE = new TrueMatcher(); private TrueMatcher() {} /** * Return a singleton instance. */ public static Matcher getInstance() { return INSTANCE; } /** {@inheritDoc} */ public boolean matches(E item) { return true; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/OrMatcher.java0000644000175000017500000000156612106516366031447 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.matchers; import ca.odell.glazedlists.matchers.Matcher; /** * A Matcher that matches if any child elements match. */ public class OrMatcher implements Matcher { /** The Matchers being combined with an "or" operator. */ private final Matcher[] matchers; public OrMatcher(Matcher... matchers) { this.matchers = matchers; } /** {@inheritDoc} */ public boolean matches(E item) { for (int i = 0; i < matchers.length; i++) { if (matchers[i].matches(item)) return true; } return false; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/NotNullMatcher.java0000644000175000017500000000167712106516366032465 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.matchers; import ca.odell.glazedlists.matchers.Matcher; /** * A simple {@link Matcher} implementation that only matches non-null objects. * * @author James Lemieux */ public final class NotNullMatcher implements Matcher { /** Singleton instance of NotNullMatcher. */ private static final Matcher INSTANCE = new NotNullMatcher(); private NotNullMatcher() {} /** * Return a singleton instance. */ public static Matcher getInstance() { return INSTANCE; } /** {@inheritDoc} */ public boolean matches(E item) { return item != null; } /** {@inheritDoc} */ @Override public String toString() { return "[NotNullMatcher]"; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/FalseMatcher.java0000644000175000017500000000164212106516366032114 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.matchers; import ca.odell.glazedlists.matchers.Matcher; /** * A {@link Matcher} implementation that never matches. Use * {@link #getInstance()} to obtain the singleton instance. * * @author Rob Eden */ public final class FalseMatcher implements Matcher { /** Singleton instance of FalseMatcher. */ private static final Matcher INSTANCE = new FalseMatcher(); private FalseMatcher() {} /** * Return a singleton instance. */ public static Matcher getInstance() { return INSTANCE; } /** {@inheritDoc} */ public boolean matches(E item) { return false; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/AndMatcher.java0000644000175000017500000000156612106516366031571 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.matchers; import ca.odell.glazedlists.matchers.Matcher; /** * A Matcher that matches if all child elements match. */ public class AndMatcher implements Matcher { /** The Matchers being combined with an "and" operator. */ private final Matcher[] matchers; public AndMatcher(Matcher... matchers) { this.matchers = matchers; } /** {@inheritDoc} */ public boolean matches(E item) { for (int i = 0; i < matchers.length; i++) { if (!matchers[i].matches(item)) return false; } return true; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/NullMatcher.java0000644000175000017500000000165112106516366031774 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.matchers; import ca.odell.glazedlists.matchers.Matcher; /** * A simple {@link Matcher} implementation that only matches null objects. * * @author James Lemieux */ public final class NullMatcher implements Matcher { /** Singleton instance of NullMatcher. */ private static final Matcher INSTANCE = new NullMatcher(); private NullMatcher() {} /** * Return a singleton instance. */ public static Matcher getInstance() { return INSTANCE; } /** {@inheritDoc} */ public boolean matches(E item) { return item == null; } /** {@inheritDoc} */ @Override public String toString() { return "[NullMatcher]"; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/matchers/RangeMatcher.java0000644000175000017500000000756012106516366032123 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.matchers; import ca.odell.glazedlists.Filterator; import ca.odell.glazedlists.matchers.Matcher; import java.util.ArrayList; import java.util.List; /** * A Matcher for matching {@link Comparable}s within a range. The list of * objects being filtered contains either: * *
        *
      • Comparable objects *
      • objects from which Comparable can be extracted via a {@link Filterator} *
      * * @author James Lemieux */ public class RangeMatcher implements Matcher { /** The start of the range; null if the range has no starting value. */ private final D start; /** The end of the range; null if the range has no ending value. */ private final D end; /** * The Filterator which can extract Comparables from the filter objects; * null implies the objects to be filtered are already * Comparable objects. */ private final Filterator filterator; /** a heavily recycled list of filter Comparables, call clear() before use */ private final List filterComparables = new ArrayList(); /** * This constructor should be used when the objects to be filtered are * already {@link Comparable} objects and thus no conversion is necessary * to be used with this matcher. * * @param start the start of the range to filter on; null * indicates there is no start to the range * @param end the end of the range to filter on; null * indicates there is no end to the range */ public RangeMatcher(D start, D end) { this(start, end, null); } /** * This constructor should be used when the objects to be filtered are not * {@link Comparable} objects, but contain {@link Comparable} objects which * can be extracted. The given filterator is used to extract * those {@link Comparable} objects from the filtered objects. * * @param start the start of the range to filter on; null * indicates there is no start to the range * @param end the end of the range to filter on; null * indicates there is no end to the range * @param filterator contains the logic to fetch {@link Comparable} objects * from the list of filtered objects */ public RangeMatcher(D start, D end, Filterator filterator) { this.start = start; this.end = end; this.filterator = filterator; } /** {@inheritDoc} */ public boolean matches(E item) { filterComparables.clear(); if (filterator == null) filterComparables.add((D) item); else filterator.getFilterValues(filterComparables, item); // ensure the range contains at least one extracted Comparable for (int c = 0; c < filterComparables.size(); c++) { D filterComparable = filterComparables.get(c); // check if the filterComparable is within the defined range if (filterComparable != null) { if (start != null && start.compareTo(filterComparable) > 0) continue; if (end != null && end.compareTo(filterComparable) < 0) continue; } // a filterComparable is within the given range, so the object matches return true; } // no filterComparable fell within this range return false; } /** {@inheritDoc} */ @Override public String toString() { return "[RangeMatcher between " + start + " and " + end + "]"; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/event/0000755000175000017500000000000012106516362026217 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/event/BlockSequence.java0000644000175000017500000002140612106516362031610 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.event; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.impl.adt.IntArrayList; import java.util.ArrayList; import java.util.List; /** * Manage a very simple list of list event blocks that occur in * increasing-only order. * * @author Jesse Wilson */ public class BlockSequence { /** the start indices of the change blocks, inclusive */ private IntArrayList starts = new IntArrayList(); /** the end indices of the change blocks, exclusive */ private IntArrayList ends = new IntArrayList(); /** the change types */ private IntArrayList types = new IntArrayList(); /** the impacted values */ private List oldValues = new ArrayList(); private List newValues = new ArrayList(); /** * @param startIndex the first updated element, inclusive * @param endIndex the last index, exclusive */ public boolean update(int startIndex, int endIndex) { return addChange(ListEvent.UPDATE, startIndex, endIndex, ListEvent.unknownValue(), ListEvent.unknownValue()); } /** * @param startIndex the first inserted element, inclusive * @param endIndex the last index, exclusive */ public boolean insert(int startIndex, int endIndex) { return addChange(ListEvent.INSERT, startIndex, endIndex, ListEvent.unknownValue(), ListEvent.unknownValue()); } /** * @param startIndex the index of the first element to remove * @param endIndex the last index, exclusive */ public boolean delete(int startIndex, int endIndex) { return addChange(ListEvent.DELETE, startIndex, endIndex, ListEvent.unknownValue(), ListEvent.unknownValue()); } /** * Add this change to the list, or return false if that failed * because the change is not in increasing order. * * @return true if the change was successfully applied, or false * if no change was made because this change could not be handled. */ public boolean addChange(int type, int startIndex, int endIndex, E oldValue, E newValue) { // remind ourselves of the most recent change int lastType; int lastStartIndex; int lastEndIndex; int lastChangedIndex; int size = types.size(); E lastOldValue; E lastNewValue; if(size == 0) { lastType = -1; lastStartIndex = -1; lastEndIndex = 0; lastChangedIndex = 0; lastOldValue = ListEvent.unknownValue(); lastNewValue = ListEvent.unknownValue(); } else { lastType = types.get(size - 1); lastStartIndex = starts.get(size - 1); lastEndIndex = ends.get(size - 1); lastChangedIndex = (lastType == ListEvent.DELETE) ? lastStartIndex : lastEndIndex; lastOldValue = (lastType == ListEvent.DELETE) ? oldValues.get(size - 1) : ListEvent.unknownValue(); lastNewValue = newValues.get(size - 1); } // this change breaks the linear-ordering requirement, convert // to a more powerful list blocks manager if(startIndex < lastChangedIndex) { return false; // concatenate this change on to the previous one } else if(lastChangedIndex == startIndex && lastType == type && oldValue == lastOldValue && newValue == lastNewValue) { int newLength = (lastEndIndex - lastStartIndex) + (endIndex - startIndex); ends.set(size - 1, lastStartIndex + newLength); return true; // add this change to the end of the list } else { starts.add(startIndex); ends.add(endIndex); types.add(type); oldValues.add(oldValue); newValues.add(newValue); return true; } } public boolean isEmpty() { return types.isEmpty(); } public void reset() { starts.clear(); ends.clear(); types.clear(); oldValues.clear(); newValues.clear(); } public Iterator iterator() { return new Iterator(); } /** {@inheritDoc} */ @Override public String toString() { StringBuffer result = new StringBuffer(); for(int i = 0; i < types.size(); i++) { if(i != 0) { result.append(", "); } // write the type int type = types.get(i); if(type == ListEvent.INSERT) result.append("+"); else if(type == ListEvent.UPDATE) result.append("U"); else if(type == ListEvent.DELETE) result.append("X"); // write the range int start = starts.get(i); int end = ends.get(i); result.append(start); if(end != start) { result.append("-"); result.append(end); } } return result.toString(); } /** * Iterate through the list of changes in this sequence. */ public class Iterator { private int blockIndex = -1; private int offset = 0; private int startIndex = -1; private int endIndex = -1; private int type = -1; public Iterator copy() { Iterator result = new Iterator(); result.blockIndex = blockIndex; result.offset = offset; result.startIndex = startIndex; result.endIndex = endIndex; result.type = type; return result; } public int getIndex() { if(type == ListEvent.INSERT || type == ListEvent.UPDATE) { return startIndex + offset; } else if(type == ListEvent.DELETE) { return startIndex; } else { throw new IllegalStateException(); } } public int getBlockStart() { if(startIndex == -1) throw new IllegalStateException("The ListEvent is not currently in a state to return a block start index"); return startIndex; } public int getBlockEnd() { if(endIndex == -1) throw new IllegalStateException("The ListEvent is not currently in a state to return a block end index"); return endIndex; } public int getType() { if(type == -1) throw new IllegalStateException("The ListEvent is not currently in a state to return a type"); return type; } public E getOldValue() { return oldValues.get(blockIndex); } public E getNewValue() { return newValues.get(blockIndex); } /** * Move to the next changed index, possibly within the same block. */ public boolean next() { // increment within the block if(offset + 1 < endIndex - startIndex) { offset++; return true; // increment to the next block } else if(blockIndex + 1 < types.size()) { blockIndex++; offset = 0; startIndex = starts.get(blockIndex); endIndex = ends.get(blockIndex); type = types.get(blockIndex); return true; // no more left } else { return false; } } /** * Move to the next changed block. */ public boolean nextBlock() { // increment to the next block if(blockIndex + 1 < types.size()) { blockIndex++; offset = 0; startIndex = starts.get(blockIndex); endIndex = ends.get(blockIndex); type = types.get(blockIndex); return true; // no more left } else { return false; } } /** * @return true if theres another changed index */ public boolean hasNext() { // increment within the block if(offset + 1 < endIndex - startIndex) return true; // increment to the next block if(blockIndex + 1 < types.size()) return true; // no more left return false; } /** * @return true if theres another changed block */ public boolean hasNextBlock() { // increment to the next block if(blockIndex + 1 < types.size()) return true; // no more left return false; } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/event/Tree4Deltas.java0000644000175000017500000002677612106516362031224 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.event; import java.util.Arrays; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.impl.adt.barcode2.Element; import ca.odell.glazedlists.impl.adt.barcode2.FourColorTree; import ca.odell.glazedlists.impl.adt.barcode2.FourColorTreeIterator; import ca.odell.glazedlists.impl.adt.barcode2.ListToByteCoder; /** * Manage and describe the differences between two revisions of the * same List, assuming either one can change at any time. * *

      Initially, the source and target lists are equal. Over time, the * target list changes. It's also possible that the source list can change, * which is necessary for long-lived buffered changes. * * @author Jesse Wilson */ public class Tree4Deltas { /** all the names of the index sets are with respect to the target */ private static final ListToByteCoder BYTE_CODER = new ListToByteCoder(Arrays.asList("+", "U", "X", "_")); public static final byte INSERT = BYTE_CODER.colorToByte("+"); public static final byte UPDATE = BYTE_CODER.colorToByte("U"); public static final byte DELETE = BYTE_CODER.colorToByte("X"); public static final byte NO_CHANGE = BYTE_CODER.colorToByte("_"); private static final byte SOURCE_INDICES = BYTE_CODER.colorsToByte(Arrays.asList("U", "X", "_")); private static final byte TARGET_INDICES = BYTE_CODER.colorsToByte(Arrays.asList("U", "+", "_")); private static final byte ALL_INDICES = BYTE_CODER.colorsToByte(Arrays.asList("U", "X", "+", "_")); private static final byte CHANGE_INDICES = BYTE_CODER.colorsToByte(Arrays.asList("U", "X", "+")); /** the trees values include removed elements */ private FourColorTree tree = new FourColorTree(BYTE_CODER); private boolean allowContradictingEvents = false; /** * When the first change to a list happens, we need to guess what the list's * capacity is. After that change, we reliably know the list's capacity, so * we don't need to keep testing the capacity one index at a time. */ private boolean initialCapacityKnown = false; public boolean horribleHackPreferMostRecentValue = false; public boolean getAllowContradictingEvents() { return allowContradictingEvents; } public void setAllowContradictingEvents(boolean allowContradictingEvents) { this.allowContradictingEvents = allowContradictingEvents; } public int targetToSource(int targetIndex) { if(!initialCapacityKnown) ensureCapacity(targetIndex + 1); return tree.convertIndexColor(targetIndex, TARGET_INDICES, SOURCE_INDICES); } public int sourceToTarget(int sourceIndex) { if(!initialCapacityKnown) ensureCapacity(sourceIndex + 1); return tree.convertIndexColor(sourceIndex, SOURCE_INDICES, TARGET_INDICES); } /** *

      We should consider removing the loop by only setting on removed elements. * * @param oldValue the previous value being replaced * @param newValue the new value * @param startIndex the first updated element, inclusive * @param endIndex the last index, exclusive */ public void targetUpdate(int startIndex, int endIndex, E oldValue, E newValue) { if(!initialCapacityKnown) ensureCapacity(endIndex); for(int i = startIndex; i < endIndex; i++) { int overallIndex = tree.convertIndexColor(i, TARGET_INDICES, ALL_INDICES); Element standingChangeToIndex = tree.get(overallIndex, ALL_INDICES); if(horribleHackPreferMostRecentValue) { byte newColor = standingChangeToIndex.getColor() == INSERT ? INSERT : UPDATE; tree.set(overallIndex, ALL_INDICES, newColor, oldValue, 1); continue; } // don't bother updating an inserted element if(standingChangeToIndex.getColor() == INSERT) { continue; } // if we're updating an update, the original replaced value stands. if(standingChangeToIndex.getColor() == UPDATE) { oldValue = standingChangeToIndex.get(); } // apply the update to our change description tree.set(overallIndex, ALL_INDICES, UPDATE, oldValue, 1); } } /** * Add a value to the target only. * *

      Since this method takes a value parameter, is is only needed * when the target doesn't store its value, for example with buffered * changes. * * @param startIndex the first inserted element, inclusive * @param endIndex the last index, exclusive * @param newValue the inserted value */ public void targetInsert(int startIndex, int endIndex, E newValue) { if(!initialCapacityKnown) ensureCapacity(endIndex); tree.add(startIndex, TARGET_INDICES, INSERT, newValue, endIndex - startIndex); } /** *

      We should consider removing the loop from this method by counting * the inserted elements between startIndex and endIndex, removing those, * then removing everything else... * * @param startIndex the index of the first element to remove * @param endIndex the last index, exclusive * @param value the removed value */ public void targetDelete(int startIndex, int endIndex, E value) { if(!initialCapacityKnown) ensureCapacity(endIndex); for(int i = startIndex; i < endIndex; i++) { if(startIndex > 0 && startIndex > tree.size(TARGET_INDICES)) { throw new IllegalArgumentException(); } int overallIndex = tree.convertIndexColor(startIndex, TARGET_INDICES, ALL_INDICES); Element standingChangeToIndex = tree.get(overallIndex, ALL_INDICES); // if we're deleting an insert, remove that insert if(standingChangeToIndex.getColor() == INSERT) { if(!allowContradictingEvents) throw new IllegalStateException("Remove " + i + " undoes prior insert at the same index! Consider enabling contradicting events."); tree.remove(overallIndex, ALL_INDICES, 1); continue; } // if we're deleting an update, the original replaced value stands. if(standingChangeToIndex.getColor() == UPDATE) { value = standingChangeToIndex.get(); } tree.set(overallIndex, ALL_INDICES, DELETE, value, 1); } } public void sourceInsert(int sourceIndex) { tree.add(sourceIndex, SOURCE_INDICES, NO_CHANGE, ListEvent.unknownValue(), 1); } public void sourceDelete(int sourceIndex) { tree.remove(sourceIndex, SOURCE_INDICES, 1); } public void sourceRevert(int sourceIndex) { tree.set(sourceIndex, SOURCE_INDICES, NO_CHANGE, ListEvent.unknownValue(), 1); } public int targetSize() { return tree.size(TARGET_INDICES); } public int sourceSize() { return tree.size(SOURCE_INDICES); } public byte getChangeType(int sourceIndex) { return tree.get(sourceIndex, SOURCE_INDICES).getColor(); } /** * Get the value at the specified target index. * * @return the value, or {@link ListEvent#UNKNOWN_VALUE} if this index * holds a value that hasn't been buffered. In this case, the value * can be obtained from the source list. */ public E getTargetValue(int targetIndex) { return tree.get(targetIndex, TARGET_INDICES).get(); } public E getSourceValue(int sourceIndex) { return tree.get(sourceIndex, SOURCE_INDICES).get(); } public void reset(int size) { tree.clear(); initialCapacityKnown = true; ensureCapacity(size); } private void ensureCapacity(int size) { int currentSize = tree.size(TARGET_INDICES); int delta = size - currentSize; if(delta > 0) { int endOfTree = tree.size(ALL_INDICES); tree.add(endOfTree, ALL_INDICES, NO_CHANGE, ListEvent.unknownValue(), delta); } } /** * Add all the specified changes to this. */ public void addAll(BlockSequence blocks) { for(BlockSequence.Iterator i = blocks.iterator(); i.nextBlock(); ) { int blockStart = i.getBlockStart(); int blockEnd = i.getBlockEnd(); int type = i.getType(); E oldValue = i.getOldValue(); E newValue = i.getNewValue(); if(type == ListEvent.INSERT) { targetInsert(blockStart, blockEnd, newValue); } else if(type == ListEvent.UPDATE) { targetUpdate(blockStart, blockEnd, oldValue, newValue); } else if(type == ListEvent.DELETE) { targetDelete(blockStart, blockEnd, oldValue); } else { throw new IllegalStateException(); } } } /** * @return true if this event contains no changes. */ public boolean isEmpty() { return tree.size(CHANGE_INDICES) == 0; } public Iterator iterator() { return new Iterator(tree); } @Override public String toString() { return tree.asSequenceOfColors(); } /** * Iterate through the list of changes in this tree. */ public static class Iterator { private final FourColorTree tree; private final FourColorTreeIterator treeIterator; private Iterator(FourColorTree tree) { this.tree = tree; this.treeIterator = new FourColorTreeIterator(tree); } private Iterator(FourColorTree tree, FourColorTreeIterator treeIterator) { this.tree = tree; this.treeIterator = treeIterator; } public Iterator copy() { return new Iterator(tree, treeIterator.copy()); } public int getIndex() { return treeIterator.index(TARGET_INDICES); } public int getEndIndex() { // this is peculiar. We add mixed types - an index of current indices // plus the size of "all indices". . . this is because we describe the // range of deleted indices from its start to finish, although it's // finish will ultimately go to zero once the change is applied. return treeIterator.nodeStartIndex(TARGET_INDICES) + treeIterator.nodeSize(ALL_INDICES); } public int getType() { byte color = treeIterator.color(); if(color == INSERT) return ListEvent.INSERT; else if(color == UPDATE) return ListEvent.UPDATE; else if(color == DELETE) return ListEvent.DELETE; else throw new IllegalStateException(); } public boolean next() { if(!hasNext()) return false; treeIterator.next(CHANGE_INDICES); return true; } public boolean nextNode() { if(!hasNextNode()) return false; treeIterator.nextNode(CHANGE_INDICES); return true; } public boolean hasNext() { return treeIterator.hasNext(CHANGE_INDICES); } public boolean hasNextNode() { return treeIterator.hasNextNode(CHANGE_INDICES); } public E getOldValue() { return treeIterator.node().get(); } } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/WeakReferenceProxy.java0000644000175000017500000001071612106516372031517 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; // the core Glazed Lists package import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import java.lang.ref.WeakReference; /** * This class is a proxy to another ListEventListener that may go out of scope * without explicitly removing itself from the source list's set of listeners. * *

      WeakReferenceProxy exists to solve a garbage collection problem. Suppose * there exists an EventList L and with an iterator I. I * must listen to L for change events in order to remain consistent. * Therefore I will register itself as a listener to L. When * I goes out of scope (as they typically do), it will remain registered * as a listener of L. This prevents I from ever being garbage * collected! But I can never used again. Because iterators are expected * to be used very frequently, this will cause an unacceptable memory leak. * *

      This problem is solved by WeakReferenceProxy. Instead of adding I * as a direct listener of L, add a proxy instead. The proxy will retain * a WeakReference to I and forward events to I as * long as it is reachable. When I is no longer reachable, the proxy * will remove itself from the list of listeners for L and all garbage * is available for collection. * *

      Specifically, the proxy stops listening to L the * next time any of the following occurs: * *

        *
      • another ListEventListener is registered with the same EventList *
      • another ListEventListener is deregistered with the same EventList *
      • another ListEvent is broadcast for the same EventList *
      * * @see java.lang.ref.WeakReference * @see Bug 21 * * @author Jesse Wilson * @author James Lemieux */ public final class WeakReferenceProxy implements ListEventListener { /** a weak reference the target ListEventListener */ private final WeakReference> proxyTargetReference; /** the list to remove this listener from when done */ private EventList source; /** * Creates a new WeakReferenceProxy that listens for events from the * specified list and forwards them to the specified listener. */ public WeakReferenceProxy(EventList source, ListEventListener proxyTarget) { if (source == null) throw new IllegalArgumentException("source may not be null"); if (proxyTarget == null) throw new IllegalArgumentException("proxyTarget may not be null"); this.source = source; this.proxyTargetReference = new WeakReference>(proxyTarget); } /** * Accepts notification for the changes and forwards them to the proxy * target if it has not yet been garbage collected. */ public void listChanged(ListEvent listChanges) { // if this listener has already been cleaned up, ignore ListEvents if (source == null) return; // fetch the underlying ListEventListener final ListEventListener proxyTarget = getReferent(); // test to see if the underlying ListEventListener still exists if (proxyTarget == null) { // it doesn't so clean it up source.removeListEventListener(this); dispose(); } else { // it does, so notify it of the ListEvent proxyTarget.listChanged(listChanges); } } /** * Returns the underlying ListEventListener or null if it has * been garbage collected. */ public ListEventListener getReferent() { return proxyTargetReference.get(); } /** * A callback to notify this WeakReferenceProxy that it has been * unregistered from the EventList to which it was listening. The * WeakReferenceProxy responds by cleaning up internal references to the * EventList and ensuring that any future ListEvents it receives are * ignored. */ public void dispose() { source = null; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/GlazedListsImpl.java0000644000175000017500000002207712106516372031021 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.FunctionList; import ca.odell.glazedlists.impl.text.LatinDiacriticsStripper; import ca.odell.glazedlists.impl.adt.KeyedCollection; import java.util.*; /** * A utility class containing all sorts of random things that are useful for * implementing Glazed Lists but not for using Glazed Lists. * * @author Jesse Wilson */ public final class GlazedListsImpl { /** * A dummy constructor to prevent instantiation of this class */ private GlazedListsImpl() { throw new UnsupportedOperationException(); } // Utility Methods // // // // // // // // // // // // // // // // // // // /** * Compare the specified objects for equality. */ public static boolean equal(Object a, Object b) { if(a == b) return true; if(a == null || b == null) return false; return a.equals(b); } /** * Concatenate two lists to create a third list. */ public static List concatenate(List a, List b) { List aAndB = new ArrayList(a.size() + b.size()); aAndB.addAll(a); aAndB.addAll(b); return aAndB; } /** * Replace all elements in the target {@link EventList} with the elements in * the source {@link List}. * *

      Note that both collections must be sorted prior to execution using the * {@link Comparator} specified. */ public static void replaceAll(EventList target, Collection source, boolean updates, Comparator comparator) { // use the default comparator if none is specified if(comparator == null) { comparator = (Comparator)GlazedLists.comparableComparator(); } // walk through each list simultaneously int targetIndex = -1; Iterator sourceIterator = source.iterator(); // define marker objects that mean we need new objects final E NEW_VALUE_NEEDED = (E)Void.class; E targetObject = NEW_VALUE_NEEDED; E sourceObject = NEW_VALUE_NEEDED; // while there are elements left in either list while(true) { // do our best to get new objects if necessary if(targetObject == NEW_VALUE_NEEDED) { if(targetIndex < target.size()) targetIndex++; if(targetIndex < target.size()) targetObject = target.get(targetIndex); } if(sourceObject == NEW_VALUE_NEEDED) { if(sourceIterator.hasNext()) sourceObject = sourceIterator.next(); } // we've exhausted our dataset if(targetObject == NEW_VALUE_NEEDED && sourceObject == NEW_VALUE_NEEDED) { break; } // figure out if this is an insert, update or delete on the target int compareResult; if(targetObject == NEW_VALUE_NEEDED) compareResult = 1; else if(sourceObject == NEW_VALUE_NEEDED) compareResult = -1; else compareResult = comparator.compare(targetObject, sourceObject); // the target value precedes the source value, delete it if(compareResult < 0) { target.remove(targetIndex); targetIndex--; targetObject = NEW_VALUE_NEEDED; // the values are equal, keep them } else if(compareResult == 0) { if(updates) { target.set(targetIndex, sourceObject); } targetObject = NEW_VALUE_NEEDED; sourceObject = NEW_VALUE_NEEDED; // the source value precedes the target, insert it } else if(compareResult > 0) { target.add(targetIndex, sourceObject); targetIndex++; sourceObject = NEW_VALUE_NEEDED; } } } /** * Get a character mapper array which strips the diacritics from Latin * characters in order to normalize word spellings between Latin-based * languages. This allows users from any Latin-based language to search * the text of any other Latin-based language intuitively. * *

      For example, with the returned character mapper array, a French * user searching for the text "rsum" would match the English text * "resume". Similarly, an English user searching for the text "resume" * would match the French text "rsum." In this way, neither user needs to * know the specifics of the foreign language they are searching, and thus * the quality of their search increases. */ public static char[] getLatinDiacriticsStripper() { return LatinDiacriticsStripper.getMapper(); } // Date Utility Methods // // // // // // // // // // // // // // // // // /** * Returns a new Date representing the first millisecond of the month for * the given date. */ public static Date getMonthBegin(Date date) { final Calendar cal = Calendar.getInstance(); cal.setTime(date); return getMonthStart(cal); } /** * Adjusts the given calendar to the start of the month and * returns the resulting {@link Date}. */ public static Date getMonthStart(Calendar calendar) { calendar.set(Calendar.DATE, 1); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); return calendar.getTime(); } /** * Returns true if the given calendar represents the * first millisecond of a month; false otherwise. */ public static boolean isMonthStart(Calendar calendar) { return calendar.get(Calendar.MILLISECOND) == 0 && calendar.get(Calendar.SECOND) == 0 && calendar.get(Calendar.MINUTE) == 0 && calendar.get(Calendar.HOUR_OF_DAY) == 0 && calendar.get(Calendar.DATE) == 1; } /** * Returns a non-symmetric Comparator that returns 0 if two Objects are * equal as specified by {@link Object#equals(Object)}, or 1 otherwise. */ public static Comparator equalsComparator() { return new EqualsComparator(); } private static class EqualsComparator implements Comparator { public int compare(T alpha, T beta) { boolean equal = alpha == null ? beta == null : alpha.equals(beta); return equal ? 0 : 1; } } /** * Returns a {@link FunctionList.Function} that simply reflects the * function's argument as its result. */ public static FunctionList.Function identityFunction() { return new IdentityFunction(); } private static class IdentityFunction implements FunctionList.Function { public E evaluate(E sourceValue) { return sourceValue; } } /** * Returns a {@link KeyedCollection} optimized for the values which can be * compared. * * @param positionComparator a Comparator to order position objects * @param valueComparator a Comparator to order value objects * @return a {@link KeyedCollection} optimized for values which can be * compared */ public static KeyedCollection keyedCollection(Comparator

      positionComparator, Comparator valueComparator) { return new KeyedCollection(positionComparator, new TreeMap(valueComparator)); } /** * Returns a {@link KeyedCollection} optimized for the values which cannot * be compared. * * @param positionComparator a Comparator to order position objects * @return a {@link KeyedCollection} optimized for values which cannot be * compared */ public static KeyedCollection keyedCollection(Comparator

      positionComparator) { return new KeyedCollection(positionComparator, new HashMap()); } /** * Removes the given value from c if it can be * located by identity. This method is thus faster than standard removes * from a Collection and conveys a stronger meaning than normal Collection * removes (namely that the exact item must be found in order to be removed) * * @param c the Collection from which to remove * @param value the value to be removed * @return true if c was altered as a result of this call */ public static boolean identityRemove(Collection c, V value) { for (Iterator i = c.iterator(); i.hasNext();) { if (i.next() == value) { i.remove(); return true; } } return false; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/Grouper.java0000644000175000017500000005620212106516372027372 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; import ca.odell.glazedlists.SortedList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.impl.adt.Barcode; import java.util.Comparator; import java.util.LinkedList; /** * This helper class manages the groups created by dividing up a * {@link SortedList} using a {@link Comparator}. This class uses a delegate * interface {@link Client} to fire the appropriate events as groups are * inserted, updated and changed. * * @author James Lemieux * @author Jesse Wilson */ public class Grouper { /** Entries in barcode are one of these constants to indicate if the element at an index is a UNIQUE or DUPLICATE. */ public static final Object UNIQUE = Barcode.BLACK; public static final Object DUPLICATE = Barcode.WHITE; /** Used only in temporary data structures to flag deleting of the FIRST group element when more elements exist. */ private static final Object UNIQUE_WITH_DUPLICATE = null; /** * A temporary barcode data structure is created when processing ListEvents in this GroupingList. * These constants identify which indexes remain to be processed. */ private static final Object TODO = Barcode.BLACK; private static final Object DONE = Barcode.WHITE; /** For reporting which side of an element its group is located. */ private static final int LEFT_GROUP = -1; private static final int NO_GROUP = 0; private static final int RIGHT_GROUP = 1; /** the sorted source of the grouping service */ private SortedList sortedList; /** The comparator used to determine the groups. */ private Comparator comparator; /** the grouping list client to notify of group changes */ private Client client; /** * The data structure which tracks which source elements are considered UNIQUE * (the first element of a group) and which are DUPLICATE (any group element NOT the first). */ private Barcode barcode; /** * Create a new {@link Grouper} that manages groups for the * specified {@link SortedList}. */ public Grouper(SortedList sortedList, Client client) { this.sortedList = sortedList; this.client = client; setComparator(sortedList.getComparator()); } /** * Set the comparator used to determine which elements are grouped together. As * a consequence this will rebuild the grouping state. */ public void setComparator(Comparator comparator) { if(this.comparator == comparator) return; this.comparator = comparator; // Populate the barcode by examining adjacent entries within the // source SortedList to check if they belong to the same group. barcode = new Barcode(); for (int i = 0, n = sortedList.size(); i < n; i++) { barcode.add(i, groupTogether(i, i-1) ? DUPLICATE : UNIQUE, 1); } } /** * Get the comparator used to determine which elements are grouped together. */ public Comparator getComparator() { return comparator; } /** * Get the client which is notified of group changes. */ public Client getClient() { return client; } /** * Get the barcode that designates where groups start. {@link #UNIQUE} * colored elements are the starts of new groups, with a * {@link #DUPLICATE} element for each following group memeber. */ public Barcode getBarcode() { return barcode; } /** * Handle changes from the {@link SortedList} by modifying the grouping state. * During this method, callbacks will be made to the {@link Client} who is * responsible for firing the appropriate change events to its listeners. */ public void listChanged(ListEvent listChanges) { // The approach to handling list uses two passes through the list of // change events. // // In pass 1, the barcode is changed and GroupingList is updated to // reflect the changes but no events are fired. Deleted and updated // original barcode values are stored in a temporary LinkedList. Updated // values' barcode entries are set to UNIQUE. // // In pass 2, the change events are reviewed again. During this second pass // we bring the IndexedTree of GroupLists up to date and also fire ListEvents. // // a) Inserted elements are tested to see if they were simply added to // existing GroupLists (in which case an update event should be fired // rather than an insert) // // b) For update events the value is tested for group membership with // adjacent values, and the result may be a combination of DELETE, INSERT // or UPDATE events. // // c) Deleted events may be forwarded with a resultant DELETE or UPDATE // event, depending on whether the deleted value was the last member of // its group. // a Barcode to track values that must be revisited to determine if they are UNIQUE or DUPLICATE final Barcode toDoList = new Barcode(); toDoList.addWhite(0, barcode.size()); // first pass -> update the barcode and accumulate the type of values removed (UNIQUE or DUPLICATE or UNIQUE_WITH_DUPLICATE) final LinkedList removedValues = new LinkedList(); int lastFakedUniqueChangeIndex = -1; while (listChanges.next()) { final int changeIndex = listChanges.getIndex(); final int changeType = listChanges.getType(); if (changeType == ListEvent.INSERT) { // assume the inserted element is unique until we determine otherwise barcode.add(changeIndex, UNIQUE, 1); // indicate we must revisit this index later to determine its uniqueness toDoList.add(changeIndex, TODO, 1); } else if (changeType == ListEvent.UPDATE) { // case: AACCC -> AABCC // if the updated index is a UNIQUE index, we MAY have just created a // new group (by modifying an element in place). Consequently, we must // mark the NEXT element as UNIQUE and revisit it later to determine // if it really is if (barcode.get(changeIndex) == UNIQUE) { if (changeIndex+1 < barcode.size() && barcode.get(changeIndex+1) == DUPLICATE) { // however, we need to make sure that the barcode UNIQUE entry we are looking at // was part of the barcode state before we started this iteration of listChanges. // Specifically, we are concerned about the case where an update on the first element // causes the barcode goes from: // X__ to XX_ // In this case, when looking at element 1, we should not treat it as if it were // a UNIQUE node that is getting updated. Instead, it should be treated as the // DUPLICATE node that it really is. // We track the index of the last element that we set to UNIQUE in this way using // lastFakedUniqueChangeIndex if (changeIndex != lastFakedUniqueChangeIndex){ barcode.set(changeIndex, UNIQUE, 2); toDoList.set(changeIndex, TODO, 1); lastFakedUniqueChangeIndex = changeIndex+1; } } } } else if (changeType == ListEvent.DELETE) { Object deleted = barcode.get(changeIndex); barcode.remove(changeIndex, 1); toDoList.remove(changeIndex, 1); if (deleted == UNIQUE) { // if the deleted UNIQUE value has a DUPLICATE, promote the DUPLICATE to be the new UNIQUE if (changeIndex < barcode.size() && barcode.get(changeIndex) == DUPLICATE) { // case: AABB -> AAB barcode.set(changeIndex, UNIQUE, 1); deleted = UNIQUE_WITH_DUPLICATE; } } removedValues.addLast(deleted); } } TryJoinResult tryJoinResult = new TryJoinResult(); // second pass, handle toDoList, update groupLists, and fire events listChanges.reset(); while(listChanges.next()) { final int changeIndex = listChanges.getIndex(); final int changeType = listChanges.getType(); E newValue = listChanges.getNewValue(); E oldValue = listChanges.getOldValue(); // inserts can result in UPDATE or INSERT events if(changeType == ListEvent.INSERT) { // if no group already exists to join, create a new group tryJoinExistingGroup(changeIndex, toDoList, tryJoinResult); if(tryJoinResult.group == NO_GROUP) { client.groupChanged(changeIndex, tryJoinResult.groupIndex, ListEvent.INSERT, true, changeType, ListEvent.unknownValue(), tryJoinResult.newFirstInGroup); } else { client.groupChanged(changeIndex, tryJoinResult.groupIndex, ListEvent.UPDATE, true, changeType, tryJoinResult.oldFirstInGroup, tryJoinResult.newFirstInGroup); } // updates can result in INSERT, UPDATE and DELETE events } else if(changeType == ListEvent.UPDATE) { // get the location of the group before the update occurred int oldGroup = 0; if(toDoList.get(changeIndex) == TODO) { if (changeIndex + 1 < barcode.size()) { oldGroup = RIGHT_GROUP; } else { // case: AACC -> AAC (list change __UX) // an update occured on the UNIQUE element of the last group, // but the following duplicates of this group were deleted. // as it's the last element remaining, treat it as UNIQUE. // in the second iteration this will lead to a group update event oldGroup = NO_GROUP; } } else if(barcode.get(changeIndex) == DUPLICATE) oldGroup = LEFT_GROUP; else if(barcode.get(changeIndex) == UNIQUE) oldGroup = NO_GROUP; // get the new group location tryJoinExistingGroup(changeIndex, toDoList, tryJoinResult); // the index of the GroupList being updated (it may or may not exist yet) int groupIndex = tryJoinResult.groupIndex; // we're the first element in a new group if(tryJoinResult.group == NO_GROUP) { if(oldGroup == NO_GROUP) { client.groupChanged(changeIndex, groupIndex, ListEvent.UPDATE, true, changeType, oldValue, tryJoinResult.newFirstInGroup); } else if(oldGroup == LEFT_GROUP) { E firstFromPreviousGroup = sortedList.get(barcode.getIndex(groupIndex - 1, UNIQUE)); client.groupChanged(changeIndex, groupIndex - 1, ListEvent.UPDATE, false, changeType, firstFromPreviousGroup, firstFromPreviousGroup); client.groupChanged(changeIndex, groupIndex, ListEvent.INSERT, true, changeType, ListEvent.unknownValue(), tryJoinResult.newFirstInGroup); } else if(oldGroup == RIGHT_GROUP) { E firstFromNextGroup = sortedList.get(barcode.getIndex(groupIndex + 1, UNIQUE)); client.groupChanged(changeIndex, groupIndex, ListEvent.INSERT, true, changeType, ListEvent.unknownValue(), tryJoinResult.newFirstInGroup); client.groupChanged(changeIndex, groupIndex + 1, ListEvent.UPDATE, false, changeType, oldValue, firstFromNextGroup); } // we are joining an existing group to our left } else if(tryJoinResult.group == LEFT_GROUP) { if(oldGroup == NO_GROUP) { client.groupChanged(changeIndex, groupIndex, ListEvent.UPDATE, true, changeType, tryJoinResult.oldFirstInGroup, tryJoinResult.newFirstInGroup); client.groupChanged(changeIndex, groupIndex + 1, ListEvent.DELETE, false, changeType, oldValue, ListEvent.unknownValue()); } else if(oldGroup == LEFT_GROUP) { client.groupChanged(changeIndex, groupIndex, ListEvent.UPDATE, true, changeType, tryJoinResult.oldFirstInGroup, tryJoinResult.newFirstInGroup); } else if(oldGroup == RIGHT_GROUP) { client.groupChanged(changeIndex, groupIndex, ListEvent.UPDATE, true, changeType, tryJoinResult.oldFirstInGroup, tryJoinResult.newFirstInGroup); if(groupIndex + 1 < barcode.blackSize()) { E firstFromNextGroup = sortedList.get(barcode.getIndex(groupIndex + 1, UNIQUE)); client.groupChanged(changeIndex, groupIndex + 1, ListEvent.UPDATE, false, changeType, oldValue, firstFromNextGroup); } } // we are joining an existing group to our right } else if(tryJoinResult.group == RIGHT_GROUP) { if (oldGroup == NO_GROUP) { client.groupChanged(changeIndex, groupIndex, ListEvent.DELETE, false, changeType, oldValue, ListEvent.unknownValue()); client.groupChanged(changeIndex, groupIndex, ListEvent.UPDATE, true, changeType, tryJoinResult.oldFirstInGroup, tryJoinResult.newFirstInGroup); } else if(oldGroup == LEFT_GROUP) { if(groupIndex - 1 >= 0) { E firstFromPreviousGroup = sortedList.get(barcode.getIndex(groupIndex - 1, UNIQUE)); client.groupChanged(changeIndex, groupIndex - 1, ListEvent.UPDATE, false, changeType, firstFromPreviousGroup, firstFromPreviousGroup); } client.groupChanged(changeIndex, groupIndex, ListEvent.UPDATE, true, changeType, tryJoinResult.oldFirstInGroup, tryJoinResult.newFirstInGroup); } else if(oldGroup == RIGHT_GROUP) { client.groupChanged(changeIndex, groupIndex, ListEvent.UPDATE, true, changeType, tryJoinResult.oldFirstInGroup, tryJoinResult.newFirstInGroup); } } // deletes can result in UPDATE or DELETE events } else if(changeType == ListEvent.DELETE) { // figure out if we deleted a UNIQUE or DUPLICATE or UNIQUE_WITH_DUPLICATE final Object deleted = removedValues.removeFirst(); // get the index of the element removed from the source list final int sourceDeletedIndex = deleted == DUPLICATE ? changeIndex - 1 : changeIndex; // determine the index of the GroupList the removal impacts final int groupDeletedIndex = sourceDeletedIndex < barcode.size() ? barcode.getBlackIndex(sourceDeletedIndex, true) : barcode.blackSize(); // fire the change event if(deleted == UNIQUE) { if (changeIndex == lastFakedUniqueChangeIndex) { // case: AACC -> AAC (list change __UX) // in the last group we have deleted a duplicate element that was marked as UNIQUE // because of an update event of the preceding UNIQUE element in the first iteration. // Duplicate deletion is a group update, but it was already triggered by the UNIQUE element update, // so nothing to do here } else { // if we removed a UNIQUE element then it was the last one and we must remove the group client.groupChanged(changeIndex, groupDeletedIndex, ListEvent.DELETE, true, changeType, oldValue, ListEvent.unknownValue()); } } else { E oldValueInGroup; E newValueInGroup; // there's only a new value if the group still exists if (groupDeletedIndex < barcode.blackSize()) { int firstInGroupIndex = barcode.getIndex(groupDeletedIndex, UNIQUE); newValueInGroup = sortedList.get(firstInGroupIndex); } else { newValueInGroup = ListEvent.unknownValue(); } if (deleted == UNIQUE_WITH_DUPLICATE) { oldValueInGroup = oldValue; } else { // assume the old value and the new value are the same. If they're not, // we'll already have the correct old value and this will be thrown away oldValueInGroup = newValueInGroup; } client.groupChanged(changeIndex, groupDeletedIndex, ListEvent.UPDATE, true, changeType, oldValueInGroup, newValueInGroup); } } } } /** * Tests if the specified values should be grouped together. * * @param sourceIndex0 the first index of the source list to test * @param sourceIndex1 the second index of the source list to test * * @return true iff the values at the specified index are equal according * to the Comparator which defines the grouping logic; false if * either index is out of range or the values shall not be grouped */ private boolean groupTogether(int sourceIndex0, int sourceIndex1) { if(sourceIndex0 < 0 || sourceIndex0 >= sortedList.size()) return false; if(sourceIndex1 < 0 || sourceIndex1 >= sortedList.size()) return false; return comparator.compare(sortedList.get(sourceIndex0), sortedList.get(sourceIndex1)) == 0; } /** * Handles whether this change index has a neighbour that existed prior * to a current change and that the values are equal. This adjusts the * duplicates list a found pair of such changes if they exist. * * @return NO_GROUP if no neighbour was found. In this case the value at the specified * index will be marked as unique, but not temporarily so. Returns LEFT_GROUP if * a neighbour was found on the left, and RIGHT_GROUP if a neighbour was found on the * right. In non-zero cases the duplicates list is updated. */ private TryJoinResult tryJoinExistingGroup(int changeIndex, Barcode toDoList, TryJoinResult result) { // test if values at changeIndex and its predecessor should be grouped int predecessorIndex = changeIndex - 1; if (groupTogether(predecessorIndex, changeIndex)) { barcode.set(changeIndex, DUPLICATE, 1); int groupIndex = barcode.getColourIndex(changeIndex, true, UNIQUE); int indexOfFirstInGroup = barcode.getIndex(groupIndex, UNIQUE); E firstElementInGroup = sortedList.get(indexOfFirstInGroup); return result.set(LEFT_GROUP, groupIndex, firstElementInGroup, firstElementInGroup); } // search for an OLD successor that should be in the same group as changeIndex int successorIndex = changeIndex + 1; while (true) { // we have found a successor that belongs in the same group if (groupTogether(changeIndex, successorIndex)) { // if the successor is OLD, have changeIndex join the existing group if (toDoList.get(successorIndex) == DONE) { barcode.set(changeIndex, UNIQUE, 1); barcode.set(successorIndex, DUPLICATE, 1); int groupIndex = barcode.getColourIndex(changeIndex, UNIQUE); E oldFirstElementInGroup = sortedList.get(successorIndex); E newFirstElementInGroup = sortedList.get(changeIndex); return result.set(RIGHT_GROUP, groupIndex, oldFirstElementInGroup, newFirstElementInGroup); // this successor is NEW, not OLD, so skip it } else { successorIndex++; } // we have no more successors that belong in the same group } else { barcode.set(changeIndex, UNIQUE, 1); int groupIndex = barcode.getColourIndex(changeIndex, UNIQUE); E onlyElementInGroup = sortedList.get(changeIndex); return result.set(NO_GROUP, groupIndex, ListEvent.unknownValue(), onlyElementInGroup); } } } /** * Reusable object to provide 2 return values from * {@link Grouper#tryJoinExistingGroup}. */ private static class TryJoinResult { int group; int groupIndex; E oldFirstInGroup; E newFirstInGroup; public TryJoinResult set(int group, int groupIndex, E oldFirstElementInGroup, E newFirstElementInGroup) { this.group = group; this.groupIndex = groupIndex; this.oldFirstInGroup = oldFirstElementInGroup; this.newFirstInGroup = newFirstElementInGroup; return this; } } /** * The grouper client is responsible for turning grouping events into * {@link ListEvent}s if desired. The client receives callbacks as groups * are created, modified and destroyed. */ public interface Client { /** * Handle the structure of a group changing. * * @param index the location in the source {@link SortedList} that * was inserted, updated or deleted. * @param groupIndex the corresponding group that the {@link SortedList} * element now belongs to, or belonged to in the case of * {@link ListEvent#DELETE} events. * @param groupChangeType one of {@link ListEvent#INSERT}, * {@link ListEvent#UPDATE} or {@link ListEvent#DELETE} signalling * what happened to the group in response to this element change. * This is potentially different from what happened to the original * list element. * @param primary whether this is a first event for this change, * impacting the current group for this element. Sometimes multiple * groups will be effected, in which case only a single callback will * be the primary callback. * @param elementChangeType the change type that caused this Sometimes * an {@link ListEvent#UPDATE} event will cause a group to become * inserted or deleted, in which case the elementChangeType * represents the original event type. */ public void groupChanged(int index, int groupIndex, int groupChangeType, boolean primary, int elementChangeType, E oldValue, E newValue); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/TypeSafetyListener.java0000644000175000017500000000703612106516372031553 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import java.util.Set; /** * This {@link ListEventListener} is created with a Set of Classes which * represent the given element types that are allowed to be stored in the * {@link EventList} to which it listens. As elements are inserted and updated * within the {@link EventList} their types are checked against the set of * permitted types. If an element with an illegal type is added to the * {@link EventList} an {@link IllegalArgumentException} is thrown explaining * precisely where and what was the offending type. * *

      Note: if the EventList is allowed to contain null values the * supplied Set of types must contain null as one of the supported * types. */ public class TypeSafetyListener implements ListEventListener { /** The element types supported by the EventList listened to */ private final Class[] types; /** * Create a {@link TypeSafetyListener} that listens for changes on the * specified source {@link EventList} and verifies the added elements are * one of the allowed types. */ public TypeSafetyListener(EventList source, Set types) { // reserve a private copy of the supported Classes this.types = types.toArray(new Class[types.size()]); // begin listening to the source list source.addListEventListener(this); } /** {@inheritDoc} */ public void listChanged(ListEvent listChanges) { final EventList source = listChanges.getSourceList(); while (listChanges.next()) { final int type = listChanges.getType(); // skip deletes as the type was validated on insertion if (type == ListEvent.DELETE) continue; // fetch the element in question final int index = listChanges.getIndex(); final E e = source.get(index); if (type == ListEvent.INSERT && !checkType(e)) { final Class badType = e == null ? null : e.getClass(); throw new IllegalArgumentException("Element with illegal type " + badType + " inserted at index " + index + ": " + e); } else if (type == ListEvent.UPDATE && !checkType(e)) { final Class badType = e == null ? null : e.getClass(); throw new IllegalArgumentException("Element with illegal type " + badType + " updated at index " + index + ": " + e); } } } /** * Returns true if e is assignable to one of the * types accepted by the {@link EventList}; false otherwise. * * @param e the object to check for type safety * @return true if e is assignable to one of the * types accepted by the {@link EventList}; false otherwise */ private boolean checkType(E e) { for (int i = 0; i < types.length; i++) { // avoid a NullPointerException from isAssignableFrom(null) if (e == null && types[i] != null) continue; if (types[i] == null ? e == null : types[i].isAssignableFrom(e.getClass())) return true; } return false; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/reflect/0000755000175000017500000000000012106516362026522 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/reflect/MoreTypes.java0000644000175000017500000003150012106516362031313 0ustar gregoagregoa/** * Copyright (C) 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ca.odell.glazedlists.impl.reflect; import java.io.Serializable; import java.lang.reflect.*; import java.util.*; /** * Static methods for working with types that we aren't publishing in the * public {@code Types} API. * *

      This class contains modifications by James Lemieux to include it in the * Glazed Lists project. The original, unmodified version of this class can be * found here. * * @author jessewilson@google.com (Jesse Wilson) * @author James Lemieux */ class MoreTypes { private MoreTypes() { } private static final Map, TypeLiteral> PRIMITIVE_TO_WRAPPER; static { final Map, TypeLiteral> primitiveToWrapper = new HashMap, TypeLiteral>(); primitiveToWrapper.put(TypeLiteral.get(boolean.class), TypeLiteral.get(Boolean.class)); primitiveToWrapper.put(TypeLiteral.get(byte.class), TypeLiteral.get(Byte.class)); primitiveToWrapper.put(TypeLiteral.get(short.class), TypeLiteral.get(Short.class)); primitiveToWrapper.put(TypeLiteral.get(int.class), TypeLiteral.get(Integer.class)); primitiveToWrapper.put(TypeLiteral.get(long.class), TypeLiteral.get(Long.class)); primitiveToWrapper.put(TypeLiteral.get(float.class), TypeLiteral.get(Float.class)); primitiveToWrapper.put(TypeLiteral.get(double.class), TypeLiteral.get(Double.class)); primitiveToWrapper.put(TypeLiteral.get(char.class), TypeLiteral.get(Character.class)); primitiveToWrapper.put(TypeLiteral.get(void.class), TypeLiteral.get(Void.class)); PRIMITIVE_TO_WRAPPER = Collections.unmodifiableMap(primitiveToWrapper); } /** * Returns an equivalent (but not necessarily equal) type literal that is * free of primitive types. Type literals of primitives will return the * corresponding wrapper types. */ public static TypeLiteral wrapPrimitives(TypeLiteral typeLiteral) { @SuppressWarnings("unchecked") TypeLiteral wrappedPrimitives = (TypeLiteral) PRIMITIVE_TO_WRAPPER.get(typeLiteral); return wrappedPrimitives != null ? wrappedPrimitives : typeLiteral; } /** * Returns a type that is functionally equal but not necessarily equal * according to {@link Object#equals(Object) Object.equals()}. The returned * type is {@link Serializable}. */ public static Type canonicalize(Type type) { if (type instanceof ParameterizedTypeImpl) { return type; } else if (type instanceof ParameterizedType) { ParameterizedType p = (ParameterizedType) type; return new ParameterizedTypeImpl(p.getOwnerType(), p.getRawType(), p.getActualTypeArguments()); } else { // type is either serializable as-is or unsupported return type; } } public static Class getRawType(Type type) { if (type instanceof Class) { // type is a normal class. return (Class) type; } else if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; // I'm not exactly sure why getRawType() returns Type instead of Class. // Neal isn't either but suspects some pathological case related // to nested classes exists. Type rawType = parameterizedType.getRawType(); if (!(rawType instanceof Class)) { throw unexpectedType(rawType, Class.class); } return (Class) rawType; } else if (type instanceof GenericArrayType) { // TODO: Is this sufficient? return Object[].class; } else { // type is a parameterized type. throw unexpectedType(type, ParameterizedType.class); } } private static AssertionError unexpectedType(Type type, Class expected) { return new AssertionError( "Unexpected type. Expected: " + expected.getName() + ", got: " + type.getClass().getName() + ", for type literal: " + type.toString() + "."); } /** * Returns true if {@code a} and {@code b} are equal. */ public static boolean equals(Type a, Type b) { if (a == b) { // also handles (a == null && b == null) return true; } else if (a instanceof Class) { // Class already specifies equals(). return a.equals(b); } else if (a instanceof ParameterizedType) { if (!(b instanceof ParameterizedType)) { return false; } ParameterizedType pa = (ParameterizedType) a; ParameterizedType pb = (ParameterizedType) b; return equal(pa.getOwnerType(), pb.getOwnerType()) && pa.getRawType().equals(pb.getRawType()) && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments()); } else if (a instanceof GenericArrayType) { if (!(b instanceof GenericArrayType)) { return false; } GenericArrayType ga = (GenericArrayType) a; GenericArrayType gb = (GenericArrayType) b; return equals(ga.getGenericComponentType(), gb.getGenericComponentType()); } else { // This isn't a type we support. Could be a generic array type, wildcard // type, etc. return false; } } public static boolean equal(Object o1, Object o2) { return (o1 == null) ? (o2 == null) : o1.equals(o2); } /** * Returns the hashCode of {@code type}. */ public static int hashCode(Type type) { if (type instanceof Class) { // Class specifies hashCode(). return type.hashCode(); } else if (type instanceof ParameterizedType) { ParameterizedType p = (ParameterizedType) type; return Arrays.hashCode(p.getActualTypeArguments()) ^ p.getRawType().hashCode() ^ hashCodeOrZero(p.getOwnerType()); } else if (type instanceof GenericArrayType) { return hashCode(((GenericArrayType) type).getGenericComponentType()); } else { // This isn't a type we support. Could be a generic array type, wildcard type, etc. return hashCodeOrZero(type); } } private static int hashCodeOrZero(Object o) { return o != null ? o.hashCode() : 0; } public static String toString(Type type) { if (type instanceof Class) { return ((Class) type).getName(); } else if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; Type[] arguments = parameterizedType.getActualTypeArguments(); Type ownerType = parameterizedType.getOwnerType(); StringBuilder stringBuilder = new StringBuilder(); if (ownerType != null) { stringBuilder.append(toString(ownerType)).append("."); } stringBuilder.append(toString(parameterizedType.getRawType())) .append("<") .append(toString(arguments[0])); for (int i = 1; i < arguments.length; i++) { stringBuilder.append(", ").append(toString(arguments[i])); } return stringBuilder.append(">").toString(); } else if (type instanceof GenericArrayType) { return toString(((GenericArrayType) type).getGenericComponentType()) + "[]"; } else { return type.toString(); } } /** * Returns the generic supertype for {@code supertype}. For example, given a class {@code * IntegerSet}, the result for when supertype is {@code Set.class} is {@code Set} and the * result when the supertype is {@code Collection.class} is {@code Collection}. */ public static Type getGenericSupertype(Type type, Class rawType, Class toResolve) { if (toResolve == rawType) { return type; } // we skip searching through interfaces if unknown is an interface if (toResolve.isInterface()) { Class[] interfaces = rawType.getInterfaces(); for (int i = 0, length = interfaces.length; i < length; i++) { if (interfaces[i] == toResolve) { return rawType.getGenericInterfaces()[i]; } else if (toResolve.isAssignableFrom(interfaces[i])) { return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve); } } } // check our supertypes if (!rawType.isInterface()) { while (rawType != Object.class) { Class rawSupertype = rawType.getSuperclass(); if (rawSupertype == toResolve) { return rawType.getGenericSuperclass(); } else if (toResolve.isAssignableFrom(rawSupertype)) { return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve); } rawType = rawSupertype; } } // we can't resolve this further return toResolve; } public static Type resolveTypeVariable(Type type, Class rawType, TypeVariable unknown) { Class declaredByRaw = declaringClassOf(unknown); // we can't reduce this further if (declaredByRaw == null) { return unknown; } Type declaredBy = getGenericSupertype(type, rawType, declaredByRaw); if (declaredBy instanceof ParameterizedType) { int index = Arrays.asList(declaredByRaw.getTypeParameters()).indexOf(unknown); return ((ParameterizedType) declaredBy).getActualTypeArguments()[index]; } return unknown; } /** * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by * a class. */ private static Class declaringClassOf(TypeVariable typeVariable) { GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration(); return genericDeclaration instanceof Class ? (Class) genericDeclaration : null; } public static class ParameterizedTypeImpl implements ParameterizedType, Serializable { private final Type ownerType; private final Type rawType; private final Type[] typeArguments; public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) { this.ownerType = ownerType == null ? null : canonicalize(ownerType); this.rawType = canonicalize(rawType); this.typeArguments = typeArguments.clone(); for (int t = 0; t < this.typeArguments.length; t++) { checkArgument(!(this.typeArguments[t] instanceof Class) || !((Class) this.typeArguments[t]).isPrimitive(), "Parameterized types may not have primitive arguments: %s", this.typeArguments[t]); this.typeArguments[t] = canonicalize(this.typeArguments[t]); } } public Type[] getActualTypeArguments() { return typeArguments.clone(); } public Type getRawType() { return rawType; } public Type getOwnerType() { return ownerType; } @Override public boolean equals(Object other) { return other instanceof ParameterizedType && MoreTypes.equals(this, (ParameterizedType) other); } @Override public int hashCode() { return MoreTypes.hashCode(this); } @Override public String toString() { return MoreTypes.toString(this); } private static final long serialVersionUID = 0; private static void checkArgument(boolean expression, String errorMessageFormat, Object... errorMessageArgs) { if (!expression) throw new IllegalArgumentException(String.format(errorMessageFormat, errorMessageArgs)); } } }././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/reflect/J2SE50ReturnTypeResolver.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/reflect/J2SE50ReturnTypeResolve0000644000175000017500000000155512106516362032725 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.reflect; import java.lang.reflect.Method; /** * An implementation of {@link ReturnTypeResolver} that adapt's Google's * {@link TypeLiteral generic type resolver}. * * @author James Lemieux */ public class J2SE50ReturnTypeResolver implements ReturnTypeResolver { public Class getReturnType(Class clazz, Method method) { return new TypeLiteral(clazz).getReturnType(method).getRawType(); } public Class getFirstParameterType(Class clazz, Method method) { return new TypeLiteral(clazz).getParameterTypes(method).get(0).getRawType(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/reflect/TypeLiteral.java0000644000175000017500000001466112106516362031633 0ustar gregoagregoa/** * Copyright (C) 2006 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ca.odell.glazedlists.impl.reflect; import static ca.odell.glazedlists.impl.Preconditions.checkArgument; import static ca.odell.glazedlists.impl.Preconditions.checkNotNull; import java.lang.reflect.*; import java.util.Arrays; import java.util.List; /** * Represents a generic type {@code T}. Java doesn't yet provide a way to * represent generic types, so this class does. Forces clients to create a * subclass of this class which enables retrieval the type information even at * runtime. * *

      For example, to create a type literal for {@code List}, you can * create an empty anonymous inner class: * *

      * {@code TypeLiteral> list = new TypeLiteral>() {};} * *

      Assumes that type {@code T} implements {@link Object#equals} and * {@link Object#hashCode()} as value (as opposed to identity) comparison. * *

      This class contains modifications by James Lemieux to include it in the * Glazed Lists project. The original, unmodified version of this class can be * found here. * * @author crazybob@google.com (Bob Lee) * @author jessewilson@google.com (Jesse Wilson) * @author James Lemieux */ public class TypeLiteral { final Class rawType; final Type type; final int hashCode; /** * Unsafe. Constructs a type literal manually. */ @SuppressWarnings("unchecked") TypeLiteral(Type type) { this.type = MoreTypes.canonicalize(checkNotNull(type, "type")); this.rawType = (Class) MoreTypes.getRawType(this.type); this.hashCode = MoreTypes.hashCode(this.type); } /** * Returns the raw (non-generic) type for this type. * * @since 2.0 */ public final Class getRawType() { return rawType; } /** * Gets underlying {@code Type} instance. */ public final Type getType() { return type; } @Override public final int hashCode() { return this.hashCode; } @Override public final boolean equals(Object o) { return o instanceof TypeLiteral && MoreTypes.equals(type, ((TypeLiteral) o).type); } @Override public final String toString() { return MoreTypes.toString(type); } /** * Gets type literal for the given {@code Type} instance. */ public static TypeLiteral get(Type type) { return new TypeLiteral(type); } /** * Gets type literal for the given {@code Class} instance. */ public static TypeLiteral get(Class type) { return new TypeLiteral(type); } /** Returns an immutable list of the resolved types. */ private List> resolveAll(Type[] types) { TypeLiteral[] result = new TypeLiteral[types.length]; for (int t = 0; t < types.length; t++) { result[t] = resolve(types[t]); } return Arrays.asList(result); } /** * Resolves known type parameters in {@code toResolve} and returns the result. */ TypeLiteral resolve(Type toResolve) { return TypeLiteral.get(resolveType(toResolve)); } Type resolveType(Type toResolve) { // this implementation is made a little more complicated in an attempt to avoid object-creation while (true) { if (toResolve instanceof TypeVariable) { TypeVariable original = (TypeVariable) toResolve; toResolve = MoreTypes.resolveTypeVariable(type, rawType, original); if (toResolve == original) { return toResolve; } } else if (toResolve instanceof ParameterizedType) { ParameterizedType original = (ParameterizedType) toResolve; Type ownerType = original.getOwnerType(); Type newOwnerType = resolveType(ownerType); boolean changed = newOwnerType != ownerType; Type[] args = original.getActualTypeArguments(); for (int t = 0, length = args.length; t < length; t++) { Type resolvedTypeArgument = resolveType(args[t]); if (resolvedTypeArgument != args[t]) { if (!changed) { args = args.clone(); changed = true; } args[t] = resolvedTypeArgument; } } return changed ? new MoreTypes.ParameterizedTypeImpl(newOwnerType, original.getRawType(), args) : original; } else { return toResolve; } } } /** * Returns the resolved generic parameter types of {@code methodOrConstructor}. * * @param methodOrConstructor a method or constructor defined by this or any supertype. * @since 2.0 */ public List> getParameterTypes(Member methodOrConstructor) { Type[] genericParameterTypes; if (methodOrConstructor instanceof Method) { Method method = (Method) methodOrConstructor; checkArgument(method.getDeclaringClass().isAssignableFrom(rawType), "%s is not defined by a supertype of %s", method, type); genericParameterTypes = method.getGenericParameterTypes(); } else if (methodOrConstructor instanceof Constructor) { Constructor constructor = (Constructor) methodOrConstructor; checkArgument(constructor.getDeclaringClass().isAssignableFrom(rawType), "%s does not construct a supertype of %s", constructor, type); genericParameterTypes = constructor.getGenericParameterTypes(); } else { throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor); } return resolveAll(genericParameterTypes); } /** * Returns the resolved generic return type of {@code method}. * * @param method a method defined by this or any supertype. * @since 2.0 */ public TypeLiteral getReturnType(Method method) { checkArgument(method.getDeclaringClass().isAssignableFrom(rawType), "%s is not defined by a supertype of %s", method, type); return resolve(method.getGenericReturnType()); } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/reflect/ReturnTypeResolver.java0000644000175000017500000000527412106516362033240 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.reflect; import java.lang.reflect.Method; /** * With the introduction of generics in JDK 1.5 it became more difficult to * know the precise return type of a method declared with a generic return * type. For example, {@link java.util.List} declares * {@link java.util.List#get get()} to return the generic type of "E". The * challenge begins when a subtype of List is created for which E is known, eg: * *
       * public class MyStringList extends List
       * 
      * * The Method object for MyStringList.get() reports a return type of * Object.class. But, it is possible to do better than this, and in fact * String.class can be found to be the true return type of the the method. * *

      This interface abstracts away different strategies for locating the * return type of a given {@link Method} depending on whether the user is * running Glazed Lists under a 1.4 or 1.5+ JRE. * * @author James Lemieux */ public interface ReturnTypeResolver { /** * Locates and returns the most precise return type that can be found for * the given method. Note, it may be more precise than the * value returned by * {@link java.lang.reflect.Method#getReturnType() method.getReturnType()}. * * @param clazz the specific class for which the method's return type is * requested (the specific class type matters if the return type is * generic) * @param method for which a precise return type is requested * @return the most precise Class type that is known to be returned by the * given method */ public Class getReturnType(Class clazz, Method method); /** * Locates and returns the most precise type of the first parameter for the * given method. Note that it may be more precise than the * value returned by * {@link java.lang.reflect.Method#getParameterTypes()}[0]. * * @param clazz the specific class for which the method's parameter type is * requested (the specific class type matters if the return type is * generic) * @param method for which a precise parameter type is requested * @return the most precise Class type that is known to be the parameter for * the given method * @throws IndexOutOfBoundsException if the given method has no parameters. */ public Class getFirstParameterType(Class clazz, Method method); }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/SerializedReadWriteLock.java0000644000175000017500000000322112106516372032453 0ustar gregoagregoa/* Glazed Lists (c) 2003-2007 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; import ca.odell.glazedlists.util.concurrent.Lock; import ca.odell.glazedlists.util.concurrent.LockFactory; import ca.odell.glazedlists.util.concurrent.ReadWriteLock; import java.io.ObjectStreamException; import java.io.Serializable; /** * A ReadWriteLock dummy implementation that's only used for Java object * serialization. The regular lock implementations for Java 1.4 and 1.5 are * representated by this class on the serialization stream. Upon * deserialization on the target JVM, an appropriate lock implementation is * reconstructed according to the capabilities of the target platform. * * @author Holger Brands */ public final class SerializedReadWriteLock implements ReadWriteLock, Serializable { /** For versioning as a {@link Serializable} */ private static final long serialVersionUID = -8627867501684280198L; /** {@inheritDoc} */ public Lock readLock() { throw new UnsupportedOperationException("SerializedReadWriteLock is only used for serialization"); } /** {@inheritDoc} */ public Lock writeLock() { throw new UnsupportedOperationException("SerializedReadWriteLock is only used for serialization"); } /** Recreate an appropriate lock implementation when deserialized on the target JVM. */ private Object readResolve() throws ObjectStreamException { return LockFactory.DEFAULT.createReadWriteLock(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/SimpleIterator.java0000644000175000017500000000352312106516372030710 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; // for being a list iterator import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; /** * The SimpleTreeIterator is an iterator that allows the user to iterate * on a list. * * @author Kevin Maltby */ public class SimpleIterator implements Iterator { /** the list being iterated */ private final List source; /** the index of the next element to view */ private int nextIndex = 0; /** * Create a new iterator that iterates over the specified source list. * * @param source the list to iterate */ public SimpleIterator(List source) { this.source = source; } /** * Returns true if this iterator has more elements when traversing the * list in the forward direction. */ public boolean hasNext() { return nextIndex < source.size(); } /** * Returns the next element in the list. */ public E next() { // there are no more values if (nextIndex == source.size()) throw new NoSuchElementException("Cannot retrieve element " + nextIndex + " on a list of size " + source.size()); // a next value exists return source.get(nextIndex++); } /** * Removes from the list the last element that was returned by next * or previous. */ public void remove() { if (nextIndex == 0) throw new IllegalStateException("Cannot remove() without a prior call to next() or previous()"); source.remove(--nextIndex); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/ThreadSafeList.java0000644000175000017500000002014412106516372030605 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.TransformedList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.util.concurrent.ReadWriteLock; import java.util.Collection; /** * An {@link EventList} that obtains a {@link ReadWriteLock} 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 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 #size() size()} * and {@link #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 #iterator() iterator()}, {@link #subList(int,int) subList()}, etc. are * not thread safe. * * @see ca.odell.glazedlists.util.concurrent * * @author Kevin Maltby */ public final class ThreadSafeList extends TransformedList { /** * Creates a {@link ThreadSafeList} that provides thread safe access to all * methods in the source {@link EventList}. */ public ThreadSafeList(EventList source) { super(source); source.addListEventListener(this); } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { // just pass on the changes updates.forwardEvent(listChanges); } /** {@inheritDoc} */ @Override public E get(int index) { getReadWriteLock().readLock().lock(); try { return source.get(index); } finally { getReadWriteLock().readLock().unlock(); } } /** {@inheritDoc} */ @Override public int size() { getReadWriteLock().readLock().lock(); try { return source.size(); } finally { getReadWriteLock().readLock().unlock(); } } /** {@inheritDoc} */ @Override protected boolean isWritable() { return true; } /** {@inheritDoc} */ @Override public boolean contains(Object object) { getReadWriteLock().readLock().lock(); try { return source.contains(object); } finally { getReadWriteLock().readLock().unlock(); } } /** {@inheritDoc} */ @Override public boolean containsAll(Collection collection) { getReadWriteLock().readLock().lock(); try { return source.containsAll(collection); } finally { getReadWriteLock().readLock().unlock(); } } /** {@inheritDoc} */ @Override public boolean equals(Object object) { getReadWriteLock().readLock().lock(); try { return source.equals(object); } finally { getReadWriteLock().readLock().unlock(); } } /** {@inheritDoc} */ @Override public int hashCode() { getReadWriteLock().readLock().lock(); try { return source.hashCode(); } finally { getReadWriteLock().readLock().unlock(); } } /** {@inheritDoc} */ @Override public int indexOf(Object object) { getReadWriteLock().readLock().lock(); try { return source.indexOf(object); } finally { getReadWriteLock().readLock().unlock(); } } /** {@inheritDoc} */ @Override public int lastIndexOf(Object object) { getReadWriteLock().readLock().lock(); try { return source.lastIndexOf(object); } finally { getReadWriteLock().readLock().unlock(); } } /** {@inheritDoc} */ @Override public boolean isEmpty() { getReadWriteLock().readLock().lock(); try { return source.isEmpty(); } finally { getReadWriteLock().readLock().unlock(); } } /** {@inheritDoc} */ @Override public Object[] toArray() { getReadWriteLock().readLock().lock(); try { return source.toArray(); } finally { getReadWriteLock().readLock().unlock(); } } /** {@inheritDoc} */ @Override public T[] toArray(T[] array) { getReadWriteLock().readLock().lock(); try { return source.toArray(array); } finally { getReadWriteLock().readLock().unlock(); } } /** {@inheritDoc} */ @Override public boolean add(E value) { getReadWriteLock().writeLock().lock(); try { return source.add(value); } finally { getReadWriteLock().writeLock().unlock(); } } /** {@inheritDoc} */ @Override public boolean remove(Object toRemove) { getReadWriteLock().writeLock().lock(); try { return source.remove(toRemove); } finally { getReadWriteLock().writeLock().unlock(); } } /** {@inheritDoc} */ @Override public boolean addAll(Collection values) { getReadWriteLock().writeLock().lock(); try { return source.addAll(values); } finally { getReadWriteLock().writeLock().unlock(); } } /** {@inheritDoc} */ @Override public boolean addAll(int index, Collection values) { getReadWriteLock().writeLock().lock(); try { return source.addAll(index, values); } finally { getReadWriteLock().writeLock().unlock(); } } /** {@inheritDoc} */ @Override public boolean removeAll(Collection values) { getReadWriteLock().writeLock().lock(); try { return source.removeAll(values); } finally { getReadWriteLock().writeLock().unlock(); } } /** {@inheritDoc} */ @Override public boolean retainAll(Collection values) { getReadWriteLock().writeLock().lock(); try { return source.retainAll(values); } finally { getReadWriteLock().writeLock().unlock(); } } /** {@inheritDoc} */ @Override public void clear() { getReadWriteLock().writeLock().lock(); try { source.clear(); } finally { getReadWriteLock().writeLock().unlock(); } } /** {@inheritDoc} */ @Override public E set(int index, E value) { getReadWriteLock().writeLock().lock(); try { return source.set(index, value); } finally { getReadWriteLock().writeLock().unlock(); } } /** {@inheritDoc} */ @Override public void add(int index, E value) { getReadWriteLock().writeLock().lock(); try { source.add(index, value); } finally { getReadWriteLock().writeLock().unlock(); } } /** {@inheritDoc} */ @Override public E remove(int index) { getReadWriteLock().writeLock().lock(); try { return source.remove(index); } finally { getReadWriteLock().writeLock().unlock(); } } /** {@inheritDoc} */ @Override public String toString() { getReadWriteLock().readLock().lock(); try { return source.toString(); } finally { getReadWriteLock().readLock().unlock(); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/java15/0000755000175000017500000000000012106516364026167 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/java15/J2SE50LockFactory.java0000644000175000017500000000543112106516364032046 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.java15; import ca.odell.glazedlists.impl.SerializedReadWriteLock; import ca.odell.glazedlists.util.concurrent.Lock; import ca.odell.glazedlists.util.concurrent.LockFactory; import ca.odell.glazedlists.util.concurrent.ReadWriteLock; import java.io.ObjectStreamException; import java.io.Serializable; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * An implementation of {@link LockFactory} that has been derived from * {@link java.util.concurrent.locks.ReadWriteLock JDK 1.5 Locks}. * * @author James Lemieux */ public class J2SE50LockFactory implements LockFactory { public ReadWriteLock createReadWriteLock() { return new J2SE50ReadWriteLock(); } public Lock createLock() { return new LockAdapter(new java.util.concurrent.locks.ReentrantLock()); } } /** * A ReadWriteLock implementation that is compatable with J2SE 5.0 and better. This * implementation is a facade over {@link java.util.concurrent.locks.ReadWriteLock}. * * @author James Lemieux */ final class J2SE50ReadWriteLock implements ReadWriteLock, Serializable { /** For versioning as a {@link Serializable} */ private static final long serialVersionUID = 188277016505951193L; private transient final Lock readLock; private transient final Lock writeLock; J2SE50ReadWriteLock() { final java.util.concurrent.locks.ReadWriteLock delegate = new ReentrantReadWriteLock(); this.readLock = new LockAdapter(delegate.readLock()); this.writeLock = new LockAdapter(delegate.writeLock()); } /** Use a {@link SerializedReadWriteLock} as a placeholder in the serialization stream. */ private Object writeReplace() throws ObjectStreamException { return new SerializedReadWriteLock(); } /** * Return the lock used for reading. */ public Lock readLock() { return this.readLock; } /** * Return the lock used for writing. */ public Lock writeLock() { return this.writeLock; } } /** * This adapts a J2SE 5.0 compatible Lock to the Glazed Lists Lock interface. * * @author James Lemieux */ final class LockAdapter implements Lock { private final java.util.concurrent.locks.Lock delegateLock; LockAdapter(java.util.concurrent.locks.Lock delegateLock) { this.delegateLock = delegateLock; } public void lock() { delegateLock.lock(); } public boolean tryLock() { return delegateLock.tryLock(); } public void unlock() { delegateLock.unlock(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/sort/0000755000175000017500000000000012106516364026067 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/sort/TableColumnComparator.java0000644000175000017500000000655412106516364033201 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.sort; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.gui.TableFormat; import java.util.Comparator; /** * A comparator that sorts a table by the column that was clicked. */ public class TableColumnComparator implements Comparator { /** the table format knows to map objects to their fields */ private TableFormat tableFormat; /** the field of interest */ private int column; /** comparison is delegated to a ComparableComparator */ private Comparator comparator = null; /** * Creates a new TableColumnComparator that sorts objects by the specified * column using the specified table format. */ public TableColumnComparator(TableFormat tableFormat, int column) { this(tableFormat, column, GlazedLists.comparableComparator()); } /** * Creates a new TableColumnComparator that sorts objects by the specified * column using the specified table format and the specified comparator. */ public TableColumnComparator(TableFormat tableFormat, int column, Comparator comparator) { this.column = column; this.tableFormat = tableFormat; this.comparator = comparator; } /** * Compares the two objects, returning a result based on how they compare. */ public int compare(E alpha, E beta) { final Object alphaField = tableFormat.getColumnValue(alpha, column); final Object betaField = tableFormat.getColumnValue(beta, column); try { return comparator.compare(alphaField, betaField); // throw a 'nicer' exception if the class does not implement Comparable } catch (ClassCastException e) { final IllegalStateException illegalStateException; if(comparator == GlazedLists.comparableComparator()) { illegalStateException = new IllegalStateException("TableComparatorChooser can not sort objects \"" + alphaField + "\", \"" + betaField + "\" that do not implement Comparable."); } else { illegalStateException = new IllegalStateException("TableComparatorChooser can not sort objects \"" + alphaField + "\", \"" + betaField + "\" using the provided Comparator."); } illegalStateException.initCause(e); throw illegalStateException; } } /** * Test if this TableColumnComparator is equal to the other specified * TableColumnComparator. */ @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final TableColumnComparator that = (TableColumnComparator) o; if (column != that.column) return false; if (!comparator.equals(that.comparator)) return false; if (!tableFormat.equals(that.tableFormat)) return false; return true; } @Override public int hashCode() { int result; result = tableFormat.hashCode(); result = 29 * result + column; result = 29 * result + comparator.hashCode(); return result; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/sort/ComparatorChain.java0000644000175000017500000000406112106516364032005 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.sort; // for specifying a sorting algorithm import java.util.Arrays; import java.util.Comparator; import java.util.List; /** * A comparator chain compares objects using a list of Comparators. The * first comparison where the objects differ is returned. * * @author Jesse Wilson */ public final class ComparatorChain implements Comparator { /** the comparators to execute in sequence */ private final Comparator[] comparators; /** * Creates a comparator chain that evaluates the specified comparators in * sequence. A defensive copy of the * * @param comparators a list of objects implementing {@link Comparator} */ public ComparatorChain(List> comparators) { this.comparators = comparators.toArray(new Comparator[comparators.size()]); } /** * Compares the two objects with each comparator in sequence. */ public int compare(T alpha, T beta) { for (int i = 0; i < comparators.length; i++) { int compareResult = comparators[i].compare(alpha, beta); if(compareResult != 0) return compareResult; } return 0; } /** * Retrieves the {@link Comparator}s composing this * ComparatorChain. */ public Comparator[] getComparators() { return comparators; } /** {@inheritDoc} */ @Override public boolean equals(Object o) { if(this == o) return true; if(o == null || getClass() != o.getClass()) return false; final ComparatorChain that = (ComparatorChain) o; if(!Arrays.equals(comparators, that.comparators)) return false; return true; } /** {@inheritDoc} */ @Override public int hashCode() { return 0; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/sort/ComparableComparator.java0000644000175000017500000000260012106516364033025 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.sort; // for specifying a sorting algorithm import java.util.Comparator; /** * A trivial comparator that requires that compared objects implement * the comparable interface. * *

      When this finds null objects, it orders them before all * other objects. * * @author Jesse Wilson */ public final class ComparableComparator implements Comparator { /** * Compares object alpha to object beta by casting object one * to Comparable, and calling its compareTo method. */ public int compare(Comparable alpha, Comparable beta) { // compare using Comparable if(alpha != null && beta != null) { return alpha.compareTo(beta); } // compare nulls if(alpha == null) { if(beta == null) return 0; return -1; } else { return 1; } } /** * This is equal to another comparator if it is a ComparableComparable. */ @Override public boolean equals(Object other) { return (other instanceof ComparableComparator); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/sort/ReverseComparator.java0000644000175000017500000000311512106516364032375 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.sort; // for specifying a sorting algorithm import java.util.Comparator; /** * A comparator that reverses the sequence of a source comparator. * * @author Jesse Wilson */ public final class ReverseComparator implements Comparator { /** the normal comparator to flip */ private Comparator source; /** * Create a new reverse comparator that reverses the sequence * of the specified comparator. */ public ReverseComparator(Comparator source) { this.source = source; } /** * Compares the specified objects and flips the result. */ public int compare(T alpha, T beta) { return source.compare(beta, alpha); } /** * Retrieves the source {@link Comparator} for this ReverseComparator */ public Comparator getSourceComparator() { return source; } /** {@inheritDoc} */ @Override public boolean equals(Object o) { if(this == o) return true; if(o == null || getClass() != o.getClass()) return false; final ReverseComparator that = (ReverseComparator) o; if(!source.equals(that.source)) return false; return true; } /** {@inheritDoc} */ @Override public int hashCode() { return source.hashCode(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/sort/BooleanComparator.java0000644000175000017500000000232312106516364032341 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.sort; // for specifying a sorting algorithm import java.util.Comparator; /** * A {@link Comparator} that compares two {@link Boolean} objects such that * the sort order will be: * null, Boolean.FALSE, Boolean.TRUE. * * @author Kevin Maltby */ public final class BooleanComparator implements Comparator { /** * Compares two Boolean objects using the following sort order: * null, Boolean.FALSE, Boolean.TRUE; */ public int compare(Boolean alpha, Boolean beta) { final int alphaOrdinal = alpha == null ? 0 : !alpha.booleanValue() ? 1 : 2; final int betaOrdinal = beta == null ? 0 : !beta.booleanValue() ? 1 : 2; return alphaOrdinal - betaOrdinal; } /** * This is equal to another comparator if it is a BooleanComparator. */ @Override public boolean equals(Object other) { return (other instanceof BooleanComparator); } }././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/sort/BeanPropertyComparator.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/sort/BeanPropertyComparator.jav0000644000175000017500000000471112106516364033236 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.sort; // for specifying a sorting algorithm import ca.odell.glazedlists.impl.beans.BeanProperty; import java.util.Comparator; /** * A {@link Comparator} that uses Reflection to compare two instances * of the specified {@link Class} by a JavaBean property. * * @author Kevin Maltby */ public final class BeanPropertyComparator implements Comparator { /** the comparator to use on the JavaBean property */ private Comparator propertyComparator; /** the accessor for the JavaBean property */ private BeanProperty beanProperty = null; /** * Create a new JavaBean property comparator that compares properties using * the provided {@link Comparator}. This should be accessed from the * {@link ca.odell.glazedlists.GlazedLists GlazedLists} tool factory. */ public BeanPropertyComparator(Class className, String property, Comparator propertyComparator) { beanProperty = new BeanProperty(className, property, true, false); this.propertyComparator = propertyComparator; } /** * Compares the specified objects by the JavaBean property. */ public int compare(T alpha, T beta) { // Inspect alpha Object alphaProperty = null; if(alpha != null) alphaProperty = beanProperty.get(alpha); // Inspect beta Object betaProperty = null; if(beta != null) betaProperty = beanProperty.get(beta); // Compare the property values return propertyComparator.compare(alphaProperty, betaProperty); } /** {@inheritDoc} */ @Override public boolean equals(Object o) { if(this == o) return true; if(o == null || getClass() != o.getClass()) return false; final BeanPropertyComparator that = (BeanPropertyComparator) o; if(!beanProperty.equals(that.beanProperty)) return false; if(!propertyComparator.equals(that.propertyComparator)) return false; return true; } /** {@inheritDoc} */ @Override public int hashCode() { int result; result = propertyComparator.hashCode(); result = 29 * result + beanProperty.hashCode(); return result; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/FunctionListMap.java0000644000175000017500000004447612106516372031040 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.FunctionList; import ca.odell.glazedlists.BasicEventList; import ca.odell.glazedlists.DisposableMap; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import java.util.*; /** * This map implementation sits atop an {@link EventList} and makes it * accessible via the convenient {@link Map} interface. It is constructed with * a {@link FunctionList.Function} which is used to create the keys of the map. * The values of the map are the lists of values from the {@link EventList}. * *

      For example, an {@link EventList} containing * *

       * {Cherry, Orange, Apple, Pineapple, Banana}
       * 
      * * paired with a Function that returns the first letter of the fruit name * produces the map: * *
       * "C" -> "Cherry"
       * "O" -> "Orange"
       * "A" -> "Apple"
       * "P" -> "Pinapple"
       * "B" -> "Banana"
       * 
      * * Note: all values MUST map to unique keys in order to use * this class. If that constraint is violated at any time, an * {@link IllegalStateException} will be thrown to indicate the violation to * the programmer. * * @author James Lemieux */ public class FunctionListMap implements DisposableMap, ListEventListener { /** The keys of this Map (used to remove entries from the {@link #delegate}) */ private List keyList; /** The keyList of this Map made to look like a Set (it is build lazily in {@link #keySet()}) */ private Set keySet; /** The values of this Map in an {@link EventList}. */ private final EventList valueList; /** The set of Map.Entry objects in this Map (it is build lazily in {@link #entrySet()}) */ private Set> entrySet; /** The function which produces keyList for this multimap. */ private final FunctionList.Function keyFunction; /** The delegate Map which is kept in synch with changes. */ private final Map delegate; /** * Construct a map which maps the keys produced by the * keyFunction, to corresponding entries from the * source. * * @param source the raw data which has not yet been grouped * @param keyFunction the function capable of producing the key of this * {@link Map} for each value */ public FunctionListMap(EventList source, FunctionList.Function keyFunction) { if (keyFunction == null) throw new IllegalArgumentException("keyFunction may not be null"); // the source is the list of values this.valueList = source; this.valueList.addListEventListener(this); this.keyFunction = keyFunction; // it is important that the keyList is a BasicEventList since we use its ListIterator, which remains // consistent with changes to its underlying data (any other Iterator would throw a ConcurrentModificationException) this.keyList = new BasicEventList(source.size()); this.delegate = new HashMap(source.size()); // populate the keyList and the delegate Map for (int i = 0, n = source.size(); i < n; i++) elementAdded(i); } /** @inheritDoc */ public void dispose() { valueList.removeListEventListener(this); keySet = null; entrySet = null; keyList.clear(); delegate.clear(); } /** @inheritDoc */ public int size() { return delegate.size(); } /** @inheritDoc */ public boolean isEmpty() { return delegate.isEmpty(); } /** @inheritDoc */ public boolean containsKey(Object key) { return delegate.containsKey(key); } /** @inheritDoc */ public boolean containsValue(Object value) { return delegate.containsValue(value); } /** @inheritDoc */ public V get(Object key) { return delegate.get(key); } /** @inheritDoc */ public V put(K key, V value) { checkKeyValueAgreement(key, value); // if no prior value exists for this key, simply add it if (!containsKey(key)) { valueList.add(value); return null; } // otherwise try to replace the old value in place final V toReplace = get(key); // try to find the old value by identity in the valueList and replace it for (ListIterator i = valueList.listIterator(); i.hasNext();) { if (i.next() == toReplace) { i.set(value); return toReplace; } } // something terrible has happened if a value exists in the delegate Map but not in the valueList throw new IllegalStateException("Found key: " + key + " in delegate map but could not find corresponding value in valueList: " + toReplace); } /** @inheritDoc */ public void putAll(Map m) { // verify the contents of the given Map and ensure all key/value pairs agree with the keyFunction for (Iterator> i = m.entrySet().iterator(); i.hasNext();) { final Entry entry = i.next(); checkKeyValueAgreement(entry.getKey(), entry.getValue()); } // add each of the elements from m into this Map for (Iterator> i = m.entrySet().iterator(); i.hasNext();) { final Entry entry = i.next(); put(entry.getKey(), entry.getValue()); } } /** * This convenience method ensures that the key matches the * key value produced for the value object. If a * mismatch is found, an {@link IllegalArgumentException} is thrown. * * @param key the expected key value of each value object * @param value the value object which should produce the given key when * run through the key function */ private void checkKeyValueAgreement(K key, V value) { final K k = key(value); if (!GlazedListsImpl.equal(key, k)) throw new IllegalArgumentException("The calculated key for the given value (" + k + ") does not match the given key (" + key + ")"); } /** @inheritDoc */ public void clear() { valueList.clear(); } /** @inheritDoc */ public V remove(Object key) { if (!containsKey(key)) return null; final V value = get(key); GlazedListsImpl.identityRemove(valueList, value); return value; } /** @inheritDoc */ public Collection values() { return valueList; } /** @inheritDoc */ public Set keySet() { if (this.keySet == null) this.keySet = new KeySet(); return this.keySet; } /** @inheritDoc */ public Set> entrySet() { if (this.entrySet == null) this.entrySet = new EntrySet(); return this.entrySet; } /** @inheritDoc */ @Override public boolean equals(Object o) { return delegate.equals(o); } /** @inheritDoc */ @Override public int hashCode() { return delegate.hashCode(); } /** * Updates this Map datastructure to reflect changes in the underlying * {@link EventList}. Specifically, new entries are added and existing * entries are updated in this Map by calculating a key using the key * function of this Map. * * The algorithm in this method operates in 2 passes. The reason for this * is that {@link #putInDelegate} contains a sanity check that ensures we * never enter an illegal state for the map (where a single key maps to two * distinct values). But, complex but valid listChanges may * temporarily break that invariant only to rectify the state at a later * index. (e.g. insert a duplicate value at i and then delete the original * value at i+1) * * By performing all remove operations first in pass 1, we preserve the * ability to check the invariant in pass 2 when additions are processed. * Thus, FunctionListMap remains proactive in locating values which break * the invariant. * * @param listChanges an event describing the changes in the FunctionList */ public void listChanged(ListEvent listChanges) { int offset = 0; // pass 1: do all remove work (this includes deletes and the front half of updates) while (listChanges.next()) { switch (listChanges.getType()) { case ListEvent.DELETE: elementRemoved(listChanges.getIndex() + offset); break; case ListEvent.UPDATE: elementRemoved(listChanges.getIndex() + offset); offset--; break; case ListEvent.INSERT: offset--; break; } } listChanges.reset(); // pass 2: do all add work (this includes inserts and the back half of updates) while (listChanges.next()) { switch (listChanges.getType()) { case ListEvent.UPDATE: elementAdded(listChanges.getIndex()); break; case ListEvent.INSERT: elementAdded(listChanges.getIndex()); break; } } } /** * Updates the internal data structures to reflect the addition of a new * element at the given index. */ private void elementAdded(int index) { final V value = valueList.get(index); final K key = key(value); keyList.add(index, key); putInDelegate(key, value); } /** * Updates the internal data structures to reflect the removal of an * element at the given index. */ private void elementRemoved(int index) { final K key = keyList.remove(index); delegate.remove(key); } /** * This method puts an entry into the delegate Map after first verifying * that the delegate Map does not contain an entry for the given * key. */ private void putInDelegate(K key, V value) { if (delegate.containsKey(key)) throw new IllegalStateException("Detected duplicate key->value mapping: attempted to put '" + key + "' -> '" + value + "' in the map, but found '" + key + "' -> '" + delegate.get(key) + "' already existed."); delegate.put(key, value); } /** * Uses the key function to return the key for a given value. * * @param value a single value from the valueList list * @return the key which maps to the given value */ private K key(V value) { return keyFunction.evaluate(value); } /** * This private {@link Set} implementation represents the {@link Map.Entry} * objects within this Map. All mutating methods are implemented to * "write through" to the backing {@link EventList} which ensures that both * the {@link EventList} and this Map always remain in sync. */ private class EntrySet extends AbstractSet> { /** {@inheritDoc} */ @Override public int size() { return keyList.size(); } /** {@inheritDoc} */ @Override public Iterator> iterator() { return new EntrySetIterator(keyList.listIterator()); } /** {@inheritDoc} */ @Override public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; final Entry e = (Entry) o; final K key = e.getKey(); final V value = e.getValue(); final V mapValue = FunctionListMap.this.get(key); return GlazedListsImpl.equal(value, mapValue); } /** {@inheritDoc} */ @Override public boolean remove(Object o) { if (!contains(o)) return false; FunctionListMap.this.remove(((Map.Entry) o).getKey()); return true; } /** {@inheritDoc} */ @Override public void clear() { FunctionListMap.this.clear(); } } /** * This private {@link Iterator} implementation iterates the {@link Set} of * {@link Map.Entry} objects within this Map. All mutating methods are * implemented to "write through" to the backing {@link EventList} which * ensures that both the {@link EventList} and this Map always remain * in sync. * *

      Note: This implementation returns a new * {@link Map.Entry} object each time {@link #next} is called. Identity is * not preserved. */ private class EntrySetIterator implements Iterator> { /** The delegate Iterator walks a List of keys for the Map. */ private final ListIterator keyIter; /** * Construct a new EntrySetIterator using a delegate Iterator that * walks the keys of the MultMap. * * @param keyIter a {@link ListIterator} that walks the keys of the Map */ EntrySetIterator(ListIterator keyIter) { this.keyIter = keyIter; } /** {@inheritDoc} */ public boolean hasNext() { return keyIter.hasNext(); } /** * Returns a new {@link Map.Entry} each time this method is called. */ public Entry next() { final K key = keyIter.next(); return new MapEntry(key, get(key)); } /** {@inheritDoc} */ public void remove() { final int index = keyIter.previousIndex(); if (index == -1) throw new IllegalStateException("Cannot remove() without a prior call to next()"); valueList.remove(index); } } /** * This is an implementation of the {@link Map.Entry} interface that is * appropriate for this Map. All mutating methods are implemented to * "write through" to the backing {@link EventList} which ensures that * both the {@link EventList} and this Map always remain in sync. */ private class MapEntry implements Map.Entry { /** The Map key for this Entry object. */ private final K key; /** The Map value for this Entry object. */ private V value; /** * Constructs a new MapEntry with the given key and * initial value. */ MapEntry(K key, V value) { if (value == null) throw new IllegalArgumentException("value cannot be null"); this.value = value; this.key = key; } /** {@inheritDoc} */ public K getKey() { return key; } /** {@inheritDoc} */ public V getValue() { return value; } /** {@inheritDoc} */ public V setValue(V newValue) { // ensure the newValue element agrees with the key of this Entry checkKeyValueAgreement(key, newValue); this.value = newValue; return FunctionListMap.this.put(key, newValue); } /** * Two MapEntry entry objects are equal iff their keys and values * are equal. */ @Override public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry) o; final boolean keysEqual = GlazedListsImpl.equal(getKey(), e.getKey()); return keysEqual && GlazedListsImpl.equal(getValue(), e.getValue()); } /** {@inheritDoc} */ @Override public int hashCode() { return (key == null ? 0 : key.hashCode()) ^ value.hashCode(); } /** {@inheritDoc} */ @Override public String toString() { return getKey() + "=" + getValue(); } } /** * This private {@link Set} implementation represents the keyList within this * Map. All mutating methods are implemented to "write through" to the * backing {@link EventList} which ensures that both the {@link EventList} * and this Map always remain in sync. */ private class KeySet extends AbstractSet { /** {@inheritDoc} */ @Override public int size() { return keyList.size(); } /** {@inheritDoc} */ @Override public Iterator iterator() { return new KeySetIterator(keyList.listIterator()); } /** {@inheritDoc} */ @Override public boolean contains(Object o) { return FunctionListMap.this.containsKey(o); } /** {@inheritDoc} */ @Override public boolean remove(Object o) { return FunctionListMap.this.remove(o) != null; } /** {@inheritDoc} */ @Override public void clear() { FunctionListMap.this.clear(); } } /** * This private {@link Iterator} implementation iterates the {@link Set} of * keyList within this Map. All mutating methods are implemented to * "write through" to the backing {@link EventList} which ensures that both * the {@link EventList} and this Map always remain in sync. */ private class KeySetIterator implements Iterator { /** The delegate Iterator walks a List of keyList for the Map. */ private final ListIterator keyIter; /** * Construct a new KeySetIterator using a delegate Iterator that walks * the list of unique keyList of the MultMap. * * @param keyIter a {@link ListIterator} that walks the keyList of the Map */ KeySetIterator(ListIterator keyIter) { this.keyIter = keyIter; } /** {@inheritDoc} */ public boolean hasNext() { return keyIter.hasNext(); } /** {@inheritDoc} */ public K next() { return keyIter.next(); } /** {@inheritDoc} */ public void remove() { final int index = keyIter.previousIndex(); if (index == -1) throw new IllegalStateException("Cannot remove() without a prior call to next()"); valueList.remove(index); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/beans/0000755000175000017500000000000012106516364026170 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/beans/StringBeanFunction.java0000644000175000017500000000250512106516364032577 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.beans; import ca.odell.glazedlists.FunctionList; /** * A {@link FunctionList.Function} that uses a {@link BeanProperty} to extract * a raw result and then formats that result as a String. * * @author James Lemieux */ public class StringBeanFunction implements FunctionList.Function { /** The {@link BeanProperty} that is capable of extracting the function's raw value from source objects. */ private final BeanProperty property; /** * Create a new JavaBean property function that produces its value by * extracting a property from source objects. This should be accessed from the * {@link ca.odell.glazedlists.GlazedLists GlazedLists} tool factory. */ public StringBeanFunction(Class beanClass, String propertyName) { this.property = new BeanProperty(beanClass, propertyName, true, false); } /** @inheritDoc */ public String evaluate(E sourceValue) { final Object rawValue = property.get(sourceValue); return rawValue == null ? null : String.valueOf(rawValue); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/beans/BeanProperty.java0000644000175000017500000003317212106516364031453 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.beans; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.UndeclaredThrowableException; import java.util.ArrayList; import java.util.List; import ca.odell.glazedlists.impl.reflect.J2SE50ReturnTypeResolver; import ca.odell.glazedlists.impl.reflect.ReturnTypeResolver; /** * Models a getter and setter for an abstract property. * * @author Jesse Wilson */ public class BeanProperty { private static final ReturnTypeResolver TYPE_RESOLVER = new J2SE50ReturnTypeResolver(); /** the target class */ private final Class beanClass; /** the property name */ private final String propertyName; /** true indicates the getter should simply reflect the value it is given */ private final boolean identityProperty; /** the value class */ private Class valueClass = null; /** the chain of methods for the getter */ private List getterChain = null; /** the chain of methods for the setter */ private List setterChain = null; /** commonly used paramters */ private static final Object[] EMPTY_ARGUMENTS = new Object[0]; private static final Class[] EMPTY_PARAMETER_TYPES = new Class[0]; /** * Creates a new {@link BeanProperty} that gets the specified property from the * specified class. */ public BeanProperty(Class beanClass, String propertyName, boolean readable, boolean writable) { if (beanClass == null) throw new IllegalArgumentException("beanClass may not be null"); if (propertyName == null) throw new IllegalArgumentException("propertyName may not be null"); if (propertyName.length() == 0) throw new IllegalArgumentException("propertyName may not be empty"); this.beanClass = beanClass; this.propertyName = propertyName; this.identityProperty = "this".equals(propertyName); if (identityProperty && writable) throw new IllegalArgumentException("The identity property name (this) cannot be writable"); // look up the common chain final String[] propertyParts = propertyName.split("\\."); final List commonChain = new ArrayList(propertyParts.length); Class currentClass = beanClass; for(int p = 0; p < propertyParts.length - 1; p++) { Method partGetter = findGetterMethod(currentClass, propertyParts[p]); commonChain.add(partGetter); currentClass = TYPE_RESOLVER.getReturnType(currentClass, partGetter); } // look up the final getter if(readable) { if (identityProperty) { valueClass = beanClass; } else { getterChain = new ArrayList(); getterChain.addAll(commonChain); Method lastGetter = findGetterMethod(currentClass, propertyParts[propertyParts.length - 1]); getterChain.add(lastGetter); valueClass = TYPE_RESOLVER.getReturnType(currentClass, lastGetter); } } // look up the final setter if(writable) { setterChain = new ArrayList(); setterChain.addAll(commonChain); Method lastSetter = findSetterMethod(currentClass, propertyParts[propertyParts.length - 1]); setterChain.add(lastSetter); if(valueClass == null) valueClass = TYPE_RESOLVER.getFirstParameterType(currentClass, lastSetter); } } /** * Finds a getter of the specified property on the specified class. */ private Method findGetterMethod(Class targetClass, String property) { Method result; Class currentClass = targetClass; while(currentClass != null) { String getProperty = "get" + capitalize(property); result = getMethod(currentClass, getProperty, EMPTY_PARAMETER_TYPES); if(result != null) { validateGetter(result); return result; } String isProperty = "is" + capitalize(property); result = getMethod(currentClass, isProperty, EMPTY_PARAMETER_TYPES); if(result != null) { validateGetter(result); return result; } currentClass = currentClass.getSuperclass(); } throw new IllegalArgumentException("Failed to find getter for property \"" + property + "\" of " + targetClass); } /** * Finds a setter of the specified property on the specified class. */ private Method findSetterMethod(Class targetClass, String property) { String setProperty = "set" + capitalize(property); // loop through the class and its superclasses Class currentClass = targetClass; while(currentClass != null) { // loop through this class' methods Method[] classMethods = currentClass.getMethods(); for(int m = 0; m < classMethods.length; m++) { if(!classMethods[m].getName().equals(setProperty)) continue; if(classMethods[m].getParameterTypes().length != 1) continue; validateSetter(classMethods[m]); return classMethods[m]; } currentClass = currentClass.getSuperclass(); } throw new IllegalArgumentException("Failed to find setter for property \"" + property + "\" of " + targetClass); } /** * Validates that the specified method is okay for reflection. This throws an * exception if the method is invalid. */ private void validateGetter(Method method) { if(!Modifier.isPublic(method.getModifiers())) { throw new IllegalArgumentException("Getter \"" + method + "\" is not public"); } if(Void.TYPE.equals(method.getReturnType())) { throw new IllegalArgumentException("Getter \"" + method + "\" returns void"); } if(method.getParameterTypes().length != 0) { throw new IllegalArgumentException("Getter \"" + method + "\" has too many parameters; expected 0 but found " + method.getParameterTypes().length); } } /** * Validates that the specified method is okay for reflection. This throws an * exception if the method is invalid. */ private void validateSetter(Method method) { if(!Modifier.isPublic(method.getModifiers())) { throw new IllegalArgumentException("Setter \"" + method + "\" is not public"); } if(method.getParameterTypes().length != 1) { throw new IllegalArgumentException("Setter \"" + method + "\" takes the wrong number of parameters; expected 1 but found " + method.getParameterTypes().length); } } /** * Returns the specified property with a capitalized first character. */ private String capitalize(String property) { StringBuffer result = new StringBuffer(); result.append(Character.toUpperCase(property.charAt(0))); result.append(property.substring(1)); return result.toString(); } /** * Gets the method with the specified name and arguments. */ private Method getMethod(Class targetClass, String methodName, Class[] parameterTypes) { try { return targetClass.getMethod(methodName, parameterTypes); } catch(NoSuchMethodException e) { return null; } } /** * Gets the base class that this getter accesses. */ public Class getBeanClass() { return beanClass; } /** * Gets the name of the property that this getter extracts. */ public String getPropertyName() { return propertyName; } /** * Gets the class of the property's value. This is the return type and not * necessarily the runtime type of the class. */ public Class getValueClass() { return valueClass; } /** * Gets whether this property can get get. */ public boolean isReadable() { return getterChain != null || identityProperty; } /** * Gets whether this property can be set. */ public boolean isWritable() { return setterChain != null; } /** * Gets the value of this property for the specified Object. */ public Object get(T member) { if(!isReadable()) throw new IllegalStateException("Property " + propertyName + " of " + beanClass + " not readable"); // the identity property simply reflects the member back unchanged if (identityProperty) return member; try { // do all the getters in sequence Object currentMember = member; for(int i = 0, n = getterChain.size(); i < n; i++) { Method currentMethod = getterChain.get(i); currentMember = currentMethod.invoke(currentMember, EMPTY_ARGUMENTS); if(currentMember == null) return null; } // return the result of the last getter return currentMember; } catch(IllegalAccessException e) { SecurityException se = new SecurityException(); se.initCause(e); throw se; } catch(InvocationTargetException e) { throw new UndeclaredThrowableException(e.getCause()); } } /** * Gets the value of this property for the specified Object. */ public Object set(T member, Object newValue) { if(!isWritable()) throw new IllegalStateException("Property " + propertyName + " of " + beanClass + " not writable"); Method setterMethod = null; try { // everything except the last setter chain element is a getter Object currentMember = member; for(int i = 0, n = setterChain.size() - 1; i < n; i++) { Method currentMethod = setterChain.get(i); currentMember = currentMethod.invoke(currentMember, EMPTY_ARGUMENTS); if(currentMember == null) return null; } // do the remaining setter setterMethod = setterChain.get(setterChain.size() - 1); return setterMethod.invoke(currentMember, new Object[] { newValue }); } catch (IllegalArgumentException e) { String message = e.getMessage(); // improve the message if possible into something like: // "MyClass.someMethod(SomeClassType) cannot be called with an instance of WrongClassType" if ("argument type mismatch".equals(message) && setterMethod != null) message = getSimpleName(setterMethod.getDeclaringClass()) + "." + setterMethod.getName() + "(" + getSimpleName(setterMethod.getParameterTypes()[0]) + ") cannot be called with an instance of " + getSimpleName(newValue.getClass()); throw new IllegalArgumentException(message); } catch(IllegalAccessException e) { SecurityException se = new SecurityException(); se.initCause(e); throw se; } catch(InvocationTargetException e) { throw new UndeclaredThrowableException(e.getCause()); } catch(RuntimeException e) { throw new RuntimeException("Failed to set property \"" + propertyName + "\" of " + beanClass + " to " + (newValue == null ? "null" : "instance of " + newValue.getClass()), e); } } /** * This method was backported from the JDK 1.5 version of java.lang.Class. * * Returns the simple name of the given class as given in the * source code. Returns an empty string if the underlying class is * anonymous. * * @return the simple name of the given class */ private static String getSimpleName(Class clazz) { Class declaringClass = clazz.getDeclaringClass(); String simpleName = declaringClass == null ? null : declaringClass.getName(); if (simpleName == null) { // top level class simpleName = clazz.getName(); return simpleName.substring(simpleName.lastIndexOf(".") + 1); // strip the package name } // Remove leading "\$[0-9]*" from the name int length = simpleName.length(); if (length < 1 || simpleName.charAt(0) != '$') throw new InternalError("Malformed class name"); int index = 1; while (index < length && isAsciiDigit(simpleName.charAt(index))) index++; // Eventually, this is the empty string iff this is an anonymous class return simpleName.substring(index); } /** * This method was backported from the JDK 1.5 version of java.lang.Class. * * Character.isDigit answers true to some non-ascii * digits. This one does not. */ private static boolean isAsciiDigit(char c) { return '0' <= c && c <= '9'; } /** {@inheritDoc} */ @Override public boolean equals(Object o) { if(this == o) return true; if(o == null || getClass() != o.getClass()) return false; final BeanProperty that = (BeanProperty) o; if(!beanClass.equals(that.beanClass)) return false; if(!propertyName.equals(that.propertyName)) return false; return true; } /** {@inheritDoc} */ @Override public int hashCode() { int result; result = beanClass.hashCode(); result = 29 * result + propertyName.hashCode(); return result; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/beans/BeanFunction.java0000644000175000017500000000230212106516364031403 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.beans; import ca.odell.glazedlists.FunctionList; /** * A {@link FunctionList.Function} that uses a {@link BeanProperty} to produce * the result of the function. * * @author James Lemieux */ public class BeanFunction implements FunctionList.Function { /** The {@link BeanProperty} that is capable of extracting the function's value from source objects. */ private final BeanProperty property; /** * Create a new JavaBean property function that produces its value by * extracting a property from source objects. This should be accessed from the * {@link ca.odell.glazedlists.GlazedLists GlazedLists} tool factory. */ public BeanFunction(Class beanClass, String propertyName) { this.property = new BeanProperty(beanClass, propertyName, true, false); } /** @inheritDoc */ public V evaluate(E sourceValue) { return (V) property.get(sourceValue); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/beans/BeanTextFilterator.java0000644000175000017500000000542312106516364032605 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.beans; import ca.odell.glazedlists.Filterator; import ca.odell.glazedlists.TextFilterator; import java.util.List; /** * TextFilterator implementation that uses reflection to be used for any * JavaBean-like Object with getProperty() and setProperty() style API. * * @author Jesse Wilson */ public class BeanTextFilterator implements TextFilterator, Filterator { /** Java Beans property names */ private String[] propertyNames; /** methods for extracting field values */ private BeanProperty[] beanProperties = null; /** * Create a BeanTextFilterator that uses the specified property names. */ public BeanTextFilterator(String... propertyNames) { this.propertyNames = propertyNames; } /** * Create a BeanTextFilterator that uses the specified property names. */ public BeanTextFilterator(Class beanClass, String... propertyNames) { this.propertyNames = propertyNames; loadPropertyDescriptors(beanClass); } /** {@inheritDoc} */ public void getFilterStrings(List baseList, E element) { if(element == null) return; // load the property descriptors on first request if(beanProperties == null) loadPropertyDescriptors(element.getClass()); // get the filter strings for(int p = 0; p < beanProperties.length; p++) { Object propertyValue = beanProperties[p].get(element); if(propertyValue == null) continue; baseList.add(propertyValue.toString()); } } /** {@inheritDoc} */ public void getFilterValues(List baseList, E element) { if(element == null) return; // load the property descriptors on first request if(beanProperties == null) loadPropertyDescriptors(element.getClass()); // get the filter strings for(int p = 0; p < beanProperties.length; p++) { Object propertyValue = beanProperties[p].get(element); if(propertyValue == null) continue; baseList.add((D)propertyValue); } } /** * Loads the property descriptors which are used to invoke property * access methods using the property names. */ private void loadPropertyDescriptors(Class beanClass) { beanProperties = new BeanProperty[propertyNames.length]; for(int p = 0; p < propertyNames.length; p++) { beanProperties[p] = new BeanProperty(beanClass, propertyNames[p], true, false); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/beans/BeanConnector.java0000644000175000017500000002522712106516364031563 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.beans; import ca.odell.glazedlists.ObservableElementList; import ca.odell.glazedlists.matchers.Matcher; import ca.odell.glazedlists.matchers.Matchers; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.EventListener; /** * An {@link ObservableElementList.Connector} for the Java beans' * {@link PropertyChangeListener}. * * @author Jesse Wilson * @author James Lemieux */ public class BeanConnector implements ObservableElementList.Connector { /** The method to use when installing a PropertyChangeListener on an object. */ private Method addListenerMethod; /** The method to use when uninstalling a PropertyChangeListener on an object. */ private Method removeListenerMethod; /** The list which contains the elements being observed via this {@link ObservableElementList.Connector}. */ private ObservableElementList list; /** The PropertyChangeListener to install on each list element. */ protected PropertyChangeListener propertyChangeListener = this.createPropertyChangeListener(); /** Matches PropertyChangeEvents to deliver to the ObservableElementList. */ private Matcher eventMatcher = Matchers.trueMatcher(); /** * Reflection is used to install/uninstall the {@link #propertyChangeListener} * on list elements, so we cache the Object[] used in the reflection call * for a speed increase. */ private final Object[] reflectionParameters = {propertyChangeListener}; /** * The types taken by the methods which add and remove PropertyChangeListeners. */ private static final Class[] REFLECTION_TYPES = {PropertyChangeListener.class}; /** * Constructs a new Connector which uses reflection to add and remove a * PropertyChangeListener from instances of the beanClass. The * methods for adding and removing PropertyChangeListener from instances of * the given beanClass are assumed to follow the naming * convention: * *

        *
      • add*(PropertyChangeListener) to add PropertyChangeListeners *
      • remove*(PropertyChangeListener) to remove PropertyChangeListeners *
      * * where the * may be replaced with any string of valid java identifier * characters. * * @param beanClass the Class of all list elements within the {@link ObservableElementList} * @throws IllegalArgumentException if beanClass does not contain methods * matching the format described */ public BeanConnector(Class beanClass) { final Method[] methods = beanClass.getMethods(); for (int m = 0; m < methods.length; m++) { if(methods[m].getParameterTypes().length != 1) continue; if(methods[m].getParameterTypes()[0] != PropertyChangeListener.class) continue; if(methods[m].getName().startsWith("add")) this.addListenerMethod = methods[m]; if(methods[m].getName().startsWith("remove")) this.removeListenerMethod = methods[m]; } if (this.addListenerMethod == null || this.removeListenerMethod == null) throw new IllegalArgumentException("Couldn't find listener methods for " + beanClass.getName()); } /** * Constructs a new Connector which uses reflection to add and remove a * PropertyChangeListener from instances of the beanClass. The * methods for adding and removing PropertyChangeListener from instances of * the given beanClass are assumed to follow the naming * convention: * *
        *
      • add*(PropertyChangeListener) to add PropertyChangeListeners *
      • remove*(PropertyChangeListener) to remove PropertyChangeListeners *
      * * where the * may be replaced with any string of valid java identifier * characters. * * @param beanClass the Class of all list elements within the {@link ObservableElementList} * @param eventMatcher the matcher for matching those PropertyChangeEvents, which should be * delivered to the ObservableElementList * @throws IllegalArgumentException if beanClass does not contain methods * matching the format described */ public BeanConnector(Class beanClass, Matcher eventMatcher) { this(beanClass); setEventMatcher(eventMatcher); } /** * Constructs a new Connector which uses reflection to add and remove a * PropertyChangeListener from instances of the beanClass * using the named methods. * * @param beanClass the Class of all list elements within the {@link ObservableElementList} * @param addListenerMethodName the name of the method which adds PropertyChangeListeners * to the elements within the {@link ObservableElementList} * @param removeListenerMethodName the name of the method which removes PropertyChangeListeners * from the elements within the {@link ObservableElementList} * @throws IllegalArgumentException if beanClass does not contain the named * methods or if the methods do no take a PropertyChangeListener as the single parameter */ public BeanConnector(Class beanClass, String addListenerMethodName, String removeListenerMethodName) { try { this.addListenerMethod = beanClass.getMethod(addListenerMethodName, REFLECTION_TYPES); this.removeListenerMethod = beanClass.getMethod(removeListenerMethodName, REFLECTION_TYPES); } catch (NoSuchMethodException e) { throw new IllegalArgumentException("Failed to find method " + e.getMessage() + " in " + beanClass); } } /** * Constructs a new Connector which uses reflection to add and remove a PropertyChangeListener * from instances of the beanClass using the named methods. * * @param beanClass the Class of all list elements within the {@link ObservableElementList} * @param addListenerMethodName the name of the method which adds PropertyChangeListeners to the * elements within the {@link ObservableElementList} * @param removeListenerMethodName the name of the method which removes PropertyChangeListeners * from the elements within the {@link ObservableElementList} * @param eventMatcher the matcher for matching those PropertyChangeEvents, which should be * delivered to the ObservableElementList * @throws IllegalArgumentException if beanClass does not contain the named * methods or if the methods do no take a PropertyChangeListener as the single parameter */ public BeanConnector(Class beanClass, String addListenerMethodName, String removeListenerMethodName, Matcher eventMatcher) { this(beanClass, addListenerMethodName, removeListenerMethodName); setEventMatcher(eventMatcher); } /** * Start listening for PropertyChangeEvents from the specified * element. The PropertyChangeListener is installed using * reflection. * * @param element the element to be observed * @return the listener that was installed on the element * to be used as a parameter to {@link #uninstallListener(Object, EventListener)} * @throws RuntimeException if the reflection call fails to successfully * install the PropertyChangeListener */ public EventListener installListener(E element) { try { this.addListenerMethod.invoke(element, this.reflectionParameters); return this.propertyChangeListener; } catch (IllegalAccessException iae) { throw new RuntimeException(iae); } catch (InvocationTargetException ite) { throw new RuntimeException(ite.getCause()); } } /** * Stop listening for PropertyChangeEvents from the specified * element. The PropertyChangeListener is uninstalled using * reflection. * * @param element the observed element * @param listener the listener that was installed on the element * in {@link #installListener(Object)} * @throws RuntimeException if the reflection call fails to successfully * uninstall the PropertyChangeListener */ public void uninstallListener(E element, EventListener listener) { try { this.removeListenerMethod.invoke(element, this.reflectionParameters); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e.getCause()); } } /** {@inheritDoc} */ public void setObservableElementList(ObservableElementList list) { this.list = list; } /** * Returns the event matcher. It matches those PropertyChangeEvents, which should be delivered * to the ObservableElementList. In other words, it serves as a filter for PropertyChangeEvents. */ public final Matcher getEventMatcher() { return eventMatcher; } /** * Sets the event matcher, may not be null. It matches those * PropertyChangeEvents, which should be delivered to the ObservableElementList. In other words, * it serves as a filter for PropertyChangeEvents. */ private void setEventMatcher(Matcher eventMatcher) { if (eventMatcher == null) throw new IllegalArgumentException("Event matcher may not be null."); this.eventMatcher = eventMatcher; } /** * A local factory method to produce the PropertyChangeListener which will * be installed on list elements. */ protected PropertyChangeListener createPropertyChangeListener() { return new PropertyChangeHandler(); } /** * The PropertyChangeListener which notifies the {@link ObservableElementList} within this * Connector of changes to list elements. */ public class PropertyChangeHandler implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent event) { if (getEventMatcher().matches(event)) { list.elementChanged(event.getSource()); } } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/beans/BeanTableFormat.java0000644000175000017500000001670012106516364032025 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.beans; import ca.odell.glazedlists.GlazedLists; import ca.odell.glazedlists.gui.AdvancedTableFormat; import ca.odell.glazedlists.gui.WritableTableFormat; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.Collections; /** * TableFormat implementation that uses reflection to be used for any * JavaBean-like Object with getProperty() and setProperty() style API. * * @author Jesse Wilson * @author Andrea Aime */ public class BeanTableFormat implements WritableTableFormat, AdvancedTableFormat { /** methods for extracting field values */ protected BeanProperty[] beanProperties = null; /** Java Beans property names */ protected String[] propertyNames; /** column labels are pretty-print column header labels */ protected String[] columnLabels; /** whether all columns can be edited */ private boolean[] editable; /** column comparators */ protected Comparator[] comparators; /** column classes */ protected Class[] classes; /** primitive class to object class conversion map */ protected static final Map primitiveToObjectMap; static { Map primitiveToObjectMapWritable = new HashMap(); primitiveToObjectMapWritable.put(boolean.class, Boolean.class); primitiveToObjectMapWritable.put(char.class, Character.class); primitiveToObjectMapWritable.put(byte.class, Byte.class); primitiveToObjectMapWritable.put(short.class, Short.class); primitiveToObjectMapWritable.put(int.class, Integer.class); primitiveToObjectMapWritable.put(long.class, Long.class); primitiveToObjectMapWritable.put(float.class, Float.class); primitiveToObjectMapWritable.put(double.class, Double.class); primitiveToObjectMap = Collections.unmodifiableMap(primitiveToObjectMapWritable); } /** * Create a BeanTableFormat that uses the specified column names * and the specified field names while offering editable columns. */ public BeanTableFormat(Class beanClass, String[] propertyNames, String[] columnLabels, boolean[] editable) { this.propertyNames = propertyNames; this.columnLabels = columnLabels; this.editable = editable; // set up the AdvancedTableFormat properties comparators = new Comparator[propertyNames.length]; classes = new Class[propertyNames.length]; // use default properties if no class is specified if(beanClass == null) { for(int c = 0; c < classes.length; c++) { classes[c] = Object.class; comparators[c] = GlazedLists.comparableComparator(); } // use detected properties if class is specified } else { loadPropertyDescriptors(beanClass); for(int c = 0; c < classes.length; c++) { // class Class rawClass = beanProperties[c].getValueClass(); if(primitiveToObjectMap.containsKey(rawClass)) { classes[c] = primitiveToObjectMap.get(rawClass); } else { classes[c] = rawClass; } // comparator if(Comparable.class.isAssignableFrom(classes[c])) comparators[c] = GlazedLists.comparableComparator(); else comparators[c] = null; } } } public BeanTableFormat(Class beanClass, String[] propertyNames, String[] columnLabels) { this(beanClass, propertyNames, columnLabels, new boolean[propertyNames.length]); } /** * Loads the property descriptors which are used to invoke property * access methods using the property names. */ protected void loadPropertyDescriptors(Class beanClass) { beanProperties = new BeanProperty[propertyNames.length]; for(int p = 0; p < propertyNames.length; p++) { beanProperties[p] = new BeanProperty(beanClass, propertyNames[p], true, editable[p]); } } // TableFormat // // // // // // // // // // // // // // // // // // // // /** * The number of columns to display. */ public int getColumnCount() { return columnLabels.length; } /** * Gets the title of the specified column. */ public String getColumnName(int column) { return columnLabels[column]; } /** * Gets the value of the specified field for the specified object. This * is the value that will be passed to the editor and renderer for the * column. If you have defined a custom renderer, you may choose to return * simply the baseObject. */ public Object getColumnValue(E baseObject, int column) { if(baseObject == null) return null; // load the property descriptors on first request if(beanProperties == null) loadPropertyDescriptors((Class) baseObject.getClass()); // get the property return beanProperties[column].get(baseObject); } // WritableTableFormat // // // // // // // // // // // // // // // // // // /** * For editing fields. This returns true if the specified Object in the * specified column can be edited by the user. * * @param baseObject the Object to test as editable or not. This will be * an element from the source list. * @param column the column to test. * @return true if the object and column are editable, false otherwise. * @since 2004-August-27, as a replacement for isColumnEditable(int). */ public boolean isEditable(E baseObject, int column) { return editable[column]; } /** * Sets the specified field of the base object to the edited value. When * a column of a table is edited, this method is called so that the user * can specify how to modify the base object for each column. * * @param baseObject the Object to be edited. This will be the original * Object from the source list. * @param editedValue the newly constructed value for the edited column * @param column the column which has been edited * @return the revised Object, or null if the revision shall be discarded. * If not null, the EventTableModel will set() this revised value in * the list and overwrite the previous value. */ public E setColumnValue(E baseObject, Object editedValue, int column) { if(baseObject == null) return null; // load the property descriptors on first request if(beanProperties == null) loadPropertyDescriptors((Class) baseObject.getClass()); // set the property beanProperties[column].set(baseObject, editedValue); // return the modified result return baseObject; } // AdvancedTableFormat // // // // // // // // // // // // // // // // // // /** * Get the class of the specified column. */ public Class getColumnClass(int column) { return classes[column]; } /** * Get the comparator for the specified column. */ public Comparator getColumnComparator(int column) { return comparators[column]; } }././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/beans/BeanThresholdEvaluator.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/beans/BeanThresholdEvaluator.ja0000644000175000017500000000304412106516364033112 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.beans; // to implement the ThresholdEvaluator interface import ca.odell.glazedlists.ThresholdList; /** * A ThresholdEvaluator that is powered by JavaBeans and Reflection. * * @author Kevin Maltby */ public final class BeanThresholdEvaluator implements ThresholdList.Evaluator { private String propertyName = null; private BeanProperty beanProperty = null; public BeanThresholdEvaluator(String propertyName) { this.propertyName = propertyName; } /** * Returns an integer value for an Object to be used to * compare that object against a threshold. This value is * not relative to any other object unlike a Comparator. */ public int evaluate(E object) { if(beanProperty == null) loadPropertyDescriptors(object); Object property = beanProperty.get(object); return ((Integer)property).intValue(); } /** * Loads the property descriptors which are used to invoke property * access methods using the property names. */ private void loadPropertyDescriptors(Object beanObject) { Class beanClass = (Class) beanObject.getClass(); beanProperty = new BeanProperty(beanClass, propertyName, true, false); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/SyncListener.java0000644000175000017500000000436612106516372030375 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import java.util.List; /** * This {@link ListEventListener} updates a plain old {@link List} so that * its contents match those of a source {@link EventList}. * * @author Jesse Wilson */ public class SyncListener implements ListEventListener { /** the list to sync against the {@link EventList}. */ private List target; /** remember sync list size to attempt to detect drifts */ private int targetSize; /** * Create a {@link SyncListener} that listens for changes on the * specified source {@link EventList} and copies its data to the * specified target {@link List}. */ public SyncListener(EventList source, List target) { this.target = target; target.clear(); target.addAll(source); // attempt to detect drifts targetSize = target.size(); // handle changes source.addListEventListener(this); } /** {@inheritDoc} */ public void listChanged(ListEvent listChanges) { EventList source = listChanges.getSourceList(); // if the list sizes don't match, we have a problem if(target.size() != targetSize) { throw new IllegalStateException("Synchronize EventList target has been modified"); } // update the target list with the EventList while(listChanges.next()) { int index = listChanges.getIndex(); int type = listChanges.getType(); if(type == ListEvent.INSERT) { target.add(index, source.get(index)); targetSize++; } else if(type == ListEvent.UPDATE) { target.set(index, source.get(index)); } else if(type == ListEvent.DELETE) { target.remove(index); targetSize--; } } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/text/0000755000175000017500000000000012106516356026065 5ustar gregoagregoa././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/text/LatinDiacriticsStripper.javalibglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/impl/text/LatinDiacriticsStripper.ja0000644000175000017500000007241312106516356033207 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists.impl.text; /** * Latin characters are mapped to strip their diacritics from each character * according to the rules of Unicode. For an introduction to unicode * normalization, go here. * *

      The complete set of encoded Latin characters within Unicode looks like so: * *

      * * * * * * * * * * *
      Basic Latin and Control Characters0x0000 -> 0x007F (0 -> 127)
      Latin-1 Supplement and C1 Controls0x0080 -> 0x00FF (128 -> 255)
      Latin Extended-A0x0100 -> 0x017F (256 -> 372)
      Latin Extended-B0x0180 -> 0x024F (384 -> 591)
      Latin Extended Additional0x1E00 -> 0x1EFF (7680 -> 7935)
      Latin Extended-C0x2C60 -> 0x2C7F (11360 -> 11391)
      Latin Extended-D0xA720 -> 0xA7FF (42784 -> 43007)
      Latin Ligatures0xFB00 -> 0xFB4F (64256 -> 64335)
      Small Forms0xFE50 -> 0xFE6F (65104 -> 65135)
      Full-Width Latin Letters0xFF00 -> 0xFFEF (65280 -> 65519)
      * * This LatinDiacriticsStripper only maps the first 4 sets (0 -> 591), since * the rest are quite fringe and extremely rare in practice. * *

      For more details see the Unicode Charts. * * @author James Lemieux */ public final class LatinDiacriticsStripper { /** The static map initialized when the class is loaded. */ private static final char[] MAPPER = new char[592]; /** * This method strips diacritics from latin characters, which allows fuzzy * matching between languages. For example, the mapped value of is e. * So, the word "rsum" could be matched by simply typing "resume". * * @return the normalized version of c, which can be any character */ public static char[] getMapper() { return MAPPER; } /** * This main method only executes under JDK 1.6. It is used to generate the * MAPPER entries to standard out. They can then be copied and pasted back * into this class. Practically speaking, we will never need to execute this * main method again as the mappings are formed according to Unicode * standards that will not changed, but this code serves as documentation * to trace how the values were produced. */ /* public static void main(String[] args) { // loop through all latin characters to the end of "Latin Extended-B" for (int i = 0; i <= 591; i++) { // get the latin character to consider final char c = (char) i; // decompose the character into its parts final String decomposed = Normalizer.normalize(String.valueOf(c), Normalizer.Form.NFD); // use the first character in the decomposition String as the normalized character final char normalized = decomposed.charAt(0); // determine whether c is a special character, and what its value should be in the mapper boolean specialChar = true; String mapperValue; if (normalized == '\n') { mapperValue = "\\n"; } else if (normalized == '\t') { mapperValue = "\\t"; } else if (normalized == '\r') { mapperValue = "\\r"; } else if (normalized == '\'') { mapperValue = "\\'"; } else if (normalized == '\\') { mapperValue = "\\\\"; } else { specialChar = Character.isISOControl(c); mapperValue = Integer.toHexString(normalized); while (mapperValue.length() < 4) mapperValue = "0" + mapperValue; mapperValue = "\\u" + mapperValue; } // create a comment that follows the mapper entry, if appropriate final String comment = specialChar ? "" : (" // (" + c + " -> " + normalized + ")"); System.out.println("MAPPER[" + i + "] = '" + mapperValue + "';" + comment); } } */ static { MAPPER[0] = '\u0000'; MAPPER[1] = '\u0001'; MAPPER[2] = '\u0002'; MAPPER[3] = '\u0003'; MAPPER[4] = '\u0004'; MAPPER[5] = '\u0005'; MAPPER[6] = '\u0006'; MAPPER[7] = '\u0007'; MAPPER[8] = '\u0008'; MAPPER[9] = '\t'; MAPPER[10] = '\n'; MAPPER[11] = '\u000b'; MAPPER[12] = '\u000c'; MAPPER[13] = '\r'; MAPPER[14] = '\u000e'; MAPPER[15] = '\u000f'; MAPPER[16] = '\u0010'; MAPPER[17] = '\u0011'; MAPPER[18] = '\u0012'; MAPPER[19] = '\u0013'; MAPPER[20] = '\u0014'; MAPPER[21] = '\u0015'; MAPPER[22] = '\u0016'; MAPPER[23] = '\u0017'; MAPPER[24] = '\u0018'; MAPPER[25] = '\u0019'; MAPPER[26] = '\u001a'; MAPPER[27] = '\u001b'; MAPPER[28] = '\u001c'; MAPPER[29] = '\u001d'; MAPPER[30] = '\u001e'; MAPPER[31] = '\u001f'; MAPPER[32] = '\u0020'; // ( -> ) MAPPER[33] = '\u0021'; // (! -> !) MAPPER[34] = '\u0022'; // (" -> ") MAPPER[35] = '\u0023'; // (# -> #) MAPPER[36] = '\u0024'; // ($ -> $) MAPPER[37] = '\u0025'; // (% -> %) MAPPER[38] = '\u0026'; // (& -> &) MAPPER[39] = '\''; MAPPER[40] = '\u0028'; // (( -> () MAPPER[41] = '\u0029'; // () -> )) MAPPER[42] = '\u002a'; // (* -> *) MAPPER[43] = '\u002b'; // (+ -> +) MAPPER[44] = '\u002c'; // (, -> ,) MAPPER[45] = '\u002d'; // (- -> -) MAPPER[46] = '\u002e'; // (. -> .) MAPPER[47] = '\u002f'; // (/ -> /) MAPPER[48] = '\u0030'; // (0 -> 0) MAPPER[49] = '\u0031'; // (1 -> 1) MAPPER[50] = '\u0032'; // (2 -> 2) MAPPER[51] = '\u0033'; // (3 -> 3) MAPPER[52] = '\u0034'; // (4 -> 4) MAPPER[53] = '\u0035'; // (5 -> 5) MAPPER[54] = '\u0036'; // (6 -> 6) MAPPER[55] = '\u0037'; // (7 -> 7) MAPPER[56] = '\u0038'; // (8 -> 8) MAPPER[57] = '\u0039'; // (9 -> 9) MAPPER[58] = '\u003a'; // (: -> :) MAPPER[59] = '\u003b'; // (; -> ;) MAPPER[60] = '\u003c'; // (< -> <) MAPPER[61] = '\u003d'; // (= -> =) MAPPER[62] = '\u003e'; // (> -> >) MAPPER[63] = '\u003f'; // (? -> ?) MAPPER[64] = '\u0040'; // (@ -> @) MAPPER[65] = '\u0041'; // (A -> A) MAPPER[66] = '\u0042'; // (B -> B) MAPPER[67] = '\u0043'; // (C -> C) MAPPER[68] = '\u0044'; // (D -> D) MAPPER[69] = '\u0045'; // (E -> E) MAPPER[70] = '\u0046'; // (F -> F) MAPPER[71] = '\u0047'; // (G -> G) MAPPER[72] = '\u0048'; // (H -> H) MAPPER[73] = '\u0049'; // (I -> I) MAPPER[74] = '\u004a'; // (J -> J) MAPPER[75] = '\u004b'; // (K -> K) MAPPER[76] = '\u004c'; // (L -> L) MAPPER[77] = '\u004d'; // (M -> M) MAPPER[78] = '\u004e'; // (N -> N) MAPPER[79] = '\u004f'; // (O -> O) MAPPER[80] = '\u0050'; // (P -> P) MAPPER[81] = '\u0051'; // (Q -> Q) MAPPER[82] = '\u0052'; // (R -> R) MAPPER[83] = '\u0053'; // (S -> S) MAPPER[84] = '\u0054'; // (T -> T) MAPPER[85] = '\u0055'; // (U -> U) MAPPER[86] = '\u0056'; // (V -> V) MAPPER[87] = '\u0057'; // (W -> W) MAPPER[88] = '\u0058'; // (X -> X) MAPPER[89] = '\u0059'; // (Y -> Y) MAPPER[90] = '\u005a'; // (Z -> Z) MAPPER[91] = '\u005b'; // ([ -> [) MAPPER[92] = '\\'; MAPPER[93] = '\u005d'; // (] -> ]) MAPPER[94] = '\u005e'; // (^ -> ^) MAPPER[95] = '\u005f'; // (_ -> _) MAPPER[96] = '\u0060'; // (` -> `) MAPPER[97] = '\u0061'; // (a -> a) MAPPER[98] = '\u0062'; // (b -> b) MAPPER[99] = '\u0063'; // (c -> c) MAPPER[100] = '\u0064'; // (d -> d) MAPPER[101] = '\u0065'; // (e -> e) MAPPER[102] = '\u0066'; // (f -> f) MAPPER[103] = '\u0067'; // (g -> g) MAPPER[104] = '\u0068'; // (h -> h) MAPPER[105] = '\u0069'; // (i -> i) MAPPER[106] = '\u006a'; // (j -> j) MAPPER[107] = '\u006b'; // (k -> k) MAPPER[108] = '\u006c'; // (l -> l) MAPPER[109] = '\u006d'; // (m -> m) MAPPER[110] = '\u006e'; // (n -> n) MAPPER[111] = '\u006f'; // (o -> o) MAPPER[112] = '\u0070'; // (p -> p) MAPPER[113] = '\u0071'; // (q -> q) MAPPER[114] = '\u0072'; // (r -> r) MAPPER[115] = '\u0073'; // (s -> s) MAPPER[116] = '\u0074'; // (t -> t) MAPPER[117] = '\u0075'; // (u -> u) MAPPER[118] = '\u0076'; // (v -> v) MAPPER[119] = '\u0077'; // (w -> w) MAPPER[120] = '\u0078'; // (x -> x) MAPPER[121] = '\u0079'; // (y -> y) MAPPER[122] = '\u007a'; // (z -> z) MAPPER[123] = '\u007b'; // ({ -> {) MAPPER[124] = '\u007c'; // (| -> |) MAPPER[125] = '\u007d'; // (} -> }) MAPPER[126] = '\u007e'; // (~ -> ~) MAPPER[127] = '\u007f'; MAPPER[128] = '\u0080'; MAPPER[129] = '\u0081'; MAPPER[130] = '\u0082'; MAPPER[131] = '\u0083'; MAPPER[132] = '\u0084'; MAPPER[133] = '\u0085'; MAPPER[134] = '\u0086'; MAPPER[135] = '\u0087'; MAPPER[136] = '\u0088'; MAPPER[137] = '\u0089'; MAPPER[138] = '\u008a'; MAPPER[139] = '\u008b'; MAPPER[140] = '\u008c'; MAPPER[141] = '\u008d'; MAPPER[142] = '\u008e'; MAPPER[143] = '\u008f'; MAPPER[144] = '\u0090'; MAPPER[145] = '\u0091'; MAPPER[146] = '\u0092'; MAPPER[147] = '\u0093'; MAPPER[148] = '\u0094'; MAPPER[149] = '\u0095'; MAPPER[150] = '\u0096'; MAPPER[151] = '\u0097'; MAPPER[152] = '\u0098'; MAPPER[153] = '\u0099'; MAPPER[154] = '\u009a'; MAPPER[155] = '\u009b'; MAPPER[156] = '\u009c'; MAPPER[157] = '\u009d'; MAPPER[158] = '\u009e'; MAPPER[159] = '\u009f'; MAPPER[160] = '\u00a0'; // ( -> ) MAPPER[161] = '\u00a1'; // ( -> ) MAPPER[162] = '\u00a2'; // ( -> ) MAPPER[163] = '\u00a3'; // ( -> ) MAPPER[164] = '\u00a4'; // ( -> ) MAPPER[165] = '\u00a5'; // ( -> ) MAPPER[166] = '\u00a6'; // ( -> ) MAPPER[167] = '\u00a7'; // ( -> ) MAPPER[168] = '\u00a8'; // ( -> ) MAPPER[169] = '\u00a9'; // ( -> ) MAPPER[170] = '\u00aa'; // ( -> ) MAPPER[171] = '\u00ab'; // ( -> ) MAPPER[172] = '\u00ac'; // ( -> ) MAPPER[173] = '\u00ad'; // ( -> ) MAPPER[174] = '\u00ae'; // ( -> ) MAPPER[175] = '\u00af'; // ( -> ) MAPPER[176] = '\u00b0'; // ( -> ) MAPPER[177] = '\u00b1'; // ( -> ) MAPPER[178] = '\u00b2'; // ( -> ) MAPPER[179] = '\u00b3'; // ( -> ) MAPPER[180] = '\u00b4'; // ( -> ) MAPPER[181] = '\u00b5'; // ( -> ) MAPPER[182] = '\u00b6'; // ( -> ) MAPPER[183] = '\u00b7'; // ( -> ) MAPPER[184] = '\u00b8'; // ( -> ) MAPPER[185] = '\u00b9'; // ( -> ) MAPPER[186] = '\u00ba'; // ( -> ) MAPPER[187] = '\u00bb'; // ( -> ) MAPPER[188] = '\u00bc'; // ( -> ) MAPPER[189] = '\u00bd'; // ( -> ) MAPPER[190] = '\u00be'; // ( -> ) MAPPER[191] = '\u00bf'; // ( -> ) MAPPER[192] = '\u0041'; // ( -> A) MAPPER[193] = '\u0041'; // ( -> A) MAPPER[194] = '\u0041'; // ( -> A) MAPPER[195] = '\u0041'; // ( -> A) MAPPER[196] = '\u0041'; // ( -> A) MAPPER[197] = '\u0041'; // ( -> A) MAPPER[198] = '\u00c6'; // ( -> ) MAPPER[199] = '\u0043'; // ( -> C) MAPPER[200] = '\u0045'; // ( -> E) MAPPER[201] = '\u0045'; // ( -> E) MAPPER[202] = '\u0045'; // ( -> E) MAPPER[203] = '\u0045'; // ( -> E) MAPPER[204] = '\u0049'; // ( -> I) MAPPER[205] = '\u0049'; // ( -> I) MAPPER[206] = '\u0049'; // ( -> I) MAPPER[207] = '\u0049'; // ( -> I) MAPPER[208] = '\u00d0'; // ( -> ) MAPPER[209] = '\u004e'; // ( -> N) MAPPER[210] = '\u004f'; // ( -> O) MAPPER[211] = '\u004f'; // ( -> O) MAPPER[212] = '\u004f'; // ( -> O) MAPPER[213] = '\u004f'; // ( -> O) MAPPER[214] = '\u004f'; // ( -> O) MAPPER[215] = '\u00d7'; // ( -> ) MAPPER[216] = '\u00d8'; // ( -> ) MAPPER[217] = '\u0055'; // ( -> U) MAPPER[218] = '\u0055'; // ( -> U) MAPPER[219] = '\u0055'; // ( -> U) MAPPER[220] = '\u0055'; // ( -> U) MAPPER[221] = '\u0059'; // ( -> Y) MAPPER[222] = '\u00de'; // ( -> ) MAPPER[223] = '\u00df'; // ( -> ) MAPPER[224] = '\u0061'; // ( -> a) MAPPER[225] = '\u0061'; // ( -> a) MAPPER[226] = '\u0061'; // ( -> a) MAPPER[227] = '\u0061'; // ( -> a) MAPPER[228] = '\u0061'; // ( -> a) MAPPER[229] = '\u0061'; // ( -> a) MAPPER[230] = '\u00e6'; // ( -> ) MAPPER[231] = '\u0063'; // ( -> c) MAPPER[232] = '\u0065'; // ( -> e) MAPPER[233] = '\u0065'; // ( -> e) MAPPER[234] = '\u0065'; // ( -> e) MAPPER[235] = '\u0065'; // ( -> e) MAPPER[236] = '\u0069'; // ( -> i) MAPPER[237] = '\u0069'; // ( -> i) MAPPER[238] = '\u0069'; // ( -> i) MAPPER[239] = '\u0069'; // ( -> i) MAPPER[240] = '\u00f0'; // ( -> ) MAPPER[241] = '\u006e'; // ( -> n) MAPPER[242] = '\u006f'; // ( -> o) MAPPER[243] = '\u006f'; // ( -> o) MAPPER[244] = '\u006f'; // ( -> o) MAPPER[245] = '\u006f'; // ( -> o) MAPPER[246] = '\u006f'; // ( -> o) MAPPER[247] = '\u00f7'; // ( -> ) MAPPER[248] = '\u00f8'; // ( -> ) MAPPER[249] = '\u0075'; // ( -> u) MAPPER[250] = '\u0075'; // ( -> u) MAPPER[251] = '\u0075'; // ( -> u) MAPPER[252] = '\u0075'; // ( -> u) MAPPER[253] = '\u0079'; // ( -> y) MAPPER[254] = '\u00fe'; // ( -> ) MAPPER[255] = '\u0079'; // ( -> y) MAPPER[256] = '\u0041'; // (? -> A) MAPPER[257] = '\u0061'; // (? -> a) MAPPER[258] = '\u0041'; // (? -> A) MAPPER[259] = '\u0061'; // (? -> a) MAPPER[260] = '\u0041'; // (? -> A) MAPPER[261] = '\u0061'; // (? -> a) MAPPER[262] = '\u0043'; // (? -> C) MAPPER[263] = '\u0063'; // (? -> c) MAPPER[264] = '\u0043'; // (? -> C) MAPPER[265] = '\u0063'; // (? -> c) MAPPER[266] = '\u0043'; // (? -> C) MAPPER[267] = '\u0063'; // (? -> c) MAPPER[268] = '\u0043'; // (? -> C) MAPPER[269] = '\u0063'; // (? -> c) MAPPER[270] = '\u0044'; // (? -> D) MAPPER[271] = '\u0064'; // (? -> d) MAPPER[272] = '\u0110'; // (? -> ?) MAPPER[273] = '\u0111'; // (? -> ?) MAPPER[274] = '\u0045'; // (? -> E) MAPPER[275] = '\u0065'; // (? -> e) MAPPER[276] = '\u0045'; // (? -> E) MAPPER[277] = '\u0065'; // (? -> e) MAPPER[278] = '\u0045'; // (? -> E) MAPPER[279] = '\u0065'; // (? -> e) MAPPER[280] = '\u0045'; // (? -> E) MAPPER[281] = '\u0065'; // (? -> e) MAPPER[282] = '\u0045'; // (? -> E) MAPPER[283] = '\u0065'; // (? -> e) MAPPER[284] = '\u0047'; // (? -> G) MAPPER[285] = '\u0067'; // (? -> g) MAPPER[286] = '\u0047'; // (? -> G) MAPPER[287] = '\u0067'; // (? -> g) MAPPER[288] = '\u0047'; // (? -> G) MAPPER[289] = '\u0067'; // (? -> g) MAPPER[290] = '\u0047'; // (? -> G) MAPPER[291] = '\u0067'; // (? -> g) MAPPER[292] = '\u0048'; // (? -> H) MAPPER[293] = '\u0068'; // (? -> h) MAPPER[294] = '\u0126'; // (? -> ?) MAPPER[295] = '\u0127'; // (? -> ?) MAPPER[296] = '\u0049'; // (? -> I) MAPPER[297] = '\u0069'; // (? -> i) MAPPER[298] = '\u0049'; // (? -> I) MAPPER[299] = '\u0069'; // (? -> i) MAPPER[300] = '\u0049'; // (? -> I) MAPPER[301] = '\u0069'; // (? -> i) MAPPER[302] = '\u0049'; // (? -> I) MAPPER[303] = '\u0069'; // (? -> i) MAPPER[304] = '\u0049'; // (? -> I) MAPPER[305] = '\u0131'; // (? -> ?) MAPPER[306] = '\u0132'; // (? -> ?) MAPPER[307] = '\u0133'; // (? -> ?) MAPPER[308] = '\u004a'; // (? -> J) MAPPER[309] = '\u006a'; // (? -> j) MAPPER[310] = '\u004b'; // (? -> K) MAPPER[311] = '\u006b'; // (? -> k) MAPPER[312] = '\u0138'; // (? -> ?) MAPPER[313] = '\u004c'; // (? -> L) MAPPER[314] = '\u006c'; // (? -> l) MAPPER[315] = '\u004c'; // (? -> L) MAPPER[316] = '\u006c'; // (? -> l) MAPPER[317] = '\u004c'; // (? -> L) MAPPER[318] = '\u006c'; // (? -> l) MAPPER[319] = '\u013f'; // (? -> ?) MAPPER[320] = '\u0140'; // (? -> ?) MAPPER[321] = '\u0141'; // (? -> ?) MAPPER[322] = '\u0142'; // (? -> ?) MAPPER[323] = '\u004e'; // (? -> N) MAPPER[324] = '\u006e'; // (? -> n) MAPPER[325] = '\u004e'; // (? -> N) MAPPER[326] = '\u006e'; // (? -> n) MAPPER[327] = '\u004e'; // (? -> N) MAPPER[328] = '\u006e'; // (? -> n) MAPPER[329] = '\u0149'; // (? -> ?) MAPPER[330] = '\u014a'; // (? -> ?) MAPPER[331] = '\u014b'; // (? -> ?) MAPPER[332] = '\u004f'; // (? -> O) MAPPER[333] = '\u006f'; // (? -> o) MAPPER[334] = '\u004f'; // (? -> O) MAPPER[335] = '\u006f'; // (? -> o) MAPPER[336] = '\u004f'; // (? -> O) MAPPER[337] = '\u006f'; // (? -> o) MAPPER[338] = '\u0152'; // ( -> ) MAPPER[339] = '\u0153'; // ( -> ) MAPPER[340] = '\u0052'; // (? -> R) MAPPER[341] = '\u0072'; // (? -> r) MAPPER[342] = '\u0052'; // (? -> R) MAPPER[343] = '\u0072'; // (? -> r) MAPPER[344] = '\u0052'; // (? -> R) MAPPER[345] = '\u0072'; // (? -> r) MAPPER[346] = '\u0053'; // (? -> S) MAPPER[347] = '\u0073'; // (? -> s) MAPPER[348] = '\u0053'; // (? -> S) MAPPER[349] = '\u0073'; // (? -> s) MAPPER[350] = '\u0053'; // (? -> S) MAPPER[351] = '\u0073'; // (? -> s) MAPPER[352] = '\u0053'; // ( -> S) MAPPER[353] = '\u0073'; // ( -> s) MAPPER[354] = '\u0054'; // (? -> T) MAPPER[355] = '\u0074'; // (? -> t) MAPPER[356] = '\u0054'; // (? -> T) MAPPER[357] = '\u0074'; // (? -> t) MAPPER[358] = '\u0166'; // (? -> ?) MAPPER[359] = '\u0167'; // (? -> ?) MAPPER[360] = '\u0055'; // (? -> U) MAPPER[361] = '\u0075'; // (? -> u) MAPPER[362] = '\u0055'; // (? -> U) MAPPER[363] = '\u0075'; // (? -> u) MAPPER[364] = '\u0055'; // (? -> U) MAPPER[365] = '\u0075'; // (? -> u) MAPPER[366] = '\u0055'; // (? -> U) MAPPER[367] = '\u0075'; // (? -> u) MAPPER[368] = '\u0055'; // (? -> U) MAPPER[369] = '\u0075'; // (? -> u) MAPPER[370] = '\u0055'; // (? -> U) MAPPER[371] = '\u0075'; // (? -> u) MAPPER[372] = '\u0057'; // (? -> W) MAPPER[373] = '\u0077'; // (? -> w) MAPPER[374] = '\u0059'; // (? -> Y) MAPPER[375] = '\u0079'; // (? -> y) MAPPER[376] = '\u0059'; // ( -> Y) MAPPER[377] = '\u005a'; // (? -> Z) MAPPER[378] = '\u007a'; // (? -> z) MAPPER[379] = '\u005a'; // (? -> Z) MAPPER[380] = '\u007a'; // (? -> z) MAPPER[381] = '\u005a'; // ( -> Z) MAPPER[382] = '\u007a'; // ( -> z) MAPPER[383] = '\u017f'; // (? -> ?) MAPPER[384] = '\u0180'; // (? -> ?) MAPPER[385] = '\u0181'; // (? -> ?) MAPPER[386] = '\u0182'; // (? -> ?) MAPPER[387] = '\u0183'; // (? -> ?) MAPPER[388] = '\u0184'; // (? -> ?) MAPPER[389] = '\u0185'; // (? -> ?) MAPPER[390] = '\u0186'; // (? -> ?) MAPPER[391] = '\u0187'; // (? -> ?) MAPPER[392] = '\u0188'; // (? -> ?) MAPPER[393] = '\u0189'; // (? -> ?) MAPPER[394] = '\u018a'; // (? -> ?) MAPPER[395] = '\u018b'; // (? -> ?) MAPPER[396] = '\u018c'; // (? -> ?) MAPPER[397] = '\u018d'; // (? -> ?) MAPPER[398] = '\u018e'; // (? -> ?) MAPPER[399] = '\u018f'; // (? -> ?) MAPPER[400] = '\u0190'; // (? -> ?) MAPPER[401] = '\u0191'; // (? -> ?) MAPPER[402] = '\u0192'; // ( -> ) MAPPER[403] = '\u0193'; // (? -> ?) MAPPER[404] = '\u0194'; // (? -> ?) MAPPER[405] = '\u0195'; // (? -> ?) MAPPER[406] = '\u0196'; // (? -> ?) MAPPER[407] = '\u0197'; // (? -> ?) MAPPER[408] = '\u0198'; // (? -> ?) MAPPER[409] = '\u0199'; // (? -> ?) MAPPER[410] = '\u019a'; // (? -> ?) MAPPER[411] = '\u019b'; // (? -> ?) MAPPER[412] = '\u019c'; // (? -> ?) MAPPER[413] = '\u019d'; // (? -> ?) MAPPER[414] = '\u019e'; // (? -> ?) MAPPER[415] = '\u019f'; // (? -> ?) MAPPER[416] = '\u004f'; // (? -> O) MAPPER[417] = '\u006f'; // (? -> o) MAPPER[418] = '\u01a2'; // (? -> ?) MAPPER[419] = '\u01a3'; // (? -> ?) MAPPER[420] = '\u01a4'; // (? -> ?) MAPPER[421] = '\u01a5'; // (? -> ?) MAPPER[422] = '\u01a6'; // (? -> ?) MAPPER[423] = '\u01a7'; // (? -> ?) MAPPER[424] = '\u01a8'; // (? -> ?) MAPPER[425] = '\u01a9'; // (? -> ?) MAPPER[426] = '\u01aa'; // (? -> ?) MAPPER[427] = '\u01ab'; // (? -> ?) MAPPER[428] = '\u01ac'; // (? -> ?) MAPPER[429] = '\u01ad'; // (? -> ?) MAPPER[430] = '\u01ae'; // (? -> ?) MAPPER[431] = '\u0055'; // (? -> U) MAPPER[432] = '\u0075'; // (? -> u) MAPPER[433] = '\u01b1'; // (? -> ?) MAPPER[434] = '\u01b2'; // (? -> ?) MAPPER[435] = '\u01b3'; // (? -> ?) MAPPER[436] = '\u01b4'; // (? -> ?) MAPPER[437] = '\u01b5'; // (? -> ?) MAPPER[438] = '\u01b6'; // (? -> ?) MAPPER[439] = '\u01b7'; // (? -> ?) MAPPER[440] = '\u01b8'; // (? -> ?) MAPPER[441] = '\u01b9'; // (? -> ?) MAPPER[442] = '\u01ba'; // (? -> ?) MAPPER[443] = '\u01bb'; // (? -> ?) MAPPER[444] = '\u01bc'; // (? -> ?) MAPPER[445] = '\u01bd'; // (? -> ?) MAPPER[446] = '\u01be'; // (? -> ?) MAPPER[447] = '\u01bf'; // (? -> ?) MAPPER[448] = '\u01c0'; // (? -> ?) MAPPER[449] = '\u01c1'; // (? -> ?) MAPPER[450] = '\u01c2'; // (? -> ?) MAPPER[451] = '\u01c3'; // (? -> ?) MAPPER[452] = '\u01c4'; // (? -> ?) MAPPER[453] = '\u01c5'; // (? -> ?) MAPPER[454] = '\u01c6'; // (? -> ?) MAPPER[455] = '\u01c7'; // (? -> ?) MAPPER[456] = '\u01c8'; // (? -> ?) MAPPER[457] = '\u01c9'; // (? -> ?) MAPPER[458] = '\u01ca'; // (? -> ?) MAPPER[459] = '\u01cb'; // (? -> ?) MAPPER[460] = '\u01cc'; // (? -> ?) MAPPER[461] = '\u0041'; // (? -> A) MAPPER[462] = '\u0061'; // (? -> a) MAPPER[463] = '\u0049'; // (? -> I) MAPPER[464] = '\u0069'; // (? -> i) MAPPER[465] = '\u004f'; // (? -> O) MAPPER[466] = '\u006f'; // (? -> o) MAPPER[467] = '\u0055'; // (? -> U) MAPPER[468] = '\u0075'; // (? -> u) MAPPER[469] = '\u0055'; // (? -> U) MAPPER[470] = '\u0075'; // (? -> u) MAPPER[471] = '\u0055'; // (? -> U) MAPPER[472] = '\u0075'; // (? -> u) MAPPER[473] = '\u0055'; // (? -> U) MAPPER[474] = '\u0075'; // (? -> u) MAPPER[475] = '\u0055'; // (? -> U) MAPPER[476] = '\u0075'; // (? -> u) MAPPER[477] = '\u01dd'; // (? -> ?) MAPPER[478] = '\u0041'; // (? -> A) MAPPER[479] = '\u0061'; // (? -> a) MAPPER[480] = '\u0041'; // (? -> A) MAPPER[481] = '\u0061'; // (? -> a) MAPPER[482] = '\u00c6'; // (? -> ) MAPPER[483] = '\u00e6'; // (? -> ) MAPPER[484] = '\u01e4'; // (? -> ?) MAPPER[485] = '\u01e5'; // (? -> ?) MAPPER[486] = '\u0047'; // (? -> G) MAPPER[487] = '\u0067'; // (? -> g) MAPPER[488] = '\u004b'; // (? -> K) MAPPER[489] = '\u006b'; // (? -> k) MAPPER[490] = '\u004f'; // (? -> O) MAPPER[491] = '\u006f'; // (? -> o) MAPPER[492] = '\u004f'; // (? -> O) MAPPER[493] = '\u006f'; // (? -> o) MAPPER[494] = '\u01b7'; // (? -> ?) MAPPER[495] = '\u0292'; // (? -> ?) MAPPER[496] = '\u006a'; // (? -> j) MAPPER[497] = '\u01f1'; // (? -> ?) MAPPER[498] = '\u01f2'; // (? -> ?) MAPPER[499] = '\u01f3'; // (? -> ?) MAPPER[500] = '\u0047'; // (? -> G) MAPPER[501] = '\u0067'; // (? -> g) MAPPER[502] = '\u01f6'; // (? -> ?) MAPPER[503] = '\u01f7'; // (? -> ?) MAPPER[504] = '\u004e'; // (? -> N) MAPPER[505] = '\u006e'; // (? -> n) MAPPER[506] = '\u0041'; // (? -> A) MAPPER[507] = '\u0061'; // (? -> a) MAPPER[508] = '\u00c6'; // (? -> ) MAPPER[509] = '\u00e6'; // (? -> ) MAPPER[510] = '\u00d8'; // (? -> ) MAPPER[511] = '\u00f8'; // (? -> ) MAPPER[512] = '\u0041'; // (? -> A) MAPPER[513] = '\u0061'; // (? -> a) MAPPER[514] = '\u0041'; // (? -> A) MAPPER[515] = '\u0061'; // (? -> a) MAPPER[516] = '\u0045'; // (? -> E) MAPPER[517] = '\u0065'; // (? -> e) MAPPER[518] = '\u0045'; // (? -> E) MAPPER[519] = '\u0065'; // (? -> e) MAPPER[520] = '\u0049'; // (? -> I) MAPPER[521] = '\u0069'; // (? -> i) MAPPER[522] = '\u0049'; // (? -> I) MAPPER[523] = '\u0069'; // (? -> i) MAPPER[524] = '\u004f'; // (? -> O) MAPPER[525] = '\u006f'; // (? -> o) MAPPER[526] = '\u004f'; // (? -> O) MAPPER[527] = '\u006f'; // (? -> o) MAPPER[528] = '\u0052'; // (? -> R) MAPPER[529] = '\u0072'; // (? -> r) MAPPER[530] = '\u0052'; // (? -> R) MAPPER[531] = '\u0072'; // (? -> r) MAPPER[532] = '\u0055'; // (? -> U) MAPPER[533] = '\u0075'; // (? -> u) MAPPER[534] = '\u0055'; // (? -> U) MAPPER[535] = '\u0075'; // (? -> u) MAPPER[536] = '\u0053'; // (? -> S) MAPPER[537] = '\u0073'; // (? -> s) MAPPER[538] = '\u0054'; // (? -> T) MAPPER[539] = '\u0074'; // (? -> t) MAPPER[540] = '\u021c'; // (? -> ?) MAPPER[541] = '\u021d'; // (? -> ?) MAPPER[542] = '\u0048'; // (? -> H) MAPPER[543] = '\u0068'; // (? -> h) MAPPER[544] = '\u0220'; // (? -> ?) MAPPER[545] = '\u0221'; // (? -> ?) MAPPER[546] = '\u0222'; // (? -> ?) MAPPER[547] = '\u0223'; // (? -> ?) MAPPER[548] = '\u0224'; // (? -> ?) MAPPER[549] = '\u0225'; // (? -> ?) MAPPER[550] = '\u0041'; // (? -> A) MAPPER[551] = '\u0061'; // (? -> a) MAPPER[552] = '\u0045'; // (? -> E) MAPPER[553] = '\u0065'; // (? -> e) MAPPER[554] = '\u004f'; // (? -> O) MAPPER[555] = '\u006f'; // (? -> o) MAPPER[556] = '\u004f'; // (? -> O) MAPPER[557] = '\u006f'; // (? -> o) MAPPER[558] = '\u004f'; // (? -> O) MAPPER[559] = '\u006f'; // (? -> o) MAPPER[560] = '\u004f'; // (? -> O) MAPPER[561] = '\u006f'; // (? -> o) MAPPER[562] = '\u0059'; // (? -> Y) MAPPER[563] = '\u0079'; // (? -> y) MAPPER[564] = '\u0234'; // (? -> ?) MAPPER[565] = '\u0235'; // (? -> ?) MAPPER[566] = '\u0236'; // (? -> ?) MAPPER[567] = '\u0237'; // (? -> ?) MAPPER[568] = '\u0238'; // (? -> ?) MAPPER[569] = '\u0239'; // (? -> ?) MAPPER[570] = '\u023a'; // (? -> ?) MAPPER[571] = '\u023b'; // (? -> ?) MAPPER[572] = '\u023c'; // (? -> ?) MAPPER[573] = '\u023d'; // (? -> ?) MAPPER[574] = '\u023e'; // (? -> ?) MAPPER[575] = '\u023f'; // (? -> ?) MAPPER[576] = '\u0240'; // (? -> ?) MAPPER[577] = '\u0241'; // (? -> ?) MAPPER[578] = '\u0242'; // (? -> ?) MAPPER[579] = '\u0243'; // (? -> ?) MAPPER[580] = '\u0244'; // (? -> ?) MAPPER[581] = '\u0245'; // (? -> ?) MAPPER[582] = '\u0246'; // (? -> ?) MAPPER[583] = '\u0247'; // (? -> ?) MAPPER[584] = '\u0248'; // (? -> ?) MAPPER[585] = '\u0249'; // (? -> ?) MAPPER[586] = '\u024a'; // (? -> ?) MAPPER[587] = '\u024b'; // (? -> ?) MAPPER[588] = '\u024c'; // (? -> ?) MAPPER[589] = '\u024d'; // (? -> ?) MAPPER[590] = '\u024e'; // (? -> ?) MAPPER[591] = '\u024f'; // (? -> ?) } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/SeparatorList.java0000644000175000017500000011613212106516400027571 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.impl.Grouper; import ca.odell.glazedlists.impl.adt.Barcode; import ca.odell.glazedlists.impl.adt.BarcodeIterator; import ca.odell.glazedlists.impl.adt.barcode2.Element; import ca.odell.glazedlists.impl.adt.barcode2.SimpleTree; import ca.odell.glazedlists.impl.adt.barcode2.SimpleTreeIterator; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * A list that adds separator objects before each group of elements. * *

      SeparatorList is writable, however, attempts to write over separators * will always produce an {@link IllegalArgumentException}. For example, * calling {@link #add(int, Object)}, {@link #set(int, Object)} or * {@link #remove(int)} with a an index that actually corresponds to a * separator in this list will produce an {@link IllegalArgumentException}. * This is because there is no corresponding index for separators in the source * list; separators are added by this SeparatorList. All index-based write * operations must be performed on indexes known to correspond to non-separator * elements. * *

      Warning: this class won't work very well with generics * because separators are mixed in, which will be a different class than the * other list elements. * *

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

      Developer Preview this class is still under heavy development * and subject to API changes. It's also really slow at the moment and won't scale * to lists of size larger than a hundred or so efficiently. * * @author Jesse Wilson */ public class SeparatorList extends TransformedList { /** delegate to an inner class to insert the separators */ private SeparatorInjectorList separatorSource; private static final Object SEPARATOR = Barcode.BLACK; private static final Object SOURCE_ELEMENT = Barcode.WHITE; /** how many elements before we get a separator, such as 1 or 2 */ private final int minimumSizeForSeparator; /** manage collapsed elements */ private Barcode collapsedElements; /** * Construct a SeparatorList overtop of the source list by * using the given comparator to compute groups of similar * source items. For each group a single separator will be present in this * SeparatorList provided the group contains at least the * minimumSizeForSeparator number of items (otherwise they are * left without a separator). In addition this SeparatorList will never * show more than the defaultLimit number of group elements * from any given group. * * @param source the list containing the raw items to be grouped * @param comparator the Comparator which defines the grouping logic * @param minimumSizeForSeparator the number of elements which must exist * in a group in order for a separator to be created * @param defaultLimit the maximum number of element to display for a group; * extra elements are truncated */ public SeparatorList(EventList source, Comparator comparator, int minimumSizeForSeparator, int defaultLimit) { super(new SeparatorInjectorList(new SortedList(source, comparator), defaultLimit)); this.separatorSource = (SeparatorInjectorList)super.source; this.minimumSizeForSeparator = minimumSizeForSeparator; // prepare the collapsed elements rebuildCollapsedElements(); // handle changes to the separators list this.separatorSource.addListEventListener(this); } /** * Rebuild the entire collapsed elements barcode. */ private void rebuildCollapsedElements() { collapsedElements = new Barcode(); collapsedElements.addBlack(0, separatorSource.size()); int groupCount = separatorSource.insertedSeparators.colourSize(SEPARATOR); for(int i = 0; i < groupCount; i++) { updateGroup(i, groupCount, false); } } /** {@inheritDoc} */ @Override public int size() { return collapsedElements.colourSize(Barcode.BLACK); } /** {@inheritDoc} */ @Override protected int getSourceIndex(int mutationIndex) { return collapsedElements.getIndex(mutationIndex, Barcode.BLACK); } /** {@inheritDoc} */ @Override protected boolean isWritable() { return true; } /** * Set the {@link Comparator} used to determine how elements are split * into groups. * *

      Performance Note: sorting will take O(N * Log N) time. * *

      Warning: This method is * thread ready but not thread safe. See {@link EventList} for an example * of thread safe code. */ public void setComparator(Comparator comparator) { final boolean isEmpty = isEmpty(); if (!isEmpty) { // this implementation loses selection, but that's the best we can do // with the current limitations of the Glazed Lists ListEventAssembler. // What we really need here is the ability to fire an event that contains // both reordering and structure change information. updates.beginEvent(); // remove all updates.addDelete(0, size() - 1); } // make the change to the sorted source, the grouper will respond but // the {@link SeparatorInjectorList} doesn't fire any events forward when // its main Comparator is changed SortedList sortedList = (SortedList)separatorSource.source; sortedList.setComparator(comparator); if (!isEmpty) { // rebuild which elements are collapsed out rebuildCollapsedElements(); // insert all again updates.addInsert(0, size() - 1); updates.commitEvent(); } } /** * Go from the current group (assumed to be black) to the next black group * to follow. This works by finding a white follower, then a black follower * of that one. * * @return true if the next group was found, or false * if there was no such group and the iterator is now in an unspecified * location, not necessarily the end of the barcode. */ private static boolean nextBlackGroup(BarcodeIterator iterator) { // step to an intermediate white group if(!iterator.hasNextWhite()) return false; iterator.nextWhite(); // then to the following black group to get a completely different group if(!iterator.hasNextBlack()) return false; iterator.nextBlack(); // success! return true; } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { updates.beginEvent(true); // when the source changes order, forward a reordering if no elements // go from being outside the limit filter to inside it if(listChanges.isReordering()) { boolean canReorder = true; for(SimpleTreeIterator.GroupSeparator> i = new SimpleTreeIterator.GroupSeparator>(separatorSource.separators); i.hasNext(); ) { i.next(); Element.GroupSeparator> node = i.node(); int limit = node.get().getLimit(); if(limit == 0) continue; if(limit >= separatorSource.size()) continue; if(limit >= node.get().size()) continue; canReorder = false; break; } // forward the reorder event, this requires a lot of rework because // we're mapping backwards and fowards unnecessarily if(canReorder) { int[] previousIndices = listChanges.getReorderMap(); int[] reorderMap = new int[collapsedElements.colourSize(Barcode.BLACK)]; // walk through the unfiltered elements, adjusting the indices // for each group of unfiltered (black) elements BarcodeIterator i = collapsedElements.iterator(); int groupStartSourceIndex = 0; while(true) { // we already know where this group starts, now we calculate // where it ends, and how many indices it's offset by in the view boolean newGroupFound; int groupEndSourceIndex; int leadingCollapsedElements; if(i.hasNextWhite()) { i.nextWhite(); groupEndSourceIndex = i.getIndex(); newGroupFound = true; leadingCollapsedElements = i.getWhiteIndex(); } else { newGroupFound = false; groupEndSourceIndex = collapsedElements.size(); leadingCollapsedElements = collapsedElements.whiteSize(); } // update the reorder map for each element in this group for(int j = groupStartSourceIndex; j < groupEndSourceIndex; j++) { reorderMap[j - leadingCollapsedElements] = previousIndices[j] - leadingCollapsedElements; } // prepare the next iteration: find the start of the next group if(newGroupFound && i.hasNextBlack()) { i.nextBlack(); groupStartSourceIndex = i.getIndex(); } else { break; } } updates.reorder(reorderMap); // fire insert/delete pairs. This loses selection because currently // Glazed Lists lacks the ability to fire a mix of move and insert/update // events } else { int size = collapsedElements.colourSize(Barcode.BLACK); if(size > 0) { updates.addDelete(0, size - 1); updates.addInsert(0, size - 1); } } // handle other changes by adjusting the limits as necessary } else { // keep this around, it's handy int groupCount = separatorSource.insertedSeparators.colourSize(SEPARATOR); // first update the barcode, optimistically while(listChanges.next()) { int changeIndex = listChanges.getIndex(); int changeType = listChanges.getType(); // if we're inserting something new, always fire an insert event, // even if we need to revoke it later if(changeType == ListEvent.INSERT) { collapsedElements.add(changeIndex, Barcode.BLACK, 1); int viewIndex = collapsedElements.getColourIndex(changeIndex, Barcode.BLACK); updates.addInsert(viewIndex); // updates are probably already accurate, don't change the state } else if(changeType == ListEvent.UPDATE) { // if its visible, fire an update event if(collapsedElements.get(changeIndex) == Barcode.BLACK) { int viewIndex = collapsedElements.getColourIndex(changeIndex, Barcode.BLACK); updates.addUpdate(viewIndex); } // fire a delete event if this is a visible element being deleted } else if(changeType == ListEvent.DELETE) { Object oldColor = collapsedElements.get(changeIndex); if(oldColor == Barcode.BLACK) { int viewIndex = collapsedElements.getColourIndex(changeIndex, Barcode.BLACK); updates.addDelete(viewIndex); } collapsedElements.remove(changeIndex, 1); } } // Now make sure our limits are correct, which they may not be // due to the fact that we made a lot of guesses in the first pass. // Note that this is really slow and needs some work for performance // reasons listChanges.reset(); while(listChanges.next()) { int changeIndex = listChanges.getIndex(); int changeType = listChanges.getType(); if(changeType == ListEvent.INSERT) { int group = separatorSource.insertedSeparators.getColourIndex(changeIndex, true, SEPARATOR); updateGroup(group, groupCount, true); } else if(changeType == ListEvent.UPDATE) { int group = separatorSource.insertedSeparators.getColourIndex(changeIndex, true, SEPARATOR); // it's possible that this impacts the previous group! if(group > 0) updateGroup(group - 1, groupCount, true); updateGroup(group, groupCount, true); // it's possible that this impacts the next group if(group < groupCount - 1) updateGroup(group + 1, groupCount, true); } else if(changeType == ListEvent.DELETE) { // if there is a group that this came from, update it if(changeIndex < separatorSource.insertedSeparators.size()) { int group = separatorSource.insertedSeparators.getColourIndex(changeIndex, true, SEPARATOR); updateGroup(group, groupCount, true); } } } } updates.commitEvent(); } /** * Update all elements in the specified group. We need to refine this method * since currently it does a linear scan through the group's elements, and * that just won't do for performance requirements. */ private void updateGroup(int group, int groupCount, boolean fireEvents) { Separator separator = separatorSource.separators.get(group).get(); int limit = separator.getLimit(); // fix up this separator int separatorStart = separatorSource.insertedSeparators.getIndex(group, SEPARATOR); int nextGroup = group + 1; int separatorEnd = nextGroup == groupCount ? separatorSource.insertedSeparators.size() : separatorSource.insertedSeparators.getIndex(nextGroup, SEPARATOR); int size = separatorEnd - separatorStart - 1; // if this is too small to show a separator if(size < minimumSizeForSeparator) { // remove the separator setVisible(separatorStart, Barcode.WHITE, fireEvents); // everything else must be visible for(int i = separatorStart + 1; i < separatorEnd; i++) { setVisible(i, Barcode.BLACK, fireEvents); } // if this is different than the limit } else { // show the separator setVisible(separatorStart, Barcode.BLACK, fireEvents); // show everything up to the limit and nothing after for(int i = separatorStart + 1; i < separatorEnd; i++) { boolean withinLimit = i - separatorStart <= limit; setVisible(i, withinLimit ? Barcode.BLACK : Barcode.WHITE, fireEvents); } } } /** * Update the visible state of the specified element. */ private void setVisible(int index, Object colour, boolean fireEvents) { Object previousColour = collapsedElements.get(index); // no change if(colour == previousColour) { return; // hide this element } else if(colour == Barcode.WHITE) { int viewIndex = collapsedElements.getColourIndex(index, Barcode.BLACK); if(fireEvents) updates.addDelete(viewIndex); collapsedElements.set(index, Barcode.WHITE, 1); // show this element } else if(colour == Barcode.BLACK) { collapsedElements.set(index, Barcode.BLACK, 1); int viewIndex = collapsedElements.getColourIndex(index, Barcode.BLACK); if(fireEvents) updates.addInsert(viewIndex); } else { throw new IllegalArgumentException(); } } /** {@inheritDoc} */ @Override public void dispose() { // dispose internal SeparatorInjectorList and SortedList separatorSource.dispose(); separatorSource.source.dispose(); super.dispose(); } /** * A separator heading the elements of a group. */ public interface Separator { /** * Get the maximum number of elements in this group to show. */ public int getLimit(); /** * Set the maximum number of elements in this group to show. This is * useful to collapse a group (limit of 0), cap the elements of a group * (limit of 5) or reverse those actions. * *

      This method requires the write lock of the {@link SeparatorList} to be * held during invocation. */ public void setLimit(int limit); /** * Get the {@link List} of all elements in this group. * *

      This method requires the read lock of the {@link SeparatorList} * to be held during invocation. */ public List getGroup(); /** * A convenience method to get the first element from this group. This * is useful to render the separator's name. */ public E first(); /** * A convenience method to get the number of elements in this group. This * is useful to render the separator. */ public int size(); } /** * This inner class handles the insertion of separators * as a separate transformation from the hiding of separators * and collapsed elements. */ static class SeparatorInjectorList extends TransformedList { /** the grouping service manages finding where to insert groups */ private final Grouper grouper; /** * The separators list is black for separators, white for * everything else. * *

      The following demonstrates the layout of the barcode for the * given source list: *

      
               *           INDICES 0         1         2
               *                   012345678901234567890
               *       SOURCE LIST AAAABBBCCCDEFF
               *   GROUPER BARCODE X___X__X__XXX_
               * SEPARATOR BARCODE X____X___X___X_X_X__
               * 
      * *

      To read this structure: *

    • the grouper barcode is an "X" for the first element in each * group (called uniques), and an "_" for the following * elements (called duplicates). *
    • the separator barcode is very similar to the grouper barcode. * In this barcode, there is an "X" for each separator and an "_" * for each element in the source list. We use the structure of the * grouper barcode to derive and maintain the separator barcode. * *

      When accessing elements, the separator barcode is queried. If it * holds an "X", the element is a separator and that separator is returned. * Otherwise if it is an "_", the corresponding source index is obtained * (by removing the number of preceding "X" elements) and the element is * retrieved from the source list. */ private Barcode insertedSeparators; /** a list of {@link Separator}s, one for each separator in the list */ private SimpleTree separators; /** the number of elements to show in each group, such as 0, 5, or {@link Integer#MAX_VALUE} */ private int defaultLimit; /** * Create a new {@link SeparatorInjectorList} that groups together like * items using the Comparator from the source. Elements * that the {@link Comparator} determines are equal will share a common * separator. * * @param source the sorted list of items with a Comparator that defines * the group boundaries * @param defaultLimit the maximum number of items to include in a * group; all remaining items will be truncated */ public SeparatorInjectorList(SortedList source, int defaultLimit) { super(source); this.defaultLimit = defaultLimit; // prepare the groups GrouperClient grouperClient = new GrouperClient(); this.grouper = new Grouper(source, grouperClient); // initialize separators state rebuildSeparators(); // handle changes via the grouper source.addListEventListener(this); } /** * Statically build the separators data structures. */ private void rebuildSeparators() { // clear the initial state of these separators insertedSeparators = new Barcode(); separators = new SimpleTree(); // prepare the separator list insertedSeparators.add(0, SOURCE_ELEMENT, source.size()); for(BarcodeIterator i = grouper.getBarcode().iterator(); i.hasNextColour(Grouper.UNIQUE); ) { i.nextColour(Grouper.UNIQUE); int groupIndex = i.getColourIndex(Grouper.UNIQUE); int sourceIndex = i.getIndex(); insertedSeparators.add(groupIndex + sourceIndex, SEPARATOR, 1); Element node = separators.add(groupIndex, new GroupSeparator(), 1); node.get().setNode(node); node.get().applyLimit(defaultLimit, false); } // update the cached values in all separators for(int i = 0; i < separators.size(); i++) { separators.get(i).get().updateCachedValues(); } } /** {@inheritDoc} */ @Override public E get(int index) { Object type = insertedSeparators.get(index); if(type == SEPARATOR) return (E)separators.get(getSeparatorIndex(index)).get(); else if(type == SOURCE_ELEMENT) return source.get(getSourceIndex(index)); else throw new IllegalStateException(); } /** {@inheritDoc} */ @Override protected int getSourceIndex(int mutationIndex) { Object type = insertedSeparators.get(mutationIndex); if(type == SEPARATOR) throw new IllegalArgumentException("No source index exists for the separator located at index " + mutationIndex); else if(type == SOURCE_ELEMENT) return insertedSeparators.getColourIndex(mutationIndex, SOURCE_ELEMENT); else throw new IllegalStateException(); } protected int getSeparatorIndex(int mutationIndex) { Object type = insertedSeparators.get(mutationIndex); if(type == SEPARATOR) return insertedSeparators.getColourIndex(mutationIndex, SEPARATOR); else if(type == SOURCE_ELEMENT) return -1; else throw new IllegalStateException(); } /** {@inheritDoc} */ @Override protected boolean isWritable() { return true; } /** {@inheritDoc} */ @Override public int size() { return insertedSeparators.size(); } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { // when the separator comparator is changed in the source list, let // the grouper know so we can rebuild our groups, then bail since // the {@link SeparatorList} already knows about this event SortedList sortedSource = (SortedList) source; Comparator sourceComparator = sortedSource.getComparator(); if(sourceComparator != grouper.getComparator()) { grouper.setComparator(sourceComparator); rebuildSeparators(); return; } updates.beginEvent(true); // reorderings should be contained within the existing groups, we // need to send these reorderings forward if(listChanges.isReordering()) { int[] previousIndices = listChanges.getReorderMap(); int[] reorderMap = new int[insertedSeparators.size()]; // walk through each group, adjusting indices in the forward // reorder map to notify listeners int groupStartIndex = -1; // inclusive int groupEndIndex = 0; // exclusive int group = -1; for(int i = 0; i < previousIndices.length; i++) { // if this is the start of a new group, add that to the reorder map if(i == groupEndIndex) { group++; reorderMap[i + group] = i + group; groupStartIndex = groupEndIndex; int nextGroup = group + 1; groupEndIndex = nextGroup < separators.size() ? separators.get(nextGroup).get().start() : insertedSeparators.size(); } // make sure the move doesn't leave the group int previousIndex = previousIndices[i]; if(previousIndex < groupStartIndex || previousIndex >= groupEndIndex) { throw new IllegalStateException(); } // adjust this change within the current group reorderMap[i + group + 1] = previousIndex + group + 1; } updates.reorder(reorderMap); // handle regular changes by adjusting the separators via our grouper } else { grouper.listChanged(listChanges); } // update the cached values in all separators for(int i = 0; i < separators.size(); i++) { separators.get(i).get().updateCachedValues(); } updates.commitEvent(); } /** * Fire two events, one for the group (the separator) and another for the * actual list element. */ private class GrouperClient implements Grouper.Client { public void groupChanged(int index, int groupIndex, int groupChangeType, boolean primary, int elementChangeType, E oldValue, E newValue) { boolean fixSeparatorForInsertGroupUpdateElement = false; // handle the group change first if(groupChangeType == ListEvent.INSERT) { int expandedIndex = index + groupIndex; insertedSeparators.add(expandedIndex, SEPARATOR, 1); updates.addInsert(expandedIndex); // add the separator and link the separator to its node Element node = separators.add(groupIndex, new GroupSeparator(), 1); node.get().setNode(node); node.get().setLimit(defaultLimit); } else if(groupChangeType == ListEvent.UPDATE) { int expandedIndex = insertedSeparators.getIndex(groupIndex, SEPARATOR); updates.addUpdate(expandedIndex); } else if(groupChangeType == ListEvent.DELETE) { int expandedIndex = insertedSeparators.getIndex(groupIndex, SEPARATOR); insertedSeparators.remove(expandedIndex, 1); updates.addDelete(expandedIndex); // invalidate the node Element node = separators.get(groupIndex); separators.remove(node); node.get().setNode(null); node.get().updateCachedValues(); groupIndex--; } // then handle the element change if(elementChangeType == ListEvent.INSERT) { int expandedIndex = index + groupIndex + 1; insertedSeparators.add(expandedIndex, SOURCE_ELEMENT, 1); updates.addInsert(expandedIndex); } else if(elementChangeType == ListEvent.UPDATE) { int expandedIndex = index + groupIndex + 1; // if we inserted a separator directly before an existing separator, // we must increase update index for element by one if (groupChangeType == ListEvent.INSERT) { int separatorCount = insertedSeparators.colourSize(SEPARATOR); if (groupIndex + 1 < separatorCount) { // separator at groupIndex is not the last one... int nextSeparatorsIndex = insertedSeparators.getIndex(groupIndex + 1, SEPARATOR); if (nextSeparatorsIndex == expandedIndex) { // separator at groupIndex + 1 is located at the element update position, // so increment update position by one... expandedIndex++; // ...and fix location of separator at groupIndex + 1 fixSeparatorForInsertGroupUpdateElement = true; } } } updates.addUpdate(expandedIndex); } else if(elementChangeType == ListEvent.DELETE) { int expandedIndex = index + groupIndex + 1; insertedSeparators.remove(expandedIndex, 1); updates.addDelete(expandedIndex); } if (fixSeparatorForInsertGroupUpdateElement) { // fix special case: // the location of separator at groupIndex + 1 must be increased by one int wrongSeparatorIndex = index + groupIndex + 1; assert wrongSeparatorIndex == insertedSeparators.getIndex(groupIndex + 1, SEPARATOR); insertedSeparators.remove(wrongSeparatorIndex, 1); updates.addDelete(wrongSeparatorIndex); insertedSeparators.add(wrongSeparatorIndex + 1, SEPARATOR, 1); updates.addInsert(wrongSeparatorIndex + 1); } // Special case out the shift operation. The Grouper automatically // handles shifts, but SeparatorList needs to manage them independently. // Here we respond to the grouper and the separators barcode getting // out of sync, and resolve that problem. // If this fix poses a problem, we might want to change the way // Grouper works to fire a special flag called 'shift' with the // value true whenever the group joined is a RIGHT_GROUP final int shiftGroupIndex = groupIndex + 1; if(groupChangeType == ListEvent.DELETE && elementChangeType != ListEvent.INSERT && shiftGroupIndex < insertedSeparators.colourSize(SEPARATOR) && shiftGroupIndex < grouper.getBarcode().colourSize(Grouper.UNIQUE)) { int collapsedGroupStartIndex = grouper.getBarcode().getIndex(shiftGroupIndex, Grouper.UNIQUE); int separatorsIndex = insertedSeparators.getIndex(shiftGroupIndex , SEPARATOR); //String was = insertedSeparators.toString(); if(collapsedGroupStartIndex + shiftGroupIndex < separatorsIndex) { insertedSeparators.remove(separatorsIndex, 1); updates.addDelete(separatorsIndex); insertedSeparators.add(collapsedGroupStartIndex + shiftGroupIndex, SEPARATOR, 1); updates.addInsert(collapsedGroupStartIndex + shiftGroupIndex); //String now = insertedSeparators.toString(); //System.out.println("Changed from " + was + " to " + now); } } // handle separator shifts for source list changes like AACCC -> AAACC or AAACC // -> AACCC: // an element at the beginning or end of an existing group is changed such that // it now should belong to the neighbour group, but the element doesn't change // its position in the SortedList // the grouper barcode adjusts correctly, but here we have to adjust the // separator positions accordingly if (groupChangeType == ListEvent.UPDATE && elementChangeType == ListEvent.UPDATE && shiftGroupIndex < insertedSeparators.colourSize(SEPARATOR) && shiftGroupIndex < grouper.getBarcode().colourSize(Grouper.UNIQUE)) { // when we have an element update and a group update we check and synchronize // the separator position of the next group with the help of the grouper barcode unique index int collapsedGroupStartIndex = grouper.getBarcode().getIndex(shiftGroupIndex, Grouper.UNIQUE); int separatorsIndex = insertedSeparators.getIndex(shiftGroupIndex , SEPARATOR); int calculatedSeparatorPos = collapsedGroupStartIndex + shiftGroupIndex; // String was = insertedSeparators.toString(); if (calculatedSeparatorPos != separatorsIndex) { // the separator position does not match the grouper barcode -> adjust it insertedSeparators.remove(separatorsIndex, 1); updates.addDelete(separatorsIndex); insertedSeparators.add(calculatedSeparatorPos, SEPARATOR, 1); // for the update event we have to account for the previous delete final int insertPos = (calculatedSeparatorPos < separatorsIndex) ? calculatedSeparatorPos : calculatedSeparatorPos - 1; updates.addInsert(insertPos); // String now = insertedSeparators.toString(); // System.out.println("Changed from " + was + " to " + now); } } } } /** * Implement the {@link Separator} interface in the most natural way. */ class GroupSeparator implements Separator { private int limit = Integer.MAX_VALUE; private int size; private E first; /** * The node allows the separator to figure out which * group in the overall list its representing. */ private Element node = null; /** {@inheritDoc} */ public int getLimit() { return limit; } /** {@inheritDoc} */ public void setLimit(int limit) { applyLimit(limit, true); } /** * Applies the maximum number of elements in this group to show. * * @param limit the limit * @param fireEvents flag to indicate if ListEvents should be fired or not */ protected void applyLimit(int limit, boolean fireEvents) { if(this.limit == limit) return; // fail gracefully if the node is null, that means this separator // has been removed from the list but its still visible to an editor if(node == null) { return; } this.limit = limit; if (fireEvents) { // notify the world of this separator change updates.beginEvent(); int groupIndex = separators.indexOfNode(node, (byte)1); int separatorIndex = insertedSeparators.getIndex(groupIndex, SEPARATOR); updates.addUpdate(separatorIndex); updates.commitEvent(); } } /** {@inheritDoc} */ public List getGroup() { if(node == null) return Collections.emptyList(); return source.subList(start(), end()); } /** {@inheritDoc} */ public E first() { return first; } /** {@inheritDoc} */ public int size() { return size; } /** * Set the {@link Element} that this {@link Separator} can * use to find its index in the overall list of {@link Separator}s; */ public void setNode(Element node) { this.node = node; } /** * The first index in the source containing an element from this group. */ private int start() { if(this.node == null) throw new IllegalStateException(); int separatorIndex = separators.indexOfNode(node, (byte)1); if(separatorIndex == -1) throw new IllegalStateException(); int groupStartIndex = insertedSeparators.getIndex(separatorIndex, SEPARATOR); return groupStartIndex - separatorIndex; } /** * The last index in the source containing an element from this group. */ private int end() { if(this.node == null) throw new IllegalStateException(); int nextSeparatorIndex = separators.indexOfNode(node, (byte)1) + 1; if(nextSeparatorIndex == 0) throw new IllegalStateException(); int nextGroupStartIndex = nextSeparatorIndex == insertedSeparators.colourSize(SEPARATOR) ? insertedSeparators.size() : insertedSeparators.getIndex(nextSeparatorIndex, SEPARATOR); return nextGroupStartIndex - nextSeparatorIndex; } /** * Update the cached {@link #first()} and {@link #size()} values, so that they * can be retrieved without the {@link SeparatorList}'s lock. */ public void updateCachedValues() { if(node != null) { int start = start(); int end = end(); this.first = source.get(start); this.size = end - start; } else { this.first = null; this.size = 0; } } /** {@inheritDoc} */ @Override public String toString() { return "" + size() + " elements starting with \"" + first() + "\""; } } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/UndoRedoSupport.java0000644000175000017500000003421712106516400030114 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import javax.swing.event.EventListenerList; import java.util.*; /** * UndoRedoSupport, as the name suggests, will provide generic support for * undoing and redoing groups of changes to an {@link EventList}. The * granularity of each undoable edit is determined by the ListEvent from which * it was generated. * *

      Not every change described in a ListEvent results in an undoable edit. * Specifically, a mutation of a list element IN PLACE does * not produce an undoable edit. For example, an {@link ObservableElementList} * which observes a change of an element, or a call to {@link List#set} with * the same object at that index produce a ListEvent that does not have a * corresponding {@link Edit} object. These ListEvents are ignored because they * lack sufficient information to undo or redo the change. * *

      In general UndoRedoSupport only makes sense for use with a * {@link BasicEventList} or a trivial wrapper around a BasicEventList which * does not affect the order or type of the elements, such as an * {@link ObservableElementList}. Advanced transformations, such as * {@link SortedList} or {@link FilterList} will not work as expected with this * UndoRedoSupport class since their contents are controlled by information * outside of themselves ({@link Comparator}s and * {@link ca.odell.glazedlists.matchers.Matcher}s). * *

      This class is agnostic to any particular GUI toolkit. As such it may be * used in a headless environment or can also be bound to a specific toolkit. * * @author James Lemieux */ public final class UndoRedoSupport { /** A wrapper around the true source EventList provides control over the granularity of ListEvents it produces. */ private TransactionList txSource; /** A ListEventListener that watches the {@link #txSource} and in turn broadcasts an {@link Edit} object to all {@link Listener}s */ private ListEventListener txSourceListener = new TXSourceListener(); /** A data structure storing all registered {@link Listener}s. */ private final EventListenerList listenerList = new EventListenerList(); /** * A count which, when greater than 0, indicates a ListEvent must be * ignored by this UndoRedoSupport because it was caused by an undo or * redo. An int is used rather than a boolean flag so that nested * undos/redos are properly handled. */ private int ignoreListEvent = 0; /** * A list of the elements in the source prior to the most * recent change. It is necessary to track the prior elements since * list removals broadcast ListEvents which do not include the removed * element as part of the ListEvent. We use this list to locate * removed elements when constructing objects that can undo the change. * todo remove this list when ListEvent can reliably furnish us with a deleted value */ private List priorElements; private UndoRedoSupport(EventList source) { // build a TransactionList that does NOT support rollback - we don't // need it and it relies on UndoRedoSupport, so we would have this.txSource = new TransactionList(source, false); this.txSource.addListEventListener(txSourceListener); this.priorElements = new ArrayList(source); } /** * Add a {@link Listener} which will receive a callback when an undoable * edit occurs on the given source {@link EventList}. */ public void addUndoSupportListener(Listener l) { listenerList.add(Listener.class, l); } /** * Remove a {@link Listener} from receiving a callback when an undoable * edit occurs on the given source {@link EventList}. */ public void removeUndoSupportListener(Listener l) { listenerList.remove(Listener.class, l); } /** * Notifies all registered {@link Listener}s of the given edit. */ private void fireUndoableEditHappened(Edit edit) { Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]== Listener.class) ((Listener) listeners[i+1]).undoableEditHappened(edit); } } /** * Installs support for undoing and redoing changes to the given * source. To be notified of undoable changes, a * {@link Listener} must be registered on the object that is returned by * this method. That Listener object will typically add the {@link Edit} * it is given over to whatever data structure is managing all undo/redo * functions for the entire application. * * @param source the EventList on which to provide undo/redo capabilities * @return an instance of UndoRedoSupport through which the undo/redo behaviour * can be customized */ public static UndoRedoSupport install(EventList source) { return new UndoRedoSupport(source); } /** * This method removes undo/redo support from the {@link EventList} it was * installed on. This method is useful when the {@link EventList} must * outlive the UndoRedoSupport object itself. The UndoRedoSupport object will become * available for garbage collection independently of the {@link EventList} * it decorated with behaviour. */ public void uninstall() { txSource.dispose(); txSource.removeListEventListener(txSourceListener); txSource = null; priorElements = null; } /** * This Listener watches the TransactionList for changes and responds by * created an {@link Edit} and broadcasting that object to all registered * {@link Listener}s. */ private class TXSourceListener implements ListEventListener { public void listChanged(ListEvent listChanges) { // if an undo or redo caused this ListEvent, it is not an undoable edit if (ignoreListEvent > 0) return; // build a CompositeEdit that describes the ListEvent and provides methods for undoing and redoing it final CompositeEdit edit = new CompositeEdit(); while (listChanges.next()) { final int changeIndex = listChanges.getIndex(); final int changeType = listChanges.getType(); // provide an AddEdit to the CompositeEdit if (changeType == ListEvent.INSERT) { final E inserted = txSource.get(changeIndex); priorElements.add(changeIndex, inserted); edit.add(new AddEdit(txSource, changeIndex, inserted)); // provide a RemoveEdit to the CompositeEdit } else if (changeType == ListEvent.DELETE) { // try to get the previous value through the ListEvent E deleted = listChanges.getOldValue(); E deletedElementFromPrivateCopy = priorElements.remove(changeIndex); // if the ListEvent could not give us the previous value, use the value from priorElements if (deleted == ListEvent.UNKNOWN_VALUE) deleted = deletedElementFromPrivateCopy; edit.add(new RemoveEdit(txSource, changeIndex, deleted)); // provide an UpdateEdit to the CompositeEdit } else if (changeType == ListEvent.UPDATE) { E previousValue = listChanges.getOldValue(); // if the ListEvent could not give us the previous value, use the value from priorElements if (previousValue == ListEvent.UNKNOWN_VALUE) previousValue = priorElements.get(changeIndex); final E newValue = txSource.get(changeIndex); // if a different object is present at the index if (newValue != previousValue) { priorElements.set(changeIndex, newValue); edit.add(new UpdateEdit(txSource, changeIndex, newValue, previousValue)); } } } // if the edit has real contents, broadcast it if (!edit.isEmpty()) fireUndoableEditHappened(edit.getSimplestEdit()); } } /** * Implementations of this Listener interface should be registered with an * UndoRedoSupport object via {@link UndoRedoSupport#addUndoSupportListener}. They * will be notified of each undoable edit that occurs to the given EventList. */ public interface Listener extends EventListener { /** * Notified of each undoable edit applied to the given EventList. */ public void undoableEditHappened(Edit edit); } /** * Provides an easy interface to undo/redo a ListEvent in its entirety. * At any point in time it is only possible to do one, and only one, of * {@link #undo} and {@link #redo}. To determine which one is allowed, use * {@link #canUndo()} and {@link #canRedo()}. */ public interface Edit { /** Undo the edit. */ public void undo(); /** Returns true if this edit may be undone. */ public boolean canUndo(); /** Re-applies the edit. */ public void redo(); /** Returns true if this edit may be redone. */ public boolean canRedo(); } private abstract class AbstractEdit implements Edit { /** Initially the Edit can be undone but not redone. */ protected boolean canUndo = true; public void undo() { // validate that we can proceed with the undo if (!canUndo()) throw new IllegalStateException("The Edit is in an incorrect state for undoing"); ignoreListEvent++; try { undoImpl(); } finally { ignoreListEvent--; } canUndo = false; } public void redo() { // validate that we can proceed with the redo if (!canRedo()) throw new IllegalStateException("The Edit is in an incorrect state for redoing"); ignoreListEvent++; try { redoImpl(); } finally { ignoreListEvent--; } canUndo = true; } protected abstract void undoImpl(); protected abstract void redoImpl(); public final boolean canUndo() { return canUndo; } public final boolean canRedo() { return !canUndo; } } /** * An Edit which acts as a container for finer-grained Edit objects. */ final class CompositeEdit extends AbstractEdit { /** The edits in the order they were made. */ private final List edits = new ArrayList(); /** Adds a single Edit to this container of Edits. */ void add(Edit edit) { edits.add(edit); } /** Returns true if this container of Edits is empty; false otherwise. */ private boolean isEmpty() { return edits.isEmpty(); } /** Returns the single Edit contained within this composite, if only one exists, otherwise it returns this entire CompositeEdit. */ private Edit getSimplestEdit() { return edits.size() == 1 ? edits.get(0) : this; } @Override public void undoImpl() { txSource.beginEvent(); try { // undo the Edits in reverse order they were applied for (ListIterator i = edits.listIterator(edits.size()); i.hasPrevious();) i.previous().undo(); } finally { txSource.commitEvent(); } } @Override public void redoImpl() { txSource.beginEvent(); try { // re-apply each edit in their original order for (Edit edit : edits) edit.redo(); } finally { txSource.commitEvent(); } } } /** * A base class implementing common logic and storage for the specific kind * of Edits which can occur to a single index in the EventList. */ private abstract class AbstractSimpleEdit extends AbstractEdit { protected final EventList source; protected final int index; protected final E value; protected AbstractSimpleEdit(EventList source, int index, E value) { this.source = source; this.index = index; this.value = value; } } /** * A class describing an undoable Add to an EventList. */ private final class AddEdit extends AbstractSimpleEdit { public AddEdit(EventList source, int index, E value) { super(source, index, value); } @Override public void undoImpl() { source.remove(index); } @Override public void redoImpl() { source.add(index, value); } } /** * A class describing an undoable Remove to an EventList. */ private final class RemoveEdit extends AbstractSimpleEdit { public RemoveEdit(EventList source, int index, E value) { super(source, index, value); } @Override public void undoImpl() { source.add(index, value); } @Override public void redoImpl() { source.remove(index); } } /** * A class describing an undoable Update to an EventList. */ private final class UpdateEdit extends AbstractSimpleEdit { private final E oldValue; public UpdateEdit(EventList source, int index, E value, E oldValue) { super(source, index, value); this.oldValue = oldValue; } @Override public void undoImpl() { source.set(index, oldValue); } @Override public void redoImpl() { source.set(index, value); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/FilterList.java0000644000175000017500000004514512106516400027063 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; // the core Glazed Lists packages import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.impl.adt.Barcode; import ca.odell.glazedlists.impl.adt.BarcodeIterator; import ca.odell.glazedlists.matchers.Matcher; import ca.odell.glazedlists.matchers.MatcherEditor; import ca.odell.glazedlists.matchers.Matchers; /** * 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. * *

      The filter can be static or dynamic. Changing the behaviour of the filter * will change which elements of the source list are included. * *

      Warning: This class * breaks the contract required by {@link java.util.List}. See {@link EventList} * for an example. * *

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

      * * * * * * * *
      EventList Overview
      Writable:yes
      Concurrency:thread ready, not thread safe
      Performance:reads: O(log N), writes O(log N), filter changes O(N)
      Memory:0 to 26 bytes per element
      Unit Tests:N/A
      Issues: * 1 * 2 * 7 * 46 * 187 * 254 * 312 *
      * * @author Jesse Wilson * @author James Lemieux */ public final class FilterList extends TransformedList { /** the flag list contains Barcode.BLACK for items that match the current filter and Barcode.WHITE for others */ private Barcode flagList = new Barcode(); /** the matcher determines whether elements get filtered in or out */ private Matcher currentMatcher = Matchers.trueMatcher(); /** the editor changes the matcher and fires events */ private MatcherEditor currentEditor = null; /** listener handles changes to the matcher */ private final MatcherEditor.Listener listener = new PrivateMatcherEditorListener(); /** is this list already disposed? */ private volatile boolean disposed; /** * Creates a {@link FilterList} that includes a subset of the specified * source {@link EventList}. */ public FilterList(EventList source) { super(source); // build a list of what is filtered and what's not flagList.addBlack(0, source.size()); // listen for changes to the source list source.addListEventListener(this); } /** * Convenience constructor for creating a {@link FilterList} and setting its * {@link Matcher}. */ public FilterList(EventList source, Matcher matcher) { this(source); // if no matcher was given, we have no further initialization work if (matcher == null) return; currentMatcher = matcher; changed(); } /** * Convenience constructor for creating a {@link FilterList} and setting its * {@link MatcherEditor}. */ public FilterList(EventList source, MatcherEditor matcherEditor) { this(source); // if no matcherEditor was given, we have no further initialization work if (matcherEditor == null) return; currentEditor = matcherEditor; currentEditor.addMatcherEditorListener(listener); currentMatcher = currentEditor.getMatcher(); changed(); } /** * Set the {@link Matcher} which specifies which elements shall be filtered. * *

      This will remove the current {@link Matcher} or {@link MatcherEditor} * and refilter the entire list. */ public void setMatcher(Matcher matcher) { // cancel the previous editor if(currentEditor != null) { currentEditor.removeMatcherEditorListener(listener); currentEditor = null; } if (matcher != null) changeMatcherWithLocks(currentEditor, matcher, MatcherEditor.Event.CHANGED); else changeMatcherWithLocks(currentEditor, null, MatcherEditor.Event.MATCH_ALL); } /** * Set the {@link MatcherEditor} which provides a dynamic {@link Matcher} * to determine which elements shall be filtered. * *

      This will remove the current {@link Matcher} or {@link MatcherEditor} * and refilter the entire list. */ public void setMatcherEditor(MatcherEditor editor) { // cancel the previous editor if (currentEditor != null) currentEditor.removeMatcherEditorListener(listener); // use the new editor currentEditor = editor; if (currentEditor != null) { currentEditor.addMatcherEditorListener(listener); changeMatcherWithLocks(currentEditor, currentEditor.getMatcher(), MatcherEditor.Event.CHANGED); } else { changeMatcherWithLocks(currentEditor, null, MatcherEditor.Event.MATCH_ALL); } } /** @inheritDoc */ @Override public void dispose() { super.dispose(); // stop listening to the MatcherEditor if one exists if (currentEditor != null) { currentEditor.removeMatcherEditorListener(listener); } // mark this list as disposed before clearing fields // this flag is checked in #changeMatcher disposed = true; currentEditor = null; currentMatcher = null; } /** {@inheritDoc} */ @Override public final void listChanged(ListEvent listChanges) { // all of these changes to this list happen "atomically" updates.beginEvent(); // handle reordering events if(listChanges.isReordering()) { int[] sourceReorderMap = listChanges.getReorderMap(); int[] filterReorderMap = new int[flagList.blackSize()]; // adjust the flaglist & construct a reorder map to propagate Barcode previousFlagList = flagList; flagList = new Barcode(); for(int i = 0; i < sourceReorderMap.length; i++) { Object flag = previousFlagList.get(sourceReorderMap[i]); flagList.add(i, flag, 1); if(flag != Barcode.WHITE) filterReorderMap[flagList.getBlackIndex(i)] = previousFlagList.getBlackIndex(sourceReorderMap[i]); } // fire the reorder updates.reorder(filterReorderMap); // handle non-reordering events } else { // for all changes, one index at a time while(listChanges.next()) { // get the current change info int sourceIndex = listChanges.getIndex(); int changeType = listChanges.getType(); // handle delete events if(changeType == ListEvent.DELETE) { // determine if this value was already filtered out or not int filteredIndex = flagList.getBlackIndex(sourceIndex); // if this value was not filtered out, it is now so add a change if(filteredIndex != -1) { E removed = listChanges.getOldValue(); updates.elementDeleted(filteredIndex, removed); } // remove this entry from the flag list flagList.remove(sourceIndex, 1); // handle insert events } else if(changeType == ListEvent.INSERT) { // whether we should add this item E element = source.get(sourceIndex); boolean include = currentMatcher.matches(element); // if this value should be included, add a change and add the item if(include) { flagList.addBlack(sourceIndex, 1); int filteredIndex = flagList.getBlackIndex(sourceIndex); updates.elementInserted(filteredIndex, element); // if this value should not be included, just add the item } else { flagList.addWhite(sourceIndex, 1); } // handle update events } else if(changeType == ListEvent.UPDATE) { // determine if this value was already filtered out or not int filteredIndex = flagList.getBlackIndex(sourceIndex); boolean wasIncluded = filteredIndex != -1; // whether we should add this item E updated = source.get(sourceIndex); boolean include = currentMatcher.matches(updated); // if this element is being removed as a result of the change if(wasIncluded && !include) { flagList.setWhite(sourceIndex, 1); updates.elementDeleted(filteredIndex, listChanges.getOldValue()); // if this element is being added as a result of the change } else if(!wasIncluded && include) { flagList.setBlack(sourceIndex, 1); updates.elementInserted(flagList.getBlackIndex(sourceIndex), updated); // this element is still here } else if(wasIncluded && include) { updates.elementUpdated(filteredIndex, listChanges.getOldValue(), updated); } } } } // commit the changes and notify listeners updates.commitEvent(); } /** * This method acquires the write lock for the FilterList and then selects * an appropriate delegate method to perform the correct work for each of * the possible changeTypes. */ private void changeMatcherWithLocks(MatcherEditor matcherEditor, Matcher matcher, int changeType) { getReadWriteLock().writeLock().lock(); try { changeMatcher(matcherEditor, matcher, changeType); } finally { getReadWriteLock().writeLock().unlock(); } } /** * This method selects an appropriate delegate method to perform the * correct work for each of the possible changeTypes. This * method does NOT acquire any locks and is thus used * during initialization of FilterList. */ private void changeMatcher(MatcherEditor matcherEditor, Matcher matcher, int changeType) { // first check if this list is already disposed if (!disposed) { // ensure the MatcherEvent is from OUR MatcherEditor if (currentEditor != matcherEditor) throw new IllegalStateException(); switch (changeType) { case MatcherEditor.Event.CONSTRAINED: currentMatcher = matcher; this.constrained(); break; case MatcherEditor.Event.RELAXED: currentMatcher = matcher; this.relaxed(); break; case MatcherEditor.Event.CHANGED: currentMatcher = matcher; this.changed(); break; case MatcherEditor.Event.MATCH_ALL: currentMatcher = Matchers.trueMatcher(); this.matchAll(); break; case MatcherEditor.Event.MATCH_NONE: currentMatcher = Matchers.falseMatcher(); this.matchNone(); break; } } } /** * Handles a constraining of the filter to a degree that guarantees no * values can be matched. That is, the filter list will act as a total * filter and not match any of the elements of the wrapped source list. */ private void matchNone() { // all of these changes to this list happen "atomically" updates.beginEvent(); // fire all the elements in the list as deleted for(int i = 0; i < size(); i++) { updates.elementDeleted(0, get(i)); } // reset the flaglist to all white (which matches nothing) flagList.clear(); flagList.addWhite(0, source.size()); // commit the changes and notify listeners updates.commitEvent(); } /** * 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. */ private void matchAll() { // all of these changes to this list happen "atomically" updates.beginEvent(); // for all filtered items, add them. // this code exploits the fact that all flags before // the current index are all conceptually black. we don't change // the flag to black immediately as a performance optimization // because the current implementation of barcode is faster for // batch operations. The call to i.getIndex() is exploiting the // fact that i.getIndex() == i.blackIndex() when all flags before // are conceptually black. Otherwise we would need to change flags // to black as we go so that flag offsets are correct for(BarcodeIterator i = flagList.iterator(); i.hasNextWhite();) { i.nextWhite(); int index = i.getIndex(); updates.elementInserted(index, source.get(index)); } flagList.clear(); flagList.addBlack(0, source.size()); // commit the changes and notify listeners updates.commitEvent(); } /** * 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. */ private void relaxed() { // all of these changes to this list happen "atomically" updates.beginEvent(); // for all filtered items, see what the change is for(BarcodeIterator i = flagList.iterator(); i.hasNextWhite();) { i.nextWhite(); E element = source.get(i.getIndex()); if(currentMatcher.matches(element)) { updates.elementInserted(i.setBlack(), element); } } // commit the changes and notify listeners updates.commitEvent(); } /** * 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. */ private void constrained() { // all of these changes to this list happen "atomically" updates.beginEvent(); // for all unfiltered items, see what the change is for(BarcodeIterator i = flagList.iterator(); i.hasNextBlack();) { i.nextBlack(); E value = source.get(i.getIndex()); if(!currentMatcher.matches(value)) { int blackIndex = i.getBlackIndex(); i.setWhite(); updates.elementDeleted(blackIndex, value); } } // commit the changes and notify listeners updates.commitEvent(); } /** * Handles changes to the behavior of the filter. This may change the contents * of this {@link EventList} as elements are filtered and unfiltered. */ private void changed() { // all of these changes to this list happen "atomically" updates.beginEvent(); // for all source items, see what the change is for(BarcodeIterator i = flagList.iterator();i.hasNext();) { i.next(); // determine if this value was already filtered out or not int filteredIndex = i.getBlackIndex(); boolean wasIncluded = filteredIndex != -1; // whether we should add this item E value = source.get(i.getIndex()); boolean include = currentMatcher.matches(value); // this element is being removed as a result of the change if(wasIncluded && !include) { i.setWhite(); updates.elementDeleted(filteredIndex, value); // this element is being added as a result of the change } else if(!wasIncluded && include) { updates.elementInserted(i.setBlack(), value); } } // commit the changes and notify listeners updates.commitEvent(); } /** * Listens to changes from the current {@link MatcherEditor} and handles them. */ private class PrivateMatcherEditorListener implements MatcherEditor.Listener { /** * This method changes the current Matcher controlling the filtering on * the FilterList. It does so in a thread-safe manner by acquiring the * write lock. * * @param matcherEvent a MatcherEvent describing the change in the * Matcher produced by the MatcherEditor */ public void changedMatcher(MatcherEditor.Event matcherEvent) { final MatcherEditor matcherEditor = matcherEvent.getMatcherEditor(); final Matcher matcher = matcherEvent.getMatcher(); final int changeType = matcherEvent.getType(); changeMatcherWithLocks(matcherEditor, matcher, changeType); } } /** {@inheritDoc} */ @Override public final int size() { return flagList.blackSize(); } /** {@inheritDoc} */ @Override protected final int getSourceIndex(int mutationIndex) { return flagList.getIndex(mutationIndex, Barcode.BLACK); } /** {@inheritDoc} */ @Override protected boolean isWritable() { return true; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/GlazedLists.java0000644000175000017500000013353712106516400027232 0ustar gregoagregoa/* 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); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/GroupingList.java0000644000175000017500000003573512106516400027434 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.impl.Grouper; import ca.odell.glazedlists.impl.adt.barcode2.Element; import ca.odell.glazedlists.impl.adt.barcode2.SimpleTree; import java.util.AbstractList; import java.util.ArrayList; import java.util.Comparator; import java.util.List; /** * A grouping list contains elements which are themselves Lists. Those Lists * are infact elements of the source list which have been grouped together into * a List. The logic of how to group the source elements is specified via a * Comparator. Elements for which the Comparator returns 0 are guaranteed to be * contained within the same group within this GroupingList. This implies * that source elements may only participate in a single group within this * GroupingList. * *

      Further transformations may be layered on top of this GroupingList to * transform the group lists into any other desirable form. * *

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

      * * * * * * * *
      EventList Overview
      Writable:yes
      Concurrency:thread ready, not thread safe
      Performance:reads: O(log N), writes O(log N)
      Memory:
      Unit Tests:GroupingListTest
      Issues: * 281 * 491 *
      * * @author James Lemieux */ public final class GroupingList extends TransformedList> { /** The GroupLists defined by the comparator. They are stored in an SimpleTree so their indices can be quickly updated. */ private SimpleTree groupLists = new SimpleTree(); /** The Grouper manages creating and deleting groups. */ private final Grouper grouper; /** * Creates a {@link GroupingList} that determines groupings via the * {@link Comparable} interface which all elements of the source * are assumed to implement. */ public static > GroupingList create(EventList source) { return new GroupingList(source); } /** * Creates a {@link GroupingList} that determines groupings via the * {@link Comparable} interface which all elements of the source * are assumed to implement. *

      Usage of factory method {@link #create(EventList)} is preferable. */ public GroupingList(EventList source) { this(source, (Comparator) GlazedLists.comparableComparator()); } /** * Creates a {@link GroupingList} that determines groups using the specified * {@link Comparator}. * * @param source the {@link EventList} containing elements to be grouped * @param comparator the {@link Comparator} used to determine groupings */ public GroupingList(EventList source, Comparator comparator) { this(new SortedList(source, comparator), comparator, null); } /** * A private constructor which provides a convenient handle to the * {@link SortedList} which will serve as the source of this list. * * @param source the elements to be grouped arranged in sorted order * @param comparator the {@link Comparator} used to determine groupings * @param dummyParameter dummy parameter to differentiate between the different * {@link GroupingList} constructors. */ private GroupingList(SortedList source, Comparator comparator, Void dummyParameter) { super(source); // the grouper handles changes to the SortedList this.grouper = new Grouper(source, new GrouperClient()); // initialize the tree of GroupLists rebuildGroupListTreeFromBarcode(); source.addListEventListener(this); } /** * After the barcode has been updated in response to a change in the * grouping {@link Comparator}, this method is used to rebuild the tree of * GroupLists which map those GroupLists to their overall indices. */ private void rebuildGroupListTreeFromBarcode() { // clear the contents of the GroupList tree groupLists.clear(); // fetch our GrouperClient final GrouperClient grouperClient = (GrouperClient) grouper.getClient(); // build the tree of GroupLists from the barcode for (int i = 0, n = grouper.getBarcode().colourSize(Grouper.UNIQUE); i < n; i++) { grouperClient.insertGroupList(i); } } /** * Return the index of the group to which the groupElement * would belong if it were hypothetically added to the source list. Note * that groupElement does NOT have to exist * in a group. This method is essentially a convenient way to locate a * group based on a prototypical element of that group. * * @param groupElement a prototype element of the group to locate * @return the index of the group that would contain groupElement * if it were added to the source list or -1 if no * currently existing group would contain the groupElement */ public int indexOfGroup(E groupElement) { // determine where the groupElement would be positioned in the source List final int sourceIndex = ((SortedList) source).sortIndex(groupElement); // if the groupElement is not a member of the group, return -1 if (sourceIndex == source.size() || grouper.getComparator().compare(source.get(sourceIndex), groupElement) != 0) return -1; // return the index of the group that includes the element at the source index return grouper.getBarcode().getColourIndex(sourceIndex, Grouper.UNIQUE); } /** * Handle changes to the grouping list groups. */ private class GrouperClient implements Grouper.Client { public void groupChanged(int index, int groupIndex, int groupChangeType, boolean primary, int elementChangeType, E oldValue, E newValue) { if(groupChangeType == ListEvent.INSERT) { insertGroupList(groupIndex); updates.addInsert(groupIndex); } else if(groupChangeType == ListEvent.DELETE) { removeGroupList(groupIndex); updates.addDelete(groupIndex); } else if(groupChangeType == ListEvent.UPDATE) { updates.addUpdate(groupIndex); } else { throw new IllegalStateException(); } } /** * Creates and inserts a new GroupList at the specified * index. * * @param index the location at which to insert an empty GroupList */ private void insertGroupList(int index) { final GroupList groupList = new GroupList(); final Element indexedTreeNode = groupLists.add(index, groupList, 1); groupList.setTreeNode(indexedTreeNode); } /** * Removes the GroupList at the given index. * * @param index the location at which to remove a GroupList */ private void removeGroupList(int index) { final Element indexedTreeNode = groupLists.get(index); groupLists.remove(indexedTreeNode); // for safety, null out the GroupList's reference to its now defunct indexedTreeNode indexedTreeNode.get().setTreeNode(null); } } /** * Change the {@link Comparator} which determines the groupings presented * by this List * * @param comparator the {@link Comparator} used to determine groupings; * null will be treated as {@link GlazedLists#comparableComparator()} */ public void setComparator(Comparator comparator) { if (comparator == null) comparator = (Comparator) GlazedLists.comparableComparator(); ((SortedList) source).setComparator(comparator); } /** {@inheritDoc} */ @Override protected int getSourceIndex(int index) { return grouper.getBarcode().getIndex(index, Grouper.UNIQUE); } /** {@inheritDoc} */ @Override protected boolean isWritable() { return true; } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { updates.beginEvent(true); // check if this ListEvent was caused due to a change in the // Comparator that creates the groups final SortedList sortedSource = (SortedList) source; final Comparator sourceComparator = sortedSource.getComparator(); if (sourceComparator != grouper.getComparator()) { // when the grouping comparator is changed in the source list, let // the grouper know so we can rebuild our groups from scratch // record the impending removal of all groups before adjusting the barcode for (int i = 0, n = size(); i < n; i++) updates.elementDeleted(0, get(i)); // adjust the Comparator used by the Grouper (which will change the barcode) grouper.setComparator(sourceComparator); // rebuild the tree which maps GroupLists to indices (so the tree matches the new barcode) rebuildGroupListTreeFromBarcode(); // insert all new groups (represented by the newly formed barcode) updates.addInsert(0, size() - 1); } else { grouper.listChanged(listChanges); } updates.commitEvent(); } /** {@inheritDoc} */ @Override public List get(int index) { return groupLists.get(index).get(); } /** {@inheritDoc} */ @Override public List remove(int index) { if(index < 0 || index >= size()) throw new IndexOutOfBoundsException("Cannot remove at " + index + " on list of size " + size()); final List removed = get(index); // make a copy of the list to return final List result = new ArrayList(removed); removed.clear(); return result; } /** {@inheritDoc} */ @Override public List set(int index, List value) { if(index < 0 || index >= size()) throw new IndexOutOfBoundsException("Cannot set at " + index + " on list of size " + size()); updates.beginEvent(true); final List result = remove(index); add(index, value); updates.commitEvent(); return result; } /** * This version of add will distribute all elements within the given * value List into groups. Existing groups will be reused and * new groups will be created as needed. As such, the index * argument is meaningless. * *

      Warning: This method * breaks the contract required by {@link List#add(int, Object)}. */ @Override public void add(int index, List value) { source.addAll(value); } /** {@inheritDoc} */ @Override public int size() { return grouper.getBarcode().colourSize(Grouper.UNIQUE); } /** {@inheritDoc} */ @Override public void dispose() { ((SortedList) source).dispose(); super.dispose(); } /** * This is the List implementation used to store groups created by this * GroupingList. It defines all mutator methods by mapping them to mutations * on the source list of GroupList. Thus, writes to this GroupList effect * all Lists sitting under GroupList. */ private class GroupList extends AbstractList { /** * The node within {@link GroupingList#groupLists} that records the * index of this GroupList within the GroupingList. */ private Element treeNode; /** * Attach the Element that tracks this GroupLists position to the * GroupList itself so it can look up its own position. */ private void setTreeNode(Element treeNode) { this.treeNode = treeNode; } /** * Returns the inclusive index of the start of this {@link GroupList} * within the source {@link SortedList}. */ private int getStartIndex() { if (treeNode == null) return -1; final int groupIndex = groupLists.indexOfNode(treeNode, (byte)1); return GroupingList.this.getSourceIndex(groupIndex); } /** * Returns the exclusive index of the end of this {@link GroupList} * within the source {@link SortedList}. */ private int getEndIndex() { if (treeNode == null) return -1; final int groupIndex = groupLists.indexOfNode(treeNode, (byte)1); // if this is before the end, its everything up to the first different element if(groupIndex < grouper.getBarcode().blackSize() - 1) { return grouper.getBarcode().getIndex(groupIndex + 1, Grouper.UNIQUE); // if this is at the end, its everything after } else { return grouper.getBarcode().size(); } } private int getSourceIndex(int index) { return getStartIndex() + index; } /** {@inheritDoc} */ @Override public E set(int index, E element) { return source.set(getSourceIndex(index), element); } /** {@inheritDoc} */ @Override public E get(int index) { return source.get(getSourceIndex(index)); } /** {@inheritDoc} */ @Override public int size() { return getEndIndex() - getStartIndex(); } /** {@inheritDoc} */ @Override public void clear() { source.subList(getStartIndex(), getEndIndex()).clear(); } /** {@inheritDoc} */ @Override public E remove(int index) { return source.remove(getSourceIndex(index)); } /** {@inheritDoc} */ @Override public void add(int index, E element) { source.add(getSourceIndex(index), element); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/EventList.java0000644000175000017500000000775112106516400026720 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import ca.odell.glazedlists.event.ListEventPublisher; import ca.odell.glazedlists.util.concurrent.ReadWriteLock; import java.util.Collection; import java.util.List; /** * An observable {@link List}. {@link ListEventListener}s can register to be * notified when this list changes. A {@link ListEvent} represents these changes * to an {@link EventList}. * *

      {@link EventList}s may be writable or read-only. Consult the Javadoc for * your {@link EventList} if you are unsure. * *

      Warning: {@link EventList}s * are thread ready but not thread safe. If you are sharing an {@link EventList} * between multiple threads, you can add thread safety by using the built-in * locks: *

       * EventList myList = ...
       * myList.getReadWriteLock().writeLock().lock();
       * try {
       *    // access myList here
       *    if(myList.size() > 3) {
       *       System.out.println(myList.get(3));
       *       myList.remove(3);
       *    }
       * } finally {
       *    myList.getReadWriteLock().writeLock().unlock();
       * }
       * 
      * * Note that you are also required to acquire and hold the lock during the * construction of an EventList if concurrent modifications are possible in * your environment, like so: * *
       * EventList source = ...
       * SortedList sorted;
       * source.getReadWriteLock().readLock().lock();
       * try {
       *    sorted = new SortedList(source);
       * } finally {
       *    source.getReadWriteLock().readLock().unlock();
       * }
       * 
      * *

      Warning: {@link EventList}s * may break the contract required by {@link java.util.List}. For example, when * you {@link #add(int,Object) add()} on a {@link SortedList}, it will ignore the specified * index so that the element will be inserted in sorted order. * * @see GlazedLists#eventListOf(Object[]) * @see GlazedLists#eventList(Collection) * @see GlazedLists#readOnlyList(EventList) * @see GlazedLists#threadSafeList(EventList) * @see GlazedLists#weakReferenceProxy(EventList, ListEventListener) * * @author Jesse Wilson */ public interface EventList extends List { /** * Registers the specified listener to receive change updates for this list. * * @param listChangeListener event listener != null * @throws NullPointerException if the specified listener is null */ public void addListEventListener(ListEventListener listChangeListener); /** * Removes the specified listener from receiving change updates for this list. * * @param listChangeListener event listener != null * @throws NullPointerException if the specified listener is null * @throws IllegalArgumentException if the specified listener wasn't added before */ public void removeListEventListener(ListEventListener listChangeListener); /** * Gets the lock required to share this list between multiple threads. It's always defined. * * @return a re-entrant {@link ReadWriteLock} that guarantees thread safe * access to this list. */ public ReadWriteLock getReadWriteLock(); /** * Get the publisher used to distribute {@link ListEvent}s. It's always defined. */ public ListEventPublisher getPublisher(); /** * Disposing an EventList will make it eligible for garbage collection. * Some EventLists install themselves as listeners to related objects so * disposing them is necessary. * *

      Warning: It is an error * to call any method on an {@link EventList} after it has been disposed. */ public void dispose(); }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/FunctionList.java0000644000175000017500000004125212106516400027416 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; import java.util.ArrayList; import java.util.List; import java.util.RandomAccess; /** * This List is meant to simplify the task of transforming each element of a * source list to an element stored at the same index in this FunctionList. * The logic of precisely how to transform the source elements is contained * within a {@link Function} that must be supplied at the time of construction * but can be changed afterward using {@link #setForwardFunction}. This * {@link Function} is called the forward function because it creates elements * in this {@link FunctionList} from elements that have been added or mutated * within the source list. The forward function may be an implementation of * either {@link Function} or {@link AdvancedFunction}. * *

      An optional reverse {@link Function} which is capable of mapping the * elements of this FunctionList back to the corresponding source element may * be supplied in order to use the following mutating methods: * *

        *
      • {@link #add(Object)} *
      • {@link #add(int, Object)} *
      • {@link #set(int, Object)} *
      * * If the reverse {@link Function} is not supplied then callers of those * methods will receive an {@link IllegalStateException} explaining that those * operations are not available without the reverse {@link Function}. * *

      If specified, the reverse {@link Function} should do its best to * maintain the invariant: * *

      o.equals(reverseFunction.evaluate(forwardFunction.evaluate(o))) * for any o that is non-null. * *

      Note: if two source elements share the same identity * (i.e. source.get(i) == source.get(j) when i != j), it is up to author of the * {@link Function} to decide if and how to * preserve the relationship of their identities after their transformation. * *

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

      * * * * * * * *
      EventList Overview
      Writable:yes
      Concurrency:thread ready, not thread safe
      Performance:reads: O(1), writes O(1) amortized
      Memory:
      Unit Tests:FunctionList
      Issues: * 282 *
      */ public final class FunctionList extends TransformedList implements RandomAccess { private final List sourceElements; /** A list of the Objects produced by running the source elements through the {@link #forward} Function. */ private final List mappedElements; /** The Function that maps source elements to FunctionList elements. */ private AdvancedFunction forward; /** The Function that maps FunctionList elements back to source elements. It may be null. */ private Function reverse; /** * Construct a {@link FunctionList} which stores the result of transforming * each source element using the given forward {@link Function}. No reverse * {@link Function} will be specified implying that {@link #add(Object)}, * {@link #add(int, Object)} and {@link #set(int, Object)} will throw * {@link IllegalArgumentException} if they are called. * * @param source the EventList to decorate with a function transformation * @param forward the function to execute on each source element */ public FunctionList(EventList source, Function forward) { this(source, forward, null); } /** * Construct a {@link FunctionList} which stores the result of transforming * each source element using the given forward {@link Function}. If the * reverse {@link Function} is not null, {@link #add(Object)}, * {@link #add(int, Object)} and {@link #set(int, Object)} will execute * as expected. * *

      Note: an {@link AdvancedFunction} can be specified for the forward * {@link Function} which allows the implementor a chance to examine the * prior value that was mapped to a source element when it must be remapped * due to a modification (from a call to {@link List#set}). * * @param source the EventList to decorate with a function transformation * @param forward the function to execute on each source element * @param reverse the function to map elements of FunctionList back to * element values in the source list */ public FunctionList(EventList source, Function forward, Function reverse) { super(source); updateForwardFunction(forward); setReverseFunction(reverse); // save a reference to the source elements this.sourceElements = new ArrayList(source); // map all of the elements within source this.mappedElements = new ArrayList(source.size()); for (int i = 0, n = source.size(); i < n; i++) { this.mappedElements.add(forward(source.get(i))); } source.addListEventListener(this); } /** * A convenience method to map a source element to a {@link FunctionList} * element using the forward {@link Function}. * * @param s the source element to be transformed * @return the result of transforming the source element */ private E forward(S s) { return forward.evaluate(s); } /** * A convenience method to remap a source element to a {@link FunctionList} * element using the forward {@link Function}. * * @param e the last prior result of transforming the source element * @param s the source element to be transformed * @return the result of transforming the source element */ private E forward(E e, S s) { return forward.reevaluate(s, e); } /** * A convenience method to map a {@link FunctionList} element to a source * element using the reverse {@link Function}. * * @param e the {@link FunctionList} element to be transformed * @return the result of transforming the {@link FunctionList} element */ private S reverse(E e) { if (reverse == null) throw new IllegalStateException("A reverse mapping function must be specified to support this List operation"); return reverse.evaluate(e); } /** * Changes the {@link Function} that evaluates source elements to produce * mapped elements. Calling this method with a different * forward Function will cause all elements in this * FunctionList to be reevaluated. * *

      Callers of this method typically also want to update the reverse * function using {@link #setReverseFunction} if one exists. */ public void setForwardFunction(Function forward) { updateForwardFunction(forward); updates.beginEvent(true); // remap all of the elements within source for (int i = 0, n = source.size(); i < n; i++) { final E oldValue = this.mappedElements.set(i, forward(source.get(i))); updates.elementUpdated(i, oldValue); } updates.commitEvent(); } /** * A convenience method to run a null check on the given * forward Function and to wrap it in a delegating * implementation of the {@link AdvancedFunction} interface as needed. */ private void updateForwardFunction(Function forward) { if (forward == null) throw new IllegalArgumentException("forward Function may not be null"); // wrap the forward function in an adapter to the AdvancedFunction interface if necessary if (forward instanceof AdvancedFunction) this.forward = (AdvancedFunction) forward; else this.forward = new AdvancedFunctionAdapter(forward); } /** * Returns the {@link Function} which maps source elements to elements * stored within this {@link FunctionList}. The {@link Function} is * guaranteed to be non-null. */ public Function getForwardFunction() { // unwrap the forward function from an AdvancedFunctionAdapter if necessary if (forward instanceof AdvancedFunctionAdapter) return ((AdvancedFunctionAdapter) forward).getDelegate(); else return forward; } /** * Changes the {@link Function} that evaluates FunctionList elements to * produce the original source element with which it corresponds. The * reverse Function will be used in all subsequent calls to: * *

        *
      • {@link #add(Object)} *
      • {@link #add(int, Object)} *
      • {@link #set(int, Object)} *
      * * This method should typically be called at the same time the forward * function is changed using {@link #setForwardFunction}. */ public void setReverseFunction(Function reverse) { this.reverse = reverse; } /** * Returns the {@link Function} which maps elements stored within this * {@link FunctionList} back to elements within the source list or * null if no such {@link Function} was specified. */ public Function getReverseFunction() { return reverse; } /** {@inheritDoc} */ @Override protected boolean isWritable() { return true; } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { updates.beginEvent(true); if (listChanges.isReordering()) { final int[] reorderMap = listChanges.getReorderMap(); final List originalMappedElements = new ArrayList(mappedElements); for (int i = 0; i < reorderMap.length; i++) { final int sourceIndex = reorderMap[i]; mappedElements.set(i, originalMappedElements.get(sourceIndex)); } updates.reorder(reorderMap); } else { while (listChanges.next()) { final int changeIndex = listChanges.getIndex(); final int changeType = listChanges.getType(); if (changeType == ListEvent.INSERT) { final S newValue = source.get(changeIndex); final E newValueTransformed = forward(newValue); sourceElements.add(changeIndex, newValue); mappedElements.add(changeIndex, newValueTransformed); updates.elementInserted(changeIndex, newValueTransformed); } else if (changeType == ListEvent.UPDATE) { final E oldValueTransformed = get(changeIndex); final S newValue = source.get(changeIndex); final E newValueTransformed = forward(oldValueTransformed, newValue); sourceElements.set(changeIndex, newValue); mappedElements.set(changeIndex, newValueTransformed); updates.elementUpdated(changeIndex, oldValueTransformed, newValueTransformed); } else if (changeType == ListEvent.DELETE) { final S oldValue = sourceElements.remove(changeIndex); final E oldValueTransformed = mappedElements.remove(changeIndex); forward.dispose(oldValue, oldValueTransformed); updates.elementDeleted(changeIndex, oldValueTransformed); } } } updates.commitEvent(); } /** {@inheritDoc} */ @Override public E get(int index) { return mappedElements.get(index); } /** {@inheritDoc} */ @Override public E remove(int index) { final E removed = get(index); source.remove(index); return removed; } /** {@inheritDoc} */ @Override public E set(int index, E value) { final E updated = get(index); source.set(index, reverse(value)); return updated; } /** {@inheritDoc} */ @Override public void add(int index, E value) { source.add(index, reverse(value)); } /** * A Function encapsulates the logic for transforming a list element into * any kind of Object. Implementations should typically create and return * new objects, though it is permissible to return the original value * unchanged (i.e. the Identity Function). */ public interface Function { /** * Transform the given sourceValue into any kind of Object. * * @param sourceValue the Object to transform * @return the transformed version of the object */ public B evaluate(A sourceValue); } /** * An AdvancedFunction is an extension of the simple Function interface * which provides more hooks in the lifecycle of the transformation of a * source element. Specifically, it includes: * *
        *
      • {@link #reevaluate} which is called when an element is mutated * in place and thus run through this mapping function again. It * provides access to the previous value returned from this function * in case it is of use when remapping the same element. * *
      • {@link #dispose} which is called when an element is removed from * the FunctionList and is meant to be location that cleans up any * resources the Function may have allocated. (like Listeners for * example) *
      * * If neither of these extensions to FunctionList are useful, users are * encouraged to implement only the Function interface for their forward * function. */ public interface AdvancedFunction extends Function { /** * Evaluate the sourceValue again to produce the * corresponding value in the FunctionList. The last * transformedValue is provided as a reference when * evaluating a sourceValue that has previously been * evaluated. * * @param sourceValue the Object to transform (again) * @param transformedValue the Object produced by this function the * last time it evaluated sourceValue * @return the transformed version of the sourceValue */ public B reevaluate(A sourceValue, B transformedValue); /** * Perform any necessary resource cleanup on the given * sourceValue and transformedValue as they * are removed from the FunctionList such as installed listeners. * * @param sourceValue the Object that was transformed * @param transformedValue the Object that resulted from the last * transformation */ public void dispose(A sourceValue, B transformedValue); } /** * This class adapts an implementation of the simple {@link Function} * interface to the {@link AdvancedFunction} interface. This is purely to * ease the implementation of FunctionList since it can treat all forward * functions as though they are AdvancedFunctions which means less casting. */ private static final class AdvancedFunctionAdapter implements AdvancedFunction { private final Function delegate; /** * Adapt the given delegate to the * {@link AdvancedFunction} interface. */ public AdvancedFunctionAdapter(Function delegate) { this.delegate = delegate; } /** * Defers to the delegate. */ public B evaluate(A sourceValue) { return delegate.evaluate(sourceValue); } /** * Defers to the delegate's {@link Function#evaluate} method. */ public B reevaluate(A sourceValue, B transformedValue) { return evaluate(sourceValue); } public void dispose(A sourceValue, B transformedValue) { // do nothing } public Function getDelegate() { return delegate; } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/TransactionList.java0000644000175000017500000002672212106516400030123 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; import java.util.List; import java.util.ArrayList; /** * A list transformation that presents traditional transaction semantics. * Typical usage resembles one of two methods: * *
       *   EventList source = ...
       *   TransactionList txList = new TransactionList(source);
       *
       *   // begin a transaction in which all ListEvents are collected by txList
       *   // into a single "super ListEvent", which is fired on commit
       *   txList.beginEvent(true);
       *
       *   // fill in the details of the transaction
       *   // (these operations actually physically change ONLY txList and its source)
       *   txList.add("A new element");
       *   txList.set(0, "A changed element");
       *   txList.remove(6);
       *
       *   // commit the transaction, which will broadcast a single ListEvent from
       *   // txList describing the aggregate of all changes made during the transaction
       *   // (this returns the entire list pipeline to a consistent state)
       *   txList.commitEvent();
       * 
      * * In this usage, all ListEventListeners "downstream" of TransactionList remain * clueless about changes made during a transaction. As a result, the * "list pipeline" is allowed to temporarily fall into an inconsistent state * because only a portion of the pipeline (TransactionList and lower) has seen * changes made during the transaction. Users must ensure that they do not * read or write through any "downstream" EventList that depends on the * TransactionList during a transaction. Typically this is done using the * built-in {@link #getReadWriteLock() locks}. * *

      If the transaction was rolled back instead of committed, the txList would * not produce a ListEvent, since none of its listeners would be aware of any * changes made during the transaction. * * The second popular usage resembles this: * *

       *   EventList source = ...
       *   TransactionList txList = new TransactionList(source);
       *
       *   // begin a transaction in which we change the ListEvent
       *   txList.beginEvent(); // this is the same as txList.beginEvent(false);
       *
       *   // fill in the details of the transaction
       *   // (these operations actually physically change the ENTIRE PIPELINE)
       *   txList.add("A new element");
       *   txList.set(0, "A changed element");
       *   txList.remove(6);
       *
       *   // commit the transaction, which will NOT broadcast a ListEvent from
       *   // txList because all of its listeners are already aware of the changes
       *   // made during the transaction
       *   txList.commitEvent();
       * 
      * * In this case, the "list pipeline" always remains consistent and reads/writes * may occur through any part EventList in the pipeline without error. * *

      If the transaction is rolled back instead of committed, the txList * produces a ListEvent describing the rollback, since its listeners are fully * aware of the changes made during the transaction and must also be given a * chance to undo their changes. * *

      Transactions may be nested arbitrarily deep using code that resembles: *

       *   txList.beginEvent();
       *     txList.add("A");
       *
       *     txList.beginEvent();
       *       txList.set(0, "B");
       *     txList.commitEvent();
       *
       *     txList.beginEvent();
       *       txList.add("C");
       *     txList.commitEvent();
       *   txList.commitEvent();
       * 
      * * @author James Lemieux */ public class TransactionList extends TransformedList { /** produces {@link UndoRedoSupport.Edit}s which are collected during a transaction to support rollback */ private UndoRedoSupport rollbackSupport; /** A stack of transactions contexts; one for each layer of nested transaction */ private final List txContextStack = new ArrayList(); /** * Constructs a TransactionList that provides traditional * transaction semantics over the given source. * * @param source the EventList over which to provide a transactional view */ public TransactionList(EventList source) { this(source, true); } /** * Constructs a TransactionList that provides traditional * transaction semantics over the given source. * *

      If rollbackSupport is true then this * TransactionList supports calling {@link #rollbackEvent()} during a * transaction. This constructor exists solely to break the constructor * cycle between UndoRedoSupport and TransactionList and should only be * used internally by Glazed Lists. * * @param source the EventList over which to provide a transactional view * @param rollbackSupport true indicates this TransactionList must * support the rollback ability; false indicates it is not * necessary */ TransactionList(EventList source, boolean rollbackSupport) { super(source); // if rollback support is requested, build the necessary infrastructure if (rollbackSupport) { this.rollbackSupport = UndoRedoSupport.install(source); this.rollbackSupport.addUndoSupportListener(new RollbackSupportListener()); } source.addListEventListener(this); } /** * Demarks the beginning of a transaction which accumulates all ListEvents * received during the transaction and fires a single aggregate ListEvent * on {@link #commitEvent()}. */ public void beginEvent() { beginEvent(true); } /** * Demarks the beginning of a transaction. If buffered is * true then all ListEvents received during the transaction are * accumulated and fired as a single aggregate ListEvent on * {@link #commitEvent()}. If buffered is false then * all ListEvents received during the transaction are forwarded immediately * and {@link #commitEvent()} produces no ListEvent of its own. * * @param buffered true indicates ListEvents should be buffered and * sent on {@link #commitEvent()}; false indicates they should * be sent on immediately */ public void beginEvent(boolean buffered) { // start a nestable ListEvent if we're supposed to buffer them if (buffered) updates.beginEvent(true); // push a new context onto the stack describing this new transaction txContextStack.add(new Context(buffered)); } /** * Demarks the successful completion of a transaction. If changes were * buffered during the transaction by calling {@link #beginEvent(boolean) beginEvent(true)} * then a single ListEvent will be fired from this TransactionList * describing the changes accumulated during the transaction. */ public void commitEvent() { // verify that there is a transaction to roll back if (rollbackSupport != null && txContextStack.isEmpty()) throw new IllegalStateException("No ListEvent exists to commit"); // pop the last context off the stack and ask it to commit txContextStack.remove(txContextStack.size()-1).commit(); } /** * Demarks the unsuccessful completion of a transaction. If changes were * NOT buffered during the transaction by calling {@link #beginEvent(boolean) beginEvent(false)} * then a single ListEvent will be fired from this TransactionList * describing the rollback of the changes accumulated during the transaction. */ public void rollbackEvent() { // check if this TransactionList was created with rollback abilities if (rollbackSupport == null) throw new IllegalStateException("This TransactionList does not support rollback"); // check if a transaction exists to rollback if (txContextStack.isEmpty()) throw new IllegalStateException("No ListEvent exists to roll back"); // pop the last context off the stack and ask it to rollback txContextStack.remove(txContextStack.size()-1).rollback(); } /** {@inheritDoc} */ @Override protected boolean isWritable() { return true; } /** @inheritDoc */ @Override public void dispose() { if (rollbackSupport != null) rollbackSupport.uninstall(); rollbackSupport = null; txContextStack.clear(); super.dispose(); } /** * Simply forwards all of the listChanges since TransactionList * doesn't transform the source data in any way. */ @Override public void listChanged(ListEvent listChanges) { updates.forwardEvent(listChanges); } /** * Accumulates all of the small Edits that occur during a transaction * within a CompositeEdit that can be undone to support rollback, if * necessary. */ private class RollbackSupportListener implements UndoRedoSupport.Listener { public void undoableEditHappened(UndoRedoSupport.Edit edit) { // if a tx context exists we are in the middle of a transaction if (!txContextStack.isEmpty()) txContextStack.get(txContextStack.size()-1).add(edit); } } /** * A small object describing the details about the transaction that was * started so that it can be properly committed or rolled back at a later * time. Specifically it tracks: * *

        *
      • a CompositeEdit which can be used to undo the transaction's changes * in the case of a rollback
      • *
      • a flag indicating wether a ListEvent was started when the * transaction began (and thus must be committed or discarded later) *
      */ private final class Context { /** collects the smaller intermediate Edits that occur during a transaction; null if no transaction exists */ private UndoRedoSupport.CompositeEdit rollbackEdit = rollbackSupport == null ? null : rollbackSupport.new CompositeEdit(); /** * true indicates a ListEvent was started when this Context * was created and must be committed or rolled back later. */ private boolean eventStarted = false; public Context(boolean eventStarted) { this.eventStarted = eventStarted; } /** * Add the given edit into this Context to support its possible rollback. */ public void add(UndoRedoSupport.Edit edit) { if (rollbackEdit != null) rollbackEdit.add(edit); } /** * Commit the changes associated with this transaction. */ public void commit() { rollbackEdit = null; if (eventStarted) updates.commitEvent(); } /** * Rollback the changes associated with this transaction. */ public void rollback() { if (rollbackEdit != null) { // rollback all changes from the transaction as a single ListEvent updates.beginEvent(true); try { rollbackEdit.undo(); } finally { updates.commitEvent(); } rollbackEdit = null; } // throw away the ListEvent if we started one if (eventStarted) updates.discardEvent(); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/DisposableMap.java0000644000175000017500000000300312106516400027510 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import java.util.Map; /** * A special kind of Map backed by an EventList that is expected to live longer * than this Map. It defines a {@link #dispose()} method which should be called * when the Map is no longer useful, but the underlying {@link EventList} is * still referenced and useful. It allows this Map to be garbage collected * before its source {@link EventList}. * * @author James Lemieux */ public interface DisposableMap extends Map { /** * Releases the resources consumed by this {@link DisposableMap} so that it * may eventually be garbage collected. * *

      A {@link DisposableMap} will be garbage collected without a call to * {@link #dispose()}, but not before its source {@link EventList} is garbage * collected. By calling {@link #dispose()}, you allow the {@link DisposableMap} * to be garbage collected before its source {@link EventList}. This is * necessary for situations where a {@link DisposableMap} is short-lived but * its source {@link EventList} is long-lived. * *

      Warning: It is an error * to call any method on a {@link DisposableMap} after it has been disposed. */ public void dispose(); }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/SortedList.java0000644000175000017500000007412012106516400027071 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.impl.GlazedListsImpl; import ca.odell.glazedlists.impl.adt.barcode2.Element; import ca.odell.glazedlists.impl.adt.barcode2.SimpleTree; import ca.odell.glazedlists.impl.adt.barcode2.SimpleTreeIterator; import java.util.*; /** * An {@link EventList} that shows its source {@link EventList} in sorted order. * *

      The sorting strategy is specified with a {@link Comparator}. If no * {@link Comparator} is specified, all of the elements of the source {@link EventList} * must implement {@link Comparable}. * *

      This {@link EventList} supports all write operations. * *

      Warning: This class * breaks the contract required by {@link List}. See {@link EventList} * for an example. * *

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

      * * * * * * * *
      EventList Overview
      Writable:yes
      Concurrency:thread ready, not thread safe
      Performance:reads: O(log N), writes O(log N), change comparator O(N log N)
      Memory:72 bytes per element
      Unit Tests:N/A
      Issues: * 39 * 40 * 58 * 60 * 62 * 66 * 161 * 170 * 206 * 239 * 255 * 261 *
      * * @author Jesse Wilson */ public final class SortedList extends TransformedList { private static final byte ALL_COLORS = 1; private static final Element EMPTY_ELEMENT = null; /** * Sorting mode where elements are always in sorted order, even if this * requires that elements be moved from one index to another when their * value is changed. */ public static final int STRICT_SORT_ORDER = 0; /** * Sorting mode where elements aren't moved when their value is changed, * even if this means they are no longer in perfect sorted order. This mode * is useful in editable lists and tables because it is annoying * for the current element to move if its value changes. */ public static final int AVOID_MOVING_ELEMENTS = 1; /** a map from the unsorted index to the sorted index */ private SimpleTree unsorted = null; /** a map from the sorted index to the unsorted index */ private SimpleTree sorted = null; /** the comparator that this list uses for sorting */ private Comparator comparator = null; /** one of {@link #STRICT_SORT_ORDER} or {@link #AVOID_MOVING_ELEMENTS}. */ private int mode = STRICT_SORT_ORDER; /** * Creates a {@link SortedList} that sorts the specified {@link EventList}. * All elements in the specified {@link EventList} must implement {@link Comparable}. * * @param source the {@link EventList} to be sorted */ public static > SortedList create(EventList source) { return new SortedList(source); } /** * Creates a {@link SortedList} that sorts the specified {@link EventList}. * Because this constructor takes no {@link Comparator} argument, all * elements in the specified {@link EventList} must implement {@link Comparable} * or a {@link ClassCastException} will be thrown. *

      Usage of factory method {@link #create(EventList)} is preferable. * * @param source the {@link EventList} to be sorted */ public SortedList(EventList source) { this(source, (Comparator)GlazedLists.comparableComparator()); } /** * Creates a {@link SortedList} that sorts the specified {@link EventList} * using the specified {@link Comparator} to determine sort order. If the * specified {@link Comparator} is null, then this {@link List} * will be unsorted. */ public SortedList(EventList source, Comparator comparator) { super(source); setComparator(comparator); source.addListEventListener(this); } /** * Modify the behaviour of this {@link SortedList} to one of the predefined modes. * * @param mode either {@link #STRICT_SORT_ORDER} or {@link #AVOID_MOVING_ELEMENTS}. */ public void setMode(int mode) { if(mode != STRICT_SORT_ORDER && mode != AVOID_MOVING_ELEMENTS) throw new IllegalArgumentException("Mode must be either SortedList.STRICT_SORT_ORDER or SortedList.AVOID_MOVING_ELEMENTS"); if(mode == this.mode) return; // apply the new mode this.mode = mode; // we need to re-sort the table on the off-chance that an element // was out of order before if(this.mode == STRICT_SORT_ORDER) { setComparator(getComparator()); } } /** * Get the behaviour mode for this {@link SortedList}. * * @return one of {@link #STRICT_SORT_ORDER} (default) or * {@link #AVOID_MOVING_ELEMENTS}. */ public int getMode() { return this.mode; } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { // handle reordering events if(listChanges.isReordering()) { int[] sourceReorder = listChanges.getReorderMap(); // remember what the mapping was before int[] previousIndexToSortedIndex = new int[sorted.size()]; int index = 0; for(SimpleTreeIterator i = new SimpleTreeIterator(sorted); i.hasNext(); index++) { i.next(); Element unsortedNode = i.value(); int unsortedIndex = unsorted.indexOfNode(unsortedNode, ALL_COLORS); previousIndexToSortedIndex[unsortedIndex] = index; } // adjust the from index for the source reorder int[] newIndexToSortedIndex = new int[sorted.size()]; for(int i = 0; i < previousIndexToSortedIndex.length; i++) { newIndexToSortedIndex[i] = previousIndexToSortedIndex[sourceReorder[i]]; } // reorder the unsorted nodes to get the new sorted order Element[] unsortedNodes = new Element[unsorted.size()]; index = 0; for(SimpleTreeIterator i = new SimpleTreeIterator(unsorted); i.hasNext(); index++) { i.next(); Element unsortedNode = i.node(); unsortedNodes[index] = unsortedNode; } Arrays.sort(unsortedNodes, sorted.getComparator()); // create a new reorder map to send the changes forward int[] reorderMap = new int[sorted.size()]; boolean indexChanged = false; index = 0; for(SimpleTreeIterator i = new SimpleTreeIterator(sorted); i.hasNext(); index++) { i.next(); Element sortedNode = i.node(); Element unsortedNode = unsortedNodes[index]; sortedNode.set(unsortedNode); unsortedNode.set(sortedNode); int unsortedIndex = unsorted.indexOfNode(unsortedNode, ALL_COLORS); reorderMap[index] = newIndexToSortedIndex[unsortedIndex]; indexChanged = indexChanged || (index != reorderMap[index]); } // notify the world of the reordering if(indexChanged) { updates.beginEvent(); updates.reorder(reorderMap); updates.commitEvent(); } return; } // This is implemented in three phases. These phases are: // 1. Update the unsorted tree for all event types. Update the sorted tree // for delete events by deleting nodes. Fire delete events. Queue unsorted // nodes for inserts and deletes in a list. // 2. Fire update events by going through the updated nodes and testing // whether they're still in sort order or if they need to be moved // 3. Process queue of unsorted nodes for inserts. Fire insert events. // This cycle is rather complex but necessarily so. The reason is that for // the two-tree SortedList to function properly, there is a very strict order // for how trees can be modified. The unsorted tree must be brought completely // up-to-date before any access is made to the sorted tree. This ensures that // the unsorted nodes can discover their indices properly. The sorted tree must // have all deleted notes removed and updated nodes marked as unsorted // before any nodes are inserted. This is because a deleted node may // have a changed value that violates the sorted order in the tree. An // insert in this case may compare against a violating node and result // in inconsistency, even if the other node is eventually deleted. // Therefore the order of operations above is essentially update // the unsorted tree, delete from the sorted tree and finally insert into the // sorted tree. // all of these changes to this list happen "atomically" updates.beginEvent(); // first update the offset tree for all changes, and keep the changed nodes in a list LinkedList insertNodes = new LinkedList(); List> updateNodes = new ArrayList>(); List previousValues = new ArrayList(); // Update the indexed tree so it matches the source. // Save the nodes to be inserted and updated as well while(listChanges.next()) { // get the current change info int unsortedIndex = listChanges.getIndex(); int changeType = listChanges.getType(); // on insert, insert the index node if(changeType == ListEvent.INSERT) { Element unsortedNode = unsorted.add(unsortedIndex, EMPTY_ELEMENT, 1); insertNodes.addLast(unsortedNode); // on update, mark the updated node as unsorted and save it so it can be moved } else if(changeType == ListEvent.UPDATE) { Element unsortedNode = unsorted.get(unsortedIndex); Element sortedNode = unsortedNode.get(); sortedNode.setSorted(Element.PENDING); updateNodes.add(sortedNode); previousValues.add(listChanges.getOldValue()); // on delete, delete the index and sorted node } else if(changeType == ListEvent.DELETE) { Element unsortedNode = unsorted.get(unsortedIndex); E deleted = listChanges.getOldValue(); unsorted.remove(unsortedNode); int deleteSortedIndex = deleteByUnsortedNode(unsortedNode); updates.elementDeleted(deleteSortedIndex, deleted); } } // decide which updated elements need to be shifted. We walk through the // tree, marking updated elements as sorted or unsorted depending on their // value relative to their neighbours for(int i = 0, size = updateNodes.size(); i < size; i++) { Element sortedNode = updateNodes.get(i); // we may have already handled this via a neighbour if(sortedNode.getSorted() != Element.PENDING) continue; // find the bounds (by value) on this element. this is the last element // preceeding current that's sorted and the first element after current // that's sorted. If there's no such element (ie. the end of the list), // then the bound element is null Element lowerBound = null; Element upperBound = null; Element firstUnsortedNode = sortedNode; for(Element leftNeighbour = sortedNode.previous(); leftNeighbour != null; leftNeighbour = leftNeighbour.previous()) { if(leftNeighbour.getSorted() != Element.SORTED) { firstUnsortedNode = leftNeighbour; continue; } lowerBound = leftNeighbour; break; } for(Element rightNeighbour = sortedNode.next(); rightNeighbour != null; rightNeighbour = rightNeighbour.next()) { if(rightNeighbour.getSorted() != Element.SORTED) continue; upperBound = rightNeighbour; break; } // walk from the leader to the follower, marking elements as in sorted // order or not. We simply compare them to our 2 potentially distant neighbours // on either side - the lower and upper bounds Comparator nodeComparator = sorted.getComparator(); for(Element current = firstUnsortedNode; current != upperBound; current = current.next()) { // ensure we're less than the upper bound if(upperBound != null && nodeComparator.compare(current.get(), upperBound.get()) > 0) { current.setSorted(Element.UNSORTED); continue; } // and greater than the lower bound if(lowerBound != null && nodeComparator.compare(current.get(), lowerBound.get()) < 0) { current.setSorted(Element.UNSORTED); continue; } // so the node is sorted, and it's our new lower bound current.setSorted(Element.SORTED); lowerBound = current; } } // fire update events for(int i = 0, size = updateNodes.size(); i < size; i++) { E previous = previousValues.get(i); Element sortedNode = updateNodes.get(i); assert(sortedNode.getSorted() != Element.PENDING); int originalIndex = sorted.indexOfNode(sortedNode, ALL_COLORS); // the element is still in sorted order, forward the update event if(sortedNode.getSorted() == Element.SORTED) { updates.elementUpdated(originalIndex, previous); // sort order is not enforced so we lose perfect sorting order // but we don't need to move elements around } else if(mode == AVOID_MOVING_ELEMENTS) { updates.elementUpdated(originalIndex, previous); // sort order is enforced so move the element to its new location } else { sorted.remove(sortedNode); updates.elementDeleted(originalIndex, previous); int insertedIndex = insertByUnsortedNode(sortedNode.get()); updates.addInsert(insertedIndex); } } // fire insert events while(!insertNodes.isEmpty()) { Element insertNode = insertNodes.removeFirst(); int insertedIndex = insertByUnsortedNode(insertNode); updates.addInsert(insertedIndex); } // commit the changes and notify listeners updates.commitEvent(); } /** * Inserts the specified unsorted node as the value in the sorted tree * and returns the sorted order. * * @return the sortIndex of the inserted object. */ private int insertByUnsortedNode(Element unsortedNode) { // add the object to the sorted set Element sortedNode = sorted.addInSortedOrder(ALL_COLORS, unsortedNode, 1); // assign the unsorted node the value of the sorted node unsortedNode.set(sortedNode); // return the sorted index return sorted.indexOfNode(sortedNode, ALL_COLORS); } /** * Deletes the node in the sorted tree based on the value of the specified * unsorted tree node. * * @return the sortIndex of the deleted object. */ private int deleteByUnsortedNode(Element unsortedNode) { // get the sorted node Element sortedNode = (Element)unsortedNode.get(); // look up the sorted index before removing the nodes int sortedIndex = sorted.indexOfNode(sortedNode, ALL_COLORS); // delete the sorted node from its tree sorted.remove(sortedIndex, 1); // return the sorted index return sortedIndex; } /** {@inheritDoc} */ @Override protected int getSourceIndex(int mutationIndex) { Element sortedNode = sorted.get(mutationIndex); Element unsortedNode = (Element)sortedNode.get(); return unsorted.indexOfNode(unsortedNode, ALL_COLORS); } /** {@inheritDoc} */ @Override protected boolean isWritable() { return true; } /** * Gets the {@link Comparator} that is being used to sort this list. * * @return the {@link Comparator} in use, or null if this list is * currently unsorted. If this is an {@link EventList} of {@link Comparable} * elements in natural order, then a ComparableComparator} will * be returned. */ public Comparator getComparator() { return comparator; } /** * Set the {@link Comparator} in use in this {@link EventList}. This will * sort the source {@link EventList} into a new order. * *

      Performance Note: sorting will take O(N * Log N) time. * *

      Warning: This method is * thread ready but not thread safe. See {@link EventList} for an example * of thread safe code. * * @param comparator the {@link Comparator} to specify how to sort the list. If * the source {@link EventList} elements implement {@link Comparable}, * you may use a {@link GlazedLists#comparableComparator()} to sort them * in their natural order. You may also specify null to put * this {@link SortedList} in unsorted order. */ public void setComparator(Comparator comparator) { // save this comparator this.comparator = comparator; // keep the old trees to construct the reordering SimpleTree previousSorted = sorted; // create the sorted list with a simple comparator final Comparator treeComparator; if(comparator != null) treeComparator = new ElementComparator(comparator); else treeComparator = new ElementRawOrderComparator(); sorted = new SimpleTree(treeComparator); // create a list which knows the offsets of the indexes to initialize this list if(previousSorted == null && unsorted == null) { unsorted = new SimpleTree(); // add all elements in the source list, in order for(int i = 0, n = source.size(); i < n; i++) { Element unsortedNode = unsorted.add(i, EMPTY_ELEMENT, 1); insertByUnsortedNode(unsortedNode); } // this is the first sort so we're done return; } // if the lists are empty, we're done if(source.size() == 0) return; // rebuild the sorted tree to reflect the new Comparator for(SimpleTreeIterator i = new SimpleTreeIterator(unsorted); i.hasNext(); ) { i.next(); Element unsortedNode = i.node(); insertByUnsortedNode(unsortedNode); } // construct the reorder map int[] reorderMap = new int[size()]; int oldSortedIndex = 0; for(SimpleTreeIterator i = new SimpleTreeIterator(previousSorted); i.hasNext(); oldSortedIndex++) { i.next(); Element oldSortedNode = i.node(); Element unsortedNode = (Element)oldSortedNode.get(); Element newSortedNode = (Element)unsortedNode.get(); int newSortedIndex = sorted.indexOfNode(newSortedNode, ALL_COLORS); reorderMap[newSortedIndex] = oldSortedIndex; } // notification about the big change updates.beginEvent(); updates.reorder(reorderMap); updates.commitEvent(); } /** {@inheritDoc} */ @Override public int indexOf(Object object) { if(mode != STRICT_SORT_ORDER || comparator == null) return super.indexOf(object); // use the fact that we have sorted data to quickly locate a position // at which we can begin a linear search for an object that .equals(object) int index = ((SimpleTree)sorted).indexOfValue(object, true, false, ALL_COLORS); // if we couldn't use the comparator to find the index, return -1 if (index == -1) return -1; // otherwise, we must now begin a linear search for the index of an element // that .equals() the given object for (; index < size(); index++) { E objectAtIndex = get(index); // if the objectAtIndex no longer compares equally with the given object, stop the linear search if (comparator.compare((E)object, objectAtIndex) != 0) return -1; // if the objectAtIndex and object are equal, return the index if (GlazedListsImpl.equal(object, objectAtIndex)) return index; } // if we fall out of the loop we could not locate the object return -1; } /** {@inheritDoc} */ @Override public int lastIndexOf(Object object) { if(mode != STRICT_SORT_ORDER || comparator == null) return super.lastIndexOf(object); // use the fact that we have sorted data to quickly locate a position // at which we can begin a linear search for an object that .equals(object) int index = ((SimpleTree)sorted).indexOfValue(object, false, false, ALL_COLORS); // if we couldn't use the comparator to find the index, return -1 if (index == -1) return -1; // otherwise, we must now begin a linear search for the index of an element // that .equals() the given object for(; index > -1; index--) { E objectAtIndex = get(index); // if the objectAtIndex no longer compares equally with the given object, stop the linear search if(comparator.compare((E)object, objectAtIndex) != 0) return -1; // if the objectAtIndex and object are equal, return the index if(GlazedListsImpl.equal(object, objectAtIndex)) return index; } // if we fall out of the loop we could not locate the object return -1; } /** * Returns the first index of the object's sort location or * the first index at which the object could be positioned if * inserted. * *

      Unlike {@link #indexOf} this method does not guarantee the given * object {@link Object#equals(Object) equals} the element at * the returned index. Instead, they are indistinguishable according to the * sorting {@link Comparator}. * * @return a value in [0, size()] inclusive */ public int sortIndex(Object object) { if (comparator == null) throw new IllegalStateException("No Comparator exists to perform this operation"); return ((SimpleTree)sorted).indexOfValue(object, true, true, ALL_COLORS); } /** * Returns the last index of the object's sort location or * the last index at which the object could be positioned if * inserted. * *

      Unlike {@link #lastIndexOf} this method does not guarantee the given * object {@link Object#equals(Object) equals} the element at * the returned index. Instead, they are indistinguishable according to the * sorting {@link Comparator}. * * @return a value in [0, size()] inclusive */ public int lastSortIndex(Object object) { if (comparator == null) throw new IllegalStateException("No Comparator exists to perform this operation"); return ((SimpleTree)sorted).indexOfValue(object, false, true, ALL_COLORS); } /** * Returns the index in this list of the first occurrence of the specified * element, or the index where that element would be in the list if it were * inserted. * * @return the index in this list of the first occurrence of the specified * element, or the index where that element would be in the list if it * were inserted. This will return a value in [0, size()], * inclusive. * * @deprecated Deprecated as of 12/11/2005. Replaced with {@link #sortIndex(Object)} * which has cleaner semantics. */ public int indexOfSimulated(Object object) { return comparator != null ? ((SimpleTree)sorted).indexOfValue(object, true, true, ALL_COLORS) : size(); } /** {@inheritDoc} */ @Override public boolean contains(Object object) { return indexOf(object) != -1; } /** * A comparator that takes an indexed node, and compares the value * of an object in a list that has the index of that node. * *

      If one of the objects passed to {@link #compare} is not an * {@link Element}, it will compare the object directly to the object * in the source {@link EventList} referenced by the {@link Element}. * This functionality is necessary to allow use of the underlying * {@link Comparator} within {@link SimpleTree} to support {@link List#indexOf}, * {@link List#lastIndexOf}, and {@link List#contains}. */ private class ElementComparator implements Comparator { /** the actual comparator used on the values found */ private Comparator comparator; /** * Creates an {@link ElementComparator} that compares the * objects in the source list based on the indexes of the tree * nodes being compared. */ public ElementComparator(Comparator comparator) { this.comparator = comparator; } /** * Compares object alpha to object beta by using the source comparator. */ public int compare(Object alpha, Object beta) { Object alphaObject = alpha; Object betaObject = beta; int alphaIndex = -1; int betaIndex = -1; if(alpha instanceof Element) { Element alphaTreeNode = (Element)alpha; alphaIndex = unsorted.indexOfNode(alphaTreeNode, ALL_COLORS); alphaObject = source.get(alphaIndex); } if(beta instanceof Element) { Element betaTreeNode = (Element)beta; betaIndex = unsorted.indexOfNode(betaTreeNode, ALL_COLORS); betaObject = source.get(betaIndex); } int result = comparator.compare(alphaObject, betaObject); if(result != 0) return result; if(alphaIndex != -1 && betaIndex != -1) return alphaIndex - betaIndex; return 0; } } /** * A comparator that takes an indexed node, and compares the index of that node. */ private class ElementRawOrderComparator implements Comparator { /** * Compares the alpha object to the beta object by their indices. */ public int compare(Object alpha, Object beta) { Element alphaTreeNode = (Element)alpha; Element betaTreeNode = (Element)beta; int alphaIndex = unsorted.indexOfNode(alphaTreeNode, ALL_COLORS); int betaIndex = unsorted.indexOfNode(betaTreeNode, ALL_COLORS); return alphaIndex - betaIndex; } } /** {@inheritDoc} */ @Override public Iterator iterator() { return new SortedListIterator(); } /** * The fast iterator for SortedList */ private class SortedListIterator implements Iterator { /** the SimpleTreeIterator to use to move across the tree */ private SimpleTreeIterator treeIterator = new SimpleTreeIterator(sorted); /** * Returns true iff there are more value to iterate on by caling next() */ public boolean hasNext() { return treeIterator.hasNext(); } /** * Returns the next value in the iteration. */ public E next() { treeIterator.next(); Element unsortedNode = treeIterator.value(); return source.get(unsorted.indexOfNode(unsortedNode, ALL_COLORS)); } /** * Removes the last value returned by this iterator. */ public void remove() { int indexToRemove = treeIterator.index(); SortedList.this.source.remove(getSourceIndex(indexToRemove)); treeIterator = new SimpleTreeIterator(sorted, indexToRemove, ALL_COLORS); } } } libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/DebugList.java0000644000175000017500000004123212106516400026655 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; import ca.odell.glazedlists.event.ListEventPublisher; import ca.odell.glazedlists.util.concurrent.Lock; import ca.odell.glazedlists.util.concurrent.LockFactory; import ca.odell.glazedlists.util.concurrent.ReadWriteLock; import java.util.*; /** * DebugList is meant to be used as a drop-in replacement for * {@link BasicEventList} at the root of pipelines of {@link EventList}s during * development. It provides methods for turning on various types of assertions * which throw {@link RuntimeException}s when they are violated. The goal is to * detect and fail fast on error conditions in much the same way Iterators * commonly throw {@link java.util.ConcurrentModificationException}s. * *

      Some of the assertions that are controlled by this DebugList include: * *

        *
      • {@link #setLockCheckingEnabled(boolean)} toggles whether this DebugList * asserts that all read operations are guarded by read locks and all * write operations are guarded by write locks. * *
      • {@link #getSanctionedReaderThreads()} is the Set of Threads which are * allowed to read from the DebugList. If the Set is empty then * ALL Threads are assumed to be sanctioned readers. * *
      • {@link #getSanctionedWriterThreads()} is the Set of Threads which are * allowed to write to the DebugList. If the Set is empty then * ALL Threads are assumed to be sanctioned writers. *
      * * This class is left non-final to allow subclassing but since it is a * debugging tool, we make no guarantees about backward compatibility between * releases. It is meant to evolve as users discover new assertions to be added. * * @author James Lemieux */ public class DebugList extends AbstractEventList { /** A flag controlling whether we check locks before performing reads and writes. */ private boolean lockCheckingEnabled; /** The Set of Threads that are allowed to read from this DebugList; empty Set indicates all Threads may read. */ private final Set sanctionedReaderThreads = new HashSet(); /** The Set of Threads that are allowed to write to this DebugList; empty Set indicates all Threads may write. */ private final Set sanctionedWriterThreads = new HashSet(); /** A delegate EventList that implements the actual List operations. */ private EventList delegate; /** Forwards ListEvents received from the {@link #delegate} */ private final ListEventListener delegateWatcher = new ListEventForwarder(); /** A special ReadWriteLock that reports the Threads that own the read lock and write lock at any given time. */ private final DebugReadWriteLock debugReadWriteLock; /** * Constructs a DebugList which, by default, performs no debugging. It must * be customized after construction to turn on the assertions which make * sense for the list pipeline. */ public DebugList() { this(null, new DebugReadWriteLock()); } /** * Creates a {@link DebugList} using the specified * {@link ListEventPublisher} and {@link ReadWriteLock}. This is * particularly useful when multiple {@link DebugList}s are used within a * {@link CompositeList} and must share the same lock and publisher. */ private DebugList(ListEventPublisher publisher, DebugReadWriteLock debugReadWriteLock) { this.debugReadWriteLock = debugReadWriteLock; // use a normal BasicEventList as the delegate implementation this.delegate = new BasicEventList(publisher, this.debugReadWriteLock); this.delegate.addListEventListener(delegateWatcher); } /** * This private ListEventListener simply forwards updates to the delegate * BasicEventList since we're only decorating the BasicEventList. */ private class ListEventForwarder implements ListEventListener { public void listChanged(ListEvent listChanges) { updates.forwardEvent(listChanges); } } /** * Returns true if DebugList is currently checking the calling * Thread for lock ownership before each read and write operation. */ public boolean isLockCheckingEnabled() { return lockCheckingEnabled; } /** * If lockCheckingEnabled is true this DebugList will * check the calling Thread for lock ownership before each read and write * operation; false indicates this check shouldn't be performed. */ public void setLockCheckingEnabled(boolean lockCheckingEnabled) { this.lockCheckingEnabled = lockCheckingEnabled; } /** * Returns the {@link Set} of Threads that are allowed to perform reads on * this DebugList. If the {@link Set} is empty, all Threads are allowed to * read from this DebugList. Users are expected to add and remove Threads * directly on this {@link Set}. */ public Set getSanctionedReaderThreads() { return sanctionedReaderThreads; } /** * Returns the {@link Set} of Threads that are allowed to perform writes on * this DebugList. If the {@link Set} is empty, all Threads are allowed to * write to this DebugList. Users are expected to add and remove Threads * directly on this {@link Set}. */ public Set getSanctionedWriterThreads() { return sanctionedWriterThreads; } /** * Returns a new empty {@link DebugList} which shares the same * {@link ListEventListener} and {@link ReadWriteLock} with this DebugList. * This method is particularly useful when debugging a {@link CompositeList} * where some member lists are DebugLists and thus must share an identical * publisher and locks in order to participate in the CompositeList. */ public DebugList createNewDebugList() { return new DebugList(getPublisher(), debugReadWriteLock); } /** * This generic method is called immediately before any read operation is * invoked. All generic read assertions should take place here. */ protected void beforeReadOperation() { // if a Set of reader Threads have been given, ensure the current Thread is one of them if (!sanctionedReaderThreads.isEmpty() && !sanctionedReaderThreads.contains(Thread.currentThread())) throw new IllegalStateException("DebugList detected an unexpected Thread (" + Thread.currentThread() + ") attempting to perform a read operation"); // if lock checking is enabled, ensure the current Thread holds a read or write lock before continuing if (isLockCheckingEnabled() && !debugReadWriteLock.isThreadHoldingReadOrWriteLock()) throw new IllegalStateException("DebugList detected a failure to acquire the readLock prior to a read operation"); } /** * This method is currently a no-op and exists for parity. Subclasses may * choose to insert extract assertion logic here. */ protected void afterReadOperation() { } /** * This generic method is called immediately before any write operation is * invoked. All generic write assertions should take place here. */ protected void beforeWriteOperation() { // if a Set of writer Threads have been given, ensure the current Thread is one of them if (!sanctionedWriterThreads.isEmpty() && !sanctionedWriterThreads.contains(Thread.currentThread())) throw new IllegalStateException("DebugList detected an unexpected Thread (" + Thread.currentThread() + ") attempting to perform a write operation"); // if lock checking is enabled, ensure the current Thread holds a write lock before continuing if (isLockCheckingEnabled() && !debugReadWriteLock.isThreadHoldingWriteLock()) throw new IllegalStateException("DebugList detected a failure to acquire the writeLock prior to a write operation"); } /** * This method is currently a no-op and exists for parity. Subclasses may * choose to insert extract assertion logic here. */ protected void afterWriteOperation() { } /** {@inheritDoc} */ @Override public ReadWriteLock getReadWriteLock() { return delegate.getReadWriteLock(); } /** {@inheritDoc} */ @Override public ListEventPublisher getPublisher() { return delegate.getPublisher(); } /** {@inheritDoc} */ @Override public E get(int index) { beforeReadOperation(); try { return delegate.get(index); } finally { afterReadOperation(); } } /** {@inheritDoc} */ @Override public int size() { beforeReadOperation(); try { return delegate.size(); } finally { afterReadOperation(); } } /** {@inheritDoc} */ @Override public boolean contains(Object object) { beforeReadOperation(); try { return delegate.contains(object); } finally { afterReadOperation(); } } /** {@inheritDoc} */ @Override public boolean containsAll(Collection collection) { beforeReadOperation(); try { return delegate.containsAll(collection); } finally { afterReadOperation(); } } /** {@inheritDoc} */ @Override public boolean equals(Object object) { beforeReadOperation(); try { return delegate.equals(object); } finally { afterReadOperation(); } } /** {@inheritDoc} */ @Override public int hashCode() { beforeReadOperation(); try { return delegate.hashCode(); } finally { afterReadOperation(); } } /** {@inheritDoc} */ @Override public int indexOf(Object object) { beforeReadOperation(); try { return delegate.indexOf(object); } finally { afterReadOperation(); } } /** {@inheritDoc} */ @Override public int lastIndexOf(Object object) { beforeReadOperation(); try { return delegate.lastIndexOf(object); } finally { afterReadOperation(); } } /** {@inheritDoc} */ @Override public boolean isEmpty() { beforeReadOperation(); try { return delegate.isEmpty(); } finally { afterReadOperation(); } } /** {@inheritDoc} */ @Override public Object[] toArray() { beforeReadOperation(); try { return delegate.toArray(); } finally { afterReadOperation(); } } /** {@inheritDoc} */ @Override public T[] toArray(T[] array) { beforeReadOperation(); try { return delegate.toArray(array); } finally { afterReadOperation(); } } /** {@inheritDoc} */ @Override public String toString() { beforeReadOperation(); try { return delegate.toString(); } finally { afterReadOperation(); } } /** {@inheritDoc} */ @Override public boolean add(E value) { beforeWriteOperation(); try { return delegate.add(value); } finally { afterWriteOperation(); } } /** {@inheritDoc} */ @Override public boolean remove(Object toRemove) { beforeWriteOperation(); try { return delegate.remove(toRemove); } finally { afterWriteOperation(); } } /** {@inheritDoc} */ @Override public boolean addAll(Collection values) { beforeWriteOperation(); try { return delegate.addAll(values); } finally { afterWriteOperation(); } } /** {@inheritDoc} */ @Override public boolean addAll(int index, Collection values) { beforeWriteOperation(); try { return delegate.addAll(index, values); } finally { afterWriteOperation(); } } /** {@inheritDoc} */ @Override public boolean removeAll(Collection values) { beforeWriteOperation(); try { return delegate.removeAll(values); } finally { afterWriteOperation(); } } /** {@inheritDoc} */ @Override public boolean retainAll(Collection values) { beforeWriteOperation(); try { return delegate.retainAll(values); } finally { afterWriteOperation(); } } /** {@inheritDoc} */ @Override public void clear() { beforeWriteOperation(); try { delegate.clear(); } finally { afterWriteOperation(); } } /** {@inheritDoc} */ @Override public E set(int index, E value) { beforeWriteOperation(); try { return delegate.set(index, value); } finally { afterWriteOperation(); } } /** {@inheritDoc} */ @Override public void add(int index, E value) { beforeWriteOperation(); try { delegate.add(index, value); } finally { afterWriteOperation(); } } /** {@inheritDoc} */ @Override public E remove(int index) { beforeWriteOperation(); try { return delegate.remove(index); } finally { afterWriteOperation(); } } /** {@inheritDoc} */ public void dispose() { delegate.removeListEventListener(this.delegateWatcher); delegate = null; } /** * This special ReadWriteLock can answer the question of whether the current * Thread holds the read or write lock. */ private static class DebugReadWriteLock implements ReadWriteLock { private final DebugLock readLock; private final DebugLock writeLock; public DebugReadWriteLock() { // decorate normaly read/write locks with Thread recording final ReadWriteLock decorated = LockFactory.DEFAULT.createReadWriteLock(); this.readLock = new DebugLock(decorated.readLock()); this.writeLock = new DebugLock(decorated.writeLock()); } public Lock readLock() { return readLock; } public Lock writeLock() { return writeLock; } /** * Returns true if and only if the current Thread holds the * write lock. */ public boolean isThreadHoldingWriteLock() { return writeLock.getThreadsHoldingLock().contains(Thread.currentThread()); } /** * Returns true if the current Thread holds the read lock or * write lock. */ public boolean isThreadHoldingReadOrWriteLock() { return readLock.getThreadsHoldingLock().contains(Thread.currentThread()) || writeLock.getThreadsHoldingLock().contains(Thread.currentThread()); } /** * A special wrapper around a conventional Lock which tracks the * Threads that current hold the lock. */ private static class DebugLock implements Lock { private final Lock delegate; private final List threadsHoldingLock = new ArrayList(); public DebugLock(Lock delegate) { this.delegate = delegate; } public void lock() { delegate.lock(); // record the current Thread as a lock holder threadsHoldingLock.add(Thread.currentThread()); } public boolean tryLock() { final boolean success = delegate.tryLock(); // if the lock was successfully acquired, record the current Thread as a lock holder if (success) threadsHoldingLock.add(Thread.currentThread()); return success; } public void unlock() { delegate.unlock(); // remove the current Thread as a lock holder threadsHoldingLock.remove(Thread.currentThread()); } /** * Returns the List of Threads holding the lock. */ public List getThreadsHoldingLock() { return threadsHoldingLock; } } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/ListSelection.java0000644000175000017500000014615312106516400027564 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventAssembler; import ca.odell.glazedlists.event.ListEventListener; import ca.odell.glazedlists.impl.adt.Barcode; import ca.odell.glazedlists.impl.adt.BarcodeIterator; import ca.odell.glazedlists.matchers.Matcher; import java.util.*; /** * A class to provide index-based selection features. This class maintains two * lists derived from a single {@link EventList}: *
        *
      • {@link #getSelected() Selected} - an {@link EventList} that contains only the selected values.
      • *
      • {@link #getDeselected() Deselected} - an {@link EventList} that contains only the deselected values.
      • *
      * *

      This design is intended to allow the sharing of selection logic between * all supported GUI toolkits as well as being available for use in non-GUI * applications and for index-based filtering. * *

      Warning: This class is * thread ready but not thread safe. See {@link EventList} for an example * of thread safe code. * * @author Kevin Maltby */ public class ListSelection implements ListEventListener { /** * A selection mode where at most one element may be selected at one time. * For convenience, this value equals {@link javax.swing.ListSelectionModel#SINGLE_SELECTION}. */ public static final int SINGLE_SELECTION = 0; /** * A selection mode where at most one range of elements may be selected at one time. * For convenience, this value equals {@link javax.swing.ListSelectionModel#SINGLE_INTERVAL_SELECTION}. */ public static final int SINGLE_INTERVAL_SELECTION = 1; /** * A selection mode where any element may be selected and elements added * adjacent to selected elements are selected. For convenience, this value * equals {@link javax.swing.ListSelectionModel#MULTIPLE_INTERVAL_SELECTION}. */ public static final int MULTIPLE_INTERVAL_SELECTION = 2; /** * A selection mode where any element may be selected and freshly added elements * are always deselected. No equivalent policy exists in * {@link javax.swing.ListSelectionModel} */ public static final int MULTIPLE_INTERVAL_SELECTION_DEFENSIVE = 103; /** the current selection colour */ private static final Object SELECTED = Barcode.BLACK; /** the current deselection colour */ private static final Object DESELECTED = Barcode.WHITE; /** the source list */ private final EventList source; /** the selected view */ private SelectedList selectedList; /** the deselected view */ private DeselectedList deselectedList; /** the toggling selected view */ private SelectionToggleList selectedToggleList; /** the toggling deselected view */ private DeselectionToggleList deselectedToggleList; /** the selection state */ private Barcode barcode = new Barcode(); /** the lead selection index */ private int leadSelectionIndex = -1; /** the anchor selection index */ private int anchorSelectionIndex = -1; /** the selection mode defines characteristics of the selection */ private int selectionMode = MULTIPLE_INTERVAL_SELECTION_DEFENSIVE; /** the Matchers, if any, which decide whether a source element can be selected or not */ private final Collection> validSelectionMatchers = new ArrayList>(); /** * the registered SelectionListeners; also used as a mutex to ensure we * build each of the following only once: * *

        *
      • {@link #selectedList}
      • *
      • {@link #deselectedList}
      • *
      • {@link #selectedToggleList}
      • *
      • {@link #deselectedToggleList}
      • *
      */ private final List selectionListeners = new ArrayList(1); /** * Creates a new ListSelection that listens to changes on the given source. * When using this constructor, all elements are deselected by default. */ public ListSelection(EventList source) { this.source = source; barcode.add(0, DESELECTED, source.size()); source.addListEventListener(this); } /** * Creates a new ListSelection that listens to changes on the given source * and initializes selection with the given array of indices. */ public ListSelection(EventList source, int[] initialSelection) { this(source); select(initialSelection); } /** * Handle changes to the source list by adjusting our selection state and * the contents of the selected and deselected lists. */ public void listChanged(ListEvent listChanges) { // keep track of what used to be selected final int minSelectionIndexBefore = getMinSelectionIndex(); final int maxSelectionIndexBefore = getMaxSelectionIndex(); boolean selectionChanged = false; // handle reordering events if(listChanges.isReordering()) { // prepare for the reordering event beginSelected(); int[] sourceReorderMap = listChanges.getReorderMap(); int[] selectReorderMap = new int[barcode.colourSize(SELECTED)]; int[] deselectReorderMap = new int[barcode.colourSize(DESELECTED)]; // adjust the flaglist & construct a reorder map to propagate Barcode previousBarcode = barcode; barcode = new Barcode(); for(int c = 0; c < sourceReorderMap.length; c++) { Object flag = previousBarcode.get(sourceReorderMap[c]); boolean wasSelected = (flag != DESELECTED); barcode.add(c, flag, 1); if(wasSelected) { int previousIndex = previousBarcode.getColourIndex(sourceReorderMap[c], SELECTED); int currentIndex = barcode.getColourIndex(c, SELECTED); selectReorderMap[currentIndex] = previousIndex; } else { int previousIndex = previousBarcode.getColourIndex(sourceReorderMap[c], DESELECTED); int currentIndex = barcode.getColourIndex(c, DESELECTED); deselectReorderMap[currentIndex] = previousIndex; } } // adjust other internal state anchorSelectionIndex = -1; leadSelectionIndex = -1; // fire the reorder on the selected list addSelectedReorder(selectReorderMap); commitSelected(); // fire the reorder on the deselected list beginDeselected(); addDeselectedReorder(deselectReorderMap); commitDeselected(); // all reordering events are assumed to have changed the selection selectionChanged = true; // handle non-reordering events } else { // prepare a sequence of changes beginAll(); // for all changes update the barcode while(listChanges.next()) { int index = listChanges.getIndex(); int changeType = listChanges.getType(); // learn about what it was int previousSelectionIndex = barcode.getColourIndex(index, SELECTED); boolean previouslySelected = previousSelectionIndex != -1; // when an element is deleted, blow it away if(changeType == ListEvent.DELETE) { // if the change occurred anywhere before the maxSelectionIndexBefore // then it effects the current selection and we must fire a ListSelectionEvent if (index <= maxSelectionIndexBefore) selectionChanged = true; // delete selected values if(previouslySelected) { barcode.remove(index, 1); addSelectedDelete(previousSelectionIndex, listChanges.getOldValue()); // delete deselected values } else { int deselectedIndex = barcode.getColourIndex(index, DESELECTED); addDeselectedDelete(deselectedIndex, listChanges.getOldValue()); barcode.remove(index, 1); } // when an element is inserted, it is selected if its index was selected } else if(changeType == ListEvent.INSERT) { // if the change occurred anywhere before the maxSelectionIndexBefore // then it effects the current selection and we must fire a ListSelectionEvent if (index <= maxSelectionIndexBefore) selectionChanged = true; // when selected, decide based on selection mode if(previouslySelected) { // select the inserted for single interval and multiple interval selection if(selectionMode == SINGLE_INTERVAL_SELECTION || selectionMode == MULTIPLE_INTERVAL_SELECTION) { barcode.add(index, SELECTED, 1); addSelectedInsert(previousSelectionIndex, listChanges.getNewValue()); // do not select the inserted for single selection and defensive selection } else { barcode.add(index, DESELECTED, 1); int deselectedIndex = barcode.getColourIndex(index, DESELECTED); addDeselectedInsert(deselectedIndex, listChanges.getNewValue()); } // add a deselected value } else { barcode.add(index, DESELECTED, 1); int deselectedIndex = barcode.getColourIndex(index, DESELECTED); addDeselectedInsert(deselectedIndex, listChanges.getNewValue()); } // when an element is changed, assume selection stays the same } else if(changeType == ListEvent.UPDATE) { // update a selected value if(previouslySelected) { addSelectedUpdate(previousSelectionIndex, listChanges.getOldValue(), listChanges.getNewValue()); // update a deselected value } else { int deselectedIndex = barcode.getColourIndex(index, DESELECTED); addDeselectedUpdate(deselectedIndex, listChanges.getOldValue(), listChanges.getNewValue()); } } // adjust other internal state anchorSelectionIndex = adjustIndex(anchorSelectionIndex, changeType, index); leadSelectionIndex = adjustIndex(leadSelectionIndex, changeType, index); } commitAll(); } // notify listeners of the selection change if(minSelectionIndexBefore != -1 && maxSelectionIndexBefore != -1 && selectionChanged) { final int minSelectionIndexAfter = getMinSelectionIndex(); final int maxSelectionIndexAfter = getMaxSelectionIndex(); int changeStart = minSelectionIndexBefore; int changeFinish = maxSelectionIndexBefore; if(minSelectionIndexAfter != -1 && minSelectionIndexAfter < changeStart) changeStart = minSelectionIndexAfter; if(maxSelectionIndexAfter != -1 && maxSelectionIndexAfter > changeFinish) changeFinish = maxSelectionIndexAfter; fireSelectionChanged(changeStart, changeFinish); } } /** * Add a matcher which decides when source elements are valid for selection. * * @param validSelectionMatcher returns true if a source elements * can be selected; false otherwise */ public void addValidSelectionMatcher(Matcher validSelectionMatcher) { validSelectionMatchers.add(validSelectionMatcher); // walk through the existing selections deselecting anything that is no longer selectable for (int i = getMinSelectionIndex(), n = getMaxSelectionIndex(); i <= n; i++) if (isSelected(i) && !isSelectable(i)) deselect(i); } /** * Remove a matcher which decides when source elements are valid for selection. * * @param validSelectionMatcher returns true if a source elements * can be selected; false otherwise */ public void removeValidSelectionMatcher(Matcher validSelectionMatcher) { validSelectionMatchers.remove(validSelectionMatcher); } /** * Adjusts the specified index to the specified change. This is used to adjust * the anchor and lead selection indices when list changes occur. */ private int adjustIndex(int indexBefore, int changeType, int changeIndex) { if(indexBefore == -1) return -1; if(changeType == ListEvent.DELETE) { if(changeIndex < indexBefore) return indexBefore-1; else if(changeIndex == indexBefore) return -1; else return indexBefore; } else if(changeType == ListEvent.UPDATE) { return indexBefore; } else if(changeType == ListEvent.INSERT) { if(changeIndex <= indexBefore) return indexBefore+1; else return indexBefore; } else { throw new IllegalStateException(); } } /** * Gets an {@link EventList} that contains only selected values and modifies * the source list on mutation. * * Adding and removing items from this list performs the same operation on * the source list. */ public EventList getSelected() { synchronized (selectionListeners) { if(selectedList == null){ selectedList = new SelectedList(source); source.getPublisher().setRelatedListener(selectedList, this); } } return selectedList; } /** * Gets an {@link EventList} that contains only selected values and modifies * the selection state on mutation. * *

      Adding an item to this list selects it and removing an item deselects it. * If an item not in the source list is added an * {@link IllegalArgumentException} is thrown. This list does not support * the {@link List#set set} method. */ public EventList getTogglingSelected() { synchronized (selectionListeners) { if(selectedToggleList == null) { selectedToggleList = new SelectionToggleList(source); source.getPublisher().setRelatedListener(selectedToggleList, this); } } return selectedToggleList; } /** * Gets an {@link EventList} that contains only deselected values add * modifies the source list on mutation. * * Adding and removing items from this list performs the same operation on * the source list. */ public EventList getDeselected() { synchronized (selectionListeners) { if(deselectedList == null) { deselectedList = new DeselectedList(source); source.getPublisher().setRelatedListener(deselectedList, this); } } return deselectedList; } /** * Gets an {@link EventList} that contains only deselected values and * modifies the selection state on mutation. * *

      Adding an item to this list deselects it and removing an item selects it. * If an item not in the source list is added an * {@link IllegalArgumentException} is thrown. This list does not support * the {@link List#set set} method. */ public EventList getTogglingDeselected() { synchronized (selectionListeners) { if(deselectedToggleList == null) { deselectedToggleList = new DeselectionToggleList(source); source.getPublisher().setRelatedListener(deselectedToggleList, this); } } return deselectedToggleList; } /** * Get the {@link EventList} that selection is being managed for. */ public EventList getSource() { return source; } /** * Inverts the current selection. */ public void invertSelection() { // Clear the anchor and lead anchorSelectionIndex = -1; leadSelectionIndex = -1; // Update the selected list to reflect the selection inversion beginAll(); for(BarcodeIterator i = barcode.iterator(); i.hasNext(); ) { Object color = i.next(); E value = source.get(i.getIndex()); int originalIndex = i.getColourIndex(color); if(color == SELECTED) { i.set(DESELECTED); int newIndex = i.getColourIndex(DESELECTED); addDeselectEvent(originalIndex, newIndex, value); } else { i.set(SELECTED); int newIndex = i.getColourIndex(SELECTED); addSelectEvent(newIndex, originalIndex, value); } } commitAll(); // notify selection listeners that selection has been inverted fireSelectionChanged(0, source.size() - 1); } /** * Returns whether or not the item with the given source index * is selected. */ public boolean isSelected(int sourceIndex) { if(sourceIndex < 0 || sourceIndex >= source.size()) { return false; } return barcode.getColourIndex(sourceIndex, SELECTED) != -1; } /** * Deselects the element at the given index. */ public void deselect(int index) { deselect(index, index); } /** * Deselects all of the elements within the given range. */ public void deselect(int start, int end) { // fast fail if the range is invalid if(start == -1 || end == -1) { return; // use single value deselection in single selection mode } else if(selectionMode == SINGLE_SELECTION) { int selectedIndex = getMaxSelectionIndex(); // only change selection if you have to if(selectedIndex >= start && selectedIndex <= end) { deselectAll(); } return; // adjust the range to prevent the creation of multiple intervals } else if(selectionMode == SINGLE_INTERVAL_SELECTION && start > getMinSelectionIndex()) { end = Math.max(end, getMaxSelectionIndex()); } // record the original anchor and lead if they are about to change final int oldAnchor = anchorSelectionIndex == start ? -1 : anchorSelectionIndex; final int oldLead = leadSelectionIndex == end ? -1 : leadSelectionIndex; // update anchor and lead anchorSelectionIndex = start; leadSelectionIndex = end; // alter selection accordingly setSubRangeOfRange(false, start, end, -1, -1, oldLead, oldAnchor); } /** * Deselects all of the elements in the given array of indices. The * array must contain indices in sorted, ascending order. */ public void deselect(int[] indices) { // keep track of the range of values that were affected int firstAffectedIndex = -1; int lastAffectedIndex = -1; // iterate through the barcode updating the selected list as you go beginAll(); int currentIndex = 0; for(BarcodeIterator i = barcode.iterator(); i.hasNext() && currentIndex != indices.length;) { Object value = i.next(); if(i.getIndex() == indices[currentIndex]) { // selection changed if(value == SELECTED) { if(firstAffectedIndex == -1) firstAffectedIndex = i.getIndex(); lastAffectedIndex = i.getIndex(); addDeselectEvent(i); } currentIndex++; } } commitAll(); // notify listeners of selection change if(firstAffectedIndex > -1) fireSelectionChanged(firstAffectedIndex, lastAffectedIndex); } /** * Deselect all elements. */ public void deselectAll() { setAllColor(DESELECTED); } /** * @param color either selected or deselected. */ private void setAllColor(Object color) { // if there is nothing selected, we're done Object oppositeColor = (color == SELECTED) ? DESELECTED : SELECTED; if(barcode.colourSize(oppositeColor) == 0) return; // keep track of the range of values that were affected int firstAffectedIndex = -1; int lastAffectedIndex = -1; // prepare the change events beginAll(); for(BarcodeIterator i = barcode.iterator(); i.hasNextColour(oppositeColor);) { i.nextColour(oppositeColor); int index = i.getIndex(); E value = source.get(index); if(color == SELECTED) { addDeselectedDelete(0, value); addSelectedInsert(index, value); } else { addSelectedDelete(0, value); addDeselectedInsert(index, value); } if(firstAffectedIndex == -1) firstAffectedIndex = index; lastAffectedIndex = index; } // reset barcode state barcode.clear(); barcode.add(0, color, source.size()); // fire events commitAll(); fireSelectionChanged(firstAffectedIndex, lastAffectedIndex); } /** * Selects the element at the given index. */ public void select(int index) { select(index, index); } /** * Selects all of the elements within the given range. */ public void select(int start, int end) { // fast fail if the range is an no-op if(start == -1 || end == -1) { return; // use single value deselection in single selection mode } else if(selectionMode == SINGLE_SELECTION) { setSelection(start); return; // adjust the range to prevent the creation of multiple intervals } else if(selectionMode == SINGLE_INTERVAL_SELECTION) { // test if the current and new selection overlap boolean overlap = false; int minSelectedIndex = getMinSelectionIndex(); int maxSelectedIndex = getMaxSelectionIndex(); if(minSelectedIndex - 1 <= start && start <= maxSelectedIndex + 1) overlap = true; if(minSelectedIndex - 1 <= end && end <= maxSelectedIndex + 1) overlap = true; if(!overlap) { setSelection(start, end); return; } } // record the original anchor and lead if they are about to change final int oldAnchor = anchorSelectionIndex == start ? -1 : anchorSelectionIndex; final int oldLead = leadSelectionIndex == end ? -1 : leadSelectionIndex; // update anchor and lead anchorSelectionIndex = start; leadSelectionIndex = end; // alter selection accordingly setSubRangeOfRange(true, start, end, -1, -1, oldLead, oldAnchor); } /** * Selects all of the elements in the given array of indices. The * array must contain indices in sorted, ascending order. */ public void select(int[] indices) { // keep track of the range of values that were affected int firstAffectedIndex = -1; int lastAffectedIndex = -1; // iterate through the barcode updating the selected list as you go beginAll(); int currentIndex = 0; for(BarcodeIterator i = barcode.iterator(); i.hasNext() && currentIndex != indices.length;) { Object value = i.next(); if(i.getIndex() == indices[currentIndex]) { // selection changed if(value != SELECTED) { if(firstAffectedIndex == -1) firstAffectedIndex = i.getIndex(); lastAffectedIndex = i.getIndex(); addSelectEvent(i); } currentIndex++; } } commitAll(); // notify listeners of selection change if(firstAffectedIndex > -1) fireSelectionChanged(firstAffectedIndex, lastAffectedIndex); } /** * Select the specified element, if it exists. * * @return the index of the newly selected element, or -1 if no * element was found. */ public int select(E value) { int index = source.indexOf(value); if(index != -1) select(index); return index; } /** * Select all of the specified values. * * @return true if the selection changed as a result of the call. */ public boolean select(Collection values) { // This implementation leaves a lot to be desired. It's inefficient and // awkward. If possible, we should clean up the entire SelectionList so // we don't need to worry as much about the deselection list. // 1. Convert our Collection of values into a SortedSet of indices SortedSet indicesToSelect = new TreeSet(); for(Iterator v = values.iterator(); v.hasNext(); ) { E value = v.next(); int index = source.indexOf(value); if(index == -1) continue; indicesToSelect.add(new Integer(index)); } if(indicesToSelect.isEmpty()) return false; // 2. convert the sorted set of Integers into an int[] int[] indicesToSelectAsInts = new int[indicesToSelect.size()]; int arrayIndex = 0; for(Iterator i = indicesToSelect.iterator(); i.hasNext(); ) { Integer selectIndex = i.next(); indicesToSelectAsInts[arrayIndex] = selectIndex.intValue(); arrayIndex++; } // 3. Delegate to the other method, and return true if the selection grew int selectionSizeBefore = getSelected().size(); select(indicesToSelectAsInts); int selectionSizeAfter = getSelected().size(); return selectionSizeAfter > selectionSizeBefore; } /** * Selects all elements. */ public void selectAll() { setAllColor(SELECTED); } /** * Sets the selection to be only the element at the given index. If the * given index is -1, the selection will be cleared. */ public void setSelection(int index) { setSelection(index, index); } /** * Sets the selection to be only elements within the given range. If the * endpoints of the range are -1, the selection will be cleared. */ public void setSelection(int start, int end) { // a range including -1 implies a deselectAll() if(start == -1 || end == -1) { deselectAll(); return; } else if(selectionMode == SINGLE_SELECTION) { end = start; } // record the original anchor and lead if they are about to change final int oldAnchor = anchorSelectionIndex == start ? -1 : anchorSelectionIndex; final int oldLead = leadSelectionIndex == end ? -1 : leadSelectionIndex; // update anchor and lead anchorSelectionIndex = start; leadSelectionIndex = end; // alter selection accordingly setSubRangeOfRange(true, start, end, getMinSelectionIndex(), getMaxSelectionIndex(), oldLead, oldAnchor); } /** * Sets the selection to be only the element in the given array of indices. * Unlike {@link #setSelection(int)} and {@link #setSelection(int,int)}, * providing a value of -1 is an error. The array must contain indices in * sorted, ascending order. */ public void setSelection(int[] indices) { // fast fail is the selection is empty if(indices.length == 0) { deselectAll(); return; } // keep track of the range of values that were affected int firstAffectedIndex = -1; int lastAffectedIndex = -1; // iterate through the barcode updating the selected list as you go beginAll(); int currentIndex = 0; for(BarcodeIterator i = barcode.iterator(); i.hasNext();) { Object value = i.next(); // this element should be selected if(i.getIndex() == indices[currentIndex]) { // selection changed if(value != SELECTED) { if(firstAffectedIndex == -1) firstAffectedIndex = i.getIndex(); lastAffectedIndex = i.getIndex(); addSelectEvent(i); } // look at the next value if(currentIndex < indices.length - 1) currentIndex++; // element was selected and isn't within the new selection } else if(value == SELECTED) { if(firstAffectedIndex == -1) firstAffectedIndex = i.getIndex(); lastAffectedIndex = i.getIndex(); addDeselectEvent(i); } } commitAll(); // notify listeners of selection change if(firstAffectedIndex > -1) fireSelectionChanged(firstAffectedIndex, lastAffectedIndex); } /** * Return the anchor of the current selection. */ public int getAnchorSelectionIndex() { return anchorSelectionIndex; } /** * Set the anchor selection index. */ public void setAnchorSelectionIndex(int anchorSelectionIndex) { // record the original anchor and lead if they are about to change final int oldAnchor = this.anchorSelectionIndex == anchorSelectionIndex ? -1 : anchorSelectionIndex; // update anchor this.anchorSelectionIndex = anchorSelectionIndex; // a value of -1 clears selection if(anchorSelectionIndex == -1 || leadSelectionIndex == -1) { deselectAll(); // only the anchor should be selected } else if(selectionMode == SINGLE_SELECTION) { setSubRangeOfRange(true, anchorSelectionIndex, anchorSelectionIndex, getMinSelectionIndex(), getMaxSelectionIndex(), -1, oldAnchor); // select the interval between anchor and lead } else if(selectionMode == SINGLE_INTERVAL_SELECTION) { setSubRangeOfRange(true, anchorSelectionIndex, leadSelectionIndex, getMinSelectionIndex(), getMaxSelectionIndex(), -1, oldAnchor); // select the interval between anchor and lead without deselecting anything } else { setSubRangeOfRange(true, anchorSelectionIndex, leadSelectionIndex, -1, -1, -1, oldAnchor); } } /** * Return the lead of the current selection. */ public int getLeadSelectionIndex() { return leadSelectionIndex; } /** * Set the lead selection index. */ public void setLeadSelectionIndex(int leadSelectionIndex) { // record the original anchor and lead if they are about to change final int oldLead = this.leadSelectionIndex == leadSelectionIndex ? -1 : leadSelectionIndex; // update lead int originalLeadIndex = this.leadSelectionIndex; this.leadSelectionIndex = leadSelectionIndex; // a value of -1 clears selection if(leadSelectionIndex == -1 || anchorSelectionIndex == -1) { deselectAll(); // select only the lead } else if(selectionMode == SINGLE_SELECTION) { setSubRangeOfRange(true, leadSelectionIndex, leadSelectionIndex, getMinSelectionIndex(), getMaxSelectionIndex(), oldLead, -1); // select the interval between anchor and lead } else if(selectionMode == SINGLE_INTERVAL_SELECTION) { setSubRangeOfRange(true, anchorSelectionIndex, leadSelectionIndex, getMinSelectionIndex(), getMaxSelectionIndex(), oldLead, -1); // select the interval between anchor and lead deselecting as necessary } else { setSubRangeOfRange(true, anchorSelectionIndex, leadSelectionIndex, anchorSelectionIndex, originalLeadIndex, oldLead, -1); } } private void addSelectedReorder(int[] selectReorderMap) { if(selectedList != null) selectedList.updates().reorder(selectReorderMap); if(selectedToggleList != null) selectedToggleList.updates().reorder(selectReorderMap); } private void addDeselectedReorder(int[] deselectReorderMap) { if(deselectedList != null) deselectedList.updates().reorder(deselectReorderMap); if(deselectedToggleList != null) deselectedToggleList.updates().reorder(deselectReorderMap); } private void addSelectEvent(BarcodeIterator i) { E value = source.get(i.getIndex()); int deselectedIndex = i.getColourIndex(DESELECTED); int selectedIndex = i.set(SELECTED); addSelectEvent(selectedIndex, deselectedIndex, value); } private void addSelectEvent(int selectIndex, int deselectIndex, E value) { addDeselectedDelete(deselectIndex, value); addSelectedInsert(selectIndex, value); } private void addDeselectEvent(BarcodeIterator i) { E value = source.get(i.getIndex()); int selectedIndex = i.getColourIndex(SELECTED); int deselectedIndex = i.set(DESELECTED); addDeselectEvent(selectedIndex, deselectedIndex, value); } private void addDeselectEvent(int selectIndex, int deselectIndex, E value) { addSelectedDelete(selectIndex, value); addDeselectedInsert(deselectIndex, value); } private void addSelectedInsert(int index, E newValue){ if(selectedList != null) selectedList.updates().elementInserted(index, newValue); if(selectedToggleList != null) selectedToggleList.updates().elementInserted(index, newValue); } private void addSelectedUpdate(int index, E oldValue, E newValue){ if(selectedList != null) selectedList.updates().elementUpdated(index, oldValue, newValue); if(selectedToggleList != null) selectedToggleList.updates().elementUpdated(index, oldValue, newValue); } private void addSelectedDelete(int index, E oldValue){ if(selectedList != null) selectedList.updates().elementDeleted(index, oldValue); if(selectedToggleList != null) selectedToggleList.updates().elementDeleted(index, oldValue); } private void addDeselectedInsert(int index, E value){ if(deselectedList != null) deselectedList.updates().elementInserted(index, value); if(deselectedToggleList != null) deselectedToggleList.updates().elementInserted(index, value); } private void addDeselectedDelete(int index, E oldValue){ if(deselectedList != null) deselectedList.updates().elementDeleted(index, oldValue); if(deselectedToggleList != null) deselectedToggleList.updates().elementDeleted(index, oldValue); } private void addDeselectedUpdate(int index, E oldValue, E newValue) { if(deselectedList != null) deselectedList.updates().elementUpdated(index, oldValue, newValue); if(deselectedToggleList != null) deselectedToggleList.updates().elementUpdated(index, oldValue, newValue); } private void beginAll() { beginSelected(); beginDeselected(); } private void commitAll() { commitSelected(); commitDeselected(); } private void beginSelected() { if(selectedList != null) { selectedList.updates().beginEvent(); } if(selectedToggleList != null) { selectedToggleList.updates().beginEvent(); } } private void commitSelected() { if(selectedList != null) { selectedList.updates().commitEvent(); } if(selectedToggleList != null) { selectedToggleList.updates().commitEvent(); } } private void beginDeselected() { if(deselectedList != null) deselectedList.updates().beginEvent(); if(deselectedToggleList != null) deselectedToggleList.updates().beginEvent(); } private void commitDeselected() { if(deselectedList != null) deselectedList.updates().commitEvent(); if(deselectedToggleList != null) deselectedToggleList.updates().commitEvent(); } /** * Set the selection mode. */ public void setSelectionMode(int selectionMode) { this.selectionMode = selectionMode; setSelection(getMinSelectionIndex(), getMaxSelectionIndex()); } /** * Returns the current selection mode. */ public int getSelectionMode() { return selectionMode; } /** * Returns the first selected index or -1 if nothing is selected. */ public int getMinSelectionIndex() { if(barcode.colourSize(SELECTED) == 0) return -1; return barcode.getIndex(0, SELECTED); } /** * Returns the last selected index or -1 if nothing is selected. */ public int getMaxSelectionIndex() { if(barcode.colourSize(SELECTED) == 0) return -1; return barcode.getIndex(barcode.colourSize(SELECTED) - 1, SELECTED); } /** * Walks through the union of the specified ranges. All values in the * first range are given the specified selection value. All values in * the second range but not in the first range are given the opposite * selection value. In effect, the first range and the intersection of * the two ranges are given the specified value. The parts that are * in the second range but not in the first range (ie. everything else) * is given the opposite selection value. * * @param select true to set values in the first range as selected and * the other values in the second range as not selected. false to * set values in the first range as not selected and the other * values in the second range as selected. * @param changeIndex0 one end of the first range, inclusive. * @param changeIndex1 the opposite end of the first range, inclusive. * This may be lower than changeIndex0. * @param invertIndex0 one end of the second range, inclusive. To * specify an empty second range, specify -1 for this value. * @param invertIndex1 the opposite end of the second range, inclusive. * This may be lower than invertIndex0. To specify an empty second * range, specify -1 for this value. */ private void setSubRangeOfRange(boolean select, int changeIndex0, int changeIndex1, int invertIndex0, int invertIndex1, int oldLead, int oldAnchor) { // verify that the first range is legitimate if(changeIndex0 >= source.size() || changeIndex1 >= source.size() || ((changeIndex0 == -1 || changeIndex1 == -1) && changeIndex0 != changeIndex1)) { throw new IndexOutOfBoundsException("Invalid range for selection: " + changeIndex0 + "-" + changeIndex1 + ", list size is " + source.size()); } // verify that the second range is legitimate if(invertIndex0 >= source.size() || invertIndex1 >= source.size() || ((invertIndex0 == -1 || invertIndex1 == -1) && invertIndex0 != invertIndex1)) { throw new IndexOutOfBoundsException("Invalid range for invert selection: " + invertIndex0 + "-" + invertIndex1 + ", list size is " + source.size()); } // when the first range is empty if(changeIndex0 == -1 && changeIndex1 == -1) { // if the second range is empty, we're done if(invertIndex0 == -1 && invertIndex1 == -1) return; // otherwise set the first range to the second range and invert the goal changeIndex0 = invertIndex0; changeIndex1 = invertIndex1; select = !select; } // when the second range is empty if(invertIndex0 == -1 && invertIndex1 == -1) { // make this a subset of the first index which behaves the same as empty invertIndex0 = changeIndex0; invertIndex1 = changeIndex1; } // now get the change interval and invert interval int minChangeIndex = Math.min(changeIndex0, changeIndex1); int maxChangeIndex = Math.max(changeIndex0, changeIndex1); int minInvertIndex = Math.min(invertIndex0, invertIndex1); int maxInvertIndex = Math.max(invertIndex0, invertIndex1); // get the union of the two ranges int minUnionIndex = Math.min(minChangeIndex, minInvertIndex); int maxUnionIndex = Math.max(maxChangeIndex, maxInvertIndex); int minChangedIndex = maxUnionIndex + 1; int maxChangedIndex = minUnionIndex - 1; beginAll(); // walk through the effected range updating selection for(int i = minUnionIndex; i <= maxUnionIndex; i++) { int selectionIndex = barcode.getColourIndex(i, SELECTED); boolean selectedBefore = (selectionIndex != -1); boolean inChangeRange = (i >= minChangeIndex && i <= maxChangeIndex); boolean selectedAfter = (inChangeRange == select) && isSelectable(i); // when there's a change if(selectedBefore != selectedAfter) { E value = source.get(i); // update change range if(i < minChangedIndex) minChangedIndex = i; if(i > maxChangedIndex) maxChangedIndex = i; // it is being deselected if(selectedBefore) { barcode.set(i, DESELECTED, 1); addDeselectEvent(selectionIndex, i - selectionIndex, value); // it is being selected } else { barcode.set(i, SELECTED, 1); int newSelectionIndex = barcode.getColourIndex(i, SELECTED); addSelectEvent(newSelectionIndex, i - newSelectionIndex, value); } } } commitAll(); // consider the original lead/anchor indexes, if any, when firing the "selection changed" event // (this forces a redraw of the old lead/anchor rows when the lead/anchor changes) if (oldLead != -1) { minChangedIndex = Math.min(minChangedIndex, oldLead); maxChangedIndex = Math.max(maxChangedIndex, oldLead); } if (oldAnchor != -1) { minChangedIndex = Math.min(minChangedIndex, oldAnchor); maxChangedIndex = Math.max(maxChangedIndex, oldAnchor); } // notify selection listeners if(minChangedIndex <= maxChangedIndex) fireSelectionChanged(minChangedIndex, maxChangedIndex); } /** * Checks the {@link #validSelectionMatchers} to determine if the value at * the given index is allowed to be selected. * * @param index the index to check * @return true if the index can be selected; false * otherwise */ private boolean isSelectable(int index) { // no matchers implies the index is selectable if (validSelectionMatchers.isEmpty()) return true; // otherwise fetch the row object and validate it against all matchers final E rowObject = source.get(index); for (Iterator> iterator = validSelectionMatchers.iterator(); iterator.hasNext();) if (!iterator.next().matches(rowObject)) return false; return true; } /** * Register a {@link ca.odell.glazedlists.ListSelection.Listener Listener} * that will be notified when selection is changed. */ public void addSelectionListener(Listener selectionListener) { selectionListeners.add(selectionListener); } /** * Remove a {@link ca.odell.glazedlists.ListSelection.Listener Listener} * so that it will no longer be notified when selection changes. */ public void removeSelectionListener(Listener selectionListener) { selectionListeners.remove(selectionListener); } /** * Fire changes in selection to all registered listeners. */ private void fireSelectionChanged(int start, int end) { // notify all for(Iterator i = selectionListeners.iterator(); i.hasNext(); ) { i.next().selectionChanged(start, end); } } /** * Disposes of this ListSelection freeing up it's resources for * garbage collection. It is an error to use a ListSelection after * dispose() has been called. */ public void dispose() { source.removeListEventListener(this); selectionListeners.clear(); // detach the publisher dependencies if(selectedList != null) source.getPublisher().clearRelatedListener(selectedList, this); if(deselectedList != null) source.getPublisher().clearRelatedListener(deselectedList, this); if(selectedToggleList != null) source.getPublisher().clearRelatedListener(selectedToggleList, this); if(deselectedToggleList != null) source.getPublisher().clearRelatedListener(deselectedToggleList, this); } /** * A generic interface to respond to changes in selection that doesn't * require including a particular GUI toolkit. */ public interface Listener { /** * Notifies this SelectionListener of a change in selection. * * @param changeStart The first zero-relative index affected by a change in selection. * @param changeEnd The last zero-relative index affected by a change in selection. */ public void selectionChanged(int changeStart, int changeEnd); } /** * The {@link EventList} that contains only values that are currently * selected. */ private class SelectedList extends TransformedList { /** * Creates an {@link EventList} that provides a view of the * selected items in a ListSelection. */ SelectedList(EventList source) { super(source); } /** {@inheritDoc} */ @Override public int size() { return barcode.colourSize(SELECTED); } /** {@inheritDoc} */ @Override protected int getSourceIndex(int mutationIndex) { return barcode.getIndex(mutationIndex, SELECTED); } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { // Do nothing as all state changes are handled in ListSelection.listChanged() } /** * This allows access to the EventAssembler for this list. */ public ListEventAssembler updates() { return updates; } /** {@inheritDoc} */ @Override protected boolean isWritable() { return true; } /** * A no-op dispose method to prevent the user from shooting themselves * in the foot. To dispose a {@link ListSelection}, call * {@link ListSelection#dispose()} on that class directly. */ @Override public void dispose() { // Do Nothing } } /** * A SelectedList that mutates the selection instead of the underlying list. */ private class SelectionToggleList extends SelectedList{ SelectionToggleList(EventList source) { super(source); } /** @throws UnsupportedOperationException unconditionally */ @Override public E set(int index, E item){ throw new UnsupportedOperationException("Toggling lists don't support setting items"); } /** * Select the specified value in the source list, regardless of its * index. If the given item is found in the source list, it is selected. * * @throws IllegalArgumentException if the element isn't found */ @Override public void add(int index, E item) { index = source.indexOf(item); if(index != -1) { select(index); } else { throw new IllegalArgumentException("Added item " + item + " must be in source list"); } } /** * Deselect the specified index. */ @Override public E remove(int index){ if(index < 0 || index >= size()) throw new IndexOutOfBoundsException("Cannot remove at " + index + " on list of size " + size()); int sourceIndex = getSourceIndex(index); deselect(sourceIndex); return source.get(sourceIndex); } } /** * The {@link EventList} that contains only values that are not currently * selected. */ private class DeselectedList extends TransformedList { /** * Creates an {@link EventList} that provides a view of the * deselected items in a ListSelection. */ DeselectedList(EventList source) { super(source); } /** {@inheritDoc} */ @Override public int size() { return barcode.colourSize(DESELECTED); } /** {@inheritDoc} */ @Override protected int getSourceIndex(int mutationIndex) { return barcode.getIndex(mutationIndex, DESELECTED); } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { // Do nothing as all state changes are handled in ListSelection.listChanged() } /** * This allows access to the EventAssembler for this list. */ public ListEventAssembler updates() { return updates; } /** {@inheritDoc} */ @Override protected boolean isWritable() { return true; } /** * A no-op dispose method to prevent the user from shooting themselves * in the foot. To dispose a {@link ListSelection}, call * {@link ListSelection#dispose()} on that class directly. */ @Override public void dispose() { // Do Nothing } } /** * A DeselectedList that mutates the selection instead of the underlying list. */ private class DeselectionToggleList extends DeselectedList{ DeselectionToggleList(EventList source) { super(source); } /** @throws UnsupportedOperationException unconditionally */ @Override public E set(int index, E item){ throw new UnsupportedOperationException("Toggling lists don't support setting items"); } /** * Deselect the specified value. * * @throws IllegalArgumentException if the element isn't found */ @Override public void add(int index, E item) { index = source.indexOf(item); if(index != -1) { deselect(index); } else { throw new IllegalArgumentException("Added item " + item + " must be in source list"); } } /** * Select the specified index. */ @Override public E remove(int index){ if(index < 0 || index >= size()) throw new IndexOutOfBoundsException("Cannot remove at " + index + " on list of size " + size()); int sourceIndex = getSourceIndex(index); select(sourceIndex); return source.get(sourceIndex); } } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/SequenceList.java0000644000175000017500000002447312106516400027407 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.RandomAccess; /** * A SequenceList contains values in adjacent indices which occur at predictable * intervals from each other. A simple SequenceList could be: *

       {-10, -5, 0, 5, 10, 15} 
      * * while a more sophisticated example could be: *
       {Jun 1, Jul 1, Aug 1, Sep 1, Oct 1} 
      * * As long as the values can be ordered via a {@link Comparator} and a * {@link Sequencer} can be implemented to reliably produce the next or previous * value in a sequence using only some value from the source list. * *

      SequenceList is a readonly list; calling any write method on this list * will produce an {@link UnsupportedOperationException}. * *

      The start and end values of the sequence are the smallest sequence values * which maintain the invariant that: * sequence start <= each value in the source list <= sequence end * *

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

      * * * * * * * *
      EventList Overview
      Writable:no
      Concurrency:thread ready, not thread safe
      Performance:reads: O(1)
      Memory:O(N)
      Unit Tests:SequenceListTest
      Issues:N/A
      * * @author James Lemieux */ public final class SequenceList extends TransformedList implements RandomAccess { /** The values participating in the sequence. */ private final List sequence = new ArrayList(); /** The comparator that defines the order of the source and sequence values. */ private final Comparator comparator; /** * The object containing the logic which produces next and previous * sequence values by inspecting any source value. */ private final Sequencer sequencer; /** * Constructs a SequenceList containing a sequence of values produced by * the sequencer which cover the range of values contained * within the source. * * @param source the raw values to build a sequence around * @param sequencer the logic to produce sequence values relative to a value */ public SequenceList(EventList source, Sequencer sequencer) { this(source, sequencer, (Comparator) GlazedLists.comparableComparator()); } /** * Constructs a SequenceList containing a sequence of values produced by * the sequencer which cover the range of values contained * within the source. The given comparator * determines the order of the sequence values. * * @param source the raw values to build a sequence around * @param sequencer the logic to produce sequence values relative to a value * @param comparator determines the order of the sequence values */ public SequenceList(EventList source, Sequencer sequencer, Comparator comparator) { this(new SortedList(source, comparator), sequencer, comparator); } private SequenceList(SortedList source, Sequencer sequencer, Comparator comparator) { super(source); if (sequencer == null) throw new IllegalArgumentException("sequencer may not be null"); if (comparator == null) throw new IllegalArgumentException("comparator may not be null"); this.sequencer = sequencer; this.comparator = comparator; this.updateSequence(); source.addListEventListener(this); } /** * @return false; SequenceList is readonly */ @Override protected boolean isWritable() { return false; } /** {@inheritDoc} */ @Override public int size() { return this.sequence.size(); } /** {@inheritDoc} */ @Override public E get(int index) { return this.sequence.get(index); } /** * A Sequencer defines the logic required to calculate the previous and * next sequence values given any value. It is important to note that the * arguments passed to {@link #previous} and {@link #next} will not always * be sequence values themselves. For example if a Sequencer is contains * logic to produce a sequence of numbers evenly divisible by 2, it must * handle returning the next and previous even number relative to * any integer. So the Sequencer logic must produce: * *

        *
      • previous(5) returns 4 *
      • previous(6) returns 4 *
      • next(5) returns 6 *
      • next(4) returns 6 *
      */ public interface Sequencer { /** * Given a sequencable value, produce the previous value * in the sequence such that value is now included in the * sequence. * * @param value a sequencable value * @return the previous value in the sequence such that value * would be included within the bounds of the sequence */ public E previous(E value); /** * Given a sequencable value, produce the next value * in the sequence such that value is now included in the * sequence. * * @param value a sequencable value * @return the next value in the sequence such that value * would be included within the bounds of the sequence */ public E next(E value); } /** * Returns true if value is exactly a sequence value * (i.e. could be stored at some index within this {@link SequenceList}. */ private boolean isSequenceValue(E value) { final E sequencedValue = sequencer.previous(sequencer.next(value)); return comparator.compare(value, sequencedValue) == 0; } /** * Returns the previous value in the sequence defined by this list or * value itself if it is a sequence value. * * @param value the value relative to which the previous sequence value is returned * @return the previous sequence value relative to the given value */ public E getPreviousSequenceValue(E value) { // if value is already a sequence value, return it if (isSequenceValue(value)) return value; // ask the sequencer for the previous value return sequencer.previous(value); } /** * Returns the next value in the sequence defined by this list or * value itself if it is a sequence value. * * @param value the value relative to which the next sequence value is returned * @return the next sequence value relative to the given value */ public E getNextSequenceValue(E value) { // if value is already a sequence value, return it if (isSequenceValue(value)) return value; // ask the sequencer for the next value return sequencer.next(value); } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { this.updateSequence(); } /** * A convenience method to update the sequence to minimally cover the * underlying SortedList. */ private void updateSequence() { updates.beginEvent(); // check for the special case when the underlying list has been completely cleared if (source.isEmpty()) { while (!sequence.isEmpty()) { updates.elementDeleted(0, sequence.remove(0)); } } else { // seed this SequenceList with the initial two values if (this.isEmpty()) { final E value = source.get(0); final E previousSequenceValue = getPreviousSequenceValue(value); final E nextSequenceValue = getNextSequenceValue(value); sequence.add(0, previousSequenceValue); updates.elementInserted(0, previousSequenceValue); sequence.add(1, nextSequenceValue); updates.elementInserted(1, nextSequenceValue); } // add the necessary leading sequence values final E firstSourceValue = source.get(0); while (comparator.compare(firstSourceValue, get(0)) == -1) { E element = sequencer.previous(get(0)); sequence.add(0, element); updates.elementInserted(0, element); } // remove the unnecessary leading sequence values while (comparator.compare(get(1), firstSourceValue) == -1) { E oldValue = sequence.remove(0); updates.elementDeleted(0, oldValue); } // add the necessary trailing sequence values final E lastSourceValue = source.get(source.size()-1); while (comparator.compare(lastSourceValue, get(size()-1)) == 1) { E element = sequencer.next(get(size() - 1)); int index = size(); sequence.add(index, element); updates.elementInserted(index, element); } // remove the unnecessary trailing sequence values while (comparator.compare(get(size()-2), lastSourceValue) == 1) { final int lastIndex = size()-1; updates.elementDeleted(lastIndex, sequence.remove(lastIndex)); } } updates.commitEvent(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/UniqueList.java0000644000175000017500000003160412106516400027077 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.impl.Grouper; import ca.odell.glazedlists.impl.adt.BarcodeIterator; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * An {@link EventList} that shows the unique elements from its source * {@link EventList}. For example, the source list {A, A, B, C, C, C, D} would * be simplified to {A, B, C, D} by this UniqueList. * *

      Warning: This class breaks * the contract required by {@link List}. See {@link EventList} for an example. * *

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

      * * * * * * * *
      EventList Overview
      Writable:yes
      Concurrency:thread ready, not thread safe
      Performance:
      Memory:N/A
      Unit Tests:N/A
      Issues: * 27 * 34 * 35 * 45 * 46 * 55 * 58 * 114 *
      * * @author Kevin Maltby * @author James Lemieux * @author Jesse Wilson */ public final class UniqueList extends TransformedList { /** the grouping service manages collapsing out duplicates */ private final Grouper grouper; /** * Creates a {@link UniqueList} that determines uniqueness via the * {@link Comparable} interface. All elements of the source {@link EventList} * must implement {@link Comparable}. * * @param source the {@link EventList} containing duplicates to remove */ public static > UniqueList create(EventList source) { return new UniqueList(source); } /** * Creates a {@link UniqueList} that determines uniqueness via the * {@link Comparable} interface. All elements of the source {@link EventList} * must implement {@link Comparable}. *

      Usage of factory method {@link #create(EventList)} is preferable. * * @param source the {@link EventList} containing duplicates to remove */ public UniqueList(EventList source) { this(source, (Comparator) GlazedLists.comparableComparator()); } /** * Creates a {@link UniqueList} that determines uniqueness using the * specified {@link Comparator}. * * @param source the {@link EventList} containing duplicates to remove * @param comparator the {@link Comparator} used to determine equality */ public UniqueList(EventList source, Comparator comparator) { this(new SortedList(source, comparator), (Void) null); } /** * A private constructor which allows us to use the {@link SortedList} as * the main decorated {@link EventList}. * *

      The current implementation of {@link UniqueList} uses the {@link Grouper} * service to manage collapsing duplicates, which is more efficient than the * previous implementation that required a longer pipeline of {@link GroupingList}s. * *

      UniqueList exposes a few extra querying methods like {@link #getCount(int)} * and {@link #getAll(int)} which require us to query the {@link Grouper}s * barcode, which retains state on groups. * * @param source a private {@link SortedList} whose {@link Comparator} never * changes, this is used to keep track of uniqueness. * @param dummyParameter dummy parameter to differentiate between the different * {@link GroupingList} constructors. */ private UniqueList(SortedList source, Void dummyParameter) { super(source); // the grouper handles changes to the SortedList this.grouper = new Grouper(source, new GrouperClient()); source.addListEventListener(this); } /** * Handle changes to the grouper's groups. */ private class GrouperClient implements Grouper.Client { public void groupChanged(int index, int groupIndex, int groupChangeType, boolean primary, int elementChangeType, E oldValue, E newValue) { switch (groupChangeType) { case ListEvent.INSERT: updates.elementInserted(groupIndex, newValue); break; case ListEvent.UPDATE: updates.elementUpdated(groupIndex, oldValue, newValue); break; case ListEvent.DELETE: updates.elementDeleted(groupIndex, oldValue); break; default: throw new IllegalStateException("Unrecognized groupChangeType: " + groupChangeType); } } } /** * Change the {@link Comparator} which determines the unique elements * of this List. * * @param comparator the {@link Comparator} used to determine groupings; * null will be treated as {@link GlazedLists#comparableComparator()} */ public void setComparator(Comparator comparator) { if (comparator == null) comparator = (Comparator) GlazedLists.comparableComparator(); ((SortedList) this.source).setComparator(comparator); } /** {@inheritDoc} */ @Override public int size() { return grouper.getBarcode().colourSize(Grouper.UNIQUE); } /** {@inheritDoc} */ @Override protected int getSourceIndex(int index) { if(index == size()) return source.size(); return grouper.getBarcode().getIndex(index, Grouper.UNIQUE); } /** * Get the first element that's not a duplicate of the element at the specified * index. This is useful for things like {@link #getCount(int)} because we can * find the full range of a value quickly. */ private int getEndIndex(int index) { if(index == (size() - 1)) return source.size(); else return getSourceIndex(index + 1); } /** {@inheritDoc} */ @Override public E remove(int index) { if(index < 0 || index >= size()) throw new IndexOutOfBoundsException("Cannot remove at " + index + " on list of size " + size()); updates.beginEvent(true); // remember the first duplicate E result = get(index); // remove all duplicates at this index int startIndex = getSourceIndex(index); int endIndex = getEndIndex(index); ((SortedList)source).subList(startIndex, endIndex).clear(); updates.commitEvent(); return result; } /** {@inheritDoc} */ @Override public E set(int index, E value) { if(index < 0 || index >= size()) throw new IndexOutOfBoundsException("Cannot set at " + index + " on list of size " + size()); updates.beginEvent(true); // remove all duplicates of this value first int startIndex = getSourceIndex(index) + 1; int endIndex = getEndIndex(index); if(endIndex > startIndex) { ((SortedList)source).subList(startIndex, endIndex).clear(); } // now do the set E result = super.set(index, value); updates.commitEvent(); return result; } /** * Returns the index in this list of the first occurrence of the specified * element, or -1 if this list does not contain this * element. More formally, returns the lowest index i * such that uniqueListComparator.compare(get(i), element) == 0, * or -1 if there is no such index. * *

      Note: This is a departure from the contract for {@link List#indexOf} * since it does not guarantee that element.equals(get(i)) where i * is a positive index returned from this method. * * @param element the element to search for. * @return the index in this list of the first occurrence of the specified * element, or -1 if this list does not contain this element * @throws ClassCastException if the type of the specified element * is incompatible with this list */ @Override public int indexOf(Object element) { final int index = Collections.binarySearch(this, (E) element, ((SortedList)source).getComparator()); // if the element is not found (index is negative) then return -1 to indicate the list does not contain it return index < 0 ? -1 : index; } /** {@inheritDoc} */ @Override protected boolean isWritable() { return true; } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { updates.beginEvent(true); // check if this ListEvent was caused due to a change in the // Comparator that defines uniqueness final SortedList sortedSource = (SortedList) source; final Comparator sourceComparator = sortedSource.getComparator(); if (sourceComparator != grouper.getComparator()) { if (!listChanges.isReordering()) { throw new IllegalStateException("source comparator changed without reordering!"); } // fire delete events for the existing events. // use the reorder map to find previous values, since everything's moved int[] reorderingMap = listChanges.getReorderMap(); int[] reverseReorderingMap = new int[reorderingMap.length]; for (int r = 0; r < reorderingMap.length; r++) { reverseReorderingMap[reorderingMap[r]] = r; } for (BarcodeIterator b = grouper.getBarcode().iterator(); b.hasNextBlack(); ) { b.nextBlack(); int sourceIndex = b.getIndex(); updates.elementDeleted(0, sortedSource.get(reverseReorderingMap[sourceIndex])); } grouper.getBarcode().clear(); // adjust the Comparator used by the Grouper (which will change the barcode) grouper.setComparator(sourceComparator); // insert all new unique values using the new barcode int uniqueIndex = 0; for (BarcodeIterator b = grouper.getBarcode().iterator(); b.hasNextBlack(); ) { b.nextBlack(); int sourceIndex = b.getIndex(); updates.elementInserted(uniqueIndex, sortedSource.get(sourceIndex)); uniqueIndex++; } } else { grouper.listChanged(listChanges); } updates.commitEvent(); } /** * Returns the number of duplicates of the value found at the specified index. */ public int getCount(int index) { int startIndex = getSourceIndex(index); int endIndex = getEndIndex(index); return endIndex - startIndex; } /** * Returns the number of duplicates of the specified value. */ public int getCount(E value) { final int index = this.indexOf(value); if(index == -1) return 0; else return getCount(index); } /** * Returns a List of all original elements represented by the value at the * given index within this {@link UniqueList}. */ public List getAll(int index) { int startIndex = getSourceIndex(index); int endIndex = getEndIndex(index); return new ArrayList(source.subList(startIndex, endIndex)); } /** * Returns a List of all original elements represented by the given * value within this {@link UniqueList}. */ public List getAll(E value) { final int index = this.indexOf(value); return index == -1 ? Collections.emptyList() : this.getAll(index); } /** {@inheritDoc} */ @Override public void dispose() { ((SortedList)source).dispose(); super.dispose(); } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/PluggableList.java0000644000175000017500000001557212106516400027541 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventPublisher; import ca.odell.glazedlists.util.concurrent.ReadWriteLock; /** * An {@link EventList} which delegates all List methods to a given source * {@link EventList} that may be replaced at runtime using * {@link #setSource(EventList)}. * *

      Note that the source {@link EventList} must use the same * {@link ListEventPublisher} and {@link ReadWriteLock}, particularly if this * {@link EventList} is to be used by multiple threads concurrently. To * construct an {@link EventList} that shares the {@link ListEventPublisher} * and {@link ReadWriteLock} with this {@link PluggableList}, use * {@link #createSourceList()}. * *

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

      * * * * * * * *
      EventList Overview
      Writable:yes
      Concurrency:only {@link #setSource(EventList)}
      Performance:delegates to source EventList
      Memory:N/A
      Unit Tests:N/A
      Issues:
      * * @author James Lemieux */ public class PluggableList extends TransformedList { /** * Constructs a PluggableList which uses the given publisher * and lock. The PluggableList will default to use a * {@link BasicEventList} that also uses the same publisher * and lock. * * @param publisher the {@link ListEventPublisher} to use within the {@link PluggableList} * @param lock the {@link ReadWriteLock} to use within the {@link PluggableList} */ public PluggableList(ListEventPublisher publisher, ReadWriteLock lock) { this(new BasicEventList(publisher, lock)); } /** * Constructs a PluggableList which delegates all List methods to the given * source. At some future time, the source EventList may be * replaced using {@link #setSource(EventList)} and this PluggableList will * produce a {@link ListEvent} describing the change in data. * * @param source the source of data to this PluggableList */ public PluggableList(EventList source) { super(source); source.addListEventListener(this); } /** * Creates a new {@link EventList} that shares its * {@link ca.odell.glazedlists.util.concurrent.ReadWriteLock} and * {@link ca.odell.glazedlists.event.ListEventPublisher} with this * {@link PluggableList}. This is necessary when this {@link PluggableList} * will be used by multiple threads. * *

      Note that the created {@link EventList} must be explicitly set as the * source of this {@link PluggableList} using {@link #setSource(EventList)}. * * @return a new EventList appropriate for use as the * {@link #setSource(EventList) source} of this PluggableList */ public EventList createSourceList() { return new BasicEventList(getPublisher(), getReadWriteLock()); } /** * Sets the source EventList to which this PluggableList will delegate all * calls. This method is the entire reason that PluggableList exists. It * allows the data source of the remaining pipeline to be altered. *

      * To ensure correct behaviour when this {@link PluggableList} is used by * multiple threads, the given source must * share the same {@link ca.odell.glazedlists.util.concurrent.ReadWriteLock} and * {@link ca.odell.glazedlists.event.ListEventPublisher} with this PluggableList. * * @param source the new source of data for this PluggableList, and all * downstream EventLists * @throws IllegalStateException if this PluggableList is already disposed * @throws IllegalArgumentException if any of the following are true *

        *
      • the given source is null
      • *
      • the given source has a different ListEventPublisher than this PluggableList
      • *
      • the given source has a different ReadWriteLock than this PluggableList
      • *
      */ public void setSource(EventList source) { // lock the pipeline while the source list is swapped getReadWriteLock().writeLock().lock(); try { if (this.source == null) throw new IllegalStateException("setSource may not be called on a disposed PluggableList"); if (source == null) throw new IllegalArgumentException("source may not be null"); if (!getReadWriteLock().equals(source.getReadWriteLock())) throw new IllegalArgumentException("source list must share lock with PluggableList"); if (!getPublisher().equals(source.getPublisher())) throw new IllegalArgumentException("source list must share publisher with PluggableList"); if (this.source == source) return; updates.beginEvent(); // add deletions to the ListEvent for all the elements in the old source for (int i = 0, n = size(); i < n; i++) updates.elementDeleted(0, get(i)); this.source.removeListEventListener(this); this.source = source; this.source.addListEventListener(this); // add insertions to the ListEvent for all the elements in the new source for (int i = 0, n = size(); i < n; i++) updates.elementInserted(i, get(i)); // broadcast the ListEvent that describes the data change updates.commitEvent(); } finally { getReadWriteLock().writeLock().unlock(); } } /** @inheritDoc */ @Override protected boolean isWritable() { return true; } /** @inheritDoc */ @Override public void listChanged(ListEvent listChanges) { updates.forwardEvent(listChanges); } /** @inheritDoc */ @Override public void dispose() { if (source != null) source.removeListEventListener(this); source = null; } }libglazedlists-java-1.9.0+dfsg.orig/source/ca/odell/glazedlists/PopularityList.java0000644000175000017500000001055712106516400030005 0ustar gregoagregoa/* Glazed Lists (c) 2003-2006 */ /* http://publicobject.com/glazedlists/ publicobject.com,*/ /* O'Dell Engineering Ltd.*/ package ca.odell.glazedlists; import ca.odell.glazedlists.event.ListEvent; import java.util.Comparator; /** * An {@link EventList} that shows the unique elements from its source * {@link EventList} ordered by the frequency of their appearance. * *

      This {@link EventList} supports all write operations. * *

      Warning: This class * breaks the contract required by {@link java.util.List}. See * {@link EventList} for an example. * *

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

      * * * * * * * *
      EventList Overview
      Writable:yes
      Concurrency:thread ready, not thread safe
      Performance:reads: O(log N), writes O(log N)
      Memory:196 bytes per element
      Unit Tests:N/A
      Issues: * 104 *
      * * @author Jesse Wilson */ public final class PopularityList extends TransformedList { /** the list of distinct elements */ private UniqueList uniqueList; /** * Creates a new {@link PopularityList} that provides frequency-ranking * for the specified {@link EventList}. All elements of the source {@link EventList} * must implement {@link Comparable}. */ public static > PopularityList create(EventList source) { return new PopularityList(UniqueList.create(source)); } /** * Creates a new {@link PopularityList} that provides frequency-ranking * for the specified {@link EventList}. All elements of the source {@link EventList} * must implement {@link Comparable}. *

      Usage of factory method {@link #create(EventList)} is preferable. */ public PopularityList(EventList source) { this(new UniqueList(source)); } /** * Creates a new {@link PopularityList} that provides frequency-ranking * for the specified {@link EventList}. * * @param uniqueComparator {@link Comparator} used to determine equality */ public PopularityList(EventList source, Comparator uniqueComparator) { this(new UniqueList(source, uniqueComparator)); } /** * Private constructor is used as a Java-language hack to allow us to save * a reference to the specified {@link UniqueList}. */ private PopularityList(UniqueList uniqueList) { super(new SortedList(uniqueList, new PopularityComparator(uniqueList))); this.uniqueList = uniqueList; // listen for changes to the source list source.addListEventListener(this); } /** {@inheritDoc} */ @Override protected boolean isWritable() { return true; } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { updates.forwardEvent(listChanges); } /** {@inheritDoc} */ @Override public void dispose() { SortedList sortedSource = (SortedList)source; super.dispose(); sortedSource.dispose(); uniqueList.dispose(); } /** * Compares objects by their popularity. */ private static class PopularityComparator implements Comparator { private UniqueList target; public PopularityComparator(UniqueList target) { this.target = target; } public int compare(E a, E b) { int aCount = target.getCount(a); int bCount = target.getCount(b); return bCount - aCount; } } }libglazedlists-java-1.9.0+dfsg.orig/readme.html0000644000175000017500000003001212106516476020753 0ustar gregoagregoa Glazed Lists 1.9.0 Release Notes

      Glazed Lists 1.9.0 Release Notes

      Glazed Lists 1.9.0 is a stable maintenance release suitable for production use.

      It contains some changes that are not backwards compatible:

      • we dropped Java 1.4 support, Glazed Lists 1.9 is built against Java 1.5.
      • we deprecated the existing Swing and SWT model adapters and introduced new ones which do not wrap the source list in a thread proxy list anymore.
      • the SWT extension is now independent of JFace.

      Please see the Upgade Notes for details.

      Glazed Lists Features

      • API Compatibility with ArrayList
      • Generic TableModels
      • Easy dynamic filtering & sorting
      • High performance
      • Designed for concurrency
      • Swing or SWT
      • Free and open

      Download

      Glazed Lists is distributed with support for Java 1.5 or higher. Download is available here.

      Glazed Lists is also deployed to the central Maven repository.

      groupIdnet.java.dev.glazedlists
      artifactIdglazedlists_java15
      version1.9.0

      Changelog

      Bug

      • [GLAZEDLISTS-412] - GroupingList doesn't handle mass update ListEvents
      • [GLAZEDLISTS-453] - SeparatorList incorrectly throws ConcurrentModificationException when setting comparator
      • [GLAZEDLISTS-463] - incorrect jar index contains 'com' directory
      • [GLAZEDLISTS-471] - RFE: SwingProxyCalculation
      • [GLAZEDLISTS-476] - Glazed Lists 1.8.0 breaks classloader (ECJ)
      • [GLAZEDLISTS-479] - EventListJXTableSorting.install sets the comparator to null
      • [GLAZEDLISTS-486] - GroupingList breaks (NPE/assertion failure) on transaction of updates
      • [GLAZEDLISTS-491] - GroupingList fails with NPE under certain update scenarios
      • [GLAZEDLISTS-493] - AutoCompleteSupport: custom renderer gets overwritten when installing with format
      • [GLAZEDLISTS-498] - Memory leak in ListEventAssembler
      • [GLAZEDLISTS-500] - SeparatorList: some element updates are not handled correctly
      • [GLAZEDLISTS-501] - Regression: null-handling in AutoCompleteSupport with strict mode
      • [GLAZEDLISTS-502] - FilterList: concurrency problem involving ThreadedMatcherEditor
      • [GLAZEDLISTS-504] - NullPointerException in AutoCompleteSupport.CheckStrictModeInvariantRunnable
      • [GLAZEDLISTS-505] - MemoryLeak in SeparatorList
      • [GLAZEDLISTS-513] - NullPointerException in BarcodeNode
      • [GLAZEDLISTS-515] - AbstractMatchEditor is not inheritable
      • [GLAZEDLISTS-516] - missing remove of registered ListEventHandler in dispose method of JEventListPanel causes memory leak
      • [GLAZEDLISTS-517] - Elements are inserted at wrong index
      • [GLAZEDLISTS-520] - TreeList.Format comparator signature incorrect
      • [GLAZEDLISTS-522] - Another NullpointerException in GroupingList
      • [GLAZEDLISTS-524] - List selection changes after filtering when first in list selected
      • [GLAZEDLISTS-528] - SortedList returns wrong "indexOf" when using SortedList .AVOID_MOVING_ELEMENTS
      • [GLAZEDLISTS-530] - Confusing Javadoc comment in typeSafetyListener(EventList<E> source, Set<Class> types) in ca.odell.glazedlists.GlazedLists
      • [GLAZEDLISTS-533] - Getting writelock after obtaining a readlock causes deadlock
      • [GLAZEDLISTS-534] - Ensure ReadWriteLock is defined for BasicEventList
      • [GLAZEDLISTS-535] - Fail fast if a null event listener is added to or removed from an EventList
      • [GLAZEDLISTS-540] - TextComponentMatcherEditor Constructor uses wrong generic type
      • [GLAZEDLISTS-546] - Exception in JSeparatorTable
      • [GLAZEDLISTS-550] - signature of GlazedLists.readOnlyList does not take generics bounds into consideration
      • [GLAZEDLISTS-557] - TreeList.remove(int) returns wrong type

      Improvement

      Task

      License

      Glazed Lists is distributed under your choice of two popular open source licenses, the LGPL and the MPL.

      • You may distribute Glazed Lists free of charge
      • You may use Glazed Lists in a commercial or closed source application
      • You may not create a closed-source fork of Glazed Lists

      Developers

      Glazed Lists, Copyright © 2003-2013 publicobject.com, O'Dell Engineering Ltd.

      libglazedlists-java-1.9.0+dfsg.orig/license0000644000175000017500000000073112106516476020202 0ustar gregoagregoaGlazed Lists Copyright (c) 2003-2006, publicobject.com, O'Dell Engineering Ltd. Glazed Lists is free software and business friendly. It allows you to * distribute Glazed Lists free of charge * use Glazed Lists in a commercial or closed source application It does not allow you to * create a fork of Glazed Lists that is closed-source It is made available under two licenses: LGPL, http://creativecommons.org/licenses/LGPL/2.1/ MPL, http://www.mozilla.org/MPL/ libglazedlists-java-1.9.0+dfsg.orig/pom.xml0000644000175000017500000001035212106516476020152 0ustar gregoagregoa 4.0.0 net.java.dev.glazedlists @glazedlists.artifactId@ @glazedlists.version@ jar net.java jvnet-parent 1 Glazed Lists Event-driven lists for dynamically filtered and sorted tables http://www.glazedlists.com/ Jira http://java.net/jira/browse/GLAZEDLISTS 2003 users@glazedlists.java.net users@glazedlists.java.net http://java.net/projects/glazedlists/lists/users/archive dev@glazedlists.java.net dev@glazedlists.java.net http://java.net/projects/glazedlists/lists/dev/archive issues@glazedlists.java.net issues@glazedlists.java.net http://java.net/projects/glazedlists/lists/issues/archive jessewilson Jesse Wilson jesse@swank.ca Java developer PDT jplemieux James Lemieux Java developer PDT kevinmaltby Kevin Maltby Java developer EST reden Rob Eden Java developer brands Holger Brands Java developer Geoffrey De Smet ge0ffrey.spam_AT_gmail.com Maven2izer +1 GNU Lesser General Public License http://www.gnu.org/copyleft/lesser.html repo Mozilla Public License Version 1.1 http://www.mozilla.org/MPL/MPL-1.1.html repo scm:svn:http://svn.java.net/svn/glazedlists~svn/trunk scm:svn:https://svn.java.net/svn/glazedlists~svn/trunk http://java.net/projects/glazedlists/sources/svn/show/trunk junit junit 3.8.1 test libglazedlists-java-1.9.0+dfsg.orig/glazedlists.bnd0000644000175000017500000000166412106516476021655 0ustar gregoagregoa# # This file is used by the task in the ANT build. # # General information about the build Manifest-Version: 1.0 Main-Class: ca.odell.glazedlists.impl.Main Sealed: true Built-By: ${user.name} Built-At: ${datestamp.dateAndTime} Implementation-Version: ${implementation.version} Implementation-Title: Glazed Lists Implementation-URL: http://publicobject.com/glazedlists/ Contributors: Jesse Wilson, Kevin Maltby, James Lemieux, Rob Eden, Holger Brands Source-Version: JDK ${java.target.version} # OSGi bundle information Bundle-ManifestVersion: 2 Bundle-SymbolicName: glazedlists_java15 Bundle-Name: Glazed Lists Bundle-SymbolicName: ca.odell.glazedlists Bundle-Version: ${bundle.version} Bundle-ClassPath: . # Define all .impl packages to be private to this OSGi bundle Include-Resource: resources=resources Private-Package: ca.odell.glazedlists.impl.* Export-Package: !ca.odell.glazedlists.impl.*,* Import-Package: *;resolution:=optionallibglazedlists-java-1.9.0+dfsg.orig/resources/0000755000175000017500000000000012106516474020644 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/resources/aqua/0000755000175000017500000000000012106516474021573 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/resources/aqua/primary_sorted_reverse.png0000644000175000017500000000036012106516474027076 0ustar gregoagregoaPNG  IHDR ^tIMEͧ# pHYsN N }gAMA aIDATxc`2`*!UWmd@ Ȇj 3 @.4l| E~gH |*42=@0q&4-x +]d+IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/aqua/primary_sorted_alternate_reverse.png0000644000175000017500000000037612106516474031144 0ustar gregoagregoaPNG  IHDR ^tIME"f pHYsN N }gAMA aIDATxc`"(2P?irt Ȇj /AN9f[a 0B͝xiooo Ȏ @~!#'6) @ n@ęЬ5 @y1,J)IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/aqua/secondary_sorted_reverse.png0000644000175000017500000000032612106516474027404 0ustar gregoagregoaPNG  IHDR ^tIME$#f pHYsN N }gAMA aeIDATxc`a(2@f -@0L2f Tl 0B-ROXI+>e񀌯@J )2)1`"@@Jj$7 X IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/aqua/secondary_sorted_alternate_reverse.png0000644000175000017500000000033712106516474031445 0ustar gregoagregoaPNG  IHDR ^tIME&$F pHYsN N }gAMA anIDATxc`a FH3a 'R)!s. *vwq'@ۻ/ہXʈ"q q4\PHb$ L!SCIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/aqua/secondary_sorted.png0000644000175000017500000000033512106516474025651 0ustar gregoagregoaPNG  IHDR ^tIME$U> pHYsN N }gAMA alIDATxc` R6f )VVVǏIA8  6T#(1 0Y(EooE E@P@5 CH e &9d!IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/aqua/primary_sorted_alternate.png0000644000175000017500000000037312106516474027406 0ustar gregoagregoaPNG  IHDR ^tIME;5/˅ pHYsN N }gAMA aIDATxc`((Ҍ@{ H8넫eBҿPVVVrǏXs @I6HEAB"FgBT"f e;_@z@':4K*݆IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/aqua/primary_sorted.png0000644000175000017500000000036712106516474025352 0ustar gregoagregoaPNG  IHDR ^tIME508 pHYsN N }gAMA aIDATxc`L@"XNZ&d h lee%w,P50'ܠ d\ (TqT  /jtq&4 Q@*JlR&P T:@ >)f (bIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/aqua/secondary_sorted_alternate.png0000644000175000017500000000033012106516474027703 0ustar gregoagregoaPNG  IHDR ^tIME' 8 pHYsN N }gAMA agIDATxc` Rf 4^ =XC>7,P1K `$f2.Q|U J >{jB"Є ] gD jQp{:ȭIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/ocean/0000755000175000017500000000000012106516474021731 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/resources/ocean/primary_sorted_reverse.png0000644000175000017500000000041412106516474027234 0ustar gregoagregoaPNG  IHDR k=bKGD pHYs  ~tIME * RPzIDATxc|%B@, =sV'UcIJ(##, zKPً޽{U]3'Yw.9e'`5uP-{ _f dؼi=Nx O3A蒐WkIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/ocean/primary_sorted_alternate_reverse.png0000644000175000017500000000041712106516474031276 0ustar gregoagregoaPNG  IHDR k=bKGD pHYs  ~tIME (:#SIDATxc|%B@, =sVG,I eDWSͻw޽{P5xU0qBr ppFW;Fl3gĘH8XÅ0C|6oZS3^`L$$ʀԕ:IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/ocean/secondary_sorted_reverse.png0000644000175000017500000000033112106516474027536 0ustar gregoagregoaPNG  IHDR k=bKGD pHYs  ~tIME ,|"fIDATxc|%B@,Ȝ9PʈgD9d&`h왾C3V/2L_Ǹ4cuKC֮]S3^C#Pl<<)IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/ocean/secondary_sorted_alternate_reverse.png0000644000175000017500000000035112106516474031577 0ustar gregoagregoaPNG  IHDR k=bKGD pHYs  ~tIME +^*vIDATxc|%B@,Ȝ9#KRB;\5??MwbϜ˲ 0mF\3gkԌ!44`h XX:\<IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/ocean/secondary_sorted.png0000644000175000017500000000031312106516474026003 0ustar gregoagregoaPNG  IHDR k=bKGD pHYs  ~tIME ** 5!XIDATxc|%B@[zO=sV/L h.e LL e aD9`(\v CIJ(#^3!<(^IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/ocean/primary_sorted_alternate.png0000644000175000017500000000041512106516474027541 0ustar gregoagregoaPNG  IHDR k=bKGD pHYs  ~tIME )&.>IDATxc|%B׀9m@ϜRc „Ks}a ;C}a ^Ci.N`Cc0e'6ݻwp\5g~UĐ1#,{yzPFdWc|?r6IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/ocean/primary_sorted.png0000644000175000017500000000036412106516474025505 0ustar gregoagregoaPNG  IHDR k=bKGD pHYs  ~tIME '%)$BIDATxc?%B׀Yk3_8gV aDYc"qzw ibpg $f7G1ą681AfBIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/ocean/secondary_sorted_alternate.png0000644000175000017500000000032612106516474030046 0ustar gregoagregoaPNG  IHDR k=bKGD pHYs  ~tIME +7v(|cIDATxc|%B@[zO=sV/L h.e LL e a¦988/ ]S0}!88!ш$%Na: 1 OIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windowsxp/0000755000175000017500000000000012106516474022706 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/resources/windowsxp/primary_sorted_reverse.png0000644000175000017500000000024512106516474030213 0ustar gregoagregoaPNG  IHDR ;tIME (%! pHYs(JgAMA a4IDATxc`DY1?!!錌0> pi)PY!N@%nIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windowsxp/primary_sorted_alternate_reverse.png0000644000175000017500000000024612106516474032253 0ustar gregoagregoaPNG  IHDR ;tIME )%` pHYs(JgAMA a5IDATxc`DY1?L $"F)Fdk`]pl6IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windowsxp/secondary_sorted_reverse.png0000644000175000017500000000023612106516474030517 0ustar gregoagregoaPNG  IHDR ;tIME  i pHYsodgAMA a-IDATxc`zXb\B"QS]B5˔+gIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windowsxp/secondary_sorted_alternate_reverse.png0000644000175000017500000000023212106516474032552 0ustar gregoagregoaPNG  IHDR ;tIME +E) pHYsodgAMA a)IDATxc``͊AVIl iC"VwHNDIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windowsxp/secondary_sorted.png0000644000175000017500000000023112106516474026757 0ustar gregoagregoaPNG  IHDR ;tIME  )b pHYsodgAMA a(IDATxc`&`͊3SΈS1650Sˆa 9ZhIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windowsxp/primary_sorted_alternate.png0000644000175000017500000000023712106516474030520 0ustar gregoagregoaPNG  IHDR ;tIME 9i! pHYsodgAMA a.IDATxc``͊3SΈS16502YXN>QIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windowsxp/primary_sorted.png0000644000175000017500000000023712106516474026461 0ustar gregoagregoaPNG  IHDR ;tIME 'ԃ pHYsodgAMA a.IDATxc``͊3SΈS1650SB\Ns bIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windowsxp/secondary_sorted_alternate.png0000644000175000017500000000023412106516474031021 0ustar gregoagregoaPNG  IHDR ;tIME 1D<) pHYsodgAMA a+IDATxc`&`͊3S"τM!L.Ć5 .ϓyIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/metal/0000755000175000017500000000000012106516474021746 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/resources/metal/primary_sorted_reverse.png0000644000175000017500000000036412106516474027255 0ustar gregoagregoaPNG  IHDR  tIME/4 P pHYs  ~gAMA aIDATxڽP K`]]tga%wa xw|`>GҐ(@Djߗ>^@V%7k=3b]'P@zDVvASz9zV|Rj es@)\vo4 ejH:xiF{IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/metal/primary_sorted_alternate_reverse.png0000644000175000017500000000035212106516474031311 0ustar gregoagregoaPNG  IHDR  tIME3 pHYs  ~gAMA ayIDATxڵ] 001#d!KaKxڣ" 61*Oƀê7sJ4Sd[Q8Z4ň9[Q6Ս%2MqnLHIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/metal/secondary_sorted_reverse.png0000644000175000017500000000032712106516474027560 0ustar gregoagregoaPNG  IHDR  tIME8+a pHYs  d_gAMA afIDATxc`@0dJp0U7xm:@(69&dĉ5b y?0F:g0dN@@ QK"n/0yħIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/metal/secondary_sorted_alternate_reverse.png0000644000175000017500000000031212106516474031611 0ustar gregoagregoaPNG  IHDR  tIME7} pHYs  ~gAMA aYIDATxc`@'PL)8y㷌v?4(`ށ J3I3 q4PLx}R(f9 }!?-5<IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/metal/secondary_sorted.png0000644000175000017500000000035312106516474026024 0ustar gregoagregoaPNG  IHDR  tIME9Q< pHYs  d_gAMA azIDATxc`@aD///~P@3oy0o!mC ~d/0?R3t0 @4tK֭Oܹ00ӧXňp4plJF_T* IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/metal/primary_sorted_alternate.png0000644000175000017500000000035312106516474027557 0ustar gregoagregoaPNG  IHDR  tIME5p"~h pHYs  ~gAMA azIDATxڵQ sxx 0"L̲&-#ȪRa_ 1] "Ӧit0g9WdԖ \:q/Y a4~f ǣZF o '+l ^IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/metal/primary_sorted.png0000644000175000017500000000041412106516474025516 0ustar gregoagregoaPNG  IHDR  tIME0e pHYs  ~gAMA aIDATxc`@FLxU:Oc})jJmƜ?.h@]9tyGW\0?ߎό  9  =1ecDWˈ-`@P42hMA&IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windows/0000755000175000017500000000000012106516476022340 5ustar gregoagregoalibglazedlists-java-1.9.0+dfsg.orig/resources/windows/primary_sorted_reverse.png0000644000175000017500000000026312106516476027645 0ustar gregoagregoaPNG  IHDR ,l6tIME6# pHYsodgAMA aBIDATxc`9()\WE<@LtU1v3b hllDXM1ΐ5\) Su;qIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windows/primary_sorted_alternate_reverse.png0000644000175000017500000000027412106516476031706 0ustar gregoagregoaPNG  IHDR ,l6tIMEw pHYsodgAMA aKIDATxc`3E$aHcBXdh1!a;-a8QC9T VHXU]IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windows/secondary_sorted_reverse.png0000644000175000017500000000026412106516476030152 0ustar gregoagregoaPNG  IHDR ,l6tIME 7 pHYs(JgAMA aCIDATxc`2`C0$jȈݕT,rCw膆$i&'IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windows/secondary_sorted_alternate_reverse.png0000644000175000017500000000027012106516476032206 0ustar gregoagregoaPNG  IHDR ,l6tIME pHYs(JgAMA aGIDATxc`ca-4f788848::2bU=P 0=hhH*[2 IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windows/secondary_sorted.png0000644000175000017500000000025212106516476026414 0ustar gregoagregoaPNG  IHDR ,l6tIME % H pHYs(JgAMA a9IDATxc` cB:4(2<&iBhvd(\gIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windows/primary_sorted_alternate.png0000644000175000017500000000030012106516476030141 0ustar gregoagregoaPNG  IHDR ,l6tIME!Iv pHYs(JgAMA aOIDATxڭ C-o@ݡΓ8dDHV3 DCP\Hv%"5Wmee)ALT'h2IENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windows/primary_sorted.png0000644000175000017500000000026712106516476026116 0ustar gregoagregoaPNG  IHDR ,l6tIME63x pHYsodgAMA aFIDATxc`@ף b¦d1@@v5m!Y1#1yh=;ĵbIENDB`libglazedlists-java-1.9.0+dfsg.orig/resources/windows/secondary_sorted_alternate.png0000644000175000017500000000025112106516476030452 0ustar gregoagregoaPNG  IHDR ,l6tIME OV pHYs(JgAMA a8IDATxc` cB:aD0>@h*F$dpV=;#KIENDB`libglazedlists-java-1.9.0+dfsg.orig/build.xml0000644000175000017500000013662012106516476020465 0ustar gregoagregoa ${doctitle}]]> Glazed Lists, Copyright © 2003 publicobject.com, O'Dell Engineering.
      Documentation build by ${user.name} at ${datestamp.dateAndTime}]]>
      To build the demojar, Glazed Lists requires the Byte Code Engineering Library (BCEL) to be available in the \lib directory of your ANT installation. The Glazed Lists build system will now automatically attempt to download the BCEL into ${ant.home}\lib on your behalf. Press Return to begin the download...