Source for org.jfree.data.xy.CategoryTableXYDataset

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2008, 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:  * CategoryTableXYDataset.java
  29:  * ---------------------------
  30:  * (C) Copyright 2004-2008, by Andreas Schroeder and Contributors.
  31:  *
  32:  * Original Author:  Andreas Schroeder;
  33:  * Contributor(s):   David Gilbert (for Object Refinery Limited);
  34:  *
  35:  * Changes
  36:  * -------
  37:  * 31-Mar-2004 : Version 1 (AS);
  38:  * 05-May-2004 : Now extends AbstractIntervalXYDataset (DG);
  39:  * 15-Jul-2004 : Switched interval access method names (DG);
  40:  * 18-Aug-2004 : Moved from org.jfree.data --> org.jfree.data.xy (DG);
  41:  * 17-Nov-2004 : Updates required by changes to DomainInfo interface (DG);
  42:  * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
  43:  * 05-Oct-2005 : Made the interval delegate a dataset change listener (DG);
  44:  * 02-Feb-2007 : Removed author tags all over JFreeChart sources (DG);
  45:  * 22-Apr-2008 : Implemented PublicCloneable, and fixed clone() method (DG);
  46:  *
  47:  */
  48: 
  49: package org.jfree.data.xy;
  50: 
  51: import org.jfree.data.DefaultKeyedValues2D;
  52: import org.jfree.data.DomainInfo;
  53: import org.jfree.data.Range;
  54: import org.jfree.data.general.DatasetChangeEvent;
  55: import org.jfree.data.general.DatasetUtilities;
  56: import org.jfree.util.PublicCloneable;
  57: 
  58: /**
  59:  * An implementation variant of the {@link TableXYDataset} where every series
  60:  * shares the same x-values (required for generating stacked area charts).
  61:  * This implementation uses a {@link DefaultKeyedValues2D} Object as backend
  62:  * implementation and is hence more "category oriented" than the {@link
  63:  * DefaultTableXYDataset} implementation.
  64:  * <p>
  65:  * This implementation provides no means to remove data items yet.
  66:  * This is due to the lack of such facility in the DefaultKeyedValues2D class.
  67:  * <p>
  68:  * This class also implements the {@link IntervalXYDataset} interface, but this
  69:  * implementation is provisional.
  70:  */
  71: public class CategoryTableXYDataset extends AbstractIntervalXYDataset
  72:         implements TableXYDataset, IntervalXYDataset, DomainInfo,
  73:                    PublicCloneable {
  74: 
  75:     /**
  76:      * The backing data structure.
  77:      */
  78:     private DefaultKeyedValues2D values;
  79: 
  80:     /** A delegate for controlling the interval width. */
  81:     private IntervalXYDelegate intervalDelegate;
  82: 
  83:     /**
  84:      * Creates a new empty CategoryTableXYDataset.
  85:      */
  86:     public CategoryTableXYDataset() {
  87:         this.values = new DefaultKeyedValues2D(true);
  88:         this.intervalDelegate = new IntervalXYDelegate(this);
  89:         addChangeListener(this.intervalDelegate);
  90:     }
  91: 
  92:     /**
  93:      * Adds a data item to this dataset and sends a {@link DatasetChangeEvent}
  94:      * to all registered listeners.
  95:      *
  96:      * @param x  the x value.
  97:      * @param y  the y value.
  98:      * @param seriesName  the name of the series to add the data item.
  99:      */
 100:     public void add(double x, double y, String seriesName) {
 101:         add(new Double(x), new Double(y), seriesName, true);
 102:     }
 103: 
 104:     /**
 105:      * Adds a data item to this dataset and, if requested, sends a
 106:      * {@link DatasetChangeEvent} to all registered listeners.
 107:      *
 108:      * @param x  the x value.
 109:      * @param y  the y value.
 110:      * @param seriesName  the name of the series to add the data item.
 111:      * @param notify  notify listeners?
 112:      */
 113:     public void add(Number x, Number y, String seriesName, boolean notify) {
 114:         this.values.addValue(y, (Comparable) x, seriesName);
 115:         if (notify) {
 116:             fireDatasetChanged();
 117:         }
 118:     }
 119: 
 120:     /**
 121:      * Removes a value from the dataset.
 122:      *
 123:      * @param x  the x-value.
 124:      * @param seriesName  the series name.
 125:      */
 126:     public void remove(double x, String seriesName) {
 127:         remove(new Double(x), seriesName, true);
 128:     }
 129: 
 130:     /**
 131:      * Removes an item from the dataset.
 132:      *
 133:      * @param x  the x-value.
 134:      * @param seriesName  the series name.
 135:      * @param notify  notify listeners?
 136:      */
 137:     public void remove(Number x, String seriesName, boolean notify) {
 138:         this.values.removeValue((Comparable) x, seriesName);
 139:         if (notify) {
 140:             fireDatasetChanged();
 141:         }
 142:     }
 143: 
 144: 
 145:     /**
 146:      * Returns the number of series in the collection.
 147:      *
 148:      * @return The series count.
 149:      */
 150:     public int getSeriesCount() {
 151:         return this.values.getColumnCount();
 152:     }
 153: 
 154:     /**
 155:      * Returns the key for a series.
 156:      *
 157:      * @param series  the series index (zero-based).
 158:      *
 159:      * @return The key for a series.
 160:      */
 161:     public Comparable getSeriesKey(int series) {
 162:         return this.values.getColumnKey(series);
 163:     }
 164: 
 165:     /**
 166:      * Returns the number of x values in the dataset.
 167:      *
 168:      * @return The item count.
 169:      */
 170:     public int getItemCount() {
 171:         return this.values.getRowCount();
 172:     }
 173: 
 174:     /**
 175:      * Returns the number of items in the specified series.
 176:      * Returns the same as {@link CategoryTableXYDataset#getItemCount()}.
 177:      *
 178:      * @param series  the series index (zero-based).
 179:      *
 180:      * @return The item count.
 181:      */
 182:     public int getItemCount(int series) {
 183:         return getItemCount();  // all series have the same number of items in
 184:                                 // this dataset
 185:     }
 186: 
 187:     /**
 188:      * Returns the x-value for the specified series and item.
 189:      *
 190:      * @param series  the series index (zero-based).
 191:      * @param item  the item index (zero-based).
 192:      *
 193:      * @return The value.
 194:      */
 195:     public Number getX(int series, int item) {
 196:         return (Number) this.values.getRowKey(item);
 197:     }
 198: 
 199:     /**
 200:      * Returns the starting X value for the specified series and item.
 201:      *
 202:      * @param series  the series index (zero-based).
 203:      * @param item  the item index (zero-based).
 204:      *
 205:      * @return The starting X value.
 206:      */
 207:     public Number getStartX(int series, int item) {
 208:         return this.intervalDelegate.getStartX(series, item);
 209:     }
 210: 
 211:     /**
 212:      * Returns the ending X value for the specified series and item.
 213:      *
 214:      * @param series  the series index (zero-based).
 215:      * @param item  the item index (zero-based).
 216:      *
 217:      * @return The ending X value.
 218:      */
 219:     public Number getEndX(int series, int item) {
 220:         return this.intervalDelegate.getEndX(series, item);
 221:     }
 222: 
 223:     /**
 224:      * Returns the y-value for the specified series and item.
 225:      *
 226:      * @param series  the series index (zero-based).
 227:      * @param item  the item index (zero-based).
 228:      *
 229:      * @return The y value (possibly <code>null</code>).
 230:      */
 231:     public Number getY(int series, int item) {
 232:         return this.values.getValue(item, series);
 233:     }
 234: 
 235:     /**
 236:      * Returns the starting Y value for the specified series and item.
 237:      *
 238:      * @param series  the series index (zero-based).
 239:      * @param item  the item index (zero-based).
 240:      *
 241:      * @return The starting Y value.
 242:      */
 243:     public Number getStartY(int series, int item) {
 244:         return getY(series, item);
 245:     }
 246: 
 247:     /**
 248:      * Returns the ending Y value for the specified series and item.
 249:      *
 250:      * @param series  the series index (zero-based).
 251:      * @param item  the item index (zero-based).
 252:      *
 253:      * @return The ending Y value.
 254:      */
 255:     public Number getEndY(int series, int item) {
 256:         return getY(series, item);
 257:     }
 258: 
 259:     /**
 260:      * Returns the minimum x-value in the dataset.
 261:      *
 262:      * @param includeInterval  a flag that determines whether or not the
 263:      *                         x-interval is taken into account.
 264:      *
 265:      * @return The minimum value.
 266:      */
 267:     public double getDomainLowerBound(boolean includeInterval) {
 268:         return this.intervalDelegate.getDomainLowerBound(includeInterval);
 269:     }
 270: 
 271:     /**
 272:      * Returns the maximum x-value in the dataset.
 273:      *
 274:      * @param includeInterval  a flag that determines whether or not the
 275:      *                         x-interval is taken into account.
 276:      *
 277:      * @return The maximum value.
 278:      */
 279:     public double getDomainUpperBound(boolean includeInterval) {
 280:         return this.intervalDelegate.getDomainUpperBound(includeInterval);
 281:     }
 282: 
 283:     /**
 284:      * Returns the range of the values in this dataset's domain.
 285:      *
 286:      * @param includeInterval  a flag that determines whether or not the
 287:      *                         x-interval is taken into account.
 288:      *
 289:      * @return The range.
 290:      */
 291:     public Range getDomainBounds(boolean includeInterval) {
 292:         if (includeInterval) {
 293:             return this.intervalDelegate.getDomainBounds(includeInterval);
 294:         }
 295:         else {
 296:             return DatasetUtilities.iterateDomainBounds(this, includeInterval);
 297:         }
 298:     }
 299: 
 300:     /**
 301:      * Returns the interval position factor.
 302:      *
 303:      * @return The interval position factor.
 304:      */
 305:     public double getIntervalPositionFactor() {
 306:         return this.intervalDelegate.getIntervalPositionFactor();
 307:     }
 308: 
 309:     /**
 310:      * Sets the interval position factor. Must be between 0.0 and 1.0 inclusive.
 311:      * If the factor is 0.5, the gap is in the middle of the x values. If it
 312:      * is lesser than 0.5, the gap is farther to the left and if greater than
 313:      * 0.5 it gets farther to the right.
 314:      *
 315:      * @param d  the new interval position factor.
 316:      */
 317:     public void setIntervalPositionFactor(double d) {
 318:         this.intervalDelegate.setIntervalPositionFactor(d);
 319:         fireDatasetChanged();
 320:     }
 321: 
 322:     /**
 323:      * Returns the full interval width.
 324:      *
 325:      * @return The interval width to use.
 326:      */
 327:     public double getIntervalWidth() {
 328:         return this.intervalDelegate.getIntervalWidth();
 329:     }
 330: 
 331:     /**
 332:      * Sets the interval width to a fixed value, and sends a
 333:      * {@link DatasetChangeEvent} to all registered listeners.
 334:      *
 335:      * @param d  the new interval width (must be > 0).
 336:      */
 337:     public void setIntervalWidth(double d) {
 338:         this.intervalDelegate.setFixedIntervalWidth(d);
 339:         fireDatasetChanged();
 340:     }
 341: 
 342:     /**
 343:      * Returns whether the interval width is automatically calculated or not.
 344:      *
 345:      * @return whether the width is automatically calculated or not.
 346:      */
 347:     public boolean isAutoWidth() {
 348:         return this.intervalDelegate.isAutoWidth();
 349:     }
 350: 
 351:     /**
 352:      * Sets the flag that indicates whether the interval width is automatically
 353:      * calculated or not.
 354:      *
 355:      * @param b  the flag.
 356:      */
 357:     public void setAutoWidth(boolean b) {
 358:         this.intervalDelegate.setAutoWidth(b);
 359:         fireDatasetChanged();
 360:     }
 361: 
 362:     /**
 363:      * Tests this dataset for equality with an arbitrary object.
 364:      *
 365:      * @param obj  the object (<code>null</code> permitted).
 366:      *
 367:      * @return A boolean.
 368:      */
 369:     public boolean equals(Object obj) {
 370:         if (!(obj instanceof CategoryTableXYDataset)) {
 371:             return false;
 372:         }
 373:         CategoryTableXYDataset that = (CategoryTableXYDataset) obj;
 374:         if (!this.intervalDelegate.equals(that.intervalDelegate)) {
 375:             return false;
 376:         }
 377:         if (!this.values.equals(that.values)) {
 378:             return false;
 379:         }
 380:         return true;
 381:     }
 382: 
 383:     /**
 384:      * Returns an independent copy of this dataset.
 385:      *
 386:      * @return A clone.
 387:      *
 388:      * @throws CloneNotSupportedException if there is some reason that cloning
 389:      *     cannot be performed.
 390:      */
 391:     public Object clone() throws CloneNotSupportedException {
 392:         CategoryTableXYDataset clone = (CategoryTableXYDataset) super.clone();
 393:         clone.values = (DefaultKeyedValues2D) this.values.clone();
 394:         clone.intervalDelegate = new IntervalXYDelegate(clone);
 395:         // need to configure the intervalDelegate to match the original
 396:         clone.intervalDelegate.setFixedIntervalWidth(getIntervalWidth());
 397:         clone.intervalDelegate.setAutoWidth(isAutoWidth());
 398:         clone.intervalDelegate.setIntervalPositionFactor(
 399:                 getIntervalPositionFactor());
 400:         return clone;
 401:     }
 402: 
 403: }