001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2008, 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 * DefaultStatisticalCategoryDataset.java
029 * --------------------------------------
030 * (C) Copyright 2002-2008, by Pascal Collet and Contributors.
031 *
032 * Original Author: Pascal Collet;
033 * Contributor(s): David Gilbert (for Object Refinery Limited);
034 *
035 * Changes
036 * -------
037 * 21-Aug-2002 : Version 1, contributed by Pascal Collet (DG);
038 * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
039 * 05-Feb-2003 : Revised implementation to use KeyedObjects2D (DG);
040 * 28-Aug-2003 : Moved from org.jfree.data --> org.jfree.data.statistics (DG);
041 * 06-Oct-2003 : Removed incorrect Javadoc text (DG);
042 * 18-Nov-2004 : Updated for changes in RangeInfo interface (DG);
043 * 11-Jan-2005 : Removed deprecated code in preparation for the 1.0.0
044 * release (DG);
045 * 01-Feb-2005 : Changed minimumRangeValue and maximumRangeValue from Double
046 * to double (DG);
047 * 05-Feb-2005 : Implemented equals() method (DG);
048 * ------------- JFREECHART 1.0.x ---------------------------------------------
049 * 08-Aug-2006 : Reworked implementation of RangeInfo methods (DG);
050 * 02-Feb-2007 : Removed author tags from all over JFreeChart sources (DG);
051 * 28-Sep-2007 : Fixed cloning bug (DG);
052 * 02-Oct-2007 : Fixed bug updating cached range values (DG);
053 *
054 */
055
056 package org.jfree.data.statistics;
057
058 import java.util.List;
059
060 import org.jfree.data.KeyedObjects2D;
061 import org.jfree.data.Range;
062 import org.jfree.data.RangeInfo;
063 import org.jfree.data.general.AbstractDataset;
064 import org.jfree.data.general.DatasetChangeEvent;
065 import org.jfree.util.PublicCloneable;
066
067 /**
068 * A convenience class that provides a default implementation of the
069 * {@link StatisticalCategoryDataset} interface.
070 */
071 public class DefaultStatisticalCategoryDataset extends AbstractDataset
072 implements StatisticalCategoryDataset, RangeInfo, PublicCloneable {
073
074 /** Storage for the data. */
075 private KeyedObjects2D data;
076
077 /** The minimum range value. */
078 private double minimumRangeValue;
079
080 /** The row index for the minimum range value. */
081 private int minimumRangeValueRow;
082
083 /** The column index for the minimum range value. */
084 private int minimumRangeValueColumn;
085
086 /** The minimum range value including the standard deviation. */
087 private double minimumRangeValueIncStdDev;
088
089 /**
090 * The row index for the minimum range value (including the standard
091 * deviation).
092 */
093 private int minimumRangeValueIncStdDevRow;
094
095 /**
096 * The column index for the minimum range value (including the standard
097 * deviation).
098 */
099 private int minimumRangeValueIncStdDevColumn;
100
101 /** The maximum range value. */
102 private double maximumRangeValue;
103
104 /** The row index for the maximum range value. */
105 private int maximumRangeValueRow;
106
107 /** The column index for the maximum range value. */
108 private int maximumRangeValueColumn;
109
110 /** The maximum range value including the standard deviation. */
111 private double maximumRangeValueIncStdDev;
112
113 /**
114 * The row index for the maximum range value (including the standard
115 * deviation).
116 */
117 private int maximumRangeValueIncStdDevRow;
118
119 /**
120 * The column index for the maximum range value (including the standard
121 * deviation).
122 */
123 private int maximumRangeValueIncStdDevColumn;
124
125 /**
126 * Creates a new dataset.
127 */
128 public DefaultStatisticalCategoryDataset() {
129 this.data = new KeyedObjects2D();
130 this.minimumRangeValue = Double.NaN;
131 this.minimumRangeValueRow = -1;
132 this.minimumRangeValueColumn = -1;
133 this.maximumRangeValue = Double.NaN;
134 this.maximumRangeValueRow = -1;
135 this.maximumRangeValueColumn = -1;
136 this.minimumRangeValueIncStdDev = Double.NaN;
137 this.minimumRangeValueIncStdDevRow = -1;
138 this.minimumRangeValueIncStdDevColumn = -1;
139 this.maximumRangeValueIncStdDev = Double.NaN;
140 this.maximumRangeValueIncStdDevRow = -1;
141 this.maximumRangeValueIncStdDevColumn = -1;
142 }
143
144 /**
145 * Returns the mean value for an item.
146 *
147 * @param row the row index (zero-based).
148 * @param column the column index (zero-based).
149 *
150 * @return The mean value (possibly <code>null</code>).
151 */
152 public Number getMeanValue(int row, int column) {
153 Number result = null;
154 MeanAndStandardDeviation masd = (MeanAndStandardDeviation)
155 this.data.getObject(row, column);
156 if (masd != null) {
157 result = masd.getMean();
158 }
159 return result;
160 }
161
162 /**
163 * Returns the value for an item (for this dataset, the mean value is
164 * returned).
165 *
166 * @param row the row index.
167 * @param column the column index.
168 *
169 * @return The value (possibly <code>null</code>).
170 */
171 public Number getValue(int row, int column) {
172 return getMeanValue(row, column);
173 }
174
175 /**
176 * Returns the value for an item (for this dataset, the mean value is
177 * returned).
178 *
179 * @param rowKey the row key.
180 * @param columnKey the columnKey.
181 *
182 * @return The value (possibly <code>null</code>).
183 */
184 public Number getValue(Comparable rowKey, Comparable columnKey) {
185 return getMeanValue(rowKey, columnKey);
186 }
187
188 /**
189 * Returns the mean value for an item.
190 *
191 * @param rowKey the row key.
192 * @param columnKey the columnKey.
193 *
194 * @return The mean value (possibly <code>null</code>).
195 */
196 public Number getMeanValue(Comparable rowKey, Comparable columnKey) {
197 Number result = null;
198 MeanAndStandardDeviation masd = (MeanAndStandardDeviation)
199 this.data.getObject(rowKey, columnKey);
200 if (masd != null) {
201 result = masd.getMean();
202 }
203 return result;
204 }
205
206 /**
207 * Returns the standard deviation value for an item.
208 *
209 * @param row the row index (zero-based).
210 * @param column the column index (zero-based).
211 *
212 * @return The standard deviation (possibly <code>null</code>).
213 */
214 public Number getStdDevValue(int row, int column) {
215 Number result = null;
216 MeanAndStandardDeviation masd = (MeanAndStandardDeviation)
217 this.data.getObject(row, column);
218 if (masd != null) {
219 result = masd.getStandardDeviation();
220 }
221 return result;
222 }
223
224 /**
225 * Returns the standard deviation value for an item.
226 *
227 * @param rowKey the row key.
228 * @param columnKey the columnKey.
229 *
230 * @return The standard deviation (possibly <code>null</code>).
231 */
232 public Number getStdDevValue(Comparable rowKey, Comparable columnKey) {
233 Number result = null;
234 MeanAndStandardDeviation masd = (MeanAndStandardDeviation)
235 this.data.getObject(rowKey, columnKey);
236 if (masd != null) {
237 result = masd.getStandardDeviation();
238 }
239 return result;
240 }
241
242 /**
243 * Returns the column index for a given key.
244 *
245 * @param key the column key (<code>null</code> not permitted).
246 *
247 * @return The column index.
248 */
249 public int getColumnIndex(Comparable key) {
250 // defer null argument check
251 return this.data.getColumnIndex(key);
252 }
253
254 /**
255 * Returns a column key.
256 *
257 * @param column the column index (zero-based).
258 *
259 * @return The column key.
260 */
261 public Comparable getColumnKey(int column) {
262 return this.data.getColumnKey(column);
263 }
264
265 /**
266 * Returns the column keys.
267 *
268 * @return The keys.
269 */
270 public List getColumnKeys() {
271 return this.data.getColumnKeys();
272 }
273
274 /**
275 * Returns the row index for a given key.
276 *
277 * @param key the row key (<code>null</code> not permitted).
278 *
279 * @return The row index.
280 */
281 public int getRowIndex(Comparable key) {
282 // defer null argument check
283 return this.data.getRowIndex(key);
284 }
285
286 /**
287 * Returns a row key.
288 *
289 * @param row the row index (zero-based).
290 *
291 * @return The row key.
292 */
293 public Comparable getRowKey(int row) {
294 return this.data.getRowKey(row);
295 }
296
297 /**
298 * Returns the row keys.
299 *
300 * @return The keys.
301 */
302 public List getRowKeys() {
303 return this.data.getRowKeys();
304 }
305
306 /**
307 * Returns the number of rows in the table.
308 *
309 * @return The row count.
310 *
311 * @see #getColumnCount()
312 */
313 public int getRowCount() {
314 return this.data.getRowCount();
315 }
316
317 /**
318 * Returns the number of columns in the table.
319 *
320 * @return The column count.
321 *
322 * @see #getRowCount()
323 */
324 public int getColumnCount() {
325 return this.data.getColumnCount();
326 }
327
328 /**
329 * Adds a mean and standard deviation to the table.
330 *
331 * @param mean the mean.
332 * @param standardDeviation the standard deviation.
333 * @param rowKey the row key.
334 * @param columnKey the column key.
335 */
336 public void add(double mean, double standardDeviation,
337 Comparable rowKey, Comparable columnKey) {
338 add(new Double(mean), new Double(standardDeviation), rowKey, columnKey);
339 }
340
341 /**
342 * Adds a mean and standard deviation to the table.
343 *
344 * @param mean the mean.
345 * @param standardDeviation the standard deviation.
346 * @param rowKey the row key.
347 * @param columnKey the column key.
348 */
349 public void add(Number mean, Number standardDeviation,
350 Comparable rowKey, Comparable columnKey) {
351 MeanAndStandardDeviation item = new MeanAndStandardDeviation(
352 mean, standardDeviation);
353 this.data.addObject(item, rowKey, columnKey);
354
355 double m = Double.NaN;
356 double sd = Double.NaN;
357 if (mean != null) {
358 m = mean.doubleValue();
359 }
360 if (standardDeviation != null) {
361 sd = standardDeviation.doubleValue();
362 }
363
364 // update cached range values
365 int r = this.data.getColumnIndex(columnKey);
366 int c = this.data.getRowIndex(rowKey);
367 if ((r == this.maximumRangeValueRow && c
368 == this.maximumRangeValueColumn) || (r
369 == this.maximumRangeValueIncStdDevRow && c
370 == this.maximumRangeValueIncStdDevColumn) || (r
371 == this.minimumRangeValueRow && c
372 == this.minimumRangeValueColumn) || (r
373 == this.minimumRangeValueIncStdDevRow && c
374 == this.minimumRangeValueIncStdDevColumn)) {
375
376 // iterate over all data items and update mins and maxes
377 updateBounds();
378 }
379 else {
380 if (!Double.isNaN(m)) {
381 if (Double.isNaN(this.maximumRangeValue)
382 || m > this.maximumRangeValue) {
383 this.maximumRangeValue = m;
384 this.maximumRangeValueRow = r;
385 this.maximumRangeValueColumn = c;
386 }
387 }
388
389 if (!Double.isNaN(m + sd)) {
390 if (Double.isNaN(this.maximumRangeValueIncStdDev)
391 || (m + sd) > this.maximumRangeValueIncStdDev) {
392 this.maximumRangeValueIncStdDev = m + sd;
393 this.maximumRangeValueIncStdDevRow = r;
394 this.maximumRangeValueIncStdDevColumn = c;
395 }
396 }
397
398 if (!Double.isNaN(m)) {
399 if (Double.isNaN(this.minimumRangeValue)
400 || m < this.minimumRangeValue) {
401 this.minimumRangeValue = m;
402 this.minimumRangeValueRow = r;
403 this.minimumRangeValueColumn = c;
404 }
405 }
406
407 if (!Double.isNaN(m - sd)) {
408 if (Double.isNaN(this.minimumRangeValueIncStdDev)
409 || (m - sd) < this.minimumRangeValueIncStdDev) {
410 this.minimumRangeValueIncStdDev = m - sd;
411 this.minimumRangeValueIncStdDevRow = r;
412 this.minimumRangeValueIncStdDevColumn = c;
413 }
414 }
415 }
416 fireDatasetChanged();
417 }
418
419 /**
420 * Removes an item from the dataset and sends a {@link DatasetChangeEvent}
421 * to all registered listeners.
422 *
423 * @param rowKey the row key (<code>null</code> not permitted).
424 * @param columnKey the column key (<code>null</code> not permitted).
425 *
426 * @see #add(double, double, Comparable, Comparable)
427 *
428 * @since 1.0.7
429 */
430 public void remove(Comparable rowKey, Comparable columnKey) {
431 // defer null argument checks
432 int r = getRowIndex(rowKey);
433 int c = getColumnIndex(columnKey);
434 this.data.removeObject(rowKey, columnKey);
435
436 // if this cell held a maximum and/or minimum value, we'll need to
437 // update the cached bounds...
438 if ((r == this.maximumRangeValueRow && c
439 == this.maximumRangeValueColumn) || (r
440 == this.maximumRangeValueIncStdDevRow && c
441 == this.maximumRangeValueIncStdDevColumn) || (r
442 == this.minimumRangeValueRow && c
443 == this.minimumRangeValueColumn) || (r
444 == this.minimumRangeValueIncStdDevRow && c
445 == this.minimumRangeValueIncStdDevColumn)) {
446
447 // iterate over all data items and update mins and maxes
448 updateBounds();
449 }
450
451 fireDatasetChanged();
452 }
453
454
455 /**
456 * Removes a row from the dataset and sends a {@link DatasetChangeEvent}
457 * to all registered listeners.
458 *
459 * @param rowIndex the row index.
460 *
461 * @see #removeColumn(int)
462 *
463 * @since 1.0.7
464 */
465 public void removeRow(int rowIndex) {
466 this.data.removeRow(rowIndex);
467 updateBounds();
468 fireDatasetChanged();
469 }
470
471 /**
472 * Removes a row from the dataset and sends a {@link DatasetChangeEvent}
473 * to all registered listeners.
474 *
475 * @param rowKey the row key (<code>null</code> not permitted).
476 *
477 * @see #removeColumn(Comparable)
478 *
479 * @since 1.0.7
480 */
481 public void removeRow(Comparable rowKey) {
482 this.data.removeRow(rowKey);
483 updateBounds();
484 fireDatasetChanged();
485 }
486
487 /**
488 * Removes a column from the dataset and sends a {@link DatasetChangeEvent}
489 * to all registered listeners.
490 *
491 * @param columnIndex the column index.
492 *
493 * @see #removeRow(int)
494 *
495 * @since 1.0.7
496 */
497 public void removeColumn(int columnIndex) {
498 this.data.removeColumn(columnIndex);
499 updateBounds();
500 fireDatasetChanged();
501 }
502
503 /**
504 * Removes a column from the dataset and sends a {@link DatasetChangeEvent}
505 * to all registered listeners.
506 *
507 * @param columnKey the column key (<code>null</code> not permitted).
508 *
509 * @see #removeRow(Comparable)
510 *
511 * @since 1.0.7
512 */
513 public void removeColumn(Comparable columnKey) {
514 this.data.removeColumn(columnKey);
515 updateBounds();
516 fireDatasetChanged();
517 }
518
519 /**
520 * Clears all data from the dataset and sends a {@link DatasetChangeEvent}
521 * to all registered listeners.
522 *
523 * @since 1.0.7
524 */
525 public void clear() {
526 this.data.clear();
527 updateBounds();
528 fireDatasetChanged();
529 }
530
531 /**
532 * Iterate over all the data items and update the cached bound values.
533 */
534 private void updateBounds() {
535 this.maximumRangeValue = Double.NaN;
536 this.maximumRangeValueRow = -1;
537 this.maximumRangeValueColumn = -1;
538 this.minimumRangeValue = Double.NaN;
539 this.minimumRangeValueRow = -1;
540 this.minimumRangeValueColumn = -1;
541 this.maximumRangeValueIncStdDev = Double.NaN;
542 this.maximumRangeValueIncStdDevRow = -1;
543 this.maximumRangeValueIncStdDevColumn = -1;
544 this.minimumRangeValueIncStdDev = Double.NaN;
545 this.minimumRangeValueIncStdDevRow = -1;
546 this.minimumRangeValueIncStdDevColumn = -1;
547
548 int rowCount = this.data.getRowCount();
549 int columnCount = this.data.getColumnCount();
550 for (int r = 0; r < rowCount; r++) {
551 for (int c = 0; c < columnCount; c++) {
552 double m = Double.NaN;
553 double sd = Double.NaN;
554 MeanAndStandardDeviation masd = (MeanAndStandardDeviation)
555 this.data.getObject(r, c);
556 if (masd == null) {
557 continue;
558 }
559 m = masd.getMeanValue();
560 sd = masd.getStandardDeviationValue();
561
562 if (!Double.isNaN(m)) {
563
564 // update the max value
565 if (Double.isNaN(this.maximumRangeValue)) {
566 this.maximumRangeValue = m;
567 this.maximumRangeValueRow = r;
568 this.maximumRangeValueColumn = c;
569 }
570 else {
571 if (m > this.maximumRangeValue) {
572 this.maximumRangeValue = m;
573 this.maximumRangeValueRow = r;
574 this.maximumRangeValueColumn = c;
575 }
576 }
577
578 // update the min value
579 if (Double.isNaN(this.minimumRangeValue)) {
580 this.minimumRangeValue = m;
581 this.minimumRangeValueRow = r;
582 this.minimumRangeValueColumn = c;
583 }
584 else {
585 if (m < this.minimumRangeValue) {
586 this.minimumRangeValue = m;
587 this.minimumRangeValueRow = r;
588 this.minimumRangeValueColumn = c;
589 }
590 }
591
592 if (!Double.isNaN(sd)) {
593 // update the max value
594 if (Double.isNaN(this.maximumRangeValueIncStdDev)) {
595 this.maximumRangeValueIncStdDev = m + sd;
596 this.maximumRangeValueIncStdDevRow = r;
597 this.maximumRangeValueIncStdDevColumn = c;
598 }
599 else {
600 if (m + sd > this.maximumRangeValueIncStdDev) {
601 this.maximumRangeValueIncStdDev = m + sd;
602 this.maximumRangeValueIncStdDevRow = r;
603 this.maximumRangeValueIncStdDevColumn = c;
604 }
605 }
606
607 // update the min value
608 if (Double.isNaN(this.minimumRangeValueIncStdDev)) {
609 this.minimumRangeValueIncStdDev = m - sd;
610 this.minimumRangeValueIncStdDevRow = r;
611 this.minimumRangeValueIncStdDevColumn = c;
612 }
613 else {
614 if (m - sd < this.minimumRangeValueIncStdDev) {
615 this.minimumRangeValueIncStdDev = m - sd;
616 this.minimumRangeValueIncStdDevRow = r;
617 this.minimumRangeValueIncStdDevColumn = c;
618 }
619 }
620 }
621 }
622 }
623 }
624 }
625
626 /**
627 * Returns the minimum y-value in the dataset.
628 *
629 * @param includeInterval a flag that determines whether or not the
630 * y-interval is taken into account.
631 *
632 * @return The minimum value.
633 *
634 * @see #getRangeUpperBound(boolean)
635 */
636 public double getRangeLowerBound(boolean includeInterval) {
637 if (includeInterval) {
638 return this.minimumRangeValueIncStdDev;
639 }
640 else {
641 return this.minimumRangeValue;
642 }
643 }
644
645 /**
646 * Returns the maximum y-value in the dataset.
647 *
648 * @param includeInterval a flag that determines whether or not the
649 * y-interval is taken into account.
650 *
651 * @return The maximum value.
652 *
653 * @see #getRangeLowerBound(boolean)
654 */
655 public double getRangeUpperBound(boolean includeInterval) {
656 if (includeInterval) {
657 return this.maximumRangeValueIncStdDev;
658 }
659 else {
660 return this.maximumRangeValue;
661 }
662 }
663
664 /**
665 * Returns the range of the values in this dataset's range.
666 *
667 * @param includeInterval a flag that determines whether or not the
668 * y-interval is taken into account.
669 *
670 * @return The range.
671 */
672 public Range getRangeBounds(boolean includeInterval) {
673 Range result = null;
674 if (includeInterval) {
675 if (!Double.isNaN(this.minimumRangeValueIncStdDev)
676 && !Double.isNaN(this.maximumRangeValueIncStdDev)) {
677 result = new Range(this.minimumRangeValueIncStdDev,
678 this.maximumRangeValueIncStdDev);
679 }
680 }
681 else {
682 if (!Double.isNaN(this.minimumRangeValue)
683 && !Double.isNaN(this.maximumRangeValue)) {
684 result = new Range(this.minimumRangeValue,
685 this.maximumRangeValue);
686 }
687 }
688 return result;
689 }
690
691 /**
692 * Tests this instance for equality with an arbitrary object.
693 *
694 * @param obj the object (<code>null</code> permitted).
695 *
696 * @return A boolean.
697 */
698 public boolean equals(Object obj) {
699 if (obj == this) {
700 return true;
701 }
702 if (!(obj instanceof DefaultStatisticalCategoryDataset)) {
703 return false;
704 }
705 DefaultStatisticalCategoryDataset that
706 = (DefaultStatisticalCategoryDataset) obj;
707 if (!this.data.equals(that.data)) {
708 return false;
709 }
710 return true;
711 }
712
713 /**
714 * Returns a clone of this dataset.
715 *
716 * @return A clone of this dataset.
717 *
718 * @throws CloneNotSupportedException if cloning cannot be completed.
719 */
720 public Object clone() throws CloneNotSupportedException {
721 DefaultStatisticalCategoryDataset clone
722 = (DefaultStatisticalCategoryDataset) super.clone();
723 clone.data = (KeyedObjects2D) this.data.clone();
724 return clone;
725 }
726 }