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

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

There is a newer version: 1.9.1
Show newest version
/* 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; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy