001    /* ========================================================================
002     * JCommon : a free general purpose class library for the Java(tm) platform
003     * ========================================================================
004     *
005     * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006     * 
007     * Project Info:  http://www.jfree.org/jcommon/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it 
010     * under the terms of the GNU Lesser General Public License as published by 
011     * the Free Software Foundation; either version 2.1 of the License, or 
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but 
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022     * USA.  
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025     * in the United States and other countries.]
026     * 
027     * -----------------------
028     * AbstractObjectList.java
029     * -----------------------
030     * (C)opyright 2003, 2004, by Object Refinery Limited and Contributors.
031     *
032     * Original Author:  David Gilbert (for Object Refinery Limited);
033     * Contributor(s):   Bill Kelemen; 
034     *                   Nicolas Brodu
035     *
036     * $Id: AbstractObjectList.java,v 1.5 2005/10/18 13:24:19 mungady Exp $
037     *
038     * Changes
039     * -------
040     * 13-Aug-2003 : Version 1, based on ObjectList (DG);
041     * 24-Aug-2003 : Fixed size (BK);
042     * 15-Sep-2003 : Fix serialization for subclasses (ShapeList, PaintList) (NB);
043     */
044    
045    package org.jfree.util;
046    
047    import java.io.IOException;
048    import java.io.ObjectInputStream;
049    import java.io.ObjectOutputStream;
050    import java.io.Serializable;
051    import java.util.Arrays;
052    
053    /**
054     * A list of objects that can grow as required.
055     *
056     * @author David Gilbert
057     */
058    public class AbstractObjectList implements Cloneable, Serializable {
059    
060        /** For serialization. */
061        private static final long serialVersionUID = 7789833772597351595L;
062        
063        /** The default initial capacity of the list. */
064        public static final int DEFAULT_INITIAL_CAPACITY = 8;
065    
066        /** Storage for the objects. */
067        private transient Object[] objects;
068    
069        /** The current list size. */
070        private int size = 0;
071    
072        /** The default increment. */
073        private int increment = DEFAULT_INITIAL_CAPACITY;
074    
075        /**
076         * Creates a new list with the default initial capacity.
077         */
078        protected AbstractObjectList() {
079            this(DEFAULT_INITIAL_CAPACITY);
080        }
081    
082        /**
083         * Creates a new list.
084         *
085         * @param initialCapacity  the initial capacity.
086         */
087        protected AbstractObjectList(final int initialCapacity) {
088            this (initialCapacity, initialCapacity);
089        }
090    
091        /**
092         * Creates a new list.
093         * 
094         * @param initialCapacity  the initial capacity.
095         * @param increment  the increment.
096         */
097        protected AbstractObjectList(final int initialCapacity, 
098                                     final int increment) {
099            this.objects = new Object[initialCapacity];
100            this.increment = increment;
101        }
102    
103        /**
104         * Returns the object at the specified index, if there is one, or 
105         * <code>null</code>.
106         *
107         * @param index  the object index.
108         *
109         * @return The object or <code>null</code>.
110         */
111        protected Object get(final int index) {
112            Object result = null;
113            if (index >= 0 && index < this.size) {
114                result = this.objects[index];
115            }
116            return result;
117        }
118    
119        /**
120         * Sets an object reference (overwriting any existing object).
121         *
122         * @param index  the object index.
123         * @param object  the object (<code>null</code> permitted).
124         */
125        protected void set(final int index, final Object object) {
126            if (index < 0) {
127                throw new IllegalArgumentException("Requires index >= 0.");
128            }
129            if (index >= this.objects.length) {
130                final Object[] enlarged = new Object[index + this.increment];
131                System.arraycopy(this.objects, 0, enlarged, 0, this.objects.length);
132                this.objects = enlarged;
133            }
134            this.objects[index] = object;
135            this.size = Math.max(this.size, index + 1);
136        }
137    
138        /**
139         * Clears the list.
140         */
141        public void clear() {
142            Arrays.fill(this.objects, null);
143            this.size = 0;
144        }
145    
146        /**
147         * Returns the size of the list.
148         *
149         * @return The size of the list.
150         */
151        public int size() {
152            return this.size;
153        }
154    
155        /**
156         * Returns the index of the specified object, or -1 if the object is not in
157         * the list.
158         *
159         * @param object  the object.
160         *
161         * @return The index or -1.
162         */
163        protected int indexOf(final Object object) {
164            for (int index = 0; index < this.size; index++) {
165                if (this.objects[index] == object) {
166                    return (index);
167                }
168            }
169            return -1;
170        }
171    
172        /**
173         * Tests this list for equality with another object.
174         *
175         * @param obj  the object to test.
176         * 
177         * @return A boolean.
178         */
179        public boolean equals(final Object obj) {
180    
181            if (obj == null) {
182                return false;
183            }
184    
185            if (obj == this) {
186                return true;
187            }
188    
189            if (!(obj instanceof AbstractObjectList)) {
190                return false;
191            }
192    
193            final AbstractObjectList other = (AbstractObjectList) obj;
194            final int listSize = size();
195            for (int i = 0; i < listSize; i++) {
196               if (!ObjectUtilities.equal(get(i), other.get(i))) {
197                   return false;
198               }
199            }
200            return true;
201        }
202    
203        /**
204         * Returns a hash code value for the object.
205         *
206         * @return the hashcode
207         */
208        public int hashCode() {
209            return super.hashCode();
210        }
211    
212        /**
213         * Clones the list of objects.  The objects in the list are not cloned, so 
214         * this is method makes a 'shallow' copy of the list.
215         *
216         * @return A clone.
217         * 
218         * @throws CloneNotSupportedException if an item in the list does not 
219         *         support cloning.
220         */
221        public Object clone() throws CloneNotSupportedException {
222    
223            final AbstractObjectList clone = (AbstractObjectList) super.clone();
224            if (this.objects != null) {
225                clone.objects = new Object[this.objects.length];
226                System.arraycopy(
227                    this.objects, 0, clone.objects, 0, this.objects.length
228                );
229            }
230            return clone;
231    
232        }
233    
234        /**
235         * Provides serialization support.
236         *
237         * @param stream  the output stream.
238         *
239         * @throws IOException  if there is an I/O error.
240         */
241        private void writeObject(final ObjectOutputStream stream) 
242            throws IOException {
243    
244            stream.defaultWriteObject();
245            final int count = size();
246            stream.writeInt(count);
247            for (int i = 0; i < count; i++) {
248                final Object object = get(i);
249                if (object != null && object instanceof Serializable) {
250                    stream.writeInt(i);
251                    stream.writeObject(object);
252                }
253                else {
254                    stream.writeInt(-1);
255                }
256            }
257    
258        }
259        
260        /**
261         * Provides serialization support.
262         *
263         * @param stream  the input stream.
264         *
265         * @throws IOException  if there is an I/O error.
266         * @throws ClassNotFoundException  if there is a classpath problem.
267         */
268        private void readObject(final ObjectInputStream stream) 
269            throws IOException, ClassNotFoundException {
270    
271            stream.defaultReadObject();
272            this.objects = new Object[this.size];
273            final int count = stream.readInt();
274            for (int i = 0; i < count; i++) {
275                final int index = stream.readInt();
276                if (index != -1) {
277                    set(index, stream.readObject());
278                }
279            }
280            
281        }
282      
283    }