001    /* ===========================================================
002     * JFreeChart : a free chart 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/jfreechart/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     * KeyedObject2D.java
029     * ------------------
030     * (C) Copyright 2003-2005, by Object Refinery Limited.
031     *
032     * Original Author:  David Gilbert (for Object Refinery Limited);
033     * Contributor(s):   -;
034     *
035     * $Id: KeyedObjects2D.java,v 1.6.2.1 2005/10/25 21:29:13 mungady Exp $
036     *
037     * Changes
038     * -------
039     * 05-Feb-2003 : Version 1 (DG);
040     * 01-Mar-2004 : Added equals() and clone() methods and implemented 
041     *               Serializable (DG);
042     *
043     */
044    
045    package org.jfree.data;
046    
047    import java.io.Serializable;
048    import java.util.Collections;
049    import java.util.Iterator;
050    import java.util.List;
051    
052    
053    /**
054     * A data structure that stores zero, one or many objects, where each object is
055     * associated with two keys (a 'row' key and a 'column' key).
056     */
057    public class KeyedObjects2D implements Cloneable, Serializable {
058    
059        /** For serialization. */
060        private static final long serialVersionUID = -1015873563138522374L;
061        
062        /** The row keys. */
063        private List rowKeys;
064    
065        /** The column keys. */
066        private List columnKeys;
067    
068        /** The row data. */
069        private List rows;
070    
071        /**
072         * Creates a new instance (initially empty).
073         */
074        public KeyedObjects2D() {
075            this.rowKeys = new java.util.ArrayList();
076            this.columnKeys = new java.util.ArrayList();
077            this.rows = new java.util.ArrayList();
078        }
079    
080        /**
081         * Returns the row count.
082         *
083         * @return The row count.
084         */
085        public int getRowCount() {
086            return this.rowKeys.size();
087        }
088    
089        /**
090         * Returns the column count.
091         *
092         * @return The column count.
093         */
094        public int getColumnCount() {
095            return this.columnKeys.size();
096        }
097    
098        /**
099         * Returns the object for a given row and column.
100         *
101         * @param row  the row index.
102         * @param column  the column index.
103         *
104         * @return The object.
105         */
106        public Object getObject(int row, int column) {
107    
108            Object result = null;
109            KeyedObjects rowData = (KeyedObjects) this.rows.get(row);
110            if (rowData != null) {
111                Comparable columnKey = (Comparable) this.columnKeys.get(column);
112                if (columnKey != null) {
113                    result = rowData.getObject(columnKey);
114                }
115            }
116            return result;
117    
118        }
119    
120        /**
121         * Returns the key for a given row.
122         *
123         * @param row  the row index (zero based).
124         *
125         * @return The row index.
126         */
127        public Comparable getRowKey(int row) {
128            return (Comparable) this.rowKeys.get(row);
129        }
130    
131        /**
132         * Returns the row index for a given key.
133         *
134         * @param key  the key.
135         *
136         * @return The row index.
137         */
138        public int getRowIndex(Comparable key) {
139            return this.rowKeys.indexOf(key);
140        }
141    
142        /**
143         * Returns the row keys.
144         *
145         * @return The row keys (never <code>null</code>).
146         */
147        public List getRowKeys() {
148            return Collections.unmodifiableList(this.rowKeys);
149        }
150    
151        /**
152         * Returns the key for a given column.
153         *
154         * @param column  the column.
155         *
156         * @return The key.
157         */
158        public Comparable getColumnKey(int column) {
159            return (Comparable) this.columnKeys.get(column);
160        }
161    
162        /**
163         * Returns the column index for a given key.
164         *
165         * @param key  the key.
166         *
167         * @return The column index.
168         */
169        public int getColumnIndex(Comparable key) {
170            return this.columnKeys.indexOf(key);
171        }
172    
173        /**
174         * Returns the column keys.
175         *
176         * @return The column keys (never <code>null</code>).
177         */
178        public List getColumnKeys() {
179            return Collections.unmodifiableList(this.columnKeys);
180        }
181    
182        /**
183         * Returns the object for the given row and column keys.
184         *
185         * @param rowKey  the row key.
186         * @param columnKey  the column key.
187         *
188         * @return The object.
189         */
190        public Object getObject(Comparable rowKey, Comparable columnKey) {
191    
192            Object result = null;
193            int row = this.rowKeys.indexOf(rowKey);
194            if (row >= 0) {
195                KeyedObjects rowData = (KeyedObjects) this.rows.get(row);
196                result = rowData.getObject(columnKey);
197            }
198            return result;
199    
200        }
201    
202        /**
203         * Adds an object to the table.  Performs the same function as setObject().
204         *
205         * @param object  the object.
206         * @param rowKey  the row key.
207         * @param columnKey  the column key.
208         */
209        public void addObject(Object object, 
210                              Comparable rowKey, 
211                              Comparable columnKey) {
212            setObject(object, rowKey, columnKey);
213        }
214    
215        /**
216         * Adds or updates an object.
217         *
218         * @param object  the object.
219         * @param rowKey  the row key.
220         * @param columnKey  the column key.
221         */
222        public void setObject(Object object, 
223                              Comparable rowKey, 
224                              Comparable columnKey) {
225    
226            KeyedObjects row;
227            int rowIndex = this.rowKeys.indexOf(rowKey);
228            if (rowIndex >= 0) {
229                row = (KeyedObjects) this.rows.get(rowIndex);
230            }
231            else {
232                this.rowKeys.add(rowKey);
233                row = new KeyedObjects();
234                this.rows.add(row);
235            }
236            row.setObject(columnKey, object);
237            int columnIndex = this.columnKeys.indexOf(columnKey);
238            if (columnIndex < 0) {
239                this.columnKeys.add(columnKey);
240            }
241    
242        }
243    
244        /**
245         * Removes an object.
246         *
247         * @param rowKey  the row key.
248         * @param columnKey  the column key.
249         */
250        public void removeObject(Comparable rowKey, Comparable columnKey) {
251            setObject(null, rowKey, columnKey);
252            // actually, a null value is different to a value that doesn't exist 
253            // at all, need to fix this code.
254        }
255    
256        /**
257         * Removes a row.
258         *
259         * @param rowIndex  the row index.
260         */
261        public void removeRow(int rowIndex) {
262            this.rowKeys.remove(rowIndex);
263            this.rows.remove(rowIndex);
264        }
265    
266        /**
267         * Removes a row.
268         *
269         * @param rowKey  the row key.
270         */
271        public void removeRow(Comparable rowKey) {
272            removeRow(getRowIndex(rowKey));
273        }
274    
275        /**
276         * Removes a column.
277         *
278         * @param columnIndex  the column index.
279         */
280        public void removeColumn(int columnIndex) {
281            Comparable columnKey = getColumnKey(columnIndex);
282            removeColumn(columnKey);
283        }
284    
285        /**
286         * Removes a column.
287         *
288         * @param columnKey  the column key.
289         */
290        public void removeColumn(Comparable columnKey) {
291            Iterator iterator = this.rows.iterator();
292            while (iterator.hasNext()) {
293                KeyedObjects rowData = (KeyedObjects) iterator.next();
294                rowData.removeValue(columnKey);
295            }
296            this.columnKeys.remove(columnKey);
297        }
298    
299        /**
300         * Tests this object for equality with an arbitrary object.
301         *
302         * @param obj  the object to test (<code>null</code> permitted).
303         *
304         * @return A boolean.
305         */
306        public boolean equals(Object obj) {
307    
308            if (obj == null) {
309                return false;
310            }
311            
312            if (obj == this) {
313                return true;
314            }
315    
316            if (!(obj instanceof KeyedObjects2D)) {
317                return false;
318            }
319            
320            KeyedObjects2D ko2D = (KeyedObjects2D) obj;
321            if (!getRowKeys().equals(ko2D.getRowKeys())) {
322                return false;
323            }
324            if (!getColumnKeys().equals(ko2D.getColumnKeys())) {
325                return false;
326            }
327            int rowCount = getRowCount();
328            if (rowCount != ko2D.getRowCount()) {
329                return false;
330            }
331    
332            int colCount = getColumnCount();
333            if (colCount != ko2D.getColumnCount()) {
334                return false;
335            }
336    
337            for (int r = 0; r < rowCount; r++) {
338                for (int c = 0; c < colCount; c++) {
339                    Object v1 = getObject(r, c);
340                    Object v2 = ko2D.getObject(r, c);
341                    if (v1 == null) {
342                        if (v2 != null) {
343                            return false;
344                        }
345                    }
346                    else {
347                        if (!v1.equals(v2)) {
348                            return false;
349                        }
350                    }
351                }
352            }
353            return true;
354        }
355    
356        /**
357         * Returns a hashcode for this object.
358         * 
359         * @return A hashcode.
360         */
361        public int hashCode() {
362            int result;
363            result = this.rowKeys.hashCode();
364            result = 29 * result + this.columnKeys.hashCode();
365            result = 29 * result + this.rows.hashCode();
366            return result;
367        }
368    
369        /**
370         * Returns a clone.
371         * 
372         * @return A clone.
373         * 
374         * @throws CloneNotSupportedException  this class will not throw this 
375         *         exception, but subclasses (if any) might.
376         */
377        public Object clone() throws CloneNotSupportedException {
378            return super.clone();
379        }
380    
381    }