Frames | No Frames |
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: * SlidingGanttCategoryDataset.java 29: * -------------------------------- 30: * (C) Copyright 2008, by Object Refinery Limited. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): -; 34: * 35: * Changes 36: * ------- 37: * 09-May-2008 : Version 1 (DG); 38: * 39: */ 40: 41: package org.jfree.data.gantt; 42: 43: import java.util.Collections; 44: import java.util.List; 45: 46: import org.jfree.data.UnknownKeyException; 47: import org.jfree.data.general.AbstractDataset; 48: import org.jfree.data.general.DatasetChangeEvent; 49: import org.jfree.util.PublicCloneable; 50: 51: /** 52: * A {@link GanttCategoryDataset} implementation that presents a subset of the 53: * categories in an underlying dataset. The index of the first "visible" 54: * category can be modified, which provides a means of "sliding" through 55: * the categories in the underlying dataset. 56: * 57: * @since 1.0.10 58: */ 59: public class SlidingGanttCategoryDataset extends AbstractDataset 60: implements GanttCategoryDataset { 61: 62: /** The underlying dataset. */ 63: private GanttCategoryDataset underlying; 64: 65: /** The index of the first category to present. */ 66: private int firstCategoryIndex; 67: 68: /** The maximum number of categories to present. */ 69: private int maximumCategoryCount; 70: 71: /** 72: * Creates a new instance. 73: * 74: * @param underlying the underlying dataset (<code>null</code> not 75: * permitted). 76: * @param firstColumn the index of the first visible column from the 77: * underlying dataset. 78: * @param maxColumns the maximumColumnCount. 79: */ 80: public SlidingGanttCategoryDataset(GanttCategoryDataset underlying, 81: int firstColumn, int maxColumns) { 82: this.underlying = underlying; 83: this.firstCategoryIndex = firstColumn; 84: this.maximumCategoryCount = maxColumns; 85: } 86: 87: /** 88: * Returns the underlying dataset that was supplied to the constructor. 89: * 90: * @return The underlying dataset (never <code>null</code>). 91: */ 92: public GanttCategoryDataset getUnderlyingDataset() { 93: return this.underlying; 94: } 95: 96: /** 97: * Returns the index of the first visible category. 98: * 99: * @return The index. 100: * 101: * @see #setFirstCategoryIndex(int) 102: */ 103: public int getFirstCategoryIndex() { 104: return this.firstCategoryIndex; 105: } 106: 107: /** 108: * Sets the index of the first category that should be used from the 109: * underlying dataset, and sends a {@link DatasetChangeEvent} to all 110: * registered listeners. 111: * 112: * @param first the index. 113: * 114: * @see #getFirstCategoryIndex() 115: */ 116: public void setFirstCategoryIndex(int first) { 117: if (first < 0 || first >= this.underlying.getColumnCount()) { 118: throw new IllegalArgumentException("Invalid index."); 119: } 120: this.firstCategoryIndex = first; 121: fireDatasetChanged(); 122: } 123: 124: /** 125: * Returns the maximum category count. 126: * 127: * @return The maximum category count. 128: * 129: * @see #setMaximumCategoryCount(int) 130: */ 131: public int getMaximumCategoryCount() { 132: return this.maximumCategoryCount; 133: } 134: 135: /** 136: * Sets the maximum category count and sends a {@link DatasetChangeEvent} 137: * to all registered listeners. 138: * 139: * @param max the maximum. 140: * 141: * @see #getMaximumCategoryCount() 142: */ 143: public void setMaximumCategoryCount(int max) { 144: if (max < 0) { 145: throw new IllegalArgumentException("Requires 'max' >= 0."); 146: } 147: this.maximumCategoryCount = max; 148: fireDatasetChanged(); 149: } 150: 151: /** 152: * Returns the index of the last column for this dataset, or -1. 153: * 154: * @return The index. 155: */ 156: private int lastCategoryIndex() { 157: if (this.maximumCategoryCount == 0) { 158: return -1; 159: } 160: return Math.min(this.firstCategoryIndex + this.maximumCategoryCount, 161: this.underlying.getColumnCount()) - 1; 162: } 163: 164: /** 165: * Returns the index for the specified column key. 166: * 167: * @param key the key. 168: * 169: * @return The column index, or -1 if the key is not recognised. 170: */ 171: public int getColumnIndex(Comparable key) { 172: int index = this.underlying.getColumnIndex(key); 173: if (index >= this.firstCategoryIndex && index <= lastCategoryIndex()) { 174: return index - this.firstCategoryIndex; 175: } 176: return -1; // we didn't find the key 177: } 178: 179: /** 180: * Returns the column key for a given index. 181: * 182: * @param column the column index (zero-based). 183: * 184: * @return The column key. 185: * 186: * @throws IndexOutOfBoundsException if <code>row</code> is out of bounds. 187: */ 188: public Comparable getColumnKey(int column) { 189: return this.underlying.getColumnKey(column + this.firstCategoryIndex); 190: } 191: 192: /** 193: * Returns the column keys. 194: * 195: * @return The keys. 196: * 197: * @see #getColumnKey(int) 198: */ 199: public List getColumnKeys() { 200: List result = new java.util.ArrayList(); 201: int last = lastCategoryIndex(); 202: for (int i = this.firstCategoryIndex; i < last; i++) { 203: result.add(this.underlying.getColumnKey(i)); 204: } 205: return Collections.unmodifiableList(result); 206: } 207: 208: /** 209: * Returns the row index for a given key. 210: * 211: * @param key the row key. 212: * 213: * @return The row index, or <code>-1</code> if the key is unrecognised. 214: */ 215: public int getRowIndex(Comparable key) { 216: return this.underlying.getRowIndex(key); 217: } 218: 219: /** 220: * Returns the row key for a given index. 221: * 222: * @param row the row index (zero-based). 223: * 224: * @return The row key. 225: * 226: * @throws IndexOutOfBoundsException if <code>row</code> is out of bounds. 227: */ 228: public Comparable getRowKey(int row) { 229: return this.underlying.getRowKey(row); 230: } 231: 232: /** 233: * Returns the row keys. 234: * 235: * @return The keys. 236: */ 237: public List getRowKeys() { 238: return this.underlying.getRowKeys(); 239: } 240: 241: /** 242: * Returns the value for a pair of keys. 243: * 244: * @param rowKey the row key (<code>null</code> not permitted). 245: * @param columnKey the column key (<code>null</code> not permitted). 246: * 247: * @return The value (possibly <code>null</code>). 248: * 249: * @throws UnknownKeyException if either key is not defined in the dataset. 250: */ 251: public Number getValue(Comparable rowKey, Comparable columnKey) { 252: int r = getRowIndex(rowKey); 253: int c = getColumnIndex(columnKey); 254: if (c != -1) { 255: return this.underlying.getValue(r, c + this.firstCategoryIndex); 256: } 257: else { 258: throw new UnknownKeyException("Unknown columnKey: " + columnKey); 259: } 260: } 261: 262: /** 263: * Returns the number of columns in the table. 264: * 265: * @return The column count. 266: */ 267: public int getColumnCount() { 268: int last = lastCategoryIndex(); 269: if (last == -1) { 270: return 0; 271: } 272: else { 273: return Math.max(last - this.firstCategoryIndex + 1, 0); 274: } 275: } 276: 277: /** 278: * Returns the number of rows in the table. 279: * 280: * @return The row count. 281: */ 282: public int getRowCount() { 283: return this.underlying.getRowCount(); 284: } 285: 286: /** 287: * Returns a value from the table. 288: * 289: * @param row the row index (zero-based). 290: * @param column the column index (zero-based). 291: * 292: * @return The value (possibly <code>null</code>). 293: */ 294: public Number getValue(int row, int column) { 295: return this.underlying.getValue(row, column + this.firstCategoryIndex); 296: } 297: 298: /** 299: * Returns the percent complete for a given item. 300: * 301: * @param rowKey the row key. 302: * @param columnKey the column key. 303: * 304: * @return The percent complete. 305: */ 306: public Number getPercentComplete(Comparable rowKey, Comparable columnKey) { 307: int r = getRowIndex(rowKey); 308: int c = getColumnIndex(columnKey); 309: if (c != -1) { 310: return this.underlying.getPercentComplete(r, 311: c + this.firstCategoryIndex); 312: } 313: else { 314: throw new UnknownKeyException("Unknown columnKey: " + columnKey); 315: } 316: } 317: 318: /** 319: * Returns the percentage complete value of a sub-interval for a given item. 320: * 321: * @param rowKey the row key. 322: * @param columnKey the column key. 323: * @param subinterval the sub-interval. 324: * 325: * @return The percent complete value (possibly <code>null</code>). 326: * 327: * @see #getPercentComplete(int, int, int) 328: */ 329: public Number getPercentComplete(Comparable rowKey, Comparable columnKey, 330: int subinterval) { 331: int r = getRowIndex(rowKey); 332: int c = getColumnIndex(columnKey); 333: if (c != -1) { 334: return this.underlying.getPercentComplete(r, 335: c + this.firstCategoryIndex, subinterval); 336: } 337: else { 338: throw new UnknownKeyException("Unknown columnKey: " + columnKey); 339: } 340: } 341: 342: /** 343: * Returns the end value of a sub-interval for a given item. 344: * 345: * @param rowKey the row key. 346: * @param columnKey the column key. 347: * @param subinterval the sub-interval. 348: * 349: * @return The end value (possibly <code>null</code>). 350: * 351: * @see #getStartValue(Comparable, Comparable, int) 352: */ 353: public Number getEndValue(Comparable rowKey, Comparable columnKey, 354: int subinterval) { 355: int r = getRowIndex(rowKey); 356: int c = getColumnIndex(columnKey); 357: if (c != -1) { 358: return this.underlying.getEndValue(r, 359: c + this.firstCategoryIndex, subinterval); 360: } 361: else { 362: throw new UnknownKeyException("Unknown columnKey: " + columnKey); 363: } 364: } 365: 366: /** 367: * Returns the end value of a sub-interval for a given item. 368: * 369: * @param row the row index (zero-based). 370: * @param column the column index (zero-based). 371: * @param subinterval the sub-interval. 372: * 373: * @return The end value (possibly <code>null</code>). 374: * 375: * @see #getStartValue(int, int, int) 376: */ 377: public Number getEndValue(int row, int column, int subinterval) { 378: return this.underlying.getEndValue(row, 379: column + this.firstCategoryIndex, subinterval); 380: } 381: 382: /** 383: * Returns the percent complete for a given item. 384: * 385: * @param series the row index (zero-based). 386: * @param category the column index (zero-based). 387: * 388: * @return The percent complete. 389: */ 390: public Number getPercentComplete(int series, int category) { 391: return this.underlying.getPercentComplete(series, 392: category + this.firstCategoryIndex); 393: } 394: 395: /** 396: * Returns the percentage complete value of a sub-interval for a given item. 397: * 398: * @param row the row index (zero-based). 399: * @param column the column index (zero-based). 400: * @param subinterval the sub-interval. 401: * 402: * @return The percent complete value (possibly <code>null</code>). 403: * 404: * @see #getPercentComplete(Comparable, Comparable, int) 405: */ 406: public Number getPercentComplete(int row, int column, int subinterval) { 407: return this.underlying.getPercentComplete(row, 408: column + this.firstCategoryIndex, subinterval); 409: } 410: 411: /** 412: * Returns the start value of a sub-interval for a given item. 413: * 414: * @param rowKey the row key. 415: * @param columnKey the column key. 416: * @param subinterval the sub-interval. 417: * 418: * @return The start value (possibly <code>null</code>). 419: * 420: * @see #getEndValue(Comparable, Comparable, int) 421: */ 422: public Number getStartValue(Comparable rowKey, Comparable columnKey, 423: int subinterval) { 424: int r = getRowIndex(rowKey); 425: int c = getColumnIndex(columnKey); 426: if (c != -1) { 427: return this.underlying.getStartValue(r, 428: c + this.firstCategoryIndex, subinterval); 429: } 430: else { 431: throw new UnknownKeyException("Unknown columnKey: " + columnKey); 432: } 433: } 434: 435: /** 436: * Returns the start value of a sub-interval for a given item. 437: * 438: * @param row the row index (zero-based). 439: * @param column the column index (zero-based). 440: * @param subinterval the sub-interval index (zero-based). 441: * 442: * @return The start value (possibly <code>null</code>). 443: * 444: * @see #getEndValue(int, int, int) 445: */ 446: public Number getStartValue(int row, int column, int subinterval) { 447: return this.underlying.getStartValue(row, 448: column + this.firstCategoryIndex, subinterval); 449: } 450: 451: /** 452: * Returns the number of sub-intervals for a given item. 453: * 454: * @param rowKey the row key. 455: * @param columnKey the column key. 456: * 457: * @return The sub-interval count. 458: * 459: * @see #getSubIntervalCount(int, int) 460: */ 461: public int getSubIntervalCount(Comparable rowKey, Comparable columnKey) { 462: int r = getRowIndex(rowKey); 463: int c = getColumnIndex(columnKey); 464: if (c != -1) { 465: return this.underlying.getSubIntervalCount(r, 466: c + this.firstCategoryIndex); 467: } 468: else { 469: throw new UnknownKeyException("Unknown columnKey: " + columnKey); 470: } 471: } 472: 473: /** 474: * Returns the number of sub-intervals for a given item. 475: * 476: * @param row the row index (zero-based). 477: * @param column the column index (zero-based). 478: * 479: * @return The sub-interval count. 480: * 481: * @see #getSubIntervalCount(Comparable, Comparable) 482: */ 483: public int getSubIntervalCount(int row, int column) { 484: return this.underlying.getSubIntervalCount(row, 485: column + this.firstCategoryIndex); 486: } 487: 488: /** 489: * Returns the start value for the interval for a given series and category. 490: * 491: * @param rowKey the series key. 492: * @param columnKey the category key. 493: * 494: * @return The start value (possibly <code>null</code>). 495: * 496: * @see #getEndValue(Comparable, Comparable) 497: */ 498: public Number getStartValue(Comparable rowKey, Comparable columnKey) { 499: int r = getRowIndex(rowKey); 500: int c = getColumnIndex(columnKey); 501: if (c != -1) { 502: return this.underlying.getStartValue(r, c + this.firstCategoryIndex); 503: } 504: else { 505: throw new UnknownKeyException("Unknown columnKey: " + columnKey); 506: } 507: } 508: 509: /** 510: * Returns the start value for the interval for a given series and category. 511: * 512: * @param row the series (zero-based index). 513: * @param column the category (zero-based index). 514: * 515: * @return The start value (possibly <code>null</code>). 516: * 517: * @see #getEndValue(int, int) 518: */ 519: public Number getStartValue(int row, int column) { 520: return this.underlying.getStartValue(row, 521: column + this.firstCategoryIndex); 522: } 523: 524: /** 525: * Returns the end value for the interval for a given series and category. 526: * 527: * @param rowKey the series key. 528: * @param columnKey the category key. 529: * 530: * @return The end value (possibly <code>null</code>). 531: * 532: * @see #getStartValue(Comparable, Comparable) 533: */ 534: public Number getEndValue(Comparable rowKey, Comparable columnKey) { 535: int r = getRowIndex(rowKey); 536: int c = getColumnIndex(columnKey); 537: if (c != -1) { 538: return this.underlying.getEndValue(r, c + this.firstCategoryIndex); 539: } 540: else { 541: throw new UnknownKeyException("Unknown columnKey: " + columnKey); 542: } 543: } 544: 545: /** 546: * Returns the end value for the interval for a given series and category. 547: * 548: * @param series the series (zero-based index). 549: * @param category the category (zero-based index). 550: * 551: * @return The end value (possibly <code>null</code>). 552: */ 553: public Number getEndValue(int series, int category) { 554: return this.underlying.getEndValue(series, 555: category + this.firstCategoryIndex); 556: } 557: 558: /** 559: * Tests this <code>SlidingCategoryDataset</code> for equality with an 560: * arbitrary object. 561: * 562: * @param obj the object (<code>null</code> permitted). 563: * 564: * @return A boolean. 565: */ 566: public boolean equals(Object obj) { 567: if (obj == this) { 568: return true; 569: } 570: if (!(obj instanceof SlidingGanttCategoryDataset)) { 571: return false; 572: } 573: SlidingGanttCategoryDataset that = (SlidingGanttCategoryDataset) obj; 574: if (this.firstCategoryIndex != that.firstCategoryIndex) { 575: return false; 576: } 577: if (this.maximumCategoryCount != that.maximumCategoryCount) { 578: return false; 579: } 580: if (!this.underlying.equals(that.underlying)) { 581: return false; 582: } 583: return true; 584: } 585: 586: /** 587: * Returns an independent copy of the dataset. Note that: 588: * <ul> 589: * <li>the underlying dataset is only cloned if it implements the 590: * {@link PublicCloneable} interface;</li> 591: * <li>the listeners registered with this dataset are not carried over to 592: * the cloned dataset.</li> 593: * </ul> 594: * 595: * @return An independent copy of the dataset. 596: * 597: * @throws CloneNotSupportedException if the dataset cannot be cloned for 598: * any reason. 599: */ 600: public Object clone() throws CloneNotSupportedException { 601: SlidingGanttCategoryDataset clone 602: = (SlidingGanttCategoryDataset) super.clone(); 603: if (this.underlying instanceof PublicCloneable) { 604: PublicCloneable pc = (PublicCloneable) this.underlying; 605: clone.underlying = (GanttCategoryDataset) pc.clone(); 606: } 607: return clone; 608: } 609: 610: }