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 }