Java/Development Class/Java Beans

Материал из Java эксперт
Версия от 18:01, 31 мая 2010; (обсуждение)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Содержание

An extension of ArrayList that provides some handy utilities for working with JavaBeans

   
/*
 * 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.ruparable;
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  
 */
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.rupareTo() 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.rupareTo(fromValue) > 0)&&(value.rupareTo(toValue) < 0)) {
                results.add(o);
            } else if(inclusive&&((value.rupareTo(fromValue) == 0)||(value.rupareTo(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.rupareTo(oc1) < 0)))) { //swap
                        this.setElementAt(o2,i);
                        this.setElementAt(o1,j);
                    }
                } else {
                    if((oc1 != oc2)&&((oc1 == null)||((oc1 != null)&&(oc2 != null)&&(oc1.rupareTo(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());
        }
    }
}





An extension of Vector that provides some handy utilities for working with JavaBeans

   
/*
 * BeanVector.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.ruparable;
import java.lang.reflect.*;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;

/** This is an extension of Vector 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  
 */
public class BeanVector<T> extends Vector<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 BeanVector() {
        super();
    }
    /**
     * This contructs a new BeanVector 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 BeanVector(String indexPropertyName,Object changeOwner) {
        this();
        this.indexPropertyName = indexPropertyName;
        this.changes = new PropertyChangeSupport(changeOwner);
    }
    /**
     * Creates a new instance of BeanVector 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 BeanVector(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 BeanVector 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 BeanVector(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 BeanVector
     * @param source Collection containing the initial values.
     */
    public BeanVector(Collection<T> source) {
        this(source.size(),0,source);
    }
    /**
     * This contructs a new BeanVector 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 BeanVector(String indexPropertyName,Object changeOwner,Collection<T> source) {
        this(source);
        this.indexPropertyName = indexPropertyName;
        this.changes = new PropertyChangeSupport(changeOwner);
    }
    /**
     * Gets a chunk of this vector.
     * @param chunkSize int number of object to return.
     * @param currentChunk int value of the chunk to get (zero index)
     * @return new BeanVector representing the chunk requested.
     */
    public BeanVector getChunk(int chunkSize,int currentChunk) {
        return new BeanVector(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.elementAt(index);
        }
        super.setElementAt(obj,index);
        if(old != null) {
            changes.fireIndexedPropertyChange(this.indexPropertyName,index,old,obj);
        }
    }
    /**
     * Filters a property using the Comparable.rupareTo() 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 BeanVector filtered on the range
     */
    public BeanVector<T> getFiltered(String propertyName,boolean inclusive,Comparable fromValue,Comparable toValue) throws java.lang.IllegalAccessException,java.beans.IntrospectionException,java.lang.reflect.InvocationTargetException {
        Hashtable cache = new Hashtable();
        String currentClass = "";
        PropertyDescriptor pd = null;
        BeanVector<T> results = new BeanVector<T>();
        for(int i = 0; i < this.size(); i++) {
            T o = this.elementAt(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.rupareTo(fromValue) > 0)&&(value.rupareTo(toValue) < 0)) {
                results.add(o);
            } else if(inclusive&&((value.rupareTo(fromValue) == 0)||(value.rupareTo(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 BeanVector filtered on the specified property
     */
    public BeanVector<T> getFilteredStringMatch(String propertyName,String match) throws java.lang.IllegalAccessException,java.beans.IntrospectionException,java.lang.reflect.InvocationTargetException {
        Hashtable cache = new Hashtable();
        String currentClass = "";
        PropertyDescriptor pd = null;
        BeanVector<T> results = new BeanVector<T>();
        for(int i = 0; i < this.size(); i++) {
            T o = this.elementAt(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>
     * (beanVector.elementAt(x).getProperty() + beanVector.elementAt(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 Vector
     */
    public int getMedianIndex(String propertyName) throws java.lang.IllegalAccessException,java.beans.IntrospectionException,java.lang.reflect.InvocationTargetException {
        BeanVector bv = new BeanVector(this.size(),0,this);
        bv.sortOnProperty(propertyName);
        int orderedIndex = bv.size() / 2;
        Object o = bv.elementAt(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;
        Hashtable cache = new Hashtable();
        String currentClass = "";
        PropertyDescriptor pd = null;
        BeanVector bv = new BeanVector(this.size(),0,this);
        bv.sortOnProperty(propertyName);
        for(int i = 0; i < bv.size(); i++) {
            if(!currentClass.equals(bv.elementAt(i).getClass().getName())) {
                pd = (PropertyDescriptor)cache.get(bv.elementAt(i).getClass().getName());
                if(pd == null) {
                    PropertyDescriptor[] pds = Introspector.getBeanInfo(bv.elementAt(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.elementAt(i).getClass().getName(),pd);
                            foundProperty = true;
                        }
                    }
                }
            }
            if(hold == null) {
                hold = pd.getReadMethod().invoke(bv.elementAt(i));
            } else {
                o = pd.getReadMethod().invoke(bv.elementAt(i));
                if((o != null)&&o.equals(hold)) {
                    count++;
                    if(count > max) {
                        max = count;
                        index = this.indexOf(bv.elementAt(i));
                    }
                } else {
                    count = 1;
                }
                hold = o;
            }
        }
        return index;
    }
    /** Returns -1 or the the index of the next chunk after the current
     * vector.
     * @return -1 or the the index of the next chunk after the current vector
     */
    public int getNextChunk() {
        return this.nextChunk;
    }
    /**
     * returns the number of chunks in the vector
     * @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 vector.
     * @return -1 or the the index of the previous chunk before the
     * current vector
     */
    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.elementAt(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 Vector.
     * 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 Vector.
     *
     * @param o element to be appended to this Vector.
     * @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 Vector, 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 Vector, and this Vector is nonempty.)
     *
     * @return <tt>true</tt> if this Vector changed as a result of the call.
     * @since 1.2
     * @param c elements to be inserted into this Vector.
     */
    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
     * Vector 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 Vector
     * in the order that they are returned by the specified Collection"s
     * iterator.
     *
     * @return <tt>true</tt> if this Vector 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 Vector.
     */
    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;
    }
    /**
     * Adds the specified component to the end of this vector,
     * increasing its size by one. The capacity of this vector is
     * increased if its size becomes greater than its capacity. <p>
     *
     * This method is identical in functionality to the add(Object) method
     * (which is part of the List interface).
     *
     * @param   obj   the component to be added.
     * @see           #add(Object)
     * @see           List
     */
    public void addElement(T obj) {
        super.addElement(obj);
        if((this.indexPropertyName != null)&&(this.changes != null)) {
            changes.fireIndexedPropertyChange(this.indexPropertyName,this.size() - 1,null,obj);
        }
    }
    /**
     * Registers propertyChangeListeners to be fired when something changes in the Vector.
     * Note, the whole vector 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 Vector.
     * Note, the whole vector is an "indexedProperty" name specified and parent delineated in
     * the constructor.
     * @param property propteryName must match what the property name of this vector 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 vector.
     */
    public synchronized void invert() {
        BeanVector<T> temp = new BeanVector<T>(this);
        for(int i = 0; i < this.size(); i++) {
            this.setElementAt(temp.lastElement(),i);
            temp.remove(temp.lastElement());
        }
    }
    /**
     * Removes the first occurrence of the specified element in this Vector
     * If the Vector 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 Vector, if present.
     * @return true if the Vector 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 Vector.
     * shifts any subsequent elements to the left (subtracts one from their
     * indices).  Returns the element that was removed from the Vector.
     *
     * @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 Vector all of its elements that are contained in the
     * specified Collection.
     *
     * @return true if this Vector changed as a result of the call.
     * @since 1.2
     * @param c a collection of elements to be removed from the Vector
     */
    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 vector 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.removeAllElements();
        if(old != null) {
            changes.firePropertyChange(this.indexPropertyName,old,this.toTypedArray());
        }
    }
    /**
     * Removes the first (lowest-indexed) occurrence of the argument
     * from this vector. If the object is found in this vector, each
     * component in the vector 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
     *          vector; <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.removeElement(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 vector 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 vector
     * 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 vector. <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.removeElementAt(index);
        if(old != null) {
            changes.firePropertyChange(this.indexPropertyName,old,this.toTypedArray());
        }
    }
    /**
     * Removes propertyChangeListeners to be fired when something changes in the Vector.
     * Note, the whole vector 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 Vector.
     * Note, the whole vector is an "indexedProperty" name specified and parent delineated in
     * the constructor.
     * @param property must match the current property name for the vector 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 vector 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 Vector that are contained in the
     * specified Collection.  In other words, removes from this Vector all
     * of its elements that are not contained in the specified Collection.
     *
     * @return true if this Vector changed as a result of the call.
     * @since 1.2
     * @param c a collection of elements to be retained in this Vector
     *          (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 Vector 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 Vector 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 Vector 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;
        Hashtable cache = new Hashtable();
        for(int i = 0; i < (this.size() - 1); i++) {
            for(int j = i + 1; j < this.size(); j++) {
                T o1 = this.elementAt(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.elementAt(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.rupareTo(oc1) < 0)))) { //swap
                        this.setElementAt(o2,i);
                        this.setElementAt(o1,j);
                    }
                } else {
                    if((oc1 != oc2)&&((oc1 == null)||((oc1 != null)&&(oc2 != null)&&(oc1.rupareTo(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 Vector.
     * @return Array representation of the current vector.
     */
    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());
        }
    }
}





Bean Container

   
/*
Swing, Second Edition
by Matthew Robinson, Pavel Vorobiev
*/
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.beans.*;
import java.lang.reflect.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;
public class BeanContainer extends JFrame implements FocusListener{
  protected File m_currentDir = new File(".");
  protected Component m_activeBean;
  protected String m_className = "clock.Clock";
  protected JFileChooser m_chooser = new JFileChooser();
  protected Hashtable m_editors = new Hashtable();
  public BeanContainer() {
    super("Simple Bean Container");
    getContentPane().setLayout(new FlowLayout());
   
    setSize(300, 300);
    JPopupMenu.setDefaultLightWeightPopupEnabled(false);
    JMenuBar menuBar = createMenuBar();
    setJMenuBar(menuBar);
    WindowListener wndCloser = new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    };
    addWindowListener(wndCloser);
    setVisible(true);
  }
  protected JMenuBar createMenuBar() {
    JMenuBar menuBar = new JMenuBar();
    
    JMenu mFile = new JMenu("File");
    JMenuItem mItem = new JMenuItem("New...");
    ActionListener lst = new ActionListener() { 
      public void actionPerformed(ActionEvent e) {  
        Thread newthread = new Thread() {
          public void run() {
            String result = (String)JOptionPane.showInputDialog(
              BeanContainer.this, 
              "Please enter class name to create a new bean", 
              "Input", JOptionPane.INFORMATION_MESSAGE, null, 
              null, m_className);
            repaint();
            if (result==null)
              return;
            try {
              m_className = result;
              Class cls = Class.forName(result);
              Object obj = cls.newInstance();
              if (obj instanceof Component) {
                m_activeBean = (Component)obj;
                m_activeBean.addFocusListener(
                  BeanContainer.this);
                m_activeBean.requestFocus();
                getContentPane().add(m_activeBean);
              }
              validate();
            }
            catch (Exception ex) {
              ex.printStackTrace();
              JOptionPane.showMessageDialog(
                BeanContainer.this, "Error: "+ex.toString(),
                "Warning", JOptionPane.WARNING_MESSAGE);
            }
          }
        };
        newthread.start();
      }
    };
    mItem.addActionListener(lst);
    mFile.add(mItem);
    mItem = new JMenuItem("Load...");
    lst = new ActionListener() { 
      public void actionPerformed(ActionEvent e) {  
        Thread newthread = new Thread() {
          public void run() {
            m_chooser.setCurrentDirectory(m_currentDir);
            m_chooser.setDialogTitle(
              "Please select file with serialized bean");
            int result = m_chooser.showOpenDialog(
              BeanContainer.this);
            repaint();
            if (result != JFileChooser.APPROVE_OPTION)
              return;
            m_currentDir = m_chooser.getCurrentDirectory();
            File fChoosen = m_chooser.getSelectedFile();
            try {
              FileInputStream fStream = 
                new FileInputStream(fChoosen);
              ObjectInput  stream  =  
                new ObjectInputStream(fStream);
              Object obj = stream.readObject();
              if (obj instanceof Component) {
                m_activeBean = (Component)obj;
                m_activeBean.addFocusListener(
                  BeanContainer.this);
                m_activeBean.requestFocus();
                getContentPane().add(m_activeBean);
              }
              stream.close();
              fStream.close();
              validate();
            }
            catch (Exception ex) {
              ex.printStackTrace();
              JOptionPane.showMessageDialog(
                BeanContainer.this, "Error: "+ex.toString(),
                "Warning", JOptionPane.WARNING_MESSAGE);
            }
            repaint();
          }
        };
        newthread.start();
      }
    };
    mItem.addActionListener(lst);
    mFile.add(mItem);
    mItem = new JMenuItem("Save...");
    lst = new ActionListener() { 
      public void actionPerformed(ActionEvent e) {
        Thread newthread = new Thread() {
          public void run() {
            if (m_activeBean == null)
              return;
            m_chooser.setDialogTitle(
              "Please choose file to serialize bean");
            m_chooser.setCurrentDirectory(m_currentDir);
            int result = m_chooser.showSaveDialog(
              BeanContainer.this);
            repaint();
            if (result != JFileChooser.APPROVE_OPTION)
              return;
            m_currentDir = m_chooser.getCurrentDirectory();
            File fChoosen = m_chooser.getSelectedFile();
            try {
              FileOutputStream fStream = 
                new FileOutputStream(fChoosen);
              ObjectOutput stream  =  
                new ObjectOutputStream(fStream);
              stream.writeObject(m_activeBean);
              stream.close();
              fStream.close();
            }
            catch (Exception ex) {
              ex.printStackTrace();
            JOptionPane.showMessageDialog(
              BeanContainer.this, "Error: "+ex.toString(),
              "Warning", JOptionPane.WARNING_MESSAGE);
            }
          }
        };
        newthread.start();
      }
    };
    mItem.addActionListener(lst);
    mFile.add(mItem);
    mFile.addSeparator();
    mItem = new JMenuItem("Exit");
    lst = new ActionListener() { 
      public void actionPerformed(ActionEvent e) {
        System.exit(0);
      }
    };
    mItem.addActionListener(lst);
    mFile.add(mItem);
    menuBar.add(mFile);
    
    JMenu mEdit = new JMenu("Edit");
    mItem = new JMenuItem("Delete");
    lst = new ActionListener() { 
      public void actionPerformed(ActionEvent e) {
        if (m_activeBean == null)
          return;
        Object obj = m_editors.get(m_activeBean);
        if (obj != null) {
          BeanEditor editor = (BeanEditor)obj;
          editor.dispose();
          m_editors.remove(m_activeBean);
        }
        getContentPane().remove(m_activeBean);
        m_activeBean = null;
        validate();
        repaint();
      }
    };
    mItem.addActionListener(lst);
    mEdit.add(mItem);
    mItem = new JMenuItem("Properties...");
    lst = new ActionListener() { 
      public void actionPerformed(ActionEvent e) {
        if (m_activeBean == null)
          return;
        Object obj = m_editors.get(m_activeBean);
        if (obj != null) {
          BeanEditor editor = (BeanEditor)obj;
          editor.setVisible(true);
          editor.toFront();
        }
        else {
          BeanEditor editor = new BeanEditor(m_activeBean);
          m_editors.put(m_activeBean, editor);
        }
      }
    };
    mItem.addActionListener(lst);
    mEdit.add(mItem);
    menuBar.add(mEdit);
    JMenu mLayout = new JMenu("Layout");
    ButtonGroup group = new ButtonGroup();
    mItem = new JRadioButtonMenuItem("FlowLayout");
    mItem.setSelected(true);
    lst = new ActionListener() { 
      public void actionPerformed(ActionEvent e){
        getContentPane().setLayout(new FlowLayout());
        validate();
        repaint();
      }
    };
    mItem.addActionListener(lst);
    group.add(mItem);
    mLayout.add(mItem);
    mItem = new JRadioButtonMenuItem("GridLayout");
    lst = new ActionListener() { 
      public void actionPerformed(ActionEvent e){
        int col = 3;
        int row = (int)Math.ceil(getContentPane().
          getComponentCount()/(double)col);
        getContentPane().setLayout(new GridLayout(row, col, 10, 10));
        validate();
        repaint();
      }
    };
    mItem.addActionListener(lst);
    group.add(mItem);
    mLayout.add(mItem);
    
    mItem = new JRadioButtonMenuItem("BoxLayout - X");
    lst = new ActionListener() { 
      public void actionPerformed(ActionEvent e) {
        getContentPane().setLayout(new BoxLayout(
          getContentPane(), BoxLayout.X_AXIS));
        validate();
        repaint();
      }
    };
    mItem.addActionListener(lst);
    group.add(mItem);
    mLayout.add(mItem);
    
    mItem = new JRadioButtonMenuItem("BoxLayout - Y");
    lst = new ActionListener() { 
      public void actionPerformed(ActionEvent e) {
        getContentPane().setLayout(new BoxLayout(
          getContentPane(), BoxLayout.Y_AXIS));
        validate();
        repaint();
      }
    };
    mItem.addActionListener(lst);
    group.add(mItem);
    mLayout.add(mItem);
    
    mItem = new JRadioButtonMenuItem("DialogLayout");
    lst = new ActionListener() { 
      public void actionPerformed(ActionEvent e) {
        getContentPane().setLayout(new DialogLayout());
        validate();
        repaint();
      }
    };
    mItem.addActionListener(lst);
    group.add(mItem);
    mLayout.add(mItem);
    menuBar.add(mLayout);
    return menuBar;
  }
  public void focusGained(FocusEvent e) {
    m_activeBean = e.getComponent();
    repaint();
  }
  public void focusLost(FocusEvent e) {}
  // This is a heavyweight component so we override paint
  // instead of paintComponent. super.paint(g) will
  // paint all child components first, and then we 
  // simply draw over top of them.
  public void paint(Graphics g) {
    super.paint(g);
    if (m_activeBean == null)
      return;
    Point pt = getLocationOnScreen();
    Point pt1 = m_activeBean.getLocationOnScreen();
    int x = pt1.x - pt.x - 2;
    int y = pt1.y - pt.y - 2;
    int w = m_activeBean.getWidth() + 2;
    int h = m_activeBean.getHeight() + 2;
    g.setColor(Color.black);
    g.drawRect(x, y, w, h);
  }
  public static void main(String argv[]) {
    new BeanContainer();
  }
}
class BeanEditor extends JFrame implements PropertyChangeListener
{
  protected Component m_bean;
  protected JTable m_table;
  protected PropertyTableData m_data;
    
  public BeanEditor(Component bean) {
    m_bean = bean;
    m_bean.addPropertyChangeListener(this);
    Point pt = m_bean.getLocationOnScreen();
    setBounds(pt.x+50, pt.y+10, 400, 300);
    getContentPane().setLayout(new BorderLayout());
    m_data = new PropertyTableData(m_bean);
    m_table = new JTable(m_data);
    JScrollPane ps = new JScrollPane();
    ps.getViewport().add(m_table);
    getContentPane().add(ps, BorderLayout.CENTER);
    setDefaultCloseOperation(HIDE_ON_CLOSE);
    setVisible(true);
  }
  public void propertyChange(PropertyChangeEvent evt) {
    m_data.setProperty(evt.getPropertyName(), evt.getNewValue());
  }
  class PropertyTableData extends AbstractTableModel 
  {
    protected String[][] m_properties;
    protected int m_numProps = 0;
    protected Vector m_v;
    public PropertyTableData(Component bean) {
      try {
        BeanInfo info = Introspector.getBeanInfo(
          m_bean.getClass());
        BeanDescriptor descr = info.getBeanDescriptor();
        setTitle("Editing "+descr.getName());
        PropertyDescriptor[] props = info.getPropertyDescriptors();
        m_numProps = props.length;
                
        m_v = new Vector(m_numProps);
        for (int k=0; k<m_numProps; k++) {
          String name = props[k].getDisplayName();
          boolean added = false;
          for (int i=0; i<m_v.size(); i++) {
            String str = ((PropertyDescriptor)m_v.elementAt(i)).
              getDisplayName();
            if (name.rupareToIgnoreCase(str) < 0) {
              m_v.insertElementAt(props[k], i);
              added = true;
              break;
            }
          }
          if (!added)
            m_v.addElement(props[k]);
        }
        m_properties = new String[m_numProps][2];
        for (int k=0; k<m_numProps; k++) {
          PropertyDescriptor prop = 
            (PropertyDescriptor)m_v.elementAt(k);
          m_properties[k][0] = prop.getDisplayName();
          Method mRead = prop.getReadMethod();
          if (mRead != null && 
           mRead.getParameterTypes().length == 0) {
            Object value = mRead.invoke(m_bean, null);
            m_properties[k][1] = objToString(value);
          }
          else
            m_properties[k][1] = "error";
        }
      }
      catch (Exception ex) {
        ex.printStackTrace();
        JOptionPane.showMessageDialog(
          BeanEditor.this, "Error: "+ex.toString(),
          "Warning", JOptionPane.WARNING_MESSAGE);
      }
    }
    public void setProperty(String name, Object value) {
      for (int k=0; k<m_numProps; k++)
        if (name.equals(m_properties[k][0])) {
          m_properties[k][1] = objToString(value);
          m_table.tableChanged(new TableModelEvent(this, k)); 
          m_table.repaint();
          break;
        }
    }
    public int getRowCount() { return m_numProps; }
    public int getColumnCount() { return 2; } 
    public String getColumnName(int nCol) { 
      return nCol==0 ? "Property" : "Value";
    }
 
    public boolean isCellEditable(int nRow, int nCol) { 
       return (nCol==1);
    }
    public Object getValueAt(int nRow, int nCol) {
      if (nRow < 0 || nRow>=getRowCount())
        return "";
      switch (nCol) {
        case 0: return m_properties[nRow][0];
        case 1: return m_properties[nRow][1];
      }
      return "";
    }
    public void setValueAt(Object value, int nRow, int nCol) {
      if (nRow < 0 || nRow>=getRowCount())
        return;
      String str = value.toString();
      PropertyDescriptor prop = (PropertyDescriptor)m_v.
        elementAt(nRow);
      Class cls = prop.getPropertyType();
      Object obj = stringToObj(str, cls);
      if (obj==null)
        return;        // can"t process
           
      Method mWrite = prop.getWriteMethod();
      if (mWrite == null || mWrite.getParameterTypes().length != 1)
        return;
      try {
        mWrite.invoke(m_bean, new Object[]{ obj });
        m_bean.getParent().doLayout();
        m_bean.getParent().repaint();
        m_bean.repaint();
      }
      catch (Exception ex) {
        ex.printStackTrace();
        JOptionPane.showMessageDialog(
          BeanEditor.this, "Error: "+ex.toString(),
          "Warning", JOptionPane.WARNING_MESSAGE);
      }
      m_properties[nRow][1] = str;
    }
    public String objToString(Object value) {
      if (value==null)
        return "null";
      if (value instanceof Dimension) {
        Dimension dim = (Dimension)value;
        return ""+dim.width+","+dim.height;
      }
      else if (value instanceof Insets) {
        Insets ins = (Insets)value;
        return ""+ins.left+","+ins.top+","+ins.right+","+ins.bottom;
      }
      else if (value instanceof Rectangle) {
        Rectangle rc = (Rectangle)value;
        return ""+rc.x+","+rc.y+","+rc.width+","+rc.height;
      }
      else if (value instanceof Color) {
        Color col = (Color)value;
        return ""+col.getRed()+","+col.getGreen()+","+col.getBlue();
      }
      return value.toString();
    }
    public Object stringToObj(String str, Class cls) {
      try {
        if (str==null)
          return null;
        String name = cls.getName();
        if (name.equals("java.lang.String"))
          return str;
        else if (name.equals("int"))
          return new Integer(str);
        else if (name.equals("long"))
          return new Long(str);
        else if (name.equals("float"))
          return new Float(str);
        else if (name.equals("double"))
          return new Double(str);
        else if (name.equals("boolean"))
          return new Boolean(str);
        else if (name.equals("java.awt.Dimension")) {
          int[] i = strToInts(str);
          return new Dimension(i[0], i[1]);
        }
        else if (name.equals("java.awt.Insets")) {
          int[] i = strToInts(str);
          return new Insets(i[0], i[1], i[2], i[3]);
        }
        else if (name.equals("java.awt.Rectangle")) {
          int[] i = strToInts(str);
          return new Rectangle(i[0], i[1], i[2], i[3]);
        }
        else if (name.equals("java.awt.Color")) {
          int[] i = strToInts(str);
          return new Color(i[0], i[1], i[2]);
        }
        return null;    // not supported
      }
      catch(Exception ex) { return null; }
    }
    public int[] strToInts(String str) throws Exception {
      int[] i = new int[4];
      StringTokenizer tokenizer = new StringTokenizer(str, ",");
      for (int k=0; k<i.length && 
       tokenizer.hasMoreTokens(); k++)
        i[k] = Integer.parseInt(tokenizer.nextToken());
      return i;
    }
  }
}

/**
  DialogLayout
*/
class DialogLayout implements LayoutManager {
  protected int m_divider = -1;
  protected int m_hGap = 10;
  protected int m_vGap = 5;
  public DialogLayout() {}
  public DialogLayout(int hGap, int vGap) 
  {
    m_hGap = hGap;
    m_vGap = vGap;
  }
  public void addLayoutComponent(String name, Component comp) {}
  public void removeLayoutComponent(Component comp) {}
  public Dimension preferredLayoutSize(Container parent)
  {
    int divider = getDivider(parent);
    
    int w = 0;
    int h = 0;
    for (int k=1 ; k<parent.getComponentCount(); k+=2) 
    {
      Component comp = parent.getComponent(k);
      Dimension d = comp.getPreferredSize();
      w = Math.max(w, d.width);
      h += d.height + m_vGap;
    }
    h -= m_vGap;
    Insets insets = parent.getInsets();
    return new Dimension(divider+w+insets.left+insets.right, 
      h+insets.top+insets.bottom);
  }
  public Dimension minimumLayoutSize(Container parent)
  {
    return preferredLayoutSize(parent);
  }
  public void layoutContainer(Container parent)
  {
    int divider = getDivider(parent);
    
    Insets insets = parent.getInsets();
    int w = parent.getWidth() - insets.left - insets.right - divider;
    int x = insets.left;
    int y = insets.top;
    for (int k=1 ; k<parent.getComponentCount(); k+=2) 
    {
      Component comp1 = parent.getComponent(k-1);
      Component comp2 = parent.getComponent(k);
      Dimension d = comp2.getPreferredSize();
      
      comp1.setBounds(x, y, divider-m_hGap, d.height);
      comp2.setBounds(x+divider, y, w, d.height);
      y += d.height + m_vGap;
    }
  }
  public int getHGap()
  {
    return m_hGap;
  }
  public int getVGap()
  {
    return m_vGap;
  }
  public void setDivider(int divider)
  {
    if (divider > 0)
      m_divider = divider;
  }
  
  public int getDivider()
  {
    return m_divider;
  }
  protected int getDivider(Container parent)
  {
    if (m_divider > 0)
      return m_divider;
    int divider = 0;
    for (int k=0 ; k<parent.getComponentCount(); k+=2) 
    {
      Component comp = parent.getComponent(k);
      Dimension d = comp.getPreferredSize();
      divider = Math.max(divider, d.width);
    }
    divider += m_hGap;
    return divider;
  }
  public String toString() 
  {
    return getClass().getName() + "[hgap=" + m_hGap + ",vgap=" 
      + m_vGap + ",divider=" + m_divider + "]";
  }
}

///////////File: Clock.java
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
public class Clock extends    JButton
  implements Customizer, Externalizable, Runnable
{
  protected PropertyChangeSupport m_helper;
  protected boolean  m_digital = false;
  protected Calendar m_calendar;
  protected Dimension m_preffSize;
  public Clock()
  {
    m_calendar = Calendar.getInstance();
    m_helper = new PropertyChangeSupport(this);
    Border br1 = new EtchedBorder(EtchedBorder.RAISED, 
      Color.white, new Color(128, 0, 0));
    Border br2 = new MatteBorder(4, 4, 4, 4, Color.red);
    setBorder(new CompoundBorder(br1, br2));
    setBackground(Color.white);
    setForeground(Color.black);
    (new Thread(this)).start();
  }
  public void writeExternal(ObjectOutput out)   
    throws IOException
  {
    out.writeBoolean(m_digital);
    out.writeObject(getBackground());
    out.writeObject(getForeground());
    out.writeObject(getPreferredSize());
  }
  public void readExternal(ObjectInput in)
    throws IOException, ClassNotFoundException
  {
    setDigital(in.readBoolean());
    setBackground((Color)in.readObject());
    setForeground((Color)in.readObject());
    setPreferredSize((Dimension)in.readObject());
  }
  public Dimension getPreferredSize()
  {
    if (m_preffSize != null)
      return m_preffSize;
    else
      return new Dimension(50, 50);
  }
  
  public void setPreferredSize(Dimension preffSize)
  {
    m_preffSize = preffSize;
  }
  public Dimension getMinimumSize()
  {
    return getPreferredSize();
  }
  public Dimension getMaximumSize()
  {
    return getPreferredSize();
  }
  public void setDigital(boolean digital)
  {
    m_helper.firePropertyChange("digital",
      new Boolean(m_digital), 
      new Boolean(digital));
    m_digital = digital;
    repaint();
  }
  public boolean getDigital()
  {
    return m_digital;
  }
  public void addPropertyChangeListener(
    PropertyChangeListener lst)
  {
    if (m_helper != null)
      m_helper.addPropertyChangeListener(lst);
  }
  public void removePropertyChangeListener(
    PropertyChangeListener lst)
  {
    if (m_helper != null)
      m_helper.removePropertyChangeListener(lst);
  }
  public void setObject(Object bean) {}
  public void paintComponent(Graphics g)
  {
                super.paintComponent(g);
                Color colorRetainer = g.getColor();
    g.setColor(getBackground());
    g.fillRect(0, 0, getWidth(), getHeight());
    getBorder().paintBorder(this, g, 0, 0, getWidth(), getHeight());
    m_calendar.setTime(new Date());  // get current time
    int hrs = m_calendar.get(Calendar.HOUR_OF_DAY);
    int min = m_calendar.get(Calendar.MINUTE);
    g.setColor(getForeground());
    if (m_digital)
    {
      String time = ""+hrs+":"+min;
      g.setFont(getFont());
      FontMetrics fm = g.getFontMetrics();
      int y = (getHeight() + fm.getAscent())/2;
      int x = (getWidth() - fm.stringWidth(time))/2;
      g.drawString(time, x, y);
    }
    else
    {
      int x = getWidth()/2;
      int y = getHeight()/2;
      int rh = getHeight()/4;
      int rm = getHeight()/3;
      double ah = ((double)hrs+min/60.0)/6.0*Math.PI;
      double am = min/30.0*Math.PI;
      g.drawLine(x, y, (int)(x+rh*Math.sin(ah)), 
        (int)(y-rh*Math.cos(ah)));
      g.drawLine(x, y, (int)(x+rm*Math.sin(am)), 
        (int)(y-rm*Math.cos(am)));
    }
                g.setColor(colorRetainer);
  }
  public void run()
  {
    while (true)
    {
      repaint();
      try
      {
        Thread.sleep(30*1000);
      }
      catch(InterruptedException ex) { break; }
    }
  }
}





Bean has a single property called property.

   
import java.io.Serializable;
public class BasicBean implements Serializable {
  boolean property;
  public BasicBean() {
  }
  public boolean getProperty() {
    return property;
  }
  public boolean isProperty() {
    return property;
  }
  public void setProperty(boolean newValue) {
    property = newValue;
  }
}





Bean Utility

  
/*
 * StringUtil.java
 *
 * Created on May 7, 2004, 7:43 PM
 *
 * Copyright (C) 2004  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.*;
import java.lang.reflect.*;
/** This class contains some generic methods for working with String.
 * @version $Rev: 87 $
 * @author  
 */
public class BeanUtility {
    
    /** Creates a new instance of StringUtil */
    private BeanUtility() { }
    
    /** This method takes a JavaBean and generates a standard toString() type result for it.
     * @param o JavaBean object to stringinate
     * @return STRINGIATION! Stringingating the countryside. Stringinating all the peasants.
     */
    public static String beanToString( Object o ){
        StringBuffer result = new StringBuffer();
        if(o == null)
            return "--- null";
        result.append( "--- begin");result.append( o.getClass().getName()); result.append(" hash: ");
        result.append( o.hashCode() ); result.append( "\r\n");
        try{
            PropertyDescriptor[] pds = Introspector.getBeanInfo( o.getClass() ).getPropertyDescriptors();
            for( int pdi = 0; pdi < pds.length; pdi ++ ){
                try{
                    result.append( "Property: "+ pds[pdi].getName() + " Value: " + pds[pdi].getReadMethod().invoke( o ) );
                } catch( IllegalAccessException iae ){
                    result.append( "Property: "+ pds[pdi].getName() + " (Illegal Access to Value) ");
                }
                catch( InvocationTargetException iae ){
                    result.append( "Property: "+ pds[pdi].getName() + " (InvocationTargetException) " + iae.toString() );
                }
                catch( Exception e ){
                     result.append( "Property: "+ pds[pdi].getName() +" (Other Exception )"+e.toString());
                }
                result.append( "\r\n");
            }
            
            
        } catch( IntrospectionException ie){
            result.append( "Introspection Exception: " + ie.toString() ); result.append( "\r\n");
        }
        result.append( "--- end " ); result.append( o.getClass().getName()); result.append(" hash: ");
        result.append( o.hashCode() ); result.append( "\n");
        return result.toString();
    }
    
    /** This method takes 2 JavaBeans of the same type and copies the properties of one bean to the other.
     * Any attempts that have an IllegalAccessException will be ignored. This will also NOT recurse into nested bean
     * results. References to existing beanage will be includes. Try using .clone() for that stuff.
     * @param from Source Bean
     * @param to Desitnation Bean
     */
    public static void copyBeanToBean( Object from, Object to ) throws InvocationTargetException, IntrospectionException{
        PropertyDescriptor[] pds = Introspector.getBeanInfo( from.getClass() ).getPropertyDescriptors();
        for( int i=0; i < pds.length; i++){
            try{
                if(pds[i].getName().equals("class")) continue;
                Object[] value = {pds[i].getReadMethod().invoke(from) };
                pds[i].getWriteMethod().invoke( to, value ) ;
            } catch( IllegalAccessException iae ){
                //Im just going to ignore any properties I don"t have access too.
            }
        }
        
    }
    
    public static String[] getPropertyNames(Object o) throws IntrospectionException {
        PropertyDescriptor[] pds = Introspector.getBeanInfo( o.getClass() ).getPropertyDescriptors();
        String[] propertyNames = new String[ pds.length];
        for( int i=0; i< pds.length; i++){
            propertyNames[i] = pds[i].getName();
        }
        return propertyNames;
    }
    
    public static Object getProperty( Object o, String propertyName ) throws Exception {
        PropertyDescriptor[] pds = Introspector.getBeanInfo( o.getClass() ).getPropertyDescriptors();
        for( int i=0; i< pds.length; i++){
            if( pds[i].getName().equals(propertyName)){
                return pds[i].getReadMethod().invoke( o ) ;
            }
        }
        throw new Exception("Property not found.");
    }
    public static void setProperty( Object o, String propertyName, Object value ) throws Exception{
        PropertyDescriptor[] pds = Introspector.getBeanInfo( o.getClass() ).getPropertyDescriptors();
        for( int i=0; i< pds.length; i++){
            if( pds[i].getName().equals(propertyName)){
                pds[i].getWriteMethod().invoke( o, value );
                return;
            }
        }
        throw new Exception("Property not found.");
    }
    
    public static Class getPropertyType( Object o, String propertyName ) throws Exception{
        PropertyDescriptor[] pds = Introspector.getBeanInfo( o.getClass() ).getPropertyDescriptors();
        for( int i=0; i< pds.length; i++){
            if( pds[i].getName().equals(propertyName)){
                return pds[i].getPropertyType();
            }
        }
        throw new Exception("Property not found.");
    }
}





Convert a bean to XML persistence

   
import java.beans.XMLEncoder;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
public class Main {
  public static void main(String[] args) throws Exception {
    Item bean = new Item();
    bean.setId(new Long(1));
    bean.setItemName("a");
    bean.setItemColour("Red");
    bean.setItemQuantities(new Integer(100));
    XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(new FileOutputStream("Bean.xml")));
    encoder.writeObject(bean);
    encoder.close();
  }
}
class Item {
  private Long id;
  private String itemName;
  private String itemColour;
  private Integer itemQuantities;
  public Long getId() {
    return id;
  }
  public void setId(Long id) {
    this.id = id;
  }
  public String getItemName() {
    return itemName;
  }
  public void setItemName(String itemName) {
    this.itemName = itemName;
  }
  public String getItemColour() {
    return itemColour;
  }
  public void setItemColour(String itemColour) {
    this.itemColour = itemColour;
  }
  public Integer getItemQuantities() {
    return itemQuantities;
  }
  public void setItemQuantities(Integer itemQuantities) {
    this.itemQuantities = itemQuantities;
  }
}





Convert an XML persistence to bean

   
import java.beans.XMLDecoder;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.Serializable;
public class Main {
  public static void main(String[] argv) throws Exception {
    XMLDecoder decoder = new XMLDecoder(new BufferedInputStream(new FileInputStream("Bean.xml")));
    Bean bean = (Bean) decoder.readObject();
    decoder.close();
    System.out.println("ID = " + bean.getId());
  }
}
class Bean implements Serializable {
  private Long id;
  public Long getId() {
    return id;
  }
  public void setId(Long id) {
    this.id = id;
  }
}
Here is our Bean.xml persistence file:
<?xml version="1.0" encoding="UTF-8"?> 
<java version="1.6.0_02" class="java.beans.XMLDecoder"> 
 <object class="org.username.example.bean.BeanToXML"> 
  <void property="id"> 
   <long>1</long> 
  </void> 
 </object> 
</java>





Create an instance a Bean

   
 
import java.beans.Beans;
import java.io.Serializable;
public class Main implements Serializable {
  private Long id;
  private String name;
  public Main() {
  }
  public static void main(String[] args) throws Exception {
    Main bean = (Main) Beans.instantiate(ClassLoader.getSystemClassLoader(),
        "Main");
    System.out.println("The Bean = " + bean);
  }
  public Long getId() {
    return id;
  }
  public void setId(Long id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  @Override
  public String toString() {
    return "[id=" + id + "; name=" + name + "]";
  }
}





Deserializing a Bean from XML

   
import java.beans.XMLDecoder;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
public class Main {
  public static void main(String[] argv) throws Exception {
    XMLDecoder decoder = new XMLDecoder(new BufferedInputStream(
        new FileInputStream("infilename.xml")));

    MyClass o = (MyClass) decoder.readObject();
    decoder.close();
    int prop = o.getProp(); // 1
    int[] props = o.getProps(); // [1, 2, 3]
  }
}
class MyClass {
  // The prop property
  int i;
  public int getProp() {
    return i;
  }
  public void setProp(int i) {
    this.i = i;
  }
  // The props property
  int[] iarray = new int[0];
  public int[] getProps() {
    return iarray;
  }
  public void setProps(int[] iarray) {
    this.iarray = iarray;
  }
}
/*
    <?xml version="1.0" encoding="UTF-8"?>
    <java version="1.4.0" class="java.beans.XMLDecoder">
        <object class="MyClass">
            <void property="prop">
                <int>1</int>
            </void>
            <void property="props">
                <array class="int" length="3">
                    <void index="0">
                        <int>1</int>
                    </void>
                    <void index="1">
                        <int>2</int>
                    </void>
                    <void index="2">
                        <int>3</int>
                    </void>
                </array>
            </void>
        </object>
    </java>
*/





Determine bean"s property type

   
 
import org.apache.rumons.beanutils.PropertyUtils;
public class Main {
  public static void main(String[] args) throws Exception {
    Recording recording = new Recording();
    recording.setTitle("Magical Mystery Tour");
    Class type = PropertyUtils.getPropertyType(recording, "title");
    System.out.println("type = " + type.getName());
    String value = (String) PropertyUtils.getProperty(recording, "title");
    System.out.println("value = " + value);
  }
}





Displaying the Current Directory in the Title of a JFileChooser Dialog

   
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import javax.swing.JFileChooser;
public class Main {
  public static void main(String[] argv) {
    final JFileChooser chooser = new JFileChooser();
    File curDir = chooser.getCurrentDirectory();
    chooser.setDialogTitle("" + curDir.getAbsolutePath());
    chooser.addPropertyChangeListener(new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent evt) {
        if (JFileChooser.DIRECTORY_CHANGED_PROPERTY.equals(evt.getPropertyName())) {
          File curDir = chooser.getCurrentDirectory();
          chooser.setDialogTitle("" + curDir.getAbsolutePath());
        }
      }
    });
  }
}





extends SimpleBeanInfo

  
/**
 * FontSelectorBeanBeanInfo.java  1.00 97/08/09 Merlin Hughes
 *
 * Copyright (c) 1997 Merlin Hughes, All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * for commercial and non-commercial purposes and without fee is
 * hereby granted provided that this copyright notice appears in
 * all copies.
 *
 * http://prominence.ru/                         ego@merlin.org
 */
import java.awt.Image;
import java.beans.SimpleBeanInfo;
public class FontSelectorBeanBeanInfo extends SimpleBeanInfo {
  public Image getIcon(int iconKind) {
    if (iconKind == ICON_COLOR_16x16) {
      return loadImage("FontSelectorBeanIconColor16.gif");
    } else {
      return null;
    }
  }
}





Get a list of selected files

   
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import javax.swing.JFileChooser;
public class Main {
  public static void main(String[] argv) throws Exception {
    JFileChooser chooser = new JFileChooser();
    chooser.addPropertyChangeListener(new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent evt) {
        if (JFileChooser.SELECTED_FILES_CHANGED_PROPERTY.equals(evt.getPropertyName())) {
          JFileChooser chooser = (JFileChooser) evt.getSource();
          File[] oldFiles = (File[]) evt.getOldValue();
          File[] newFiles = (File[]) evt.getNewValue();
          // Get list of selected files
          File[] files = chooser.getSelectedFiles();
        }    
      }
    });
  }
}





Get and set an Object type property

   
import java.beans.Expression;
import java.beans.Statement;
public class Main {
  public static void main(String[] argv) throws Exception {
    Object o = new MyBean();
    // Get the value of prop1
    Expression expr = new Expression(o, "getProp1", new Object[0]);
    expr.execute();
    String s = (String) expr.getValue();
    // Set the value of prop1
    Statement stmt = new Statement(o, "setProp1", new Object[] { "new string" });
    stmt.execute();
  }
}
class MyBean {
  String prop1;
  public String getProp1() {
    return prop1;
  }
  public void setProp1(String s) {
    prop1 = s;
  }
  int prop2;
  public int getProp2() {
    return prop2;
  }
  public void setProp2(int i) {
    prop2 = i;
  }
  byte[] prop3;
  public byte[] getProp3() {
    return prop3;
  }
  public void setProp3(byte[] bytes) {
    prop3 = bytes;
  }
}





Get and set properties on a bean

  
/*
 * The contents of this file are subject to the Sapient Public License
 * Version 1.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://carbon.sf.net/License.html.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * The Original Code is The Carbon Component Framework.
 *
 * The Initial Developer of the Original Code is Sapient Corporation
 *
 * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
 */

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.StringTokenizer;

/**
 * <p>This class implements the ability to get and set properties on a
 * bean.  This included the concept of embedded properties that may
 * be referenced like <code>Bean.property.property.property</code>.</p>
 *
 * Copyright 2002 Sapient
 * @since carbon 1.0
 * @author Greg Hinkle, January 2002
 * @version $Revision: 1.11 $ ($Author: dvoet $)
 */
public final class BeanUtil {
    /**
     * String used to separate multiple properties inside of embedded
     * beans.
     */
    private static final String PROPERTY_SEPARATOR = ".";
    /**
     * An empty class array used for null parameter method reflection
     */
    private static Class[] NO_PARAMETERS_ARRAY = new Class[] {
    };
    /**
     * an empty object array used for null parameter method reflection
     */
    private static Object[] NO_ARGUMENTS_ARRAY = new Object[] {
    };
    /**
     * The constructor is private so that <tt>new</tt> cannot be used.
     */
    private BeanUtil() {
    }
    /**
     * Retreives a property descriptor object for a given property.
     * <p>
     * Uses the classes in <code>java.beans</code> to get back
     * a descriptor for a property.  Read-only and write-only
     * properties will have a slower return time.
     * </p>
     *
     * @param propertyName The programmatic name of the property
     * @param beanClass The Class object for the target bean.
     *                  For example sun.beans.OurButton.class.
     * @return a PropertyDescriptor for a property that follows the
     *         standard Java naming conventions.
     * @throws PropertyNotFoundException indicates that the property
     *         could not be found on the bean class.
     */
    private static final PropertyDescriptor getPropertyDescriptor(
        String propertyName,
        Class beanClass) {
        PropertyDescriptor resultPropertyDescriptor = null;

        char[] pNameArray = propertyName.toCharArray();
        pNameArray[0] = Character.toLowerCase(pNameArray[0]);
        propertyName = new String(pNameArray);
        try {
            resultPropertyDescriptor =
                new PropertyDescriptor(propertyName, beanClass);
        } catch (IntrospectionException e1) {
            // Read-only and write-only properties will throw this
            // exception.  The properties must be looked up using
            // brute force.
            // This will get the list of all properties and iterate
            // through looking for one that matches the property
            // name passed into the method.
            try {
                BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
                PropertyDescriptor[] propertyDescriptors =
                    beanInfo.getPropertyDescriptors();
                for (int i = 0; i < propertyDescriptors.length; i++) {
                    if (propertyDescriptors[i]
                        .getName()
                        .equals(propertyName)) {
                        // If the names match, this this describes the
                        // property being searched for.  Break out of
                        // the iteration.
                        resultPropertyDescriptor = propertyDescriptors[i];
                        break;
                    }
                }
            } catch (IntrospectionException e2) {
                e2.printStackTrace();
            }
        }
        // If no property descriptor was found, then this bean does not
        // have a property matching the name passed in.
        if (resultPropertyDescriptor == null) {
            System.out.println("resultPropertyDescriptor == null");
        }
        return resultPropertyDescriptor;
    }
    /**
     * <p>Gets the specified attribute from the specified object.  For example,
     * <code>getObjectAttribute(o, "address.line1")</code> will return
     * the result of calling <code>o.getAddress().getLine1()</code>.<p>
     *
     * <p>The attribute specified may contain as many levels as you like. If at
     * any time a null reference is acquired by calling one of the successive
     * getter methods, then the return value from this method is also null.</p>
     *
     * <p>When reading from a boolean property the underlying bean introspector
     * first looks for an is&lt;Property&gt; read method, not finding one it will
     * still look for a get&lt;Property&gt; read method.  Not finding either, the
     * property is considered write-only.</p>
     *
     * @param bean the bean to set the property on
     * @param propertyNames the name of the propertie(s) to retrieve.  If this is
     *        null or the empty string, then <code>bean</code> will be returned.
     * @return the object value of the bean attribute
     *
     * @throws PropertyNotFoundException indicates the the given property
     *         could not be found on the bean
     * @throws NoSuchMethodException Not thrown
     * @throws InvocationTargetException if a specified getter method throws an
     *   exception.
     * @throws IllegalAccessException if a getter method is
     *   not public or property is write-only.
     */
    public static Object getObjectAttribute(Object bean, String propertyNames)
        throws
            NoSuchMethodException,
            InvocationTargetException,
            IllegalAccessException {

        Object result = bean;
        StringTokenizer propertyTokenizer =
            new StringTokenizer(propertyNames, PROPERTY_SEPARATOR);
        // Run through the tokens, calling get methods and
        // replacing result with the new object each time.
        // If the result equals null, then simply return null.
        while (propertyTokenizer.hasMoreElements() && result != null) {
            Class resultClass = result.getClass();
            String currentPropertyName = propertyTokenizer.nextToken();
            PropertyDescriptor propertyDescriptor =
                getPropertyDescriptor(currentPropertyName, resultClass);
            Method readMethod = propertyDescriptor.getReadMethod();
            if (readMethod == null) {
                throw new IllegalAccessException(
                    "User is attempting to "
                        + "read from a property that has no read method.  "
                        + " This is likely a write-only bean property.  Caused "
                        + "by property ["
                        + currentPropertyName
                        + "] on class ["
                        + resultClass
                        + "]");
            }
            result = readMethod.invoke(result, NO_ARGUMENTS_ARRAY);
        }
        return result;
    }
    /**
     * <p>Sets the specified attribute on the specified object.  For example,
     * <code>getObjectAttribute(o, "address.line1", value)</code> will call
     * <code>o.getAddress().setLine1(value)</code>.<p>
     *
     * <p>The attribute specified may contain as many levels as you like. If at
     * any time a null reference is acquired by calling one of the successive
     * getter methods, then a <code>NullPointerException</code> is thrown.</p>
     *
     * @param bean the bean to call the getters on
     * @param propertyNames the name of the attribute(s) to set.  If this is
     *        null or the empty string, then an exception is thrown.
     * @param value the value of the object to set on the bean property
     *
     * @throws PropertyNotFoundException indicates the the given property
     *         could not be found on the bean
     * @throws IllegalArgumentException if the supplied parameter is not of
     *   a valid type
     * @throws NoSuchMethodException never
     * @throws IllegalAccessException if a getter or setter method is
     *   not public or property is read-only.
     * @throws InvocationTargetException if a specified getter method throws an
     *   exception.
     */
    public static void setObjectAttribute(
        Object bean,
        String propertyNames,
        Object value)
        throws
            IllegalAccessException,
            IllegalArgumentException,
            InvocationTargetException,
            NoSuchMethodException {

        Object result = bean;
        String propertyName = propertyNames;
        // Check if this has some embedded properties.  If it does, use the
        // getObjectAttribute to get the proper object to call this on.
        int indexOfLastPropertySeparator =
            propertyName.lastIndexOf(PROPERTY_SEPARATOR);
        if (indexOfLastPropertySeparator >= 0) {
            String embeddedProperties =
                propertyName.substring(0, indexOfLastPropertySeparator);
            // Grab the final property name after the last property separator
            propertyName =
                propertyName.substring(
                    indexOfLastPropertySeparator + PROPERTY_SEPARATOR.length());
            result = getObjectAttribute(result, embeddedProperties);
        }
        Class resultClass = result.getClass();
        PropertyDescriptor propertyDescriptor =
            getPropertyDescriptor(propertyName, resultClass);
        Method writeMethod = propertyDescriptor.getWriteMethod();
        if (writeMethod == null) {
            throw new IllegalAccessException(
                "User is attempting to write "
                    + "to a property that has no write method.  This is likely "
                    + "a read-only bean property.  Caused by property ["
                    + propertyName
                    + "] on class ["
                    + resultClass
                    + "]");
        }
        writeMethod.invoke(result, new Object[] { value });
    }
}





Get and set the value of a property in a bean using Expression and Statement

   
import java.beans.Expression;
import java.beans.Statement;
public class Main {
  public static void main(String[] argv) throws Exception {
    Object o = new MyBean();
    // Get the value of prop1
    Expression expr = new Expression(o, "getProp1", new Object[0]);
    expr.execute();
    String s = (String) expr.getValue();
    // Set the value of prop1
    Statement stmt = new Statement(o, "setProp1", new Object[] { "new string" });
    stmt.execute();
  }
}
class MyBean {
  String prop1;
  public String getProp1() {
    return prop1;
  }
  public void setProp1(String s) {
    prop1 = s;
  }
  int prop2;
  public int getProp2() {
    return prop2;
  }
  public void setProp2(int i) {
    prop2 = i;
  }
  byte[] prop3;
  public byte[] getProp3() {
    return prop3;
  }
  public void setProp3(byte[] bytes) {
    prop3 = bytes;
  }
}





gets and sets an array type property

   
import java.beans.Expression;
import java.beans.Statement;
public class Main {
  public static void main(String[] argv) throws Exception {
    Object o = new MyBean();
    Expression expr = new Expression(o, "getProp3", new Object[0]);
    expr.execute();
    byte[] bytes = (byte[]) expr.getValue();
    Statement stmt = new Statement(o, "setProp3", new Object[] { new byte[] {
        0x12, 0x23 } });
    stmt.execute();
  }
}
class MyBean {
  String prop1;
  public String getProp1() {
    return prop1;
  }
  public void setProp1(String s) {
    prop1 = s;
  }
  int prop2;
  public int getProp2() {
    return prop2;
  }
  public void setProp2(int i) {
    prop2 = i;
  }
  byte[] prop3;
  public byte[] getProp3() {
    return prop3;
  }
  public void setProp3(byte[] bytes) {
    prop3 = bytes;
  }
}





gets and sets a primitive type property

   
import java.beans.Expression;
import java.beans.Statement;
public class Main {
  public static void main(String[] argv) throws Exception {
    Object o = new MyBean();
    // Get the value of prop2
    Expression expr = new Expression(o, "getProp2", new Object[0]);
    expr.execute();
    int i = ((Integer) expr.getValue()).intValue();
    // Set the value of prop2
    Statement stmt = new Statement(o, "setProp2", new Object[] { new Integer(123) });
    stmt.execute();
  }
}
class MyBean {
  String prop1;
  public String getProp1() {
    return prop1;
  }
  public void setProp1(String s) {
    prop1 = s;
  }
  int prop2;
  public int getProp2() {
    return prop2;
  }
  public void setProp2(int i) {
    prop2 = i;
  }
  byte[] prop3;
  public byte[] getProp3() {
    return prop3;
  }
  public void setProp3(byte[] bytes) {
    prop3 = bytes;
  }
}





Getting and Setting a Property of a Bean

   
import java.beans.Expression;
import java.beans.Statement;
public class Main {
  public static void main(String[] argv) throws Exception {
    Object o = new MyBean();
    // Get the value of prop1
    Expression expr = new Expression(o, "getProp1", new Object[0]);
    expr.execute();
    String s = (String) expr.getValue();
    // Set the value of prop1
    Statement stmt = new Statement(o, "setProp1", new Object[] { "new string" });
    stmt.execute();
  }
}
class MyBean {
  String prop1;
  public String getProp1() {
    return prop1;
  }
  public void setProp1(String s) {
    prop1 = s;
  }
  int prop2;
  public int getProp2() {
    return prop2;
  }
  public void setProp2(int i) {
    prop2 = i;
  }
  byte[] prop3;
  public byte[] getProp3() {
    return prop3;
  }
  public void setProp3(byte[] bytes) {
    prop3 = bytes;
  }
}





Implementing a Bound Property

   

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public class MyBean {
  PropertyChangeSupport pceListeners = new PropertyChangeSupport(this);
  int myProperty;
  public int getMyProperty() {
    return myProperty;
  }
  public void setMyProperty(int newValue) {
    int oldValue = myProperty;
    myProperty = newValue;
    pceListeners.firePropertyChange("myProperty", new Integer(oldValue),
        new Integer(newValue));
  }
  public synchronized void addPropertyChangeListener(
      PropertyChangeListener listener) {
    pceListeners.addPropertyChangeListener(listener);
  }
  public synchronized void removePropertyChangeListener(
      PropertyChangeListener listener) {
    pceListeners.removePropertyChangeListener(listener);
  }
}





Implementing a Constrained Property: fires a PropertyChangeEvent whenever its value is about to be changed.

   
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
public class MyBean {
  VetoableChangeSupport vceListeners = new VetoableChangeSupport(this);
  int myProperty;
  public int getMyProperty() {
    return myProperty;
  }
  public void setMyProperty(int newValue) throws PropertyVetoException {
    try {
      vceListeners.fireVetoableChange("myProperty", new Integer(myProperty),
          new Integer(newValue));
      myProperty = newValue;
    } catch (PropertyVetoException e) {
      throw e;
    }
  }
  public synchronized void addVetoableChangeListener(
      VetoableChangeListener listener) {
    vceListeners.addVetoableChangeListener(listener);
  }
  public synchronized void removeVetoableChangeListener(
      VetoableChangeListener listener) {
    vceListeners.removeVetoableChangeListener(listener);
  }
}





Instantiating a Bean

   
import java.beans.Beans;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
public class Main {
  public static void main(String[] argv) throws Exception {
    MyBean bean = (MyBean) Beans.instantiate(
        ClassLoader.getSystemClassLoader(), "MyBean");
  }
}
class MyBean {
  VetoableChangeSupport vceListeners = new VetoableChangeSupport(this);
  int myProperty;
  public int getMyProperty() {
    return myProperty;
  }
  public void setMyProperty(int newValue) throws PropertyVetoException {
    try {
      vceListeners.fireVetoableChange("myProperty", new Integer(myProperty),
          new Integer(newValue));
      myProperty = newValue;
    } catch (PropertyVetoException e) {
      throw e;
    }
  }
  public synchronized void addVetoableChangeListener(
      VetoableChangeListener listener) {
    vceListeners.addVetoableChangeListener(listener);
  }
  public synchronized void removeVetoableChangeListener(
      VetoableChangeListener listener) {
    vceListeners.removeVetoableChangeListener(listener);
  }
}





Introspecting a Bean

   
// : c14:BeanDumper.java
// Introspecting a Bean.
// From "Thinking in Java, 3rd ed." (c) Bruce Eckel 2002
// www.BruceEckel.ru. See copyright notice in CopyRight.txt.
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.BeanInfo;
import java.beans.EventSetDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class BeanDumper extends JFrame {
  private JTextField query = new JTextField(20);
  private JTextArea results = new JTextArea();
  public void print(String s) {
    results.append(s + "\n");
  }
  public void dump(Class bean) {
    results.setText("");
    BeanInfo bi = null;
    try {
      bi = Introspector.getBeanInfo(bean, Object.class);
    } catch (IntrospectionException e) {
      print("Couldn"t introspect " + bean.getName());
      return;
    }
    PropertyDescriptor[] properties = bi.getPropertyDescriptors();
    for (int i = 0; i < properties.length; i++) {
      Class p = properties[i].getPropertyType();
      if (p == null)
        continue;
      print("Property type:\n  " + p.getName() + "Property name:\n  "
          + properties[i].getName());
      Method readMethod = properties[i].getReadMethod();
      if (readMethod != null)
        print("Read method:\n  " + readMethod);
      Method writeMethod = properties[i].getWriteMethod();
      if (writeMethod != null)
        print("Write method:\n  " + writeMethod);
      print("====================");
    }
    print("Public methods:");
    MethodDescriptor[] methods = bi.getMethodDescriptors();
    for (int i = 0; i < methods.length; i++)
      print(methods[i].getMethod().toString());
    print("======================");
    print("Event support:");
    EventSetDescriptor[] events = bi.getEventSetDescriptors();
    for (int i = 0; i < events.length; i++) {
      print("Listener type:\n  " + events[i].getListenerType().getName());
      Method[] lm = events[i].getListenerMethods();
      for (int j = 0; j < lm.length; j++)
        print("Listener method:\n  " + lm[j].getName());
      MethodDescriptor[] lmd = events[i].getListenerMethodDescriptors();
      for (int j = 0; j < lmd.length; j++)
        print("Method descriptor:\n  " + lmd[j].getMethod());
      Method addListener = events[i].getAddListenerMethod();
      print("Add Listener Method:\n  " + addListener);
      Method removeListener = events[i].getRemoveListenerMethod();
      print("Remove Listener Method:\n  " + removeListener);
      print("====================");
    }
  }
  class Dumper implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      String name = query.getText();
      Class c = null;
      try {
        c = Class.forName(name);
      } catch (ClassNotFoundException ex) {
        results.setText("Couldn"t find " + name);
        return;
      }
      dump(c);
    }
  }
  public BeanDumper() {
    Container cp = getContentPane();
    JPanel p = new JPanel();
    p.setLayout(new FlowLayout());
    p.add(new JLabel("Qualified bean name:"));
    p.add(query);
    cp.add(BorderLayout.NORTH, p);
    cp.add(new JScrollPane(results));
    Dumper dmpr = new Dumper();
    query.addActionListener(dmpr);
    query.setText("frogbean.Frog");
    // Force evaluation
    dmpr.actionPerformed(new ActionEvent(dmpr, 0, ""));
  }
  public static void main(String[] args) {
    run(new BeanDumper(), 600, 500);
  }
  public static void run(JFrame frame, int width, int height) {
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(width, height);
    frame.setVisible(true);
  }
} ///:~





Is JavaBean Compliant Setter

 

import java.lang.reflect.Method;
public class Utils {
  public static boolean isJavaBeanCompliantSetter (Method method)
  {
      if (method == null)
          return false;
      
      if (method.getReturnType() != Void.TYPE)
          return false;
      
      if (!method.getName().startsWith("set"))
          return false;
      
      if (method.getParameterTypes().length != 1)
          return false;
      
      return true;
  }
}





JavaBean: BeanContextSupport

   
/*
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 * -Redistribution in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */
import java.beans.beancontext.BeanContextChildSupport;
import java.beans.beancontext.BeanContextSupport;
/**
 * A test program that adds a bean to a beancontext, and reports on various
 * aspects of the context"s membership state. This program also shows that a
 * bean"s getBeanContext() method can be called to get a reference to its
 * enclosing context.
 */
public class Example1 {
  private static BeanContextSupport context = new BeanContextSupport(); // The
                                      // BeanContext
  private static BeanContextChildSupport bean = new BeanContextChildSupport(); // The
                                         // JavaBean
  public static void main(String[] args) {
    report();
    // Add the bean to the context
    System.out.println("Adding bean to context...");
    context.add(bean);
    report();
  }
  private static void report() {
    // Print out a report of the context"s membership state.
    System.out.println("=============================================");
    // Is the context empty?
    System.out.println("Is the context empty? " + context.isEmpty());
    // Has the context been set for the child bean?
    boolean result = (bean.getBeanContext() != null);
    System.out.println("Does the bean have a context yet? " + result);
    // Number of children in the context
    System.out.println("Number of children in the context: "
        + context.size());
    // Is the specific bean a member of the context?
    System.out.println("Is the bean a member of the context? "
        + context.contains(bean));
    // Equality test
    if (bean.getBeanContext() != null) {
      boolean isEqual = (bean.getBeanContext() == context); // true means
                                  // both
                                  // references
                                  // point to
                                  // the same
                                  // object
      System.out.println("Contexts are the same? " + isEqual);
    }
    System.out.println("=============================================");
  }
}





JavaBean: creates all of the objects, a tests the service capabilities

   
/*
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 * -Redistribution in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */
import java.beans.beancontext.BeanContextChildSupport;
import java.beans.beancontext.BeanContextServiceAvailableEvent;
import java.beans.beancontext.BeanContextServiceProvider;
import java.beans.beancontext.BeanContextServiceRevokedEvent;
import java.beans.beancontext.BeanContextServices;
import java.beans.beancontext.BeanContextServicesSupport;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.Iterator;
import java.util.StringTokenizer;
/**
 * A test program that creates all of the objects, a tests the service
 * capabilities. Run this program from the command line using java
 * DocumentTester
 */
public class DocumentTester {
  public static void main(String[] args) {
    BeanContextServicesSupport context = new BeanContextServicesSupport(); // a
    // bean
    // context
    DocumentBean doc1 = new DocumentBean("Test.txt");
    context.add(doc1);
    context.addBeanContextServicesListener(doc1); // listen for new services
    WordCountServiceProvider provider = new WordCountServiceProvider();
    context.addService(WordCount.class, provider); // add the service to the
    // context
  }
}
/**
 * A JavaBean that encapsulates a text file. When added to a bean context, this
 * bean listens for a WordCount service to become available. When the service
 * does become available, the DocumentBean requests an instance of the service.
 * The service then counts the number of words in the file, and prints a report
 * to standard output.
 */
final class DocumentBean extends BeanContextChildSupport {
  private File document;
  private BeanContextServices context;
  /**
   * Creates a new DocumentBean given the name of the file to read from.
   * 
   * @param fileName
   *            the name of the file to read from
   */
  public DocumentBean(String fileName) {
    document = new File(fileName);
  }
  /**
   * Called when this bean detects that a new service has been registered with
   * its context.
   * 
   * @param bcsae
   *            the BeanContextServiceAvailableEvent
   */
  public void serviceAvailable(BeanContextServiceAvailableEvent bcsae) {
    System.out.println("[Detected a service being added to the context]");
    // Get a reference to the context
    BeanContextServices context = bcsae.getSourceAsBeanContextServices();
    System.out.println("Is the context offering a WordCount service? "
        + context.hasService(WordCount.class));
    // Use the service, if it"s available
    if (context.hasService(WordCount.class)) {
      System.out.println("Attempting to use the service...");
      try {
        WordCount service = (WordCount) context.getService(this, this,
            WordCount.class, document, this);
        System.out.println("Got the service!");
        service.countWords();
      } catch (Exception e) {
      }
    }
  }
  /**
   * Called when this bean detects that a service has been revoked from the
   * context.
   * 
   * @param bcsre
   *            the BeanContextServiceRevokedEvent
   */
  public void serviceRevoked(BeanContextServiceRevokedEvent bcsre) {
    System.out
        .println("[Detected a service being revoked from the context]");
  }
}
/**
 * The WordCount service. Implementations of the countWords() method are
 * provided by the WordCountServiceProvider class.
 */
interface WordCount {
  /**
   * Counts the number of words in the file.
   */
  public abstract void countWords();
}
/**
 * This class is the factory that delivers a word counting service. The 3
 * methods defined in this class are the concrete implementations of the
 * BeanContextServiceProvider interface. For this demonstration, the primary
 * method of interest is getService(). The getService() methods returns a new
 * WordCount instance. It is called by the bean context when a nested JavaBean
 * requests the service.
 */
final class WordCountServiceProvider implements BeanContextServiceProvider {
  public Object getService(BeanContextServices bcs, Object requestor,
      Class serviceClass, Object serviceSelector) {
    // For this demo, we know that the cast from serviceSelector
    // to File will always work.
    final File document = (File) serviceSelector;
    /*
     * Return an instance of the service. The service itself is the
     * WordCount interface, which is implemented here using an anonymous
     * inner class.
     */
    return new WordCount() {
      public void countWords() {
        try {
          // Create a Reader to the DocumentBean"s File
          BufferedReader br = new BufferedReader(new FileReader(
              document));
          String line = null;
          int wordCount = 0;
          while ((line = br.readLine()) != null) {
            StringTokenizer st = new StringTokenizer(line);
            while (st.hasMoreTokens()) {
              System.out.println("Word " + (++wordCount)
                  + " is: " + st.nextToken());
            }
          }
          System.out
              .println("Total number of words in the document: "
                  + wordCount);
          System.out
              .println("[WordCount service brought to you by WordCountServiceProvider]");
          br.close();
        } catch (Exception e) {
        }
      }
    };
  }
  public void releaseService(BeanContextServices bcs, Object requestor,
      Object service) {
    // do nothing
  }
  public Iterator getCurrentServiceSelectors(BeanContextServices bcs,
      Class serviceClass) {
    return null; // do nothing
  }
}
//file: Test.txt
/*
This   text will  be analyzed  
 
 by the WordCount 
service.

*/





JavaBean: how to use the instantiateChild() convenience method to create a bean

   
/*
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 * -Redistribution in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */
import java.beans.beancontext.BeanContextChildSupport;
import java.beans.beancontext.BeanContextSupport;
import java.io.IOException;
/**
 * An example of how to use the instantiateChild() convenience method to create
 * a bean automatically nested into a bean context.
 */
public class Example3 {
  public static void main(String[] args) {
    BeanContextSupport context = new BeanContextSupport();
    System.out.println("Number of children nested into the context: "
        + context.size());
    BeanContextChildSupport child = null;
    try {
      child = (BeanContextChildSupport) context
          .instantiateChild("java.beans.beancontext.BeanContextChildSupport");
    } catch (IOException e) {
      System.out.println("IOException occurred: " + e.getMessage());
    } catch (ClassNotFoundException e) {
      System.out.println("Class not found: " + e.getMessage());
    }
    System.out.println("Number of children nested into the context: "
        + context.size());
  }
}





JavaBean: illustrate delivery of the BeanContextMembershipEvent

   
/*
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 * -Redistribution in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */

import java.beans.beancontext.BeanContextChildSupport;
import java.beans.beancontext.BeanContextMembershipEvent;
import java.beans.beancontext.BeanContextMembershipListener;
import java.beans.beancontext.BeanContextSupport;
/**
 * A simple test program to illustrate delivery of the
 * BeanContextMembershipEvent.
 */
public class MembershipTest {
  public static void main(String[] args) {
    BeanContextSupport context = new BeanContextSupport(); // the context
    MyMembershipListener listener = new MyMembershipListener();
    BeanContextChildSupport bean = new BeanContextChildSupport(); // a
                                    // JavaBean
    context.addBeanContextMembershipListener(listener); // now listening!
    context.add(bean);
    context.remove(bean);
  }
}
/**
 * A custom implementation of the BeanContextMembershipListener interface.
 */
class MyMembershipListener implements BeanContextMembershipListener {
  public void childrenAdded(BeanContextMembershipEvent bcme) {
    System.out.println("Another bean has been added to the context.");
  }
  public void childrenRemoved(BeanContextMembershipEvent bcme) {
    System.out.println("A bean has been removed from the context.");
  }
}





JavaBean: Test program that adds 100 beans to a context

   
/*
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 * -Redistribution in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */

import java.beans.beancontext.BeanContextChildSupport;
import java.beans.beancontext.BeanContextSupport;
/**
 * Test program that adds 100 beans to a context, and calls size() to report the
 * number of beans currently nested. Finally, this test calls toArray() to get
 * references to all child beans.
 */
public class Example2 {
  public static void main(String[] args) {
    // A BeanContext
    BeanContextSupport context = new BeanContextSupport();
    // Many JavaBeans
    BeanContextChildSupport[] beans = new BeanContextChildSupport[100];
    System.out.println("Number of children in the context: "
        + context.size());
    // Create the beans and add them to the context
    for (int i = 0; i < beans.length; i++) {
      beans[i] = new BeanContextSupport();
      context.add(beans[i]);
    }
    System.out.println("Number of children in the context: "
        + context.size());
    // Context now has 100 beans in it, get references to them all
    Object[] children = context.toArray();
    System.out.println("Number of objects retrieved from the context: "
        + children.length);
  }
}





Listen for a constrained property change

   
 
import java.beans.PropertyChangeEvent;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
 
public class Main {
    private double interest;
 
    private VetoableChangeSupport vcs = new VetoableChangeSupport(this);
 
    public Main() {
        vcs.addVetoableChangeListener(new VetoChangeListener());
    }
 
    public double getInterest() {
        return interest;
    }
 
    public void setInterest(double interest) {
        try {
            vcs.fireVetoableChange("interest", new Double(this.interest), new Double(interest));
             this.interest = interest;
        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) {
        Main bean = new Main();
        bean.setInterest(10.99);
        bean.setInterest(15.99);
        
        bean.setInterest(20.99);
    }
}
 
class VetoChangeListener implements VetoableChangeListener {
    public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
        String eventName = evt.getPropertyName();
        if (eventName.equalsIgnoreCase("interest")) {
            double interest = ((Double) evt.getNewValue()).doubleValue();
            if (interest > 20.00) {
                throw new PropertyVetoException("Interest must be below 20.00", evt);                
            }
            System.out.println("Interest applied = " + interest);
        }
    }
}





Listen for bean"s property change event

   
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
public class Main {
  public static void main(String[] argv) throws Exception {
    MyBean bean = new MyBean();
    bean.setName("A");
    bean.setName("B");
    bean.setName("C");
  }
}
class MyBean implements PropertyChangeListener, Serializable {
  private Long id;
  private String name;
  private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
  public MyBean() {
    pcs.addPropertyChangeListener(this);
  }
  public void propertyChange(PropertyChangeEvent evt) {
    System.out.println("Name      = " + evt.getPropertyName());
    System.out.println("Old Value = " + evt.getOldValue());
    System.out.println("New Value = " + evt.getNewValue());
  }
  public Long getId() {
    return id;
  }
  public void setId(Long id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    String oldValue = this.name;
    this.name = name;
    pcs.firePropertyChange("name", oldValue, name);
  }
}





Listening for a Property Change Event: A property change event is fired when a bound property is changed.

   
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public class Main {
  public static void main(String[] argv) throws Exception {
    MyBean bean = new MyBean();
    bean.addPropertyChangeListener(new MyPropertyChangeListener());
  }
}
class MyPropertyChangeListener implements PropertyChangeListener {
  public void propertyChange(PropertyChangeEvent evt) {
    Object oldValue = evt.getOldValue();
    Object newValue = evt.getNewValue();
  }
}
class MyBean {
  PropertyChangeSupport pceListeners = new PropertyChangeSupport(this);
  int myProperty;
  public int getMyProperty() {
    return myProperty;
  }
  public void setMyProperty(int newValue) {
    int oldValue = myProperty;
    myProperty = newValue;
    pceListeners.firePropertyChange("myProperty", new Integer(oldValue),
        new Integer(newValue));
  }
  public synchronized void addPropertyChangeListener(
      PropertyChangeListener listener) {
    pceListeners.addPropertyChangeListener(listener);
  }
  public synchronized void removePropertyChangeListener(
      PropertyChangeListener listener) {
    pceListeners.removePropertyChangeListener(listener);
  }
}





Listening for a Vetoable Property Change Event

   
// A vetoable property change event is fired when a constrained property is changed. 
// A listener can veto the change by throwing PropertyVetoException and preventing the change.
import java.beans.PropertyChangeEvent;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
public class Main {
  public static void main(String[] argv) throws Exception {
    MyBean bean = new MyBean();
    bean.addVetoableChangeListener(new MyVetoableChangeListener());
  }
}
class MyVetoableChangeListener implements VetoableChangeListener {
  public void vetoableChange(PropertyChangeEvent evt)
      throws PropertyVetoException {
    Object oldValue = evt.getOldValue();
    Object newValue = evt.getNewValue();
    boolean veto = false;
    if (veto) {
      throw new PropertyVetoException("the reason for the veto", evt);
    }
  }
}
class MyBean {
  VetoableChangeSupport vceListeners = new VetoableChangeSupport(this);
  int myProperty;
  public int getMyProperty() {
    return myProperty;
  }
  public void setMyProperty(int newValue) throws PropertyVetoException {
    try {
      vceListeners.fireVetoableChange("myProperty", new Integer(myProperty),
          new Integer(newValue));
      myProperty = newValue;
    } catch (PropertyVetoException e) {
      throw e;
    }
  }
  public synchronized void addVetoableChangeListener(
      VetoableChangeListener listener) {
    vceListeners.addVetoableChangeListener(listener);
  }
  public synchronized void removeVetoableChangeListener(
      VetoableChangeListener listener) {
    vceListeners.removeVetoableChangeListener(listener);
  }
}





Listening for Changes to the Current Directory in a JFileChooser Dialog

   
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import javax.swing.JFileChooser;
public class Main {
  public static void main(String[] argv) throws Exception {
    final JFileChooser chooser = new JFileChooser();
    chooser.addPropertyChangeListener(new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent evt) {
        if (JFileChooser.DIRECTORY_CHANGED_PROPERTY.equals(evt.getPropertyName())) {
          JFileChooser chooser = (JFileChooser) evt.getSource();
          File oldDir = (File) evt.getOldValue();
          File newDir = (File) evt.getNewValue();
          File curDir = chooser.getCurrentDirectory();
        }
      }
    });
  }
}





Listening for Changes to the Selected File in a JFileChooser Dialog

   
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import javax.swing.JFileChooser;
public class Main {
  public static void main(String[] argv) throws Exception {
    JFileChooser chooser = new JFileChooser();
    chooser.addPropertyChangeListener(new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent evt) {
        if (JFileChooser.SELECTED_FILE_CHANGED_PROPERTY.equals(evt.getPropertyName())) {
          JFileChooser chooser = (JFileChooser) evt.getSource();
          File oldFile = (File) evt.getOldValue();
          File newFile = (File) evt.getNewValue();
          
          File curFile = chooser.getSelectedFile();
        }       
      }
    });
  }
}





Listing the Property Names of a Bean

   
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
public class Main {
  public static void main(String[] argv) throws Exception {
    BeanInfo bi = Introspector.getBeanInfo(MyBean.class);
    PropertyDescriptor[] pds = bi.getPropertyDescriptors();
    for (int i = 0; i < pds.length; i++) {
      String propName = pds[i].getDisplayName();
      System.out.println(propName);
    }
  }
}
class MyBean {
  public String getProp1() {
    return null;
  }
  public void setProp1(String s) {
  }
  public int getProp2() {
    return 0;
  }
  public void setProp2(int i) {
  }
  public byte[] getPROP3() {
    return null;
  }
  public void setPROP3(byte[] bytes) {
  }
}





List property names of a Bean

   
 
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
public class Main {
  public static void main(String[] argv) throws Exception {
    BeanInfo beanInfo = Introspector.getBeanInfo(Fruit.class);
    PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
    for (PropertyDescriptor pd : pds) {
      String propertyName = pd.getName();
      System.out.println("propertyName = " + propertyName);
    }
  }
}
class Fruit implements Serializable {
  private Long id;
  private String name;
  private double price;
  public Fruit() {
  }
  public Long getId() {
    return id;
  }
  public void setId(Long id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public double getPrice() {
    return price;
  }
  public void setPrice(double price) {
    this.price = price;
  }
}





Prevent bean"s property being serialized to XML

   
 
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.beans.XMLEncoder;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
public class Main {
  public static void main(String[] argv) throws Exception {
    BeanInfo bi = Introspector.getBeanInfo(BeanToXmlTransient.class);
    PropertyDescriptor[] pds = bi.getPropertyDescriptors();
    for (int i = 0; i < pds.length; i++) {
      PropertyDescriptor propertyDescriptor = pds[i];
      if (propertyDescriptor.getName().equals("itemQuantities")) {
        propertyDescriptor.setValue("transient", Boolean.TRUE);
      }
    }
    BeanToXmlTransient bean = new BeanToXmlTransient();
    bean.setId(new Long(1));
    bean.setItemName("Item");
    bean.setItemColour("Red");
    bean.setItemQuantities(new Integer(100));
    XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(new FileOutputStream(
        "BeanTransient.xml")));
    encoder.writeObject(bean);
    encoder.close();
  }
}
class BeanToXmlTransient {
  private Long id;
  private String itemName;
  private String itemColour;
  private Integer itemQuantities;
  public Long getId() {
    return id;
  }
  public void setId(Long id) {
    this.id = id;
  }
  public String getItemName() {
    return itemName;
  }
  public void setItemName(String itemName) {
    this.itemName = itemName;
  }
  public String getItemColour() {
    return itemColour;
  }
  public void setItemColour(String itemColour) {
    this.itemColour = itemColour;
  }
  public Integer getItemQuantities() {
    return itemQuantities;
  }
  public void setItemQuantities(Integer itemQuantities) {
    this.itemQuantities = itemQuantities;
  }
}





Preventing a Bean Property from Being Serialized to XML

   

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.beans.XMLEncoder;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
public class Main {
  public static void main(String[] argv) throws Exception {
    MyClass o = new MyClass();
    o.setProp(1);
    o.setProps(new int[] { 1, 2, 3 });
    XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(
        new FileOutputStream("outfilename.xml")));
    encoder.writeObject(o);
    encoder.close();
  }
}
class MyClass {
  int i;
  public int getProp() {
    return i;
  }
  public void setProp(int i) {
    this.i = i;
  }
  int[] iarray = new int[0];
  public int[] getProps() {
    return iarray;
  }
  public void setProps(int[] iarray) {
    this.iarray = iarray;
  }
  static {
    try {
      BeanInfo info = Introspector.getBeanInfo(MyClass.class);
      PropertyDescriptor[] propertyDescriptors = info.getPropertyDescriptors();
      for (int i = 0; i < propertyDescriptors.length; ++i) {
        PropertyDescriptor pd = propertyDescriptors[i];
        if (pd.getName().equals("props")) {
          pd.setValue("transient", Boolean.TRUE);
        }
      }
    } catch (IntrospectionException e) {
    }
  }
}





PropertyTable

   
/*
 * Copyright (c) 2000 David Flanagan.  All rights reserved.
 * This code is from the book Java Examples in a Nutshell, 2nd Edition.
 * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
 * You may study, use, and modify it for any non-commercial purpose.
 * You may distribute it non-commercially as long as you retain this notice.
 * For a commercial use license, or to purchase the book (recommended),
 * visit http://www.davidflanagan.ru/javaexamples2.
 */
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.Arrays;
import java.util.ruparator;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
/**
 * This class is a JTable subclass that displays a table of the JavaBeans
 * properties of any specified class.
 */
public class PropertyTable extends JTable {
  /** This main method allows the class to be demonstrated standalone */
  public static void main(String[] args) {
    // Specify the name of the class as a command-line argument
    Class beanClass = null;
    try {
      // Use reflection to get the Class from the classname
      beanClass = Class.forName("javax.swing.JLabel");
    } catch (Exception e) { // Report errors
      System.out.println("Can"t find specified class: " + e.getMessage());
      System.out.println("Usage: java TableDemo <JavaBean class name>");
      System.exit(0);
    }
    // Create a table to display the properties of the specified class
    JTable table = new PropertyTable(beanClass);
    // Then put the table in a scrolling window, put the scrolling
    // window into a frame, and pop it all up on to the screen
    JScrollPane scrollpane = new JScrollPane(table);
    JFrame frame = new JFrame("Properties of JavaBean: ");
    frame.getContentPane().add(scrollpane);
    frame.setSize(500, 400);
    frame.setVisible(true);
  }
  /**
   * This constructor method specifies what data the table will display (the
   * table model) and uses the TableColumnModel to customize the way that the
   * table displays it. The hard work is done by the TableModel implementation
   * below.
   */
  public PropertyTable(Class beanClass) {
    // Set the data model for this table
    try {
      setModel(new JavaBeanPropertyTableModel(beanClass));
    } catch (IntrospectionException e) {
      System.err.println("WARNING: can"t introspect: " + beanClass);
    }
    // Tweak the appearance of the table by manipulating its column model
    TableColumnModel colmodel = getColumnModel();
    // Set column widths
    colmodel.getColumn(0).setPreferredWidth(125);
    colmodel.getColumn(1).setPreferredWidth(200);
    colmodel.getColumn(2).setPreferredWidth(75);
    colmodel.getColumn(3).setPreferredWidth(50);
    // Right justify the text in the first column
    TableColumn namecol = colmodel.getColumn(0);
    DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
    renderer.setHorizontalAlignment(SwingConstants.RIGHT);
    namecol.setCellRenderer(renderer);
  }
  /**
   * This class implements TableModel and represents JavaBeans property data
   * in a way that the JTable component can display. If you"ve got some type
   * of tabular data to display, implement a TableModel class to describe that
   * data, and the JTable component will be able to display it.
   */
  static class JavaBeanPropertyTableModel extends AbstractTableModel {
    PropertyDescriptor[] properties; // The properties to display
    /**
     * The constructor: use the JavaBeans introspector mechanism to get
     * information about all the properties of a bean. Once we"ve got this
     * information, the other methods will interpret it for JTable.
     */
    public JavaBeanPropertyTableModel(Class beanClass)
        throws java.beans.IntrospectionException {
      // Use the introspector class to get "bean info" about the class.
      BeanInfo beaninfo = Introspector.getBeanInfo(beanClass);
      // Get the property descriptors from that BeanInfo class
      properties = beaninfo.getPropertyDescriptors();
      // Now do a case-insensitive sort by property name
      // The anonymous Comparator implementation specifies how to
      // sort PropertyDescriptor objects by name
      Arrays.sort(properties, new Comparator() {
        public int compare(Object p, Object q) {
          PropertyDescriptor a = (PropertyDescriptor) p;
          PropertyDescriptor b = (PropertyDescriptor) q;
          return a.getName().rupareToIgnoreCase(b.getName());
        }
        public boolean equals(Object o) {
          return o == this;
        }
      });
    }
    // These are the names of the columns represented by this TableModel
    static final String[] columnNames = new String[] { "Name", "Type",
        "Access", "Bound" };
    // These are the types of the columns represented by this TableModel
    static final Class[] columnTypes = new Class[] { String.class,
        Class.class, String.class, Boolean.class };
    // These simple methods return basic information about the table
    public int getColumnCount() {
      return columnNames.length;
    }
    public int getRowCount() {
      return properties.length;
    }
    public String getColumnName(int column) {
      return columnNames[column];
    }
    public Class getColumnClass(int column) {
      return columnTypes[column];
    }
    /**
     * This method returns the value that appears at the specified row and
     * column of the table
     */
    public Object getValueAt(int row, int column) {
      PropertyDescriptor prop = properties[row];
      switch (column) {
      case 0:
        return prop.getName();
      case 1:
        return prop.getPropertyType();
      case 2:
        return getAccessType(prop);
      case 3:
        return new Boolean(prop.isBound());
      default:
        return null;
      }
    }
    // A helper method called from getValueAt() above
    String getAccessType(PropertyDescriptor prop) {
      java.lang.reflect.Method reader = prop.getReadMethod();
      java.lang.reflect.Method writer = prop.getWriteMethod();
      if ((reader != null) && (writer != null))
        return "Read/Write";
      else if (reader != null)
        return "Read-Only";
      else if (writer != null)
        return "Write-Only";
      else
        return "No Access"; // should never happen
    }
  }
}





Read bean"s property value

   
import java.lang.reflect.InvocationTargetException;
import org.apache.rumons.beanutils.PropertyUtils;
public class Main {
  public static void main(String[] args) {
    MyClass track = new MyClass();
    track.setTitle("this is a test");
    String title = (String) PropertyUtils.getSimpleProperty(track, "title");
    System.out.println("Title = " + title);
  }
}
class MyClass {
  private Integer id;
  private String title;
  public Integer getId() {
    return id;
  }
  public void setId(Integer id) {
    this.id = id;
  }
  public String getTitle() {
    return title;
  }
  public void setTitle(String title) {
    this.title = title;
  }
}





Saving and restoring the state of a pretend CAD system

   
// : c12:CADState.java
// Saving and restoring the state of a pretend CAD system.
// {Clean: CADState.out}
//package c12;
// From "Thinking in Java, 3rd ed." (c) Bruce Eckel 2002
// www.BruceEckel.ru. See copyright notice in CopyRight.txt.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
abstract class Shape implements Serializable {
  public static final int RED = 1, BLUE = 2, GREEN = 3;
  private int xPos, yPos, dimension;
  private static Random r = new Random();
  private static int counter = 0;
  public abstract void setColor(int newColor);
  public abstract int getColor();
  public Shape(int xVal, int yVal, int dim) {
    xPos = xVal;
    yPos = yVal;
    dimension = dim;
  }
  public String toString() {
    return getClass() + "color[" + getColor() + "] xPos[" + xPos
        + "] yPos[" + yPos + "] dim[" + dimension + "]\n";
  }
  public static Shape randomFactory() {
    int xVal = r.nextInt(100);
    int yVal = r.nextInt(100);
    int dim = r.nextInt(100);
    switch (counter++ % 3) {
    default:
    case 0:
      return new Circle(xVal, yVal, dim);
    case 1:
      return new Square(xVal, yVal, dim);
    case 2:
      return new Line(xVal, yVal, dim);
    }
  }
}
class Circle extends Shape {
  private static int color = RED;
  public Circle(int xVal, int yVal, int dim) {
    super(xVal, yVal, dim);
  }
  public void setColor(int newColor) {
    color = newColor;
  }
  public int getColor() {
    return color;
  }
}
class Square extends Shape {
  private static int color;
  public Square(int xVal, int yVal, int dim) {
    super(xVal, yVal, dim);
    color = RED;
  }
  public void setColor(int newColor) {
    color = newColor;
  }
  public int getColor() {
    return color;
  }
}
class Line extends Shape {
  private static int color = RED;
  public static void serializeStaticState(ObjectOutputStream os)
      throws IOException {
    os.writeInt(color);
  }
  public static void deserializeStaticState(ObjectInputStream os)
      throws IOException {
    color = os.readInt();
  }
  public Line(int xVal, int yVal, int dim) {
    super(xVal, yVal, dim);
  }
  public void setColor(int newColor) {
    color = newColor;
  }
  public int getColor() {
    return color;
  }
}
public class CADState {
  public static void main(String[] args) throws Exception {
    List shapeTypes, shapes;
    if (args.length == 0) {
      shapeTypes = new ArrayList();
      shapes = new ArrayList();
      // Add references to the class objects:
      shapeTypes.add(Circle.class);
      shapeTypes.add(Square.class);
      shapeTypes.add(Line.class);
      // Make some shapes:
      for (int i = 0; i < 10; i++)
        shapes.add(Shape.randomFactory());
      // Set all the static colors to GREEN:
      for (int i = 0; i < 10; i++)
        ((Shape) shapes.get(i)).setColor(Shape.GREEN);
      // Save the state vector:
      ObjectOutputStream out = new ObjectOutputStream(
          new FileOutputStream("CADState.out"));
      out.writeObject(shapeTypes);
      Line.serializeStaticState(out);
      out.writeObject(shapes);
    } else { // There"s a command-line argument
      ObjectInputStream in = new ObjectInputStream(new FileInputStream(
          args[0]));
      // Read in the same order they were written:
      shapeTypes = (List) in.readObject();
      Line.deserializeStaticState(in);
      shapes = (List) in.readObject();
    }
    // Display the shapes:
    System.out.println(shapes);
  }
} ///:~





Serializing a Bean to XML: XMLEncoder only persists the value of public properties.

   
import java.beans.XMLEncoder;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
public class Main {
  public static void main(String[] argv) throws Exception {
    MyClass o = new MyClass();
    o.setProp(1);
    o.setProps(new int[] { 1, 2, 3 });
    XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(
        new FileOutputStream("outfilename.xml")));
    encoder.writeObject(o);
    encoder.close();
  }
}
class MyClass {
  // The prop property
  int i;
  public int getProp() {
    return i;
  }
  public void setProp(int i) {
    this.i = i;
  }
  // The props property
  int[] iarray = new int[0];
  public int[] getProps() {
    return iarray;
  }
  public void setProps(int[] iarray) {
    this.iarray = iarray;
  }
}





Serializing an Immutable Bean Property to XML

   

import java.beans.DefaultPersistenceDelegate;
import java.beans.XMLEncoder;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
public class Main {
  public static void main(String[] argv) throws Exception {
    MyClass o = new MyClass(123);
    XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(
        new FileOutputStream("outfilename.xml")));
    String[] propertyNames = new String[] { "prop" };
    encoder.setPersistenceDelegate(MyClass.class,
        new DefaultPersistenceDelegate(propertyNames));
    encoder.writeObject(o);
    encoder.close();
  }
}
class MyClass {
  int prop;
  public MyClass(int prop) {
    this.prop = prop;
  }
  public int getProp() {
    return prop;
  }
}





Setting an Accessory Component in a JFileChooser Dialog

   
import java.awt.Dimension;
import java.awt.Graphics;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
public class Main {
  public static void main(String[] argv) {
    JFileChooser chooser = new JFileChooser();
    chooser.setAccessory(new MyAccessory(chooser));
    chooser.showOpenDialog(null);
  }
}
class MyAccessory extends JComponent implements PropertyChangeListener {
  public MyAccessory(JFileChooser chooser) {
    chooser.addPropertyChangeListener(this);
    setPreferredSize(new Dimension(50, 50));
  }
  public void propertyChange(PropertyChangeEvent evt) {
    if (JFileChooser.SELECTED_FILE_CHANGED_PROPERTY.equals(evt.getPropertyName())) {
      JFileChooser chooser = (JFileChooser) evt.getSource();
      // Get the new selected file
      File newFile = (File) evt.getNewValue();
      repaint();
    }
  }
  public void paint(Graphics g) {
    // Paint a preview of the selected file
  }
}