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