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 * CategoryToPieDataset.java 029 * ------------------------- 030 * (C) Copyright 2003-2008, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Christian W. Zuckschwerdt; 034 * 035 * Changes 036 * ------- 037 * 23-Jan-2003 : Version 1 (DG); 038 * 30-Jul-2003 : Pass through DatasetChangeEvent (CZ); 039 * 29-Jan-2004 : Replaced 'extract' int with TableOrder (DG); 040 * 11-Jan-2005 : Removed deprecated code in preparation for the 1.0.0 041 * release (DG); 042 * ------------- JFREECHART 1.0.0 RELEASED ------------------------------------ 043 * 26-Jul-2006 : Added serialVersionUID, changed constructor to allow null 044 * for source, and added getSource(), getExtractType() and 045 * getExtractIndex() methods - see feature request 1477915 (DG); 046 * 047 */ 048 049 package org.jfree.data.category; 050 051 import java.util.Collections; 052 import java.util.List; 053 054 import org.jfree.data.general.AbstractDataset; 055 import org.jfree.data.general.DatasetChangeEvent; 056 import org.jfree.data.general.DatasetChangeListener; 057 import org.jfree.data.general.PieDataset; 058 import org.jfree.util.TableOrder; 059 060 /** 061 * A {@link PieDataset} implementation that obtains its data from one row or 062 * column of a {@link CategoryDataset}. 063 */ 064 public class CategoryToPieDataset extends AbstractDataset 065 implements PieDataset, DatasetChangeListener { 066 067 /** For serialization. */ 068 static final long serialVersionUID = 5516396319762189617L; 069 070 /** The source. */ 071 private CategoryDataset source; 072 073 /** The extract type. */ 074 private TableOrder extract; 075 076 /** The row or column index. */ 077 private int index; 078 079 /** 080 * An adaptor class that converts any {@link CategoryDataset} into a 081 * {@link PieDataset}, by taking the values from a single row or column. 082 * <p> 083 * If <code>source</code> is <code>null</code>, the created dataset will 084 * be empty. 085 * 086 * @param source the source dataset (<code>null</code> permitted). 087 * @param extract extract data from rows or columns? (<code>null</code> 088 * not permitted). 089 * @param index the row or column index. 090 */ 091 public CategoryToPieDataset(CategoryDataset source, 092 TableOrder extract, 093 int index) { 094 if (extract == null) { 095 throw new IllegalArgumentException("Null 'extract' argument."); 096 } 097 this.source = source; 098 if (this.source != null) { 099 this.source.addChangeListener(this); 100 } 101 this.extract = extract; 102 this.index = index; 103 } 104 105 /** 106 * Returns the underlying dataset. 107 * 108 * @return The underlying dataset (possibly <code>null</code>). 109 * 110 * @since 1.0.2 111 */ 112 public CategoryDataset getUnderlyingDataset() { 113 return this.source; 114 } 115 116 /** 117 * Returns the extract type, which determines whether data is read from 118 * one row or one column of the underlying dataset. 119 * 120 * @return The extract type. 121 * 122 * @since 1.0.2 123 */ 124 public TableOrder getExtractType() { 125 return this.extract; 126 } 127 128 /** 129 * Returns the index of the row or column from which to extract the data. 130 * 131 * @return The extract index. 132 * 133 * @since 1.0.2 134 */ 135 public int getExtractIndex() { 136 return this.index; 137 } 138 139 /** 140 * Returns the number of items (values) in the collection. If the 141 * underlying dataset is <code>null</code>, this method returns zero. 142 * 143 * @return The item count. 144 */ 145 public int getItemCount() { 146 int result = 0; 147 if (this.source != null) { 148 if (this.extract == TableOrder.BY_ROW) { 149 result = this.source.getColumnCount(); 150 } 151 else if (this.extract == TableOrder.BY_COLUMN) { 152 result = this.source.getRowCount(); 153 } 154 } 155 return result; 156 } 157 158 /** 159 * Returns a value from the dataset. 160 * 161 * @param item the item index (zero-based). 162 * 163 * @return The value (possibly <code>null</code>). 164 * 165 * @throws IndexOutOfBoundsException if <code>item</code> is not in the 166 * range <code>0</code> to <code>getItemCount() - 1</code>. 167 */ 168 public Number getValue(int item) { 169 Number result = null; 170 if (item < 0 || item >= getItemCount()) { 171 // this will include the case where the underlying dataset is null 172 throw new IndexOutOfBoundsException( 173 "The 'item' index is out of bounds."); 174 } 175 if (this.extract == TableOrder.BY_ROW) { 176 result = this.source.getValue(this.index, item); 177 } 178 else if (this.extract == TableOrder.BY_COLUMN) { 179 result = this.source.getValue(item, this.index); 180 } 181 return result; 182 } 183 184 /** 185 * Returns the key at the specified index. 186 * 187 * @param index the item index (in the range <code>0</code> to 188 * <code>getItemCount() - 1</code>). 189 * 190 * @return The key. 191 * 192 * @throws IndexOutOfBoundsException if <code>index</code> is not in the 193 * specified range. 194 */ 195 public Comparable getKey(int index) { 196 Comparable result = null; 197 if (index < 0 || index >= getItemCount()) { 198 // this includes the case where the underlying dataset is null 199 throw new IndexOutOfBoundsException("Invalid 'index': " + index); 200 } 201 if (this.extract == TableOrder.BY_ROW) { 202 result = this.source.getColumnKey(index); 203 } 204 else if (this.extract == TableOrder.BY_COLUMN) { 205 result = this.source.getRowKey(index); 206 } 207 return result; 208 } 209 210 /** 211 * Returns the index for a given key, or <code>-1</code> if there is no 212 * such key. 213 * 214 * @param key the key. 215 * 216 * @return The index for the key, or <code>-1</code>. 217 */ 218 public int getIndex(Comparable key) { 219 int result = -1; 220 if (this.source != null) { 221 if (this.extract == TableOrder.BY_ROW) { 222 result = this.source.getColumnIndex(key); 223 } 224 else if (this.extract == TableOrder.BY_COLUMN) { 225 result = this.source.getRowIndex(key); 226 } 227 } 228 return result; 229 } 230 231 /** 232 * Returns the keys for the dataset. 233 * <p> 234 * If the underlying dataset is <code>null</code>, this method returns an 235 * empty list. 236 * 237 * @return The keys. 238 */ 239 public List getKeys() { 240 List result = Collections.EMPTY_LIST; 241 if (this.source != null) { 242 if (this.extract == TableOrder.BY_ROW) { 243 result = this.source.getColumnKeys(); 244 } 245 else if (this.extract == TableOrder.BY_COLUMN) { 246 result = this.source.getRowKeys(); 247 } 248 } 249 return result; 250 } 251 252 /** 253 * Returns the value for a given key. If the key is not recognised, the 254 * method should return <code>null</code> (but note that <code>null</code> 255 * can be associated with a valid key also). 256 * 257 * @param key the key. 258 * 259 * @return The value (possibly <code>null</code>). 260 */ 261 public Number getValue(Comparable key) { 262 Number result = null; 263 int keyIndex = getIndex(key); 264 if (keyIndex != -1) { 265 if (this.extract == TableOrder.BY_ROW) { 266 result = this.source.getValue(this.index, keyIndex); 267 } 268 else if (this.extract == TableOrder.BY_COLUMN) { 269 result = this.source.getValue(keyIndex, this.index); 270 } 271 } 272 return result; 273 } 274 275 /** 276 * Sends a {@link DatasetChangeEvent} to all registered listeners, with 277 * this (not the underlying) dataset as the source. 278 * 279 * @param event the event (ignored, a new event with this dataset as the 280 * source is sent to the listeners). 281 */ 282 public void datasetChanged(DatasetChangeEvent event) { 283 fireDatasetChanged(); 284 } 285 286 /** 287 * Tests this dataset for equality with an arbitrary object, returning 288 * <code>true</code> if <code>obj</code> is a dataset containing the same 289 * keys and values in the same order as this dataset. 290 * 291 * @param obj the object to test (<code>null</code> permitted). 292 * 293 * @return A boolean. 294 */ 295 public boolean equals(Object obj) { 296 if (obj == this) { 297 return true; 298 } 299 if (!(obj instanceof PieDataset)) { 300 return false; 301 } 302 PieDataset that = (PieDataset) obj; 303 int count = getItemCount(); 304 if (that.getItemCount() != count) { 305 return false; 306 } 307 for (int i = 0; i < count; i++) { 308 Comparable k1 = getKey(i); 309 Comparable k2 = that.getKey(i); 310 if (!k1.equals(k2)) { 311 return false; 312 } 313 314 Number v1 = getValue(i); 315 Number v2 = that.getValue(i); 316 if (v1 == null) { 317 if (v2 != null) { 318 return false; 319 } 320 } 321 else { 322 if (!v1.equals(v2)) { 323 return false; 324 } 325 } 326 } 327 return true; 328 } 329 330 }