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