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: * SlidingCategoryDataset.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: * 08-May-2008 : Version 1 (DG); 38: * 39: */ 40: 41: package org.jfree.data.category; 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 CategoryDataset} 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 SlidingCategoryDataset extends AbstractDataset 60: implements CategoryDataset { 61: 62: /** The underlying dataset. */ 63: private CategoryDataset 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 SlidingCategoryDataset(CategoryDataset underlying, int firstColumn, 81: 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 CategoryDataset 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: * Tests this <code>SlidingCategoryDataset</code> for equality with an 300: * arbitrary object. 301: * 302: * @param obj the object (<code>null</code> permitted). 303: * 304: * @return A boolean. 305: */ 306: public boolean equals(Object obj) { 307: if (obj == this) { 308: return true; 309: } 310: if (!(obj instanceof SlidingCategoryDataset)) { 311: return false; 312: } 313: SlidingCategoryDataset that = (SlidingCategoryDataset) obj; 314: if (this.firstCategoryIndex != that.firstCategoryIndex) { 315: return false; 316: } 317: if (this.maximumCategoryCount != that.maximumCategoryCount) { 318: return false; 319: } 320: if (!this.underlying.equals(that.underlying)) { 321: return false; 322: } 323: return true; 324: } 325: 326: /** 327: * Returns an independent copy of the dataset. Note that: 328: * <ul> 329: * <li>the underlying dataset is only cloned if it implements the 330: * {@link PublicCloneable} interface;</li> 331: * <li>the listeners registered with this dataset are not carried over to 332: * the cloned dataset.</li> 333: * </ul> 334: * 335: * @return An independent copy of the dataset. 336: * 337: * @throws CloneNotSupportedException if the dataset cannot be cloned for 338: * any reason. 339: */ 340: public Object clone() throws CloneNotSupportedException { 341: SlidingCategoryDataset clone = (SlidingCategoryDataset) super.clone(); 342: if (this.underlying instanceof PublicCloneable) { 343: PublicCloneable pc = (PublicCloneable) this.underlying; 344: clone.underlying = (CategoryDataset) pc.clone(); 345: } 346: return clone; 347: } 348: 349: }