1. /* ===========================================================
  2. * JFreeChart : a free chart library for the Java(tm) platform
  3. * ===========================================================
  4. *
  5. * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
  6. *
  7. * Project Info: http://www.jfree.org/jfreechart/index.html
  8. *
  9. * This library is free software; you can redistribute it and/or modify it under the terms
  10. * of the GNU Lesser General Public License as published by the Free Software Foundation;
  11. * either version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  14. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15. * See the GNU Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public License along with this
  18. * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  19. * Boston, MA 02111-1307, USA.
  20. *
  21. * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  22. * in the United States and other countries.]
  23. *
  24. * -----------------------
  25. * XYSeriesCollection.java
  26. * -----------------------
  27. * (C) Copyright 2001-2005, by Object Refinery Limited and Contributors.
  28. *
  29. * Original Author: David Gilbert (for Object Refinery Limited);
  30. * Contributor(s): Aaron Metzger;
  31. *
  32. * $Id: XYSeriesCollection.java,v 1.8 2005/01/11 17:35:03 mungady Exp $
  33. *
  34. * Changes
  35. * -------
  36. * 15-Nov-2001 : Version 1 (DG);
  37. * 03-Apr-2002 : Added change listener code (DG);
  38. * 29-Apr-2002 : Added removeSeries, removeAllSeries methods (ARM);
  39. * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
  40. * 26-Mar-2003 : Implemented Serializable (DG);
  41. * 04-Aug-2003 : Added getSeries() method (DG);
  42. * 31-Mar-2004 : Modified to use an XYIntervalDelegate.
  43. * 05-May-2004 : Now extends AbstractIntervalXYDataset (DG);
  44. * 18-Aug-2004 : Moved from org.jfree.data --> org.jfree.data.xy (DG);
  45. * 17-Nov-2004 : Updated for changes to DomainInfo interface (DG);
  46. * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
  47. *
  48. */
  49. package org.jfree.data.xy;
  50. import java.io.Serializable;
  51. import java.util.Collections;
  52. import java.util.List;
  53. import org.jfree.data.DomainInfo;
  54. import org.jfree.data.Range;
  55. import org.jfree.data.general.DatasetChangeEvent;
  56. import org.jfree.util.ObjectUtilities;
  57. /**
  58. * Represents a collection of {@link XYSeries} objects that can be used as a dataset.
  59. */
  60. public class XYSeriesCollection extends AbstractIntervalXYDataset
  61. implements IntervalXYDataset, DomainInfo, Serializable {
  62. /** The series that are included in the collection. */
  63. private List data;
  64. /** The interval delegate (used to calculate the start and end x-values). */
  65. private IntervalXYDelegate intervalDelegate;
  66. /**
  67. * Constructs an empty dataset.
  68. */
  69. public XYSeriesCollection() {
  70. this(null);
  71. }
  72. /**
  73. * Constructs a dataset and populates it with a single series.
  74. *
  75. * @param series the series (<code>null</code> ignored).
  76. */
  77. public XYSeriesCollection(XYSeries series) {
  78. this.data = new java.util.ArrayList();
  79. this.intervalDelegate = new IntervalXYDelegate(this, false);
  80. if (series != null) {
  81. this.data.add(series);
  82. series.addChangeListener(this);
  83. }
  84. }
  85. /**
  86. * Adds a series to the collection and sends a {@link DatasetChangeEvent} to all
  87. * registered listeners.
  88. *
  89. * @param series the series (<code>null</code> not permitted).
  90. */
  91. public void addSeries(XYSeries series) {
  92. if (series == null) {
  93. throw new IllegalArgumentException("Null 'series' argument.");
  94. }
  95. this.data.add(series);
  96. this.intervalDelegate.seriesAdded(this.data.size() - 1);
  97. series.addChangeListener(this);
  98. fireDatasetChanged();
  99. }
  100. /**
  101. * Removes a series from the collection and sends a {@link DatasetChangeEvent} to
  102. * all registered listeners.
  103. *
  104. * @param series the series index (zero-based).
  105. */
  106. public void removeSeries(int series) {
  107. if ((series < 0) || (series > getSeriesCount())) {
  108. throw new IllegalArgumentException("Series index out of bounds.");
  109. }
  110. // fetch the series, remove the change listener, then remove the series.
  111. XYSeries ts = (XYSeries) this.data.get(series);
  112. ts.removeChangeListener(this);
  113. this.data.remove(series);
  114. this.intervalDelegate.seriesRemoved();
  115. fireDatasetChanged();
  116. }
  117. /**
  118. * Removes a series from the collection and sends a {@link DatasetChangeEvent} to all
  119. * registered listeners.
  120. *
  121. * @param series the series (<code>null</code> not permitted).
  122. */
  123. public void removeSeries(XYSeries series) {
  124. if (series == null) {
  125. throw new IllegalArgumentException("Null 'series' argument.");
  126. }
  127. if (this.data.contains(series)) {
  128. series.removeChangeListener(this);
  129. this.data.remove(series);
  130. this.intervalDelegate.seriesRemoved();
  131. fireDatasetChanged();
  132. }
  133. }
  134. /**
  135. * Removes all the series from the collection and sends a {@link DatasetChangeEvent} to
  136. * all registered listeners.
  137. */
  138. public void removeAllSeries() {
  139. // Unregister the collection as a change listener to each series in the collection.
  140. for (int i = 0; i < this.data.size(); i++) {
  141. XYSeries series = (XYSeries) this.data.get(i);
  142. series.removeChangeListener(this);
  143. }
  144. // Remove all the series from the collection and notify listeners.
  145. this.data.clear();
  146. this.intervalDelegate.seriesRemoved();
  147. fireDatasetChanged();
  148. }
  149. /**
  150. * Returns the number of series in the collection.
  151. *
  152. * @return The series count.
  153. */
  154. public int getSeriesCount() {
  155. return this.data.size();
  156. }
  157. /**
  158. * Returns a list of all the series in the collection.
  159. *
  160. * @return The list (which is unmodifiable).
  161. */
  162. public List getSeries() {
  163. return Collections.unmodifiableList(this.data);
  164. }
  165. /**
  166. * Returns a series from the collection.
  167. *
  168. * @param series the series index (zero-based).
  169. *
  170. * @return The series.
  171. */
  172. public XYSeries getSeries(int series) {
  173. if ((series < 0) || (series > getSeriesCount())) {
  174. throw new IllegalArgumentException("Series index out of bounds");
  175. }
  176. return (XYSeries) this.data.get(series);
  177. }
  178. /**
  179. * Returns the name of a series.
  180. *
  181. * @param series the series index (zero-based).
  182. *
  183. * @return The name of a series.
  184. */
  185. public String getSeriesName(int series) {
  186. // defer argument checking
  187. return getSeries(series).getName();
  188. }
  189. /**
  190. * Returns the number of items in the specified series.
  191. *
  192. * @param series the series (zero-based index).
  193. *
  194. * @return The item count.
  195. */
  196. public int getItemCount(int series) {
  197. // defer argument checking
  198. return getSeries(series).getItemCount();
  199. }
  200. /**
  201. * Returns the x-value for the specified series and item.
  202. *
  203. * @param series the series (zero-based index).
  204. * @param item the item (zero-based index).
  205. *
  206. * @return The value.
  207. */
  208. public Number getX(int series, int item) {
  209. XYSeries ts = (XYSeries) this.data.get(series);
  210. XYDataItem xyItem = ts.getDataItem(item);
  211. return xyItem.getX();
  212. }
  213. /**
  214. * Returns the starting X value for the specified series and item.
  215. *
  216. * @param series the series (zero-based index).
  217. * @param item the item (zero-based index).
  218. *
  219. * @return The starting X value.
  220. */
  221. public Number getStartX(int series, int item) {
  222. return this.intervalDelegate.getStartX(series, item);
  223. }
  224. /**
  225. * Returns the ending X value for the specified series and item.
  226. *
  227. * @param series the series (zero-based index).
  228. * @param item the item (zero-based index).
  229. *
  230. * @return The ending X value.
  231. */
  232. public Number getEndX(int series, int item) {
  233. return this.intervalDelegate.getEndX(series, item);
  234. }
  235. /**
  236. * Returns the y-value for the specified series and item.
  237. *
  238. * @param series the series (zero-based index).
  239. * @param index the index of the item of interest (zero-based).
  240. *
  241. * @return The value (possibly <code>null</code>).
  242. */
  243. public Number getY(int series, int index) {
  244. XYSeries ts = (XYSeries) this.data.get(series);
  245. XYDataItem xyItem = ts.getDataItem(index);
  246. return xyItem.getY();
  247. }
  248. /**
  249. * Returns the starting Y value for the specified series and item.
  250. *
  251. * @param series the series (zero-based index).
  252. * @param item the item (zero-based index).
  253. *
  254. * @return The starting Y value.
  255. */
  256. public Number getStartY(int series, int item) {
  257. return getY(series, item);
  258. }
  259. /**
  260. * Returns the ending Y value for the specified series and item.
  261. *
  262. * @param series the series (zero-based index).
  263. * @param item the item (zero-based index).
  264. *
  265. * @return The ending Y value.
  266. */
  267. public Number getEndY(int series, int item) {
  268. return getY(series, item);
  269. }
  270. /**
  271. * Tests this collection for equality with an arbitrary object.
  272. *
  273. * @param obj the object (<code>null</code> permitted).
  274. *
  275. * @return A boolean.
  276. */
  277. public boolean equals(Object obj) {
  278. /*
  279. * XXX
  280. *
  281. * what about the interval delegate...?
  282. * The interval width etc wasn't considered
  283. * before, hence i did not add it here (AS)
  284. *
  285. */
  286. if (obj == this) {
  287. return true;
  288. }
  289. if (!(obj instanceof XYSeriesCollection)) {
  290. return false;
  291. }
  292. XYSeriesCollection that = (XYSeriesCollection) obj;
  293. return ObjectUtilities.equal(this.data, that.data);
  294. }
  295. /**
  296. * Returns a hash code.
  297. *
  298. * @return A hash code.
  299. */
  300. public int hashCode() {
  301. // Same question as for equals (AS)
  302. return (this.data != null ? this.data.hashCode() : 0);
  303. }
  304. /**
  305. * Returns the minimum x-value in the dataset.
  306. *
  307. * @param includeInterval a flag that determines whether or not the
  308. * x-interval is taken into account.
  309. *
  310. * @return The minimum value.
  311. */
  312. public double getDomainLowerBound(boolean includeInterval) {
  313. return this.intervalDelegate.getDomainLowerBound(includeInterval);
  314. }
  315. /**
  316. * Returns the maximum x-value in the dataset.
  317. *
  318. * @param includeInterval a flag that determines whether or not the
  319. * x-interval is taken into account.
  320. *
  321. * @return The maximum value.
  322. */
  323. public double getDomainUpperBound(boolean includeInterval) {
  324. return this.intervalDelegate.getDomainUpperBound(includeInterval);
  325. }
  326. /**
  327. * Returns the range of the values in this dataset's domain.
  328. *
  329. * @param includeInterval a flag that determines whether or not the
  330. * x-interval is taken into account.
  331. *
  332. * @return The range.
  333. */
  334. public Range getDomainBounds(boolean includeInterval) {
  335. return this.intervalDelegate.getDomainBounds(includeInterval);
  336. }
  337. /**
  338. * Returns the interval width. This is used to calculate the start and end x-values, if/when
  339. * the dataset is used as an {@link IntervalXYDataset}.
  340. *
  341. * @return The interval width.
  342. */
  343. public double getIntervalWidth() {
  344. return this.intervalDelegate.getIntervalWidth();
  345. }
  346. /**
  347. * Sets the interval width and sends a {@link DatasetChangeEvent} to all registered listeners.
  348. *
  349. * @param width the width (negative values not permitted).
  350. */
  351. public void setIntervalWidth(double width) {
  352. if (width < 0.0) {
  353. throw new IllegalArgumentException("Negative 'width' argument.");
  354. }
  355. this.intervalDelegate.setIntervalWidth(width);
  356. fireDatasetChanged();
  357. }
  358. /**
  359. * Returns the interval position factor.
  360. *
  361. * @return The interval position factor.
  362. */
  363. public double getIntervalPositionFactor() {
  364. return this.intervalDelegate.getIntervalPositionFactor();
  365. }
  366. /**
  367. * Sets the interval position factor. This controls where the x-value is in relation to
  368. * the interval surrounding the x-value (0.0 means the x-value will be positioned at the start,
  369. * 0.5 in the middle, and 1.0 at the end).
  370. *
  371. * @param factor the factor.
  372. */
  373. public void setIntervalPositionFactor(double factor) {
  374. this.intervalDelegate.setIntervalPositionFactor(factor);
  375. fireDatasetChanged();
  376. }
  377. /**
  378. * Returns whether the interval width is automatically calculated or not.
  379. *
  380. * @return Whether the width is automatically calculated or not.
  381. */
  382. public boolean isAutoWidth() {
  383. return this.intervalDelegate.isAutoWidth();
  384. }
  385. /**
  386. * Sets the flag that indicates wether the interval width is automatically
  387. * calculated or not.
  388. *
  389. * @param b a boolean.
  390. */
  391. public void setAutoWidth(boolean b) {
  392. this.intervalDelegate.setAutoWidth(b);
  393. fireDatasetChanged();
  394. }
  395. }