001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2013, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ------------------------- 028 * CategoryToPieDataset.java 029 * ------------------------- 030 * (C) Copyright 2003-2013, 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 * 03-Jul-2013 : Use ParamChecks (DG); 047 * 048 */ 049 050package org.jfree.data.category; 051 052import java.util.Collections; 053import java.util.List; 054import org.jfree.chart.util.ParamChecks; 055 056import org.jfree.data.general.AbstractDataset; 057import org.jfree.data.general.DatasetChangeEvent; 058import org.jfree.data.general.DatasetChangeListener; 059import org.jfree.data.general.PieDataset; 060import 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 */ 066public class CategoryToPieDataset extends AbstractDataset 067 implements PieDataset, DatasetChangeListener { 068 069 /** For serialization. */ 070 static final long serialVersionUID = 5516396319762189617L; 071 072 /** The source. */ 073 private CategoryDataset source; 074 075 /** The extract type. */ 076 private TableOrder extract; 077 078 /** The row or column index. */ 079 private int index; 080 081 /** 082 * An adaptor class that converts any {@link CategoryDataset} into a 083 * {@link PieDataset}, by taking the values from a single row or column. 084 * <p> 085 * If <code>source</code> is <code>null</code>, the created dataset will 086 * be empty. 087 * 088 * @param source the source dataset (<code>null</code> permitted). 089 * @param extract extract data from rows or columns? (<code>null</code> 090 * not permitted). 091 * @param index the row or column index. 092 */ 093 public CategoryToPieDataset(CategoryDataset source, TableOrder extract, 094 int index) { 095 ParamChecks.nullNotPermitted(extract, "extract"); 096 this.source = source; 097 if (this.source != null) { 098 this.source.addChangeListener(this); 099 } 100 this.extract = extract; 101 this.index = index; 102 } 103 104 /** 105 * Returns the underlying dataset. 106 * 107 * @return The underlying dataset (possibly <code>null</code>). 108 * 109 * @since 1.0.2 110 */ 111 public CategoryDataset getUnderlyingDataset() { 112 return this.source; 113 } 114 115 /** 116 * Returns the extract type, which determines whether data is read from 117 * one row or one column of the underlying dataset. 118 * 119 * @return The extract type. 120 * 121 * @since 1.0.2 122 */ 123 public TableOrder getExtractType() { 124 return this.extract; 125 } 126 127 /** 128 * Returns the index of the row or column from which to extract the data. 129 * 130 * @return The extract index. 131 * 132 * @since 1.0.2 133 */ 134 public int getExtractIndex() { 135 return this.index; 136 } 137 138 /** 139 * Returns the number of items (values) in the collection. If the 140 * underlying dataset is <code>null</code>, this method returns zero. 141 * 142 * @return The item count. 143 */ 144 @Override 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 @Override 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 @Override 197 public Comparable getKey(int index) { 198 Comparable result = null; 199 if (index < 0 || index >= getItemCount()) { 200 // this includes the case where the underlying dataset is null 201 throw new IndexOutOfBoundsException("Invalid 'index': " + index); 202 } 203 if (this.extract == TableOrder.BY_ROW) { 204 result = this.source.getColumnKey(index); 205 } 206 else if (this.extract == TableOrder.BY_COLUMN) { 207 result = this.source.getRowKey(index); 208 } 209 return result; 210 } 211 212 /** 213 * Returns the index for a given key, or <code>-1</code> if there is no 214 * such key. 215 * 216 * @param key the key. 217 * 218 * @return The index for the key, or <code>-1</code>. 219 */ 220 @Override 221 public int getIndex(Comparable key) { 222 int result = -1; 223 if (this.source != null) { 224 if (this.extract == TableOrder.BY_ROW) { 225 result = this.source.getColumnIndex(key); 226 } 227 else if (this.extract == TableOrder.BY_COLUMN) { 228 result = this.source.getRowIndex(key); 229 } 230 } 231 return result; 232 } 233 234 /** 235 * Returns the keys for the dataset. 236 * <p> 237 * If the underlying dataset is <code>null</code>, this method returns an 238 * empty list. 239 * 240 * @return The keys. 241 */ 242 @Override 243 public List getKeys() { 244 List result = Collections.EMPTY_LIST; 245 if (this.source != null) { 246 if (this.extract == TableOrder.BY_ROW) { 247 result = this.source.getColumnKeys(); 248 } 249 else if (this.extract == TableOrder.BY_COLUMN) { 250 result = this.source.getRowKeys(); 251 } 252 } 253 return result; 254 } 255 256 /** 257 * Returns the value for a given key. If the key is not recognised, the 258 * method should return <code>null</code> (but note that <code>null</code> 259 * can be associated with a valid key also). 260 * 261 * @param key the key. 262 * 263 * @return The value (possibly <code>null</code>). 264 */ 265 @Override 266 public Number getValue(Comparable key) { 267 Number result = null; 268 int keyIndex = getIndex(key); 269 if (keyIndex != -1) { 270 if (this.extract == TableOrder.BY_ROW) { 271 result = this.source.getValue(this.index, keyIndex); 272 } 273 else if (this.extract == TableOrder.BY_COLUMN) { 274 result = this.source.getValue(keyIndex, this.index); 275 } 276 } 277 return result; 278 } 279 280 /** 281 * Sends a {@link DatasetChangeEvent} to all registered listeners, with 282 * this (not the underlying) dataset as the source. 283 * 284 * @param event the event (ignored, a new event with this dataset as the 285 * source is sent to the listeners). 286 */ 287 @Override 288 public void datasetChanged(DatasetChangeEvent event) { 289 fireDatasetChanged(); 290 } 291 292 /** 293 * Tests this dataset for equality with an arbitrary object, returning 294 * <code>true</code> if <code>obj</code> is a dataset containing the same 295 * keys and values in the same order as this dataset. 296 * 297 * @param obj the object to test (<code>null</code> permitted). 298 * 299 * @return A boolean. 300 */ 301 @Override 302 public boolean equals(Object obj) { 303 if (obj == this) { 304 return true; 305 } 306 if (!(obj instanceof PieDataset)) { 307 return false; 308 } 309 PieDataset that = (PieDataset) obj; 310 int count = getItemCount(); 311 if (that.getItemCount() != count) { 312 return false; 313 } 314 for (int i = 0; i < count; i++) { 315 Comparable k1 = getKey(i); 316 Comparable k2 = that.getKey(i); 317 if (!k1.equals(k2)) { 318 return false; 319 } 320 321 Number v1 = getValue(i); 322 Number v2 = that.getValue(i); 323 if (v1 == null) { 324 if (v2 != null) { 325 return false; 326 } 327 } 328 else { 329 if (!v1.equals(v2)) { 330 return false; 331 } 332 } 333 } 334 return true; 335 } 336 337}