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 implements
067: 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, int index) {
094: if (extract == null) {
095: throw new IllegalArgumentException(
096: "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: } 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: } else if (this .extract == TableOrder.BY_COLUMN) {
178: result = this .source.getValue(item, this .index);
179: }
180: return result;
181: }
182:
183: /**
184: * Returns the key at the specified index.
185: *
186: * @param index the item index (in the range <code>0</code> to
187: * <code>getItemCount() - 1</code>).
188: *
189: * @return The key.
190: *
191: * @throws IndexOutOfBoundsException if <code>index</code> is not in the
192: * specified range.
193: */
194: public Comparable getKey(int index) {
195: Comparable result = null;
196: if (index < 0 || index >= getItemCount()) {
197: // this includes the case where the underlying dataset is null
198: throw new IndexOutOfBoundsException("Invalid 'index': "
199: + index);
200: }
201: if (this .extract == TableOrder.BY_ROW) {
202: result = this .source.getColumnKey(index);
203: } else if (this .extract == TableOrder.BY_COLUMN) {
204: result = this .source.getRowKey(index);
205: }
206: return result;
207: }
208:
209: /**
210: * Returns the index for a given key, or <code>-1</code> if there is no
211: * such key.
212: *
213: * @param key the key.
214: *
215: * @return The index for the key, or <code>-1</code>.
216: */
217: public int getIndex(Comparable key) {
218: int result = -1;
219: if (this .source != null) {
220: if (this .extract == TableOrder.BY_ROW) {
221: result = this .source.getColumnIndex(key);
222: } else if (this .extract == TableOrder.BY_COLUMN) {
223: result = this .source.getRowIndex(key);
224: }
225: }
226: return result;
227: }
228:
229: /**
230: * Returns the keys for the dataset.
231: * <p>
232: * If the underlying dataset is <code>null</code>, this method returns an
233: * empty list.
234: *
235: * @return The keys.
236: */
237: public List getKeys() {
238: List result = Collections.EMPTY_LIST;
239: if (this .source != null) {
240: if (this .extract == TableOrder.BY_ROW) {
241: result = this .source.getColumnKeys();
242: } else if (this .extract == TableOrder.BY_COLUMN) {
243: result = this .source.getRowKeys();
244: }
245: }
246: return result;
247: }
248:
249: /**
250: * Returns the value for a given key. If the key is not recognised, the
251: * method should return <code>null</code> (but note that <code>null</code>
252: * can be associated with a valid key also).
253: *
254: * @param key the key.
255: *
256: * @return The value (possibly <code>null</code>).
257: */
258: public Number getValue(Comparable key) {
259: Number result = null;
260: int keyIndex = getIndex(key);
261: if (keyIndex != -1) {
262: if (this .extract == TableOrder.BY_ROW) {
263: result = this .source.getValue(this .index, keyIndex);
264: } else if (this .extract == TableOrder.BY_COLUMN) {
265: result = this .source.getValue(keyIndex, this .index);
266: }
267: }
268: return result;
269: }
270:
271: /**
272: * Sends a {@link DatasetChangeEvent} to all registered listeners, with
273: * this (not the underlying) dataset as the source.
274: *
275: * @param event the event (ignored, a new event with this dataset as the
276: * source is sent to the listeners).
277: */
278: public void datasetChanged(DatasetChangeEvent event) {
279: fireDatasetChanged();
280: }
281:
282: /**
283: * Tests this dataset for equality with an arbitrary object, returning
284: * <code>true</code> if <code>obj</code> is a dataset containing the same
285: * keys and values in the same order as this dataset.
286: *
287: * @param obj the object to test (<code>null</code> permitted).
288: *
289: * @return A boolean.
290: */
291: public boolean equals(Object obj) {
292: if (obj == this ) {
293: return true;
294: }
295: if (!(obj instanceof PieDataset)) {
296: return false;
297: }
298: PieDataset that = (PieDataset) obj;
299: int count = getItemCount();
300: if (that.getItemCount() != count) {
301: return false;
302: }
303: for (int i = 0; i < count; i++) {
304: Comparable k1 = getKey(i);
305: Comparable k2 = that.getKey(i);
306: if (!k1.equals(k2)) {
307: return false;
308: }
309:
310: Number v1 = getValue(i);
311: Number v2 = that.getValue(i);
312: if (v1 == null) {
313: if (v2 != null) {
314: return false;
315: }
316: } else {
317: if (!v1.equals(v2)) {
318: return false;
319: }
320: }
321: }
322: return true;
323: }
324:
325: }
|