Java tutorial
/* * BeanArrayList.java * * Created on May 21, 2004, 7:23 PM * * Copyright (C) 2004, 2005 Robert Cooper, Temple of the Screaming Penguin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.beans.PropertyDescriptor; import java.lang.Comparable; import java.lang.reflect.*; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.StringTokenizer; import java.util.ArrayList; /** This is an extension of ArrayList that provides some handy utilities for working with JavaBeans. * Includes basic statistical information, page chunking and storing and can also be used as a manager for * indexed properies and supports PropertyChangeEvents. * * @version $Rev: 79 $ * @author <a href="cooper@screaming-penguin.com">Robert Cooper</a> */ public class BeanArrayList<T> extends ArrayList<T> { /** * DOCUMENT ME! */ Collection source; /** * DOCUMENT ME! */ private PropertyChangeSupport changes; /** * DOCUMENT ME! */ private String indexPropertyName; /** * DOCUMENT ME! */ private int nextChunk = -1; /** * DOCUMENT ME! */ private int numberOfChunks = 1; /** * DOCUMENT ME! */ private int previousChunk = -1; /** No Args Contstructor */ public BeanArrayList() { super(); } /** * This contructs a new BeanArrayList with PropertyChangeEvent support. * @param indexPropertyName the property name of the parent object to fire events for * @param changeOwner the owner object that change events should "come from" */ public BeanArrayList(String indexPropertyName, Object changeOwner) { this(); this.indexPropertyName = indexPropertyName; this.changes = new PropertyChangeSupport(changeOwner); } /** * Creates a new instance of BeanArrayList prepopulating off a * collection, limited to only the desired chunk. * @param source Collection to read from * @param chunkSize int number of object to return. * @param currentChunk int value of the chunk to get (zero index) */ public BeanArrayList(int chunkSize, int currentChunk, Collection<T> source) { super(); this.source = source; Iterator<T> itr = source.iterator(); if (source.size() > 0) { this.numberOfChunks = source.size() / chunkSize; if ((source.size() % chunkSize) > 0) { this.numberOfChunks++; } //spin for (int i = 0; i < (chunkSize * currentChunk); i++) { if (!itr.hasNext()) { continue; } itr.next(); } for (int i = 0; i < chunkSize; i++) { if (!itr.hasNext()) { continue; } this.add(itr.next()); } if (currentChunk != 0) { previousChunk = currentChunk - 1; } if (source.size() > ((currentChunk + 1) * chunkSize)) { nextChunk = currentChunk + 1; } } } /** * Creates a new instance of BeanArrayList prepopulating off a * collection, limited to only the desired chunk, and includes * PropertyChangeEvent support. * @param indexPropertyName the property name of the parent object to fire events for * @param changeOwner the object change events should come from * @param chunkSize number of objects to return * @param currentChunk int value of the chunk to get (zero index) * @param source source Collection to read from. */ public BeanArrayList(String indexPropertyName, Object changeOwner, int chunkSize, int currentChunk, Collection<T> source) { this(chunkSize, currentChunk, source); this.indexPropertyName = indexPropertyName; this.changes = new PropertyChangeSupport(changeOwner); } /** Creates a new instance of BeanArrayList * @param source Collection containing the initial values. */ public BeanArrayList(Collection<T> source) { this(source.size(), 0, source); } /** * This contructs a new BeanArrayList with PropertyChangeEvent support. * @param indexPropertyName the property name of the parent object to fire events for * @param changeOwner the object change events should come from * @param source Collection to prepopulate from. */ public BeanArrayList(String indexPropertyName, Object changeOwner, Collection<T> source) { this(source); this.indexPropertyName = indexPropertyName; this.changes = new PropertyChangeSupport(changeOwner); } /** * Gets a chunk of this ArrayList. * @param chunkSize int number of object to return. * @param currentChunk int value of the chunk to get (zero index) * @return new BeanArrayList representing the chunk requested. */ public BeanArrayList getChunk(int chunkSize, int currentChunk) { return new BeanArrayList(chunkSize, currentChunk, this); } /** * Overrides the parent to support PropertyChangeEvents * @param obj new object value * @param index index position to place the object */ public void setElementAt(T obj, int index) { T old = null; if ((this.indexPropertyName != null) && (this.changes != null)) { old = this.get(index); } super.set(index, obj); if (old != null) { changes.fireIndexedPropertyChange(this.indexPropertyName, index, old, obj); } } /** * Filters a property using the Comparable.compareTo() on the porperty to * test for a range * @param propertyName property to filter on * @param inclusive include the values of the range limiters * @param fromValue low range value * @param toValue high range value * @throws java.lang.IllegalAccessException reflection exception * @throws java.beans.IntrospectionException reflection exception * @throws java.lang.reflect.InvocationTargetException reflection exception * @return new BeanArrayList filtered on the range */ public BeanArrayList<T> getFiltered(String propertyName, boolean inclusive, Comparable fromValue, Comparable toValue) throws java.lang.IllegalAccessException, java.beans.IntrospectionException, java.lang.reflect.InvocationTargetException { HashMap cache = new HashMap(); String currentClass = ""; PropertyDescriptor pd = null; BeanArrayList<T> results = new BeanArrayList<T>(); for (int i = 0; i < this.size(); i++) { T o = this.get(i); if (!currentClass.equals(o.getClass().getName())) { pd = (PropertyDescriptor) cache.get(o.getClass().getName()); if (pd == null) { PropertyDescriptor[] pds = Introspector.getBeanInfo(o.getClass()).getPropertyDescriptors(); boolean foundProperty = false; for (int pdi = 0; (pdi < pds.length) && !foundProperty; pdi++) { if (pds[pdi].getName().equals(propertyName)) { pd = pds[pdi]; cache.put(o.getClass().getName(), pd); foundProperty = true; } } } } Comparable value = (Comparable) pd.getReadMethod().invoke(o); if ((value.compareTo(fromValue) > 0) && (value.compareTo(toValue) < 0)) { results.add(o); } else if (inclusive && ((value.compareTo(fromValue) == 0) || (value.compareTo(toValue) == 0))) { results.add(o); } } return results; } /** * This method does a string match on values of a property. * @param propertyName String value containing the name of the property to match. * @param match Value to search for. This is a case-insensitive value that takes % as a multiple character wildcard value. * @throws java.lang.IllegalAccessException reflection exception * @throws java.beans.IntrospectionException reflection exception * @throws java.lang.reflect.InvocationTargetException reflection exception * @return a new BeanArrayList filtered on the specified property */ public BeanArrayList<T> getFilteredStringMatch(String propertyName, String match) throws java.lang.IllegalAccessException, java.beans.IntrospectionException, java.lang.reflect.InvocationTargetException { HashMap cache = new HashMap(); String currentClass = ""; PropertyDescriptor pd = null; BeanArrayList<T> results = new BeanArrayList<T>(); for (int i = 0; i < this.size(); i++) { T o = this.get(i); if (!currentClass.equals(o.getClass().getName())) { pd = (PropertyDescriptor) cache.get(o.getClass().getName()); if (pd == null) { PropertyDescriptor[] pds = Introspector.getBeanInfo(o.getClass()).getPropertyDescriptors(); boolean foundProperty = false; for (int pdi = 0; (pdi < pds.length) && !foundProperty; pdi++) { if (pds[pdi].getName().equals(propertyName)) { pd = pds[pdi]; cache.put(o.getClass().getName(), pd); foundProperty = true; } } } } String value = pd.getReadMethod().invoke(o).toString().toLowerCase(); StringTokenizer st = new StringTokenizer(match.toLowerCase(), "%"); boolean isMatch = true; int matchIndex = 0; while (st.hasMoreTokens() && isMatch) { String tk = st.nextToken(); if (value.indexOf(tk, matchIndex) == -1) { isMatch = false; } else { matchIndex = value.indexOf(tk, matchIndex) + tk.length(); } } if (isMatch) { results.add(o); } } return results; } /** * This method returns the average value of a numerical property. * @param propertyName String value of the property name to calculate. * @throws java.lang.IllegalAccessException reflection exception * @throws java.beans.IntrospectionException reflection exception * @throws java.lang.reflect.InvocationTargetException reflection exception * @return Average of property values. */ public Number getMeanOfProperty(String propertyName) throws java.lang.IllegalAccessException, java.beans.IntrospectionException, java.lang.reflect.InvocationTargetException { double mean = this.getSumOfProperty(propertyName).doubleValue() / this.size(); return new Double(mean); } /** * This method returns the index of the bean with the median value * on the specified property. * * <p>If there is an odd number of items in the dataset, the one below * the 50% mark will be returned. The true mathmatical mean, therefore * would be: * <code> * (beanArrayList.get(x).getProperty() + beanArrayList.get(x+1).getProperty() )/2 * </code></p> * @param propertyName String value of the property name to calculate. * @throws java.lang.IllegalAccessException reflection exception * @throws java.beans.IntrospectionException reflection exception * @throws java.lang.reflect.InvocationTargetException reflection exception * @return int value of the median index of the ArrayList */ public int getMedianIndex(String propertyName) throws java.lang.IllegalAccessException, java.beans.IntrospectionException, java.lang.reflect.InvocationTargetException { BeanArrayList bv = new BeanArrayList(this.size(), 0, this); bv.sortOnProperty(propertyName); int orderedIndex = bv.size() / 2; Object o = bv.get(orderedIndex); return this.indexOf(o); } /** * This method returns the index of an object representing the * mode value of a property name. * @param propertyName String value of the property name to calculate. * @throws java.lang.IllegalAccessException reflection exception * @throws java.beans.IntrospectionException reflection exception * @throws java.lang.reflect.InvocationTargetException reflection exception * @return int value of the mode index */ public int getModeIndex(String propertyName) throws java.lang.IllegalAccessException, java.beans.IntrospectionException, java.lang.reflect.InvocationTargetException { int index = -1; int max = 0; int count = 0; Object o = null; Object hold = null; HashMap cache = new HashMap(); String currentClass = ""; PropertyDescriptor pd = null; BeanArrayList bv = new BeanArrayList(this.size(), 0, this); bv.sortOnProperty(propertyName); for (int i = 0; i < bv.size(); i++) { if (!currentClass.equals(bv.get(i).getClass().getName())) { pd = (PropertyDescriptor) cache.get(bv.get(i).getClass().getName()); if (pd == null) { PropertyDescriptor[] pds = Introspector.getBeanInfo(bv.get(i).getClass()) .getPropertyDescriptors(); boolean foundProperty = false; for (int pdi = 0; (pdi < pds.length) && !foundProperty; pdi++) { if (pds[pdi].getName().equals(propertyName)) { pd = pds[pdi]; cache.put(bv.get(i).getClass().getName(), pd); foundProperty = true; } } } } if (hold == null) { hold = pd.getReadMethod().invoke(bv.get(i)); } else { o = pd.getReadMethod().invoke(bv.get(i)); if ((o != null) && o.equals(hold)) { count++; if (count > max) { max = count; index = this.indexOf(bv.get(i)); } } else { count = 1; } hold = o; } } return index; } /** Returns -1 or the the index of the next chunk after the current * ArrayList. * @return -1 or the the index of the next chunk after the current ArrayList */ public int getNextChunk() { return this.nextChunk; } /** * returns the number of chunks in the ArrayList * @return int value number of chunks */ public int getNumberOfChunks() { return this.numberOfChunks; } /** Returns -1 or the the index of the previous chunk before the * current ArrayList. * @return -1 or the the index of the previous chunk before the * current ArrayList */ public int getPreviousChunk() { return this.previousChunk; } /** * This method returns the sum of all values of a numerical * property. * @param propertyName String value of the property name to calculate. * @throws java.lang.IllegalAccessException reflection exception * @throws java.beans.IntrospectionException reflection exception * @throws java.lang.reflect.InvocationTargetException reflection exception * @return sum of a numerical property */ public Number getSumOfProperty(String propertyName) throws java.lang.IllegalAccessException, java.beans.IntrospectionException, java.lang.reflect.InvocationTargetException { double d = 0.0; String currentClass = ""; PropertyDescriptor pd = null; for (int i = 0; i < this.size(); i++) { T o = this.get(i); if (!currentClass.equals(o.getClass().getName())) { PropertyDescriptor[] pds = Introspector.getBeanInfo(o.getClass()).getPropertyDescriptors(); boolean foundProperty = false; for (int pdi = 0; (pdi < pds.length) && !foundProperty; pdi++) { if (pds[pdi].getName().equals(propertyName)) { pd = pds[pdi]; foundProperty = true; } } } if (o != null) { Number n = (Number) pd.getReadMethod().invoke(o); d += n.doubleValue(); } } return new Double(d); } /** * Inserts the specified element at the specified position in this ArrayList. * Shifts the element currently at that position (if any) and any * subsequent elements to the right (adds one to their indices). * * @since 1.2 * @param index index at which the specified element is to be inserted. * @param element element to be inserted. */ public void add(int index, T element) { T[] old = null; if ((this.indexPropertyName != null) && (this.changes != null)) { old = this.toTypedArray(); } super.add(index, element); if (old != null) { changes.firePropertyChange(this.indexPropertyName, old, this.toTypedArray()); } } /** * Appends the specified element to the end of this ArrayList. * * @param o element to be appended to this ArrayList. * @return true (as per the general contract of Collection.add). * @since 1.2 */ public boolean add(T o) { boolean retValue; retValue = super.add(o); if ((this.indexPropertyName != null) && (this.changes != null)) { changes.fireIndexedPropertyChange(this.indexPropertyName, this.size() - 1, null, o); } return retValue; } /** * Appends all of the elements in the specified Collection to the end of * this ArrayList, in the order that they are returned by the specified * Collection's Iterator. The behavior of this operation is undefined if * the specified Collection is modified while the operation is in progress. * (This implies that the behavior of this call is undefined if the * specified Collection is this ArrayList, and this ArrayList is nonempty.) * * @return <tt>true</tt> if this ArrayList changed as a result of the call. * @since 1.2 * @param c elements to be inserted into this ArrayList. */ public boolean addAll(Collection<? extends T> c) { boolean retValue; T[] old = null; if ((this.indexPropertyName != null) && (this.changes != null)) { old = this.toTypedArray(); } retValue = super.addAll(c); if (retValue && (old != null)) { changes.firePropertyChange(this.indexPropertyName, old, this.toTypedArray()); } return retValue; } /** * Inserts all of the elements in the specified Collection into this * ArrayList at the specified position. 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 the ArrayList * in the order that they are returned by the specified Collection's * iterator. * * @return <tt>true</tt> if this ArrayList changed as a result of the call. * @since 1.2 * @param index index at which to insert first element * from the specified collection. * @param c elements to be inserted into this ArrayList. */ public boolean addAll(int index, Collection<? extends T> c) { boolean retValue; T[] old = null; if ((this.indexPropertyName != null) && (this.changes != null)) { old = this.toTypedArray(); } retValue = super.addAll(index, c); if (retValue && (old != null)) { changes.firePropertyChange(this.indexPropertyName, old, this.toTypedArray()); } return retValue; } /** * Registers propertyChangeListeners to be fired when something changes in the ArrayList. * Note, the whole ArrayList is an "indexedProperty" name specified and parent delineated in * the constructor. * @param listener PropertyChangeListener to register */ public void addPropertyChangeListener(PropertyChangeListener listener) { changes.addPropertyChangeListener(listener); } /** * Registers propertyChangeListeners to be fired when something changes in the ArrayList. * Note, the whole ArrayList is an "indexedProperty" name specified and parent delineated in * the constructor. * @param property propteryName must match what the property name of this ArrayList is or the listener will be ignored. * @param listener the listener to register */ public void addPropertyChangeListener(String property, PropertyChangeListener listener) { if ((property != null) && property.equals(this.indexPropertyName)) { changes.addPropertyChangeListener(property, listener); } } /** * This method reverses the order of the ArrayList. */ public synchronized void invert() { BeanArrayList<T> temp = new BeanArrayList<T>(this); for (int i = 0; i < this.size(); i++) { this.set(i, temp.get(temp.size() - 1)); temp.remove(temp.get(temp.size() - 1)); } } /** * Removes the first occurrence of the specified element in this ArrayList * If the ArrayList does not contain the element, it is unchanged. More * formally, removes the element with the lowest index i such that * <code>(o==null ? get(i)==null : o.equals(get(i)))</code> (if such * an element exists). * * @param o element to be removed from this ArrayList, if present. * @return true if the ArrayList contained the specified element. * @since 1.2 */ public boolean remove(Object o) { boolean retValue; T[] old = null; if ((this.indexPropertyName != null) && (this.changes != null)) { old = this.toTypedArray(); } retValue = super.remove(o); if (retValue && (old != null)) { changes.firePropertyChange(this.indexPropertyName, old, this.toTypedArray()); } return retValue; } /** * Removes the element at the specified position in this ArrayList. * shifts any subsequent elements to the left (subtracts one from their * indices). Returns the element that was removed from the ArrayList. * * @return element that was removed * @since 1.2 * @param index the index of the element to removed. */ public T remove(int index) { T retValue; T[] old = null; if ((this.indexPropertyName != null) && (this.changes != null)) { old = this.toTypedArray(); } retValue = super.remove(index); if ((retValue != null) && (old != null)) { changes.firePropertyChange(this.indexPropertyName, old, this.toTypedArray()); } return retValue; } /** * Removes from this ArrayList all of its elements that are contained in the * specified Collection. * * @return true if this ArrayList changed as a result of the call. * @since 1.2 * @param c a collection of elements to be removed from the ArrayList */ public boolean removeAll(Collection<?> c) { boolean retValue; T[] old = null; if ((this.indexPropertyName != null) && (this.changes != null)) { old = this.toTypedArray(); } retValue = super.removeAll(c); if (retValue && (old != null)) { changes.firePropertyChange(this.indexPropertyName, old, this.toTypedArray()); } return retValue; } /** * Removes all components from this ArrayList and sets its size to zero.<p> * * This method is identical in functionality to the clear method * (which is part of the List interface). * * @see #clear * @see List */ public void removeAllElements() { T[] old = null; if ((this.indexPropertyName != null) && (this.changes != null)) { old = this.toTypedArray(); } super.clear(); if (old != null) { changes.firePropertyChange(this.indexPropertyName, old, this.toTypedArray()); } } /** * Removes the first (lowest-indexed) occurrence of the argument * from this ArrayList. If the object is found in this ArrayList, each * component in the ArrayList with an index greater or equal to the * object's index is shifted downward to have an index one smaller * than the value it had previously.<p> * * This method is identical in functionality to the remove(Object) * method (which is part of the List interface). * * @param obj the component to be removed. * @return <code>true</code> if the argument was a component of this * ArrayList; <code>false</code> otherwise. * @see List#remove(Object) * @see List */ public boolean removeElement(Object obj) { boolean retValue; T[] old = null; if ((this.indexPropertyName != null) && (this.changes != null)) { old = this.toTypedArray(); } retValue = super.remove(obj); if (retValue && (old != null)) { changes.firePropertyChange(this.indexPropertyName, old, this.toTypedArray()); } return retValue; } /** * Deletes the component at the specified index. Each component in * this ArrayList with an index greater or equal to the specified * <code>index</code> is shifted downward to have an index one * smaller than the value it had previously. The size of this ArrayList * is decreased by <tt>1</tt>.<p> * * The index must be a value greater than or equal to <code>0</code> * and less than the current size of the ArrayList. <p> * * This method is identical in functionality to the remove method * (which is part of the List interface). Note that the remove method * returns the old value that was stored at the specified position. * * @see #size() * @see #remove(int) * @see List * @param index the index of the object to remove. */ public void removeElementAt(int index) { T[] old = null; if ((this.indexPropertyName != null) && (this.changes != null)) { old = this.toTypedArray(); } super.remove(index); if (old != null) { changes.firePropertyChange(this.indexPropertyName, old, this.toTypedArray()); } } /** * Removes propertyChangeListeners to be fired when something changes in the ArrayList. * Note, the whole ArrayList is an "indexedProperty" name specified and parent delineated in * the constructor. * @param listener listener to remove */ public void removePropertyChangeListener(PropertyChangeListener listener) { changes.removePropertyChangeListener(listener); } /** * Removes propertyChangeListeners to be fired when something changes in the ArrayList. * Note, the whole ArrayList is an "indexedProperty" name specified and parent delineated in * the constructor. * @param property must match the current property name for the ArrayList or will be ignored * @param listener listener to remove */ public void removePropertyChangeListener(String property, PropertyChangeListener listener) { if ((property != null) && property.equals(this.indexPropertyName)) { changes.removePropertyChangeListener(property, listener); } } /** * Resets the contents of the ArrayList to the values provided. * @param contents Array of object to replace the current contents with */ public synchronized void resetContents(T[] contents) { if ((this.indexPropertyName != null) && (this.changes != null)) { changes.firePropertyChange(this.indexPropertyName, this.toTypedArray(), contents); } this.removeAllElements(); for (T t : contents) this.add(t); } /** * Retains only the elements in this ArrayList that are contained in the * specified Collection. In other words, removes from this ArrayList all * of its elements that are not contained in the specified Collection. * * @return true if this ArrayList changed as a result of the call. * @since 1.2 * @param c a collection of elements to be retained in this ArrayList * (all other elements are removed) */ public boolean retainAll(Collection<?> c) { boolean retValue; T[] old = null; if ((this.indexPropertyName != null) && (this.changes != null)) { old = this.toTypedArray(); } retValue = super.retainAll(c); if (retValue && (old != null)) { changes.firePropertyChange(this.indexPropertyName, old, this.toTypedArray()); } return retValue; } /** * Replaces the element at the specified position in this ArrayList with the * specified element. * * @return the element previously at the specified position. * @since 1.2 * @param index index of element to replace. * @param element element to be stored at the specified position. */ public T set(int index, T element) { T retValue; retValue = super.set(index, element); if ((this.indexPropertyName != null) && (this.changes != null)) { changes.fireIndexedPropertyChange(this.indexPropertyName, index, retValue, element); } return retValue; } /** * performs a selection sort on all the beans in the ArrayList by PropertyName * * <p>You can use a mixture of bean classes as long as all the beans support * the same property (getName() for instance), and all have the same return * type.</p> * * <p>For optimal performance, it is recommended that if you have a * mixed class set the you have them grouped with like classes together as this * will minimize reflection inspections.</p> * @param propertyName String value containing the property to sort * on. * @throws java.lang.IllegalAccessException Property was not accessible * @throws java.beans.IntrospectionException Couldn't introspect * @throws java.lang.reflect.InvocationTargetException Is the proper signature getProperty() ? */ public void sortOnProperty(String propertyName) throws java.lang.IllegalAccessException, java.beans.IntrospectionException, java.lang.reflect.InvocationTargetException { this.sortOnProperty(propertyName, true); } /** * performs a selection sort on all the beans in the ArrayList by * PropertyName * * <p>You can use a mixture of bean classes as long as all the beans * support the same property (getName() for instance), and all have the * same return type, or can be compareTo()ed each other.</p> * * <p>For optimal performance, it is recommended that if you have a * mixed class set the you have them grouped with like classes together * as this will minimize reflection inspections.</p> * @param propertyName String value containing the property to sort on. * @param ascending == sorts up if true, down if not. * @throws java.lang.IllegalAccessException reflection exception * @throws java.beans.IntrospectionException reflection exception * @throws java.lang.reflect.InvocationTargetException reflection exception */ public synchronized void sortOnProperty(String propertyName, boolean ascending) throws java.lang.IllegalAccessException, java.beans.IntrospectionException, java.lang.reflect.InvocationTargetException { T[] old = null; if ((this.indexPropertyName != null) && (this.changes != null)) { old = this.toTypedArray(); } T temp = null; String currentClass = ""; PropertyDescriptor pd = null; HashMap cache = new HashMap(); for (int i = 0; i < (this.size() - 1); i++) { for (int j = i + 1; j < this.size(); j++) { T o1 = this.get(i); if (!currentClass.equals(o1.getClass().getName())) { pd = (PropertyDescriptor) cache.get(o1.getClass().getName()); if (pd == null) { PropertyDescriptor[] pds = Introspector.getBeanInfo(o1.getClass()).getPropertyDescriptors(); boolean foundProperty = false; for (int pdi = 0; (pdi < pds.length) && !foundProperty; pdi++) { if (pds[pdi].getName().equals(propertyName)) { pd = pds[pdi]; cache.put(o1.getClass().getName(), pd); foundProperty = true; } } } } //System.out.println( "o1: "+o1+" "+pd); //System.out.println( propertyName +" "+ (pd == null )); Comparable oc1 = (Comparable) pd.getReadMethod().invoke(o1); T o2 = this.get(j); if (!currentClass.equals(o2.getClass().getName())) { pd = (PropertyDescriptor) cache.get(o2.getClass().getName()); if (pd == null) { PropertyDescriptor[] pds = Introspector.getBeanInfo(o2.getClass()).getPropertyDescriptors(); boolean foundProperty = false; for (int pdi = 0; (pdi < pds.length) && !foundProperty; pdi++) { if (pds[pdi].getName().equals(propertyName)) { pd = pds[pdi]; foundProperty = true; } } } } Comparable oc2 = (Comparable) pd.getReadMethod().invoke(o2); if (ascending) { if ((oc1 != oc2) && ((oc2 == null) || ((oc1 != null) && (oc2 != null) && (oc2.compareTo(oc1) < 0)))) { //swap this.setElementAt(o2, i); this.setElementAt(o1, j); } } else { if ((oc1 != oc2) && ((oc1 == null) || ((oc1 != null) && (oc2 != null) && (oc1.compareTo(oc2) < 0)))) { //swap this.setElementAt(o2, i); this.setElementAt(o1, j); } } } if (old != null) { changes.firePropertyChange(this.indexPropertyName, old, this.toTypedArray()); } } } /** * Returns an Array of the generic type associated with this ArrayList. * @return Array representation of the current ArrayList. */ public T[] toTypedArray() { return (T[]) this.toArray(); } /** * Removes from this List all of the elements whose index is between * fromIndex, inclusive and toIndex, exclusive. Shifts any succeeding * elements to the left (reduces their index). * This call shortens the ArrayList by (toIndex - fromIndex) elements. (If * toIndex==fromIndex, this operation has no effect.) * * @param fromIndex index of first element to be removed. * @param toIndex index after last element to be removed. */ protected void removeRange(int fromIndex, int toIndex) { T[] old = null; if ((this.indexPropertyName != null) && (this.changes != null)) { old = this.toTypedArray(); } super.removeRange(fromIndex, toIndex); if (old != null) { changes.firePropertyChange(this.indexPropertyName, old, this.toTypedArray()); } } }