001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-present, by David Gilbert 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
025 * Other names may be trademarks of their respective owners.]
026 *
027 * ---------------------------
028 * DefaultCategoryDataset.java
029 * ---------------------------
030 * (C) Copyright 2002-present, by David Gilbert.
031 *
032 * Original Author:  David Gilbert;
033 * Contributor(s):   -;
034 *
035 */
036
037package org.jfree.data.category;
038
039import java.io.Serializable;
040import java.util.List;
041import org.jfree.chart.util.PublicCloneable;
042
043import org.jfree.data.DefaultKeyedValues2D;
044import org.jfree.data.UnknownKeyException;
045import org.jfree.data.general.AbstractDataset;
046import org.jfree.data.general.DatasetChangeEvent;
047
048/**
049 * A default implementation of the {@link CategoryDataset} interface.
050 */
051public class DefaultCategoryDataset extends AbstractDataset
052        implements CategoryDataset, PublicCloneable, Serializable {
053
054    /** For serialization. */
055    private static final long serialVersionUID = -8168173757291644622L;
056
057    /** A storage structure for the data. */
058    private DefaultKeyedValues2D data;
059
060    /**
061     * Creates a new (empty) dataset.
062     */
063    public DefaultCategoryDataset() {
064        this.data = new DefaultKeyedValues2D();
065    }
066
067    /**
068     * Returns the number of rows in the table.
069     *
070     * @return The row count.
071     *
072     * @see #getColumnCount()
073     */
074    @Override
075    public int getRowCount() {
076        return this.data.getRowCount();
077    }
078
079    /**
080     * Returns the number of columns in the table.
081     *
082     * @return The column count.
083     *
084     * @see #getRowCount()
085     */
086    @Override
087    public int getColumnCount() {
088        return this.data.getColumnCount();
089    }
090
091    /**
092     * Returns a value from the table.
093     *
094     * @param row  the row index (zero-based).
095     * @param column  the column index (zero-based).
096     *
097     * @return The value (possibly {@code null}).
098     *
099     * @see #addValue(Number, Comparable, Comparable)
100     * @see #removeValue(Comparable, Comparable)
101     */
102    @Override
103    public Number getValue(int row, int column) {
104        return this.data.getValue(row, column);
105    }
106
107    /**
108     * Returns the key for the specified row.
109     *
110     * @param row  the row index (zero-based).
111     *
112     * @return The row key.
113     *
114     * @see #getRowIndex(Comparable)
115     * @see #getRowKeys()
116     * @see #getColumnKey(int)
117     */
118    @Override
119    public Comparable getRowKey(int row) {
120        return this.data.getRowKey(row);
121    }
122
123    /**
124     * Returns the row index for a given key.
125     *
126     * @param key  the row key ({@code null} not permitted).
127     *
128     * @return The row index.
129     *
130     * @see #getRowKey(int)
131     */
132    @Override
133    public int getRowIndex(Comparable key) {
134        // defer null argument check
135        return this.data.getRowIndex(key);
136    }
137
138    /**
139     * Returns the row keys.
140     *
141     * @return The keys.
142     *
143     * @see #getRowKey(int)
144     */
145    @Override
146    public List getRowKeys() {
147        return this.data.getRowKeys();
148    }
149
150    /**
151     * Returns a column key.
152     *
153     * @param column  the column index (zero-based).
154     *
155     * @return The column key.
156     *
157     * @see #getColumnIndex(Comparable)
158     */
159    @Override
160    public Comparable getColumnKey(int column) {
161        return this.data.getColumnKey(column);
162    }
163
164    /**
165     * Returns the column index for a given key.
166     *
167     * @param key  the column key ({@code null} not permitted).
168     *
169     * @return The column index.
170     *
171     * @see #getColumnKey(int)
172     */
173    @Override
174    public int getColumnIndex(Comparable key) {
175        // defer null argument check
176        return this.data.getColumnIndex(key);
177    }
178
179    /**
180     * Returns the column keys.
181     *
182     * @return The keys.
183     *
184     * @see #getColumnKey(int)
185     */
186    @Override
187    public List getColumnKeys() {
188        return this.data.getColumnKeys();
189    }
190
191    /**
192     * Returns the value for a pair of keys.
193     *
194     * @param rowKey  the row key ({@code null} not permitted).
195     * @param columnKey  the column key ({@code null} not permitted).
196     *
197     * @return The value (possibly {@code null}).
198     *
199     * @throws UnknownKeyException if either key is not defined in the dataset.
200     *
201     * @see #addValue(Number, Comparable, Comparable)
202     */
203    @Override
204    public Number getValue(Comparable rowKey, Comparable columnKey) {
205        return this.data.getValue(rowKey, columnKey);
206    }
207
208    /**
209     * Adds a value to the table.  Performs the same function as setValue().
210     *
211     * @param value  the value.
212     * @param rowKey  the row key.
213     * @param columnKey  the column key.
214     *
215     * @see #getValue(Comparable, Comparable)
216     * @see #removeValue(Comparable, Comparable)
217     */
218    public void addValue(Number value, Comparable rowKey,
219                         Comparable columnKey) {
220        this.data.addValue(value, rowKey, columnKey);
221        fireDatasetChanged();
222    }
223
224    /**
225     * Adds a value to the table.
226     *
227     * @param value  the value.
228     * @param rowKey  the row key.
229     * @param columnKey  the column key.
230     *
231     * @see #getValue(Comparable, Comparable)
232     */
233    public void addValue(double value, Comparable rowKey,
234                         Comparable columnKey) {
235        addValue(Double.valueOf(value), rowKey, columnKey);
236    }
237
238    /**
239     * Adds or updates a value in the table and sends a
240     * {@link DatasetChangeEvent} to all registered listeners.
241     *
242     * @param value  the value ({@code null} permitted).
243     * @param rowKey  the row key ({@code null} not permitted).
244     * @param columnKey  the column key ({@code null} not permitted).
245     *
246     * @see #getValue(Comparable, Comparable)
247     */
248    public void setValue(Number value, Comparable rowKey,
249                         Comparable columnKey) {
250        this.data.setValue(value, rowKey, columnKey);
251        fireDatasetChanged();
252    }
253
254    /**
255     * Adds or updates a value in the table and sends a
256     * {@link DatasetChangeEvent} to all registered listeners.
257     *
258     * @param value  the value.
259     * @param rowKey  the row key ({@code null} not permitted).
260     * @param columnKey  the column key ({@code null} not permitted).
261     *
262     * @see #getValue(Comparable, Comparable)
263     */
264    public void setValue(double value, Comparable rowKey,
265                         Comparable columnKey) {
266        setValue(Double.valueOf(value), rowKey, columnKey);
267    }
268
269    /**
270     * Adds the specified value to an existing value in the dataset (if the
271     * existing value is {@code null}, it is treated as if it were 0.0).
272     *
273     * @param value  the value.
274     * @param rowKey  the row key ({@code null} not permitted).
275     * @param columnKey  the column key ({@code null} not permitted).
276     *
277     * @throws UnknownKeyException if either key is not defined in the dataset.
278     */
279    public void incrementValue(double value,
280                               Comparable rowKey,
281                               Comparable columnKey) {
282        double existing = 0.0;
283        Number n = getValue(rowKey, columnKey);
284        if (n != null) {
285            existing = n.doubleValue();
286        }
287        setValue(existing + value, rowKey, columnKey);
288    }
289
290    /**
291     * Removes a value from the dataset and sends a {@link DatasetChangeEvent}
292     * to all registered listeners.
293     *
294     * @param rowKey  the row key.
295     * @param columnKey  the column key.
296     *
297     * @see #addValue(Number, Comparable, Comparable)
298     */
299    public void removeValue(Comparable rowKey, Comparable columnKey) {
300        this.data.removeValue(rowKey, columnKey);
301        fireDatasetChanged();
302    }
303
304    /**
305     * Removes a row from the dataset and sends a {@link DatasetChangeEvent}
306     * to all registered listeners.
307     *
308     * @param rowIndex  the row index.
309     *
310     * @see #removeColumn(int)
311     */
312    public void removeRow(int rowIndex) {
313        this.data.removeRow(rowIndex);
314        fireDatasetChanged();
315    }
316
317    /**
318     * Removes a row from the dataset and sends a {@link DatasetChangeEvent}
319     * to all registered listeners.
320     *
321     * @param rowKey  the row key.
322     *
323     * @see #removeColumn(Comparable)
324     */
325    public void removeRow(Comparable rowKey) {
326        this.data.removeRow(rowKey);
327        fireDatasetChanged();
328    }
329
330    /**
331     * Removes a column from the dataset and sends a {@link DatasetChangeEvent}
332     * to all registered listeners.
333     *
334     * @param columnIndex  the column index.
335     *
336     * @see #removeRow(int)
337     */
338    public void removeColumn(int columnIndex) {
339        this.data.removeColumn(columnIndex);
340        fireDatasetChanged();
341    }
342
343    /**
344     * Removes a column from the dataset and sends a {@link DatasetChangeEvent}
345     * to all registered listeners.
346     *
347     * @param columnKey  the column key ({@code null} not permitted).
348     *
349     * @see #removeRow(Comparable)
350     *
351     * @throws UnknownKeyException if {@code columnKey} is not defined
352     *         in the dataset.
353     */
354    public void removeColumn(Comparable columnKey) {
355        this.data.removeColumn(columnKey);
356        fireDatasetChanged();
357    }
358
359    /**
360     * Clears all data from the dataset and sends a {@link DatasetChangeEvent}
361     * to all registered listeners.
362     */
363    public void clear() {
364        this.data.clear();
365        fireDatasetChanged();
366    }
367
368    /**
369     * Tests this dataset for equality with an arbitrary object.
370     *
371     * @param obj  the object ({@code null} permitted).
372     *
373     * @return A boolean.
374     */
375    @Override
376    public boolean equals(Object obj) {
377        if (obj == this) {
378            return true;
379        }
380        if (!(obj instanceof CategoryDataset)) {
381            return false;
382        }
383        CategoryDataset that = (CategoryDataset) obj;
384        if (!getRowKeys().equals(that.getRowKeys())) {
385            return false;
386        }
387        if (!getColumnKeys().equals(that.getColumnKeys())) {
388            return false;
389        }
390        int rowCount = getRowCount();
391        int colCount = getColumnCount();
392        for (int r = 0; r < rowCount; r++) {
393            for (int c = 0; c < colCount; c++) {
394                Number v1 = getValue(r, c);
395                Number v2 = that.getValue(r, c);
396                if (v1 == null) {
397                    if (v2 != null) {
398                        return false;
399                    }
400                }
401                else if (!v1.equals(v2)) {
402                    return false;
403                }
404            }
405        }
406        return true;
407    }
408
409    /**
410     * Returns a hash code for the dataset.
411     *
412     * @return A hash code.
413     */
414    @Override
415    public int hashCode() {
416        return this.data.hashCode();
417    }
418
419    /**
420     * Returns a clone of the dataset.
421     *
422     * @return A clone.
423     *
424     * @throws CloneNotSupportedException if there is a problem cloning the
425     *         dataset.
426     */
427    @Override
428    public Object clone() throws CloneNotSupportedException {
429        DefaultCategoryDataset clone = (DefaultCategoryDataset) super.clone();
430        clone.data = (DefaultKeyedValues2D) this.data.clone();
431        return clone;
432    }
433
434}