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