001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2007, 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-2007, 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 }