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: 
  44: import java.util.ArrayList;
  45: import java.util.Collections;
  46: import java.util.Iterator;
  47: import java.util.List;
  48: 
  49: import org.jfree.data.KeyedObjects2D;
  50: import org.jfree.data.Range;
  51: import org.jfree.data.RangeInfo;
  52: import org.jfree.data.general.AbstractDataset;
  53: import org.jfree.data.general.DatasetChangeEvent;
  54: import org.jfree.util.PublicCloneable;
  55: 
  56: /**
  57:  * A category dataset that defines multiple values for each item.
  58:  * 
  59:  * @since 1.0.7
  60:  */
  61: public class DefaultMultiValueCategoryDataset extends AbstractDataset 
  62:         implements MultiValueCategoryDataset, RangeInfo, PublicCloneable {
  63: 
  64:     /**
  65:      * Storage for the data.
  66:      */
  67:     protected KeyedObjects2D data;
  68:     
  69:     /**
  70:      * The minimum range value.
  71:      */
  72:     private Number minimumRangeValue;
  73: 
  74:     /**
  75:      * The maximum range value.
  76:      */
  77:     private Number maximumRangeValue;
  78: 
  79:     /**
  80:      * The range of values.
  81:      */
  82:     private Range rangeBounds;
  83: 
  84:     /**
  85:      * Creates a new dataset.
  86:      */
  87:     public DefaultMultiValueCategoryDataset() {
  88:         this.data = new KeyedObjects2D();
  89:         this.minimumRangeValue = null;
  90:         this.maximumRangeValue = null;
  91:         this.rangeBounds = new Range(0.0, 0.0);
  92:     }
  93: 
  94:     /**
  95:      * Adds a list of values to the dataset (<code>null</code> and Double.NaN 
  96:      * items are automatically removed) and sends a {@link DatasetChangeEvent}
  97:      * to all registered listeners.
  98:      *
  99:      * @param values  a list of values (<code>null</code> not permitted).
 100:      * @param rowKey  the row key (<code>null</code> not permitted).
 101:      * @param columnKey  the column key (<code>null</code> not permitted).
 102:      */
 103:     public void add(List values, Comparable rowKey, Comparable columnKey) {
 104:         
 105:         if (values == null) {
 106:             throw new IllegalArgumentException("Null 'values' argument.");
 107:         }
 108:         if (rowKey == null) {
 109:             throw new IllegalArgumentException("Null 'rowKey' argument.");
 110:         }
 111:         if (columnKey == null) {
 112:             throw new IllegalArgumentException("Null 'columnKey' argument.");
 113:         }
 114:         List vlist = new ArrayList(values.size());
 115:         Iterator iterator = values.listIterator();
 116:         while (iterator.hasNext()) {
 117:             Object obj = iterator.next();
 118:             if (obj instanceof Number) {
 119:                 Number n = (Number) obj;
 120:                 double v = n.doubleValue();
 121:                 if (!Double.isNaN(v)) {
 122:                     vlist.add(n);
 123:                 }
 124:             }
 125:         }
 126:         Collections.sort(vlist);
 127:         this.data.addObject(vlist, rowKey, columnKey);
 128:         
 129:         if (vlist.size() > 0) {
 130:             double maxval = Double.NEGATIVE_INFINITY;
 131:             double minval = Double.POSITIVE_INFINITY;
 132:             for (int i = 0; i < vlist.size(); i++) {
 133:                 Number n = (Number) vlist.get(i);
 134:                 double v = n.doubleValue();   
 135:                 minval = Math.min(minval, v);
 136:                 maxval = Math.max(maxval, v);
 137:             }
 138:         
 139:             // update the cached range values...
 140:             if (this.maximumRangeValue == null) {
 141:                 this.maximumRangeValue = new Double(maxval);
 142:             } 
 143:             else if (maxval > this.maximumRangeValue.doubleValue()) {
 144:                 this.maximumRangeValue = new Double(maxval);
 145:             }
 146: 
 147:             if (this.minimumRangeValue == null) {
 148:                 this.minimumRangeValue = new Double(minval);
 149:             } 
 150:             else if (minval < this.minimumRangeValue.doubleValue()) {
 151:                 this.minimumRangeValue = new Double(minval);
 152:             }
 153:             this.rangeBounds = new Range(this.minimumRangeValue.doubleValue(),
 154:                     this.maximumRangeValue.doubleValue());
 155:         }
 156: 
 157:         fireDatasetChanged();
 158:     }
 159: 
 160:     /**
 161:      * Returns a list (possibly empty) of the values for the specified item.
 162:      * The returned list should be unmodifiable.
 163:      * 
 164:      * @param row  the row index (zero-based).
 165:      * @param column   the column index (zero-based).
 166:      * 
 167:      * @return The list of values.
 168:      */
 169:     public List getValues(int row, int column) {
 170:         List values = (List) this.data.getObject(row, column);
 171:         if (values != null) {
 172:             return Collections.unmodifiableList(values);
 173:         }
 174:         else {
 175:             return Collections.EMPTY_LIST;
 176:         }
 177:     }
 178: 
 179:     /**
 180:      * Returns a list (possibly empty) of the values for the specified item.
 181:      * The returned list should be unmodifiable.
 182:      * 
 183:      * @param rowKey  the row key (<code>null</code> not permitted).
 184:      * @param columnKey  the column key (<code>null</code> not permitted).
 185:      *
 186:      * @return The list of values.
 187:      */
 188:     public List getValues(Comparable rowKey, Comparable columnKey) {
 189:         return Collections.unmodifiableList((List) this.data.getObject(rowKey, 
 190:                 columnKey));
 191:     }
 192: 
 193:     /**
 194:      * Returns the average value for the specified item.
 195:      *
 196:      * @param row  the row key.
 197:      * @param column  the column key.
 198:      * 
 199:      * @return The average value.
 200:      */
 201:     public Number getValue(Comparable row, Comparable column) {
 202:         List l = (List) this.data.getObject(row, column);
 203:         double average = 0.0d;
 204:         int count = 0;
 205:         if (l != null && l.size() > 0) {
 206:             for (int i = 0; i < l.size(); i++) {
 207:                 Number n = (Number) l.get(i);
 208:                 average += n.doubleValue();
 209:                 count += 1;
 210:             }
 211:             if (count > 0) {
 212:                 average = average / count;
 213:             }
 214:         }
 215:         if (count == 0) {
 216:             return null;
 217:         }
 218:         return new Double(average);
 219:     }
 220: 
 221:     /**
 222:      * Returns the average value for the specified item.
 223:      *
 224:      * @param row  the row index.
 225:      * @param column  the column index.
 226:      * 
 227:      * @return The average value.
 228:      */
 229:     public Number getValue(int row, int column) {
 230:         List l = (List) this.data.getObject(row, column);
 231:         double average = 0.0d;
 232:         int count = 0;
 233:         if (l != null && l.size() > 0) {
 234:             for (int i = 0; i < l.size(); i++) {
 235:                 Number n = (Number) l.get(i);
 236:                 average += n.doubleValue();
 237:                 count += 1;
 238:             }
 239:             if (count > 0) {
 240:                 average = average / count;
 241:             }
 242:         }
 243:         if (count == 0) {
 244:             return null;
 245:         }
 246:         return new Double(average);
 247:     }
 248: 
 249:     /**
 250:      * Returns the column index for a given key.
 251:      *
 252:      * @param key  the column key.
 253:      * 
 254:      * @return The column index.
 255:      */
 256:     public int getColumnIndex(Comparable key) {
 257:         return this.data.getColumnIndex(key);
 258:     }
 259: 
 260:     /**
 261:      * Returns a column key.
 262:      *
 263:      * @param column the column index (zero-based).
 264:      * 
 265:      * @return The column key.
 266:      */
 267:     public Comparable getColumnKey(int column) {
 268:         return this.data.getColumnKey(column);
 269:     }
 270: 
 271:     /**
 272:      * Returns the column keys.
 273:      *
 274:      * @return The keys.
 275:      */
 276:     public List getColumnKeys() {
 277:         return this.data.getColumnKeys();
 278:     }
 279: 
 280:     /**
 281:      * Returns the row index for a given key.
 282:      *
 283:      * @param key the row key.
 284:      * 
 285:      * @return The row index.
 286:      */
 287:     public int getRowIndex(Comparable key) {
 288:         return this.data.getRowIndex(key);
 289:     }
 290: 
 291:     /**
 292:      * Returns a row key.
 293:      *
 294:      * @param row the row index (zero-based).
 295:      * 
 296:      * @return The row key.
 297:      */
 298:     public Comparable getRowKey(int row) {
 299:         return this.data.getRowKey(row);
 300:     }
 301: 
 302:     /**
 303:      * Returns the row keys.
 304:      *
 305:      * @return The keys.
 306:      */
 307:     public List getRowKeys() {
 308:         return this.data.getRowKeys();
 309:     }
 310: 
 311:     /**
 312:      * Returns the number of rows in the table.
 313:      *
 314:      * @return The row count.
 315:      */
 316:     public int getRowCount() {
 317:         return this.data.getRowCount();
 318:     }
 319: 
 320:     /**
 321:      * Returns the number of columns in the table.
 322:      *
 323:      * @return The column count.
 324:      */
 325:     public int getColumnCount() {
 326:         return this.data.getColumnCount();
 327:     }
 328: 
 329:     /**
 330:      * Returns the minimum y-value in the dataset.
 331:      *
 332:      * @param includeInterval a flag that determines whether or not the
 333:      *                        y-interval is taken into account.
 334:      *                        
 335:      * @return The minimum value.
 336:      */
 337:     public double getRangeLowerBound(boolean includeInterval) {
 338:         double result = Double.NaN;
 339:         if (this.minimumRangeValue != null) {
 340:             result = this.minimumRangeValue.doubleValue();
 341:         }
 342:         return result;
 343:     }
 344: 
 345:     /**
 346:      * Returns the maximum y-value in the dataset.
 347:      *
 348:      * @param includeInterval a flag that determines whether or not the
 349:      *                        y-interval is taken into account.
 350:      *                        
 351:      * @return The maximum value.
 352:      */
 353:     public double getRangeUpperBound(boolean includeInterval) {
 354:         double result = Double.NaN;
 355:         if (this.maximumRangeValue != null) {
 356:             result = this.maximumRangeValue.doubleValue();
 357:         }
 358:         return result;
 359:     }
 360: 
 361:     /**
 362:      * Returns the range of the values in this dataset's range.
 363:      *
 364:      * @param includeInterval a flag that determines whether or not the
 365:      *                        y-interval is taken into account.
 366:      * @return The range.
 367:      */
 368:     public Range getRangeBounds(boolean includeInterval) {
 369:         return this.rangeBounds;
 370:     }
 371:     
 372:     /**
 373:      * Tests this dataset for equality with an arbitrary object.
 374:      * 
 375:      * @param obj  the object (<code>null</code> permitted).
 376:      * 
 377:      * @return A boolean.
 378:      */
 379:     public boolean equals(Object obj) {
 380:         if (obj == this) {
 381:             return true;
 382:         }
 383:         if (!(obj instanceof DefaultMultiValueCategoryDataset)) {
 384:             return false;
 385:         }
 386:         DefaultMultiValueCategoryDataset that 
 387:                 = (DefaultMultiValueCategoryDataset) obj;
 388:         return this.data.equals(that.data);
 389:     }
 390:     
 391:     /**
 392:      * Returns a clone of this instance.
 393:      * 
 394:      * @return A clone.
 395:      * 
 396:      * @throws CloneNotSupportedException if the dataset cannot be cloned.
 397:      */
 398:     public Object clone() throws CloneNotSupportedException {
 399:         DefaultMultiValueCategoryDataset clone 
 400:                 = (DefaultMultiValueCategoryDataset) super.clone();
 401:         clone.data = (KeyedObjects2D) this.data.clone();
 402:         return clone;
 403:     }
 404: }