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