Source for org.jfree.data.statistics.DefaultMultiValueCategoryDataset

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * -------------------------------------
  28:  * DefaultMultiValueCategoryDataset.java
  29:  * -------------------------------------
  30:  * (C) Copyright 2007, by David Forslund and Contributors.
  31:  *
  32:  * Original Author:  David Forslund;
  33:  * Contributor(s):   David Gilbert (for Object Refinery Limited);
  34:  *
  35:  * Changes
  36:  * -------
  37:  * 08-Oct-2007 : Version 1, see patch 1780779 (DG);
  38:  * 06-Nov-2007 : Return EMPTY_LIST not null from getValues() (DG);
  39:  */
  40: 
  41: package org.jfree.data.statistics;
  42: 
  43: import java.util.ArrayList;
  44: import java.util.Collections;
  45: import java.util.Iterator;
  46: import java.util.List;
  47: 
  48: import org.jfree.data.KeyedObjects2D;
  49: import org.jfree.data.Range;
  50: import org.jfree.data.RangeInfo;
  51: import org.jfree.data.general.AbstractDataset;
  52: import org.jfree.data.general.DatasetChangeEvent;
  53: import org.jfree.util.PublicCloneable;
  54: 
  55: /**
  56:  * A category dataset that defines multiple values for each item.
  57:  * 
  58:  * @since 1.0.7
  59:  */
  60: public class DefaultMultiValueCategoryDataset extends AbstractDataset 
  61:         implements MultiValueCategoryDataset, RangeInfo, PublicCloneable {
  62: 
  63:     /**
  64:      * Storage for the data.
  65:      */
  66:     protected KeyedObjects2D data;
  67:     
  68:     /**
  69:      * The minimum range value.
  70:      */
  71:     private Number minimumRangeValue;
  72: 
  73:     /**
  74:      * The maximum range value.
  75:      */
  76:     private Number maximumRangeValue;
  77: 
  78:     /**
  79:      * The range of values.
  80:      */
  81:     private Range rangeBounds;
  82: 
  83:     /**
  84:      * Creates a new dataset.
  85:      */
  86:     public DefaultMultiValueCategoryDataset() {
  87:         this.data = new KeyedObjects2D();
  88:         this.minimumRangeValue = null;
  89:         this.maximumRangeValue = null;
  90:         this.rangeBounds = new Range(0.0, 0.0);
  91:     }
  92: 
  93:     /**
  94:      * Adds a list of values to the dataset (<code>null</code> and Double.NaN 
  95:      * items are automatically removed) and sends a {@link DatasetChangeEvent}
  96:      * to all registered listeners.
  97:      *
  98:      * @param values  a list of values (<code>null</code> not permitted).
  99:      * @param rowKey  the row key (<code>null</code> not permitted).
 100:      * @param columnKey  the column key (<code>null</code> not permitted).
 101:      */
 102:     public void add(List values, Comparable rowKey, Comparable columnKey) {
 103:         
 104:         if (values == null) {
 105:             throw new IllegalArgumentException("Null 'values' argument.");
 106:         }
 107:         if (rowKey == null) {
 108:             throw new IllegalArgumentException("Null 'rowKey' argument.");
 109:         }
 110:         if (columnKey == null) {
 111:             throw new IllegalArgumentException("Null 'columnKey' argument.");
 112:         }
 113:         List vlist = new ArrayList(values.size());
 114:         Iterator iterator = values.listIterator();
 115:         while (iterator.hasNext()) {
 116:             Object obj = iterator.next();
 117:             if (obj instanceof Number) {
 118:                 Number n = (Number) obj;
 119:                 double v = n.doubleValue();
 120:                 if (!Double.isNaN(v)) {
 121:                     vlist.add(n);
 122:                 }
 123:             }
 124:         }
 125:         Collections.sort(vlist);
 126:         this.data.addObject(vlist, rowKey, columnKey);
 127:         
 128:         if (vlist.size() > 0) {
 129:             double maxval = Double.NEGATIVE_INFINITY;
 130:             double minval = Double.POSITIVE_INFINITY;
 131:             for (int i = 0; i < vlist.size(); i++) {
 132:                 Number n = (Number) vlist.get(i);
 133:                 double v = n.doubleValue();   
 134:                 minval = Math.min(minval, v);
 135:                 maxval = Math.max(maxval, v);
 136:             }
 137:         
 138:             // update the cached range values...
 139:             if (this.maximumRangeValue == null) {
 140:                 this.maximumRangeValue = new Double(maxval);
 141:             } 
 142:             else if (maxval > this.maximumRangeValue.doubleValue()) {
 143:                 this.maximumRangeValue = new Double(maxval);
 144:             }
 145: 
 146:             if (this.minimumRangeValue == null) {
 147:                 this.minimumRangeValue = new Double(minval);
 148:             } 
 149:             else if (minval < this.minimumRangeValue.doubleValue()) {
 150:                 this.minimumRangeValue = new Double(minval);
 151:             }
 152:             this.rangeBounds = new Range(this.minimumRangeValue.doubleValue(),
 153:                     this.maximumRangeValue.doubleValue());
 154:         }
 155: 
 156:         fireDatasetChanged();
 157:     }
 158: 
 159:     /**
 160:      * Returns a list (possibly empty) of the values for the specified item.
 161:      * The returned list should be unmodifiable.
 162:      * 
 163:      * @param row  the row index (zero-based).
 164:      * @param column   the column index (zero-based).
 165:      * 
 166:      * @return The list of values.
 167:      */
 168:     public List getValues(int row, int column) {
 169:         List values = (List) this.data.getObject(row, column);
 170:         if (values != null) {
 171:             return Collections.unmodifiableList(values);
 172:         }
 173:         else {
 174:             return Collections.EMPTY_LIST;
 175:         }
 176:     }
 177: 
 178:     /**
 179:      * Returns a list (possibly empty) of the values for the specified item.
 180:      * The returned list should be unmodifiable.
 181:      * 
 182:      * @param rowKey  the row key (<code>null</code> not permitted).
 183:      * @param columnKey  the column key (<code>null</code> not permitted).
 184:      *
 185:      * @return The list of values.
 186:      */
 187:     public List getValues(Comparable rowKey, Comparable columnKey) {
 188:         return Collections.unmodifiableList((List) this.data.getObject(rowKey, 
 189:                 columnKey));
 190:     }
 191: 
 192:     /**
 193:      * Returns the average value for the specified item.
 194:      *
 195:      * @param row  the row key.
 196:      * @param column  the column key.
 197:      * 
 198:      * @return The average value.
 199:      */
 200:     public Number getValue(Comparable row, Comparable column) {
 201:         List l = (List) this.data.getObject(row, column);
 202:         double average = 0.0d;
 203:         int count = 0;
 204:         if (l != null && l.size() > 0) {
 205:             for (int i = 0; i < l.size(); i++) {
 206:                 Number n = (Number) l.get(i);
 207:                 average += n.doubleValue();
 208:                 count += 1;
 209:             }
 210:             if (count > 0) {
 211:                 average = average / count;
 212:             }
 213:         }
 214:         if (count == 0) {
 215:             return null;
 216:         }
 217:         return new Double(average);
 218:     }
 219: 
 220:     /**
 221:      * Returns the average value for the specified item.
 222:      *
 223:      * @param row  the row index.
 224:      * @param column  the column index.
 225:      * 
 226:      * @return The average value.
 227:      */
 228:     public Number getValue(int row, int column) {
 229:         List l = (List) this.data.getObject(row, column);
 230:         double average = 0.0d;
 231:         int count = 0;
 232:         if (l != null && l.size() > 0) {
 233:             for (int i = 0; i < l.size(); i++) {
 234:                 Number n = (Number) l.get(i);
 235:                 average += n.doubleValue();
 236:                 count += 1;
 237:             }
 238:             if (count > 0) {
 239:                 average = average / count;
 240:             }
 241:         }
 242:         if (count == 0) {
 243:             return null;
 244:         }
 245:         return new Double(average);
 246:     }
 247: 
 248:     /**
 249:      * Returns the column index for a given key.
 250:      *
 251:      * @param key  the column key.
 252:      * 
 253:      * @return The column index.
 254:      */
 255:     public int getColumnIndex(Comparable key) {
 256:         return this.data.getColumnIndex(key);
 257:     }
 258: 
 259:     /**
 260:      * Returns a column key.
 261:      *
 262:      * @param column the column index (zero-based).
 263:      * 
 264:      * @return The column key.
 265:      */
 266:     public Comparable getColumnKey(int column) {
 267:         return this.data.getColumnKey(column);
 268:     }
 269: 
 270:     /**
 271:      * Returns the column keys.
 272:      *
 273:      * @return The keys.
 274:      */
 275:     public List getColumnKeys() {
 276:         return this.data.getColumnKeys();
 277:     }
 278: 
 279:     /**
 280:      * Returns the row index for a given key.
 281:      *
 282:      * @param key the row key.
 283:      * 
 284:      * @return The row index.
 285:      */
 286:     public int getRowIndex(Comparable key) {
 287:         return this.data.getRowIndex(key);
 288:     }
 289: 
 290:     /**
 291:      * Returns a row key.
 292:      *
 293:      * @param row the row index (zero-based).
 294:      * 
 295:      * @return The row key.
 296:      */
 297:     public Comparable getRowKey(int row) {
 298:         return this.data.getRowKey(row);
 299:     }
 300: 
 301:     /**
 302:      * Returns the row keys.
 303:      *
 304:      * @return The keys.
 305:      */
 306:     public List getRowKeys() {
 307:         return this.data.getRowKeys();
 308:     }
 309: 
 310:     /**
 311:      * Returns the number of rows in the table.
 312:      *
 313:      * @return The row count.
 314:      */
 315:     public int getRowCount() {
 316:         return this.data.getRowCount();
 317:     }
 318: 
 319:     /**
 320:      * Returns the number of columns in the table.
 321:      *
 322:      * @return The column count.
 323:      */
 324:     public int getColumnCount() {
 325:         return this.data.getColumnCount();
 326:     }
 327: 
 328:     /**
 329:      * Returns the minimum y-value in the dataset.
 330:      *
 331:      * @param includeInterval a flag that determines whether or not the
 332:      *                        y-interval is taken into account.
 333:      *                        
 334:      * @return The minimum value.
 335:      */
 336:     public double getRangeLowerBound(boolean includeInterval) {
 337:         double result = Double.NaN;
 338:         if (this.minimumRangeValue != null) {
 339:             result = this.minimumRangeValue.doubleValue();
 340:         }
 341:         return result;
 342:     }
 343: 
 344:     /**
 345:      * Returns the maximum y-value in the dataset.
 346:      *
 347:      * @param includeInterval a flag that determines whether or not the
 348:      *                        y-interval is taken into account.
 349:      *                        
 350:      * @return The maximum value.
 351:      */
 352:     public double getRangeUpperBound(boolean includeInterval) {
 353:         double result = Double.NaN;
 354:         if (this.maximumRangeValue != null) {
 355:             result = this.maximumRangeValue.doubleValue();
 356:         }
 357:         return result;
 358:     }
 359: 
 360:     /**
 361:      * Returns the range of the values in this dataset's range.
 362:      *
 363:      * @param includeInterval a flag that determines whether or not the
 364:      *                        y-interval is taken into account.
 365:      * @return The range.
 366:      */
 367:     public Range getRangeBounds(boolean includeInterval) {
 368:         return this.rangeBounds;
 369:     }
 370:     
 371:     /**
 372:      * Tests this dataset for equality with an arbitrary object.
 373:      * 
 374:      * @param obj  the object (<code>null</code> permitted).
 375:      * 
 376:      * @return A boolean.
 377:      */
 378:     public boolean equals(Object obj) {
 379:         if (obj == this) {
 380:             return true;
 381:         }
 382:         if (!(obj instanceof DefaultMultiValueCategoryDataset)) {
 383:             return false;
 384:         }
 385:         DefaultMultiValueCategoryDataset that 
 386:                 = (DefaultMultiValueCategoryDataset) obj;
 387:         return this.data.equals(that.data);
 388:     }
 389:     
 390:     /**
 391:      * Returns a clone of this instance.
 392:      * 
 393:      * @return A clone.
 394:      * 
 395:      * @throws CloneNotSupportedException if the dataset cannot be cloned.
 396:      */
 397:     public Object clone() throws CloneNotSupportedException {
 398:         DefaultMultiValueCategoryDataset clone 
 399:                 = (DefaultMultiValueCategoryDataset) super.clone();
 400:         clone.data = (KeyedObjects2D) this.data.clone();
 401:         return clone;
 402:     }
 403: }