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 * CategoryTableXYDataset.java 029 * --------------------------- 030 * (C) Copyright 2004-2008, by Andreas Schroeder and Contributors. 031 * 032 * Original Author: Andreas Schroeder; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * 035 * Changes 036 * ------- 037 * 31-Mar-2004 : Version 1 (AS); 038 * 05-May-2004 : Now extends AbstractIntervalXYDataset (DG); 039 * 15-Jul-2004 : Switched interval access method names (DG); 040 * 18-Aug-2004 : Moved from org.jfree.data --> org.jfree.data.xy (DG); 041 * 17-Nov-2004 : Updates required by changes to DomainInfo interface (DG); 042 * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG); 043 * 05-Oct-2005 : Made the interval delegate a dataset change listener (DG); 044 * 02-Feb-2007 : Removed author tags all over JFreeChart sources (DG); 045 * 22-Apr-2008 : Implemented PublicCloneable, and fixed clone() method (DG); 046 * 047 */ 048 049 package org.jfree.data.xy; 050 051 import org.jfree.data.DefaultKeyedValues2D; 052 import org.jfree.data.DomainInfo; 053 import org.jfree.data.Range; 054 import org.jfree.data.general.DatasetChangeEvent; 055 import org.jfree.data.general.DatasetUtilities; 056 import org.jfree.util.PublicCloneable; 057 058 /** 059 * An implementation variant of the {@link TableXYDataset} where every series 060 * shares the same x-values (required for generating stacked area charts). 061 * This implementation uses a {@link DefaultKeyedValues2D} Object as backend 062 * implementation and is hence more "category oriented" than the {@link 063 * DefaultTableXYDataset} implementation. 064 * <p> 065 * This implementation provides no means to remove data items yet. 066 * This is due to the lack of such facility in the DefaultKeyedValues2D class. 067 * <p> 068 * This class also implements the {@link IntervalXYDataset} interface, but this 069 * implementation is provisional. 070 */ 071 public class CategoryTableXYDataset extends AbstractIntervalXYDataset 072 implements TableXYDataset, IntervalXYDataset, DomainInfo, 073 PublicCloneable { 074 075 /** 076 * The backing data structure. 077 */ 078 private DefaultKeyedValues2D values; 079 080 /** A delegate for controlling the interval width. */ 081 private IntervalXYDelegate intervalDelegate; 082 083 /** 084 * Creates a new empty CategoryTableXYDataset. 085 */ 086 public CategoryTableXYDataset() { 087 this.values = new DefaultKeyedValues2D(true); 088 this.intervalDelegate = new IntervalXYDelegate(this); 089 addChangeListener(this.intervalDelegate); 090 } 091 092 /** 093 * Adds a data item to this dataset and sends a {@link DatasetChangeEvent} 094 * to all registered listeners. 095 * 096 * @param x the x value. 097 * @param y the y value. 098 * @param seriesName the name of the series to add the data item. 099 */ 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 }