Frames | No Frames |
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: * TimePeriodValuesCollection.java 29: * ------------------------------- 30: * (C) Copyright 2003-2007, by Object Refinery Limited. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): -; 34: * 35: * Changes 36: * ------- 37: * 22-Apr-2003 : Version 1 (DG); 38: * 05-May-2004 : Now extends AbstractIntervalXYDataset (DG); 39: * 15-Jul-2004 : Switched getX() with getXValue() and getY() with 40: * getYValue() (DG); 41: * 06-Oct-2004 : Updated for changes in DomainInfo interface (DG); 42: * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG); 43: * ------------- JFREECHART 1.0.x --------------------------------------------- 44: * 03-Oct-2006 : Deprecated get/setDomainIsPointsInTime() (DG); 45: * 11-Jun-2007 : Fixed bug in getDomainBounds() method, and changed default 46: * value for domainIsPointsInTime to false (DG); 47: * 48: */ 49: 50: package org.jfree.data.time; 51: 52: import java.io.Serializable; 53: import java.util.Iterator; 54: import java.util.List; 55: 56: import org.jfree.data.DomainInfo; 57: import org.jfree.data.Range; 58: import org.jfree.data.xy.AbstractIntervalXYDataset; 59: import org.jfree.data.xy.IntervalXYDataset; 60: import org.jfree.util.ObjectUtilities; 61: 62: /** 63: * A collection of {@link TimePeriodValues} objects. 64: * <P> 65: * This class implements the {@link org.jfree.data.xy.XYDataset} interface, as 66: * well as the extended {@link IntervalXYDataset} interface. This makes it a 67: * convenient dataset for use with the {@link org.jfree.chart.plot.XYPlot} 68: * class. 69: */ 70: public class TimePeriodValuesCollection extends AbstractIntervalXYDataset 71: implements IntervalXYDataset, DomainInfo, Serializable { 72: 73: /** For serialization. */ 74: private static final long serialVersionUID = -3077934065236454199L; 75: 76: /** Storage for the time series. */ 77: private List data; 78: 79: /** 80: * The position within a time period to return as the x-value (START, 81: * MIDDLE or END). 82: */ 83: private TimePeriodAnchor xPosition; 84: 85: /** 86: * A flag that indicates that the domain is 'points in time'. If this 87: * flag is true, only the x-value is used to determine the range of values 88: * in the domain, the start and end x-values are ignored. 89: */ 90: private boolean domainIsPointsInTime; 91: 92: /** 93: * Constructs an empty dataset. 94: */ 95: public TimePeriodValuesCollection() { 96: this((TimePeriodValues) null); 97: } 98: 99: /** 100: * Constructs a dataset containing a single series. Additional series can 101: * be added. 102: * 103: * @param series the series (<code>null</code> ignored). 104: */ 105: public TimePeriodValuesCollection(TimePeriodValues series) { 106: this.data = new java.util.ArrayList(); 107: this.xPosition = TimePeriodAnchor.MIDDLE; 108: this.domainIsPointsInTime = false; 109: if (series != null) { 110: this.data.add(series); 111: series.addChangeListener(this); 112: } 113: } 114: 115: /** 116: * Returns the position of the X value within each time period. 117: * 118: * @return The position (never <code>null</code>). 119: * 120: * @see #setXPosition(TimePeriodAnchor) 121: */ 122: public TimePeriodAnchor getXPosition() { 123: return this.xPosition; 124: } 125: 126: /** 127: * Sets the position of the x axis within each time period. 128: * 129: * @param position the position (<code>null</code> not permitted). 130: * 131: * @see #getXPosition() 132: */ 133: public void setXPosition(TimePeriodAnchor position) { 134: if (position == null) { 135: throw new IllegalArgumentException("Null 'position' argument."); 136: } 137: this.xPosition = position; 138: } 139: 140: /** 141: * Returns the number of series in the collection. 142: * 143: * @return The series count. 144: */ 145: public int getSeriesCount() { 146: return this.data.size(); 147: } 148: 149: /** 150: * Returns a series. 151: * 152: * @param series the index of the series (zero-based). 153: * 154: * @return The series. 155: */ 156: public TimePeriodValues getSeries(int series) { 157: if ((series < 0) || (series >= getSeriesCount())) { 158: throw new IllegalArgumentException("Index 'series' out of range."); 159: } 160: return (TimePeriodValues) this.data.get(series); 161: } 162: 163: /** 164: * Returns the key for a series. 165: * 166: * @param series the index of the series (zero-based). 167: * 168: * @return The key for a series. 169: */ 170: public Comparable getSeriesKey(int series) { 171: // defer argument checking 172: return getSeries(series).getKey(); 173: } 174: 175: /** 176: * Adds a series to the collection. A 177: * {@link org.jfree.data.general.DatasetChangeEvent} is sent to all 178: * registered listeners. 179: * 180: * @param series the time series. 181: */ 182: public void addSeries(TimePeriodValues series) { 183: 184: if (series == null) { 185: throw new IllegalArgumentException("Null 'series' argument."); 186: } 187: 188: this.data.add(series); 189: series.addChangeListener(this); 190: fireDatasetChanged(); 191: 192: } 193: 194: /** 195: * Removes the specified series from the collection. 196: * 197: * @param series the series to remove (<code>null</code> not permitted). 198: */ 199: public void removeSeries(TimePeriodValues series) { 200: 201: if (series == null) { 202: throw new IllegalArgumentException("Null 'series' argument."); 203: } 204: this.data.remove(series); 205: series.removeChangeListener(this); 206: fireDatasetChanged(); 207: 208: } 209: 210: /** 211: * Removes a series from the collection. 212: * 213: * @param index the series index (zero-based). 214: */ 215: public void removeSeries(int index) { 216: TimePeriodValues series = getSeries(index); 217: if (series != null) { 218: removeSeries(series); 219: } 220: } 221: 222: /** 223: * Returns the number of items in the specified series. 224: * <P> 225: * This method is provided for convenience. 226: * 227: * @param series the index of the series of interest (zero-based). 228: * 229: * @return The number of items in the specified series. 230: */ 231: public int getItemCount(int series) { 232: return getSeries(series).getItemCount(); 233: } 234: 235: /** 236: * Returns the x-value for the specified series and item. 237: * 238: * @param series the series (zero-based index). 239: * @param item the item (zero-based index). 240: * 241: * @return The x-value for the specified series and item. 242: */ 243: public Number getX(int series, int item) { 244: TimePeriodValues ts = (TimePeriodValues) this.data.get(series); 245: TimePeriodValue dp = ts.getDataItem(item); 246: TimePeriod period = dp.getPeriod(); 247: return new Long(getX(period)); 248: } 249: 250: /** 251: * Returns the x-value for a time period. 252: * 253: * @param period the time period. 254: * 255: * @return The x-value. 256: */ 257: private long getX(TimePeriod period) { 258: 259: if (this.xPosition == TimePeriodAnchor.START) { 260: return period.getStart().getTime(); 261: } 262: else if (this.xPosition == TimePeriodAnchor.MIDDLE) { 263: return period.getStart().getTime() 264: / 2 + period.getEnd().getTime() / 2; 265: } 266: else if (this.xPosition == TimePeriodAnchor.END) { 267: return period.getEnd().getTime(); 268: } 269: else { 270: throw new IllegalStateException("TimePeriodAnchor unknown."); 271: } 272: 273: } 274: 275: /** 276: * Returns the starting X value for the specified series and item. 277: * 278: * @param series the series (zero-based index). 279: * @param item the item (zero-based index). 280: * 281: * @return The starting X value for the specified series and item. 282: */ 283: public Number getStartX(int series, int item) { 284: TimePeriodValues ts = (TimePeriodValues) this.data.get(series); 285: TimePeriodValue dp = ts.getDataItem(item); 286: return new Long(dp.getPeriod().getStart().getTime()); 287: } 288: 289: /** 290: * Returns the ending X value for the specified series and item. 291: * 292: * @param series the series (zero-based index). 293: * @param item the item (zero-based index). 294: * 295: * @return The ending X value for the specified series and item. 296: */ 297: public Number getEndX(int series, int item) { 298: TimePeriodValues ts = (TimePeriodValues) this.data.get(series); 299: TimePeriodValue dp = ts.getDataItem(item); 300: return new Long(dp.getPeriod().getEnd().getTime()); 301: } 302: 303: /** 304: * Returns the y-value for the specified series and item. 305: * 306: * @param series the series (zero-based index). 307: * @param item the item (zero-based index). 308: * 309: * @return The y-value for the specified series and item. 310: */ 311: public Number getY(int series, int item) { 312: TimePeriodValues ts = (TimePeriodValues) this.data.get(series); 313: TimePeriodValue dp = ts.getDataItem(item); 314: return dp.getValue(); 315: } 316: 317: /** 318: * Returns the starting Y value for the specified series and item. 319: * 320: * @param series the series (zero-based index). 321: * @param item the item (zero-based index). 322: * 323: * @return The starting Y value for the specified series and item. 324: */ 325: public Number getStartY(int series, int item) { 326: return getY(series, item); 327: } 328: 329: /** 330: * Returns the ending Y value for the specified series and item. 331: * 332: * @param series the series (zero-based index). 333: * @param item the item (zero-based index). 334: * 335: * @return The ending Y value for the specified series and item. 336: */ 337: public Number getEndY(int series, int item) { 338: return getY(series, item); 339: } 340: 341: /** 342: * Returns the minimum x-value in the dataset. 343: * 344: * @param includeInterval a flag that determines whether or not the 345: * x-interval is taken into account. 346: * 347: * @return The minimum value. 348: */ 349: public double getDomainLowerBound(boolean includeInterval) { 350: double result = Double.NaN; 351: Range r = getDomainBounds(includeInterval); 352: if (r != null) { 353: result = r.getLowerBound(); 354: } 355: return result; 356: } 357: 358: /** 359: * Returns the maximum x-value in the dataset. 360: * 361: * @param includeInterval a flag that determines whether or not the 362: * x-interval is taken into account. 363: * 364: * @return The maximum value. 365: */ 366: public double getDomainUpperBound(boolean includeInterval) { 367: double result = Double.NaN; 368: Range r = getDomainBounds(includeInterval); 369: if (r != null) { 370: result = r.getUpperBound(); 371: } 372: return result; 373: } 374: 375: /** 376: * Returns the range of the values in this dataset's domain. 377: * 378: * @param includeInterval a flag that determines whether or not the 379: * x-interval is taken into account. 380: * 381: * @return The range. 382: */ 383: public Range getDomainBounds(boolean includeInterval) { 384: boolean interval = includeInterval || this.domainIsPointsInTime; 385: Range result = null; 386: Range temp = null; 387: Iterator iterator = this.data.iterator(); 388: while (iterator.hasNext()) { 389: TimePeriodValues series = (TimePeriodValues) iterator.next(); 390: int count = series.getItemCount(); 391: if (count > 0) { 392: TimePeriod start = series.getTimePeriod( 393: series.getMinStartIndex()); 394: TimePeriod end = series.getTimePeriod(series.getMaxEndIndex()); 395: if (!interval) { 396: if (this.xPosition == TimePeriodAnchor.START) { 397: TimePeriod maxStart = series.getTimePeriod( 398: series.getMaxStartIndex()); 399: temp = new Range(start.getStart().getTime(), 400: maxStart.getStart().getTime()); 401: } 402: else if (this.xPosition == TimePeriodAnchor.MIDDLE) { 403: TimePeriod minMiddle = series.getTimePeriod( 404: series.getMinMiddleIndex()); 405: long s1 = minMiddle.getStart().getTime(); 406: long e1 = minMiddle.getEnd().getTime(); 407: TimePeriod maxMiddle = series.getTimePeriod( 408: series.getMaxMiddleIndex()); 409: long s2 = maxMiddle.getStart().getTime(); 410: long e2 = maxMiddle.getEnd().getTime(); 411: temp = new Range(s1 + (e1 - s1) / 2, 412: s2 + (e2 - s2) / 2); 413: } 414: else if (this.xPosition == TimePeriodAnchor.END) { 415: TimePeriod minEnd = series.getTimePeriod( 416: series.getMinEndIndex()); 417: temp = new Range(minEnd.getEnd().getTime(), 418: end.getEnd().getTime()); 419: } 420: } 421: else { 422: temp = new Range(start.getStart().getTime(), 423: end.getEnd().getTime()); 424: } 425: result = Range.combine(result, temp); 426: } 427: } 428: return result; 429: } 430: 431: /** 432: * Tests this instance for equality with an arbitrary object. 433: * 434: * @param obj the object (<code>null</code> permitted). 435: * 436: * @return A boolean. 437: */ 438: public boolean equals(Object obj) { 439: if (obj == this) { 440: return true; 441: } 442: if (!(obj instanceof TimePeriodValuesCollection)) { 443: return false; 444: } 445: TimePeriodValuesCollection that = (TimePeriodValuesCollection) obj; 446: if (this.domainIsPointsInTime != that.domainIsPointsInTime) { 447: return false; 448: } 449: if (this.xPosition != that.xPosition) { 450: return false; 451: } 452: if (!ObjectUtilities.equal(this.data, that.data)) { 453: return false; 454: } 455: return true; 456: } 457: 458: // --- DEPRECATED METHODS ------------------------------------------------- 459: 460: /** 461: * Returns a flag that controls whether the domain is treated as 'points 462: * in time'. This flag is used when determining the max and min values for 463: * the domain. If true, then only the x-values are considered for the max 464: * and min values. If false, then the start and end x-values will also be 465: * taken into consideration 466: * 467: * @return The flag. 468: * 469: * @deprecated This flag is no longer used by JFreeChart (as of version 470: * 1.0.3). 471: */ 472: public boolean getDomainIsPointsInTime() { 473: return this.domainIsPointsInTime; 474: } 475: 476: /** 477: * Sets a flag that controls whether the domain is treated as 'points in 478: * time', or time periods. 479: * 480: * @param flag the new value of the flag. 481: * 482: * @deprecated This flag is no longer used by JFreeChart (as of version 483: * 1.0.3). 484: */ 485: public void setDomainIsPointsInTime(boolean flag) { 486: this.domainIsPointsInTime = flag; 487: } 488: 489: }