KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > plot > CombinedRangeCategoryPlot


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
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this library; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22  *
23  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
24  * in the United States and other countries.]
25  *
26  * ------------------------------
27  * CombinedRangeCategoryPlot.java
28  * ------------------------------
29  * (C) Copyright 2003-2005, by Object Refinery Limited.
30  *
31  * Original Author: David Gilbert (for Object Refinery Limited);
32  * Contributor(s): Nicolas Brodu;
33  *
34  * $Id: CombinedRangeCategoryPlot.java,v 1.13 2005/05/19 14:03:41 mungady Exp $
35  *
36  * Changes:
37  * --------
38  * 16-May-2003 : Version 1 (DG);
39  * 08-Aug-2003 : Adjusted totalWeight in remove() method (DG);
40  * 19-Aug-2003 : Implemented Cloneable (DG);
41  * 11-Sep-2003 : Fix cloning support (subplots) (NB);
42  * 15-Sep-2003 : Implemented PublicCloneable. Fixed errors in cloning and
43  * serialization (DG);
44  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
45  * 17-Sep-2003 : Updated handling of 'clicks' (DG);
46  * 04-May-2004 : Added getter/setter methods for 'gap' attributes (DG);
47  * 12-Nov-2004 : Implements the new Zoomable interface (DG);
48  * 25-Nov-2004 : Small update to clone() implementation (DG);
49  * 21-Feb-2005 : Fixed bug in remove() method (id = 1121172) (DG);
50  * 21-Feb-2005 : The getLegendItems() method now returns the fixed legend
51  * items if set (DG);
52  * 05-May-2005 : Updated draw() method parameters (DG);
53  *
54  */

55  
56 package org.jfree.chart.plot;
57
58 import java.awt.Graphics2D JavaDoc;
59 import java.awt.geom.Point2D JavaDoc;
60 import java.awt.geom.Rectangle2D JavaDoc;
61 import java.io.IOException JavaDoc;
62 import java.io.ObjectInputStream JavaDoc;
63 import java.io.Serializable JavaDoc;
64 import java.util.Collections JavaDoc;
65 import java.util.Iterator JavaDoc;
66 import java.util.List JavaDoc;
67
68 import org.jfree.chart.LegendItemCollection;
69 import org.jfree.chart.axis.AxisSpace;
70 import org.jfree.chart.axis.AxisState;
71 import org.jfree.chart.axis.NumberAxis;
72 import org.jfree.chart.axis.ValueAxis;
73 import org.jfree.chart.event.PlotChangeEvent;
74 import org.jfree.chart.event.PlotChangeListener;
75 import org.jfree.data.Range;
76 import org.jfree.ui.RectangleEdge;
77 import org.jfree.ui.RectangleInsets;
78 import org.jfree.util.ObjectUtilities;
79 import org.jfree.util.PublicCloneable;
80
81 /**
82  * A combined category plot where the range axis is shared.
83  */

84 public class CombinedRangeCategoryPlot extends CategoryPlot
85                                        implements Zoomable,
86                                                   Cloneable JavaDoc, PublicCloneable,
87                                                   Serializable JavaDoc,
88                                                   PlotChangeListener {
89
90     /** For serialization. */
91     private static final long serialVersionUID = 7260210007554504515L;
92     
93     /** Storage for the subplot references. */
94     private List JavaDoc subplots;
95
96     /** Total weight of all charts. */
97     private int totalWeight;
98
99     /** The gap between subplots. */
100     private double gap;
101
102     /** Temporary storage for the subplot areas. */
103     private transient Rectangle2D JavaDoc[] subplotArea; // TODO: move to plot state
104

105     /**
106      * Default constructor.
107      */

108     public CombinedRangeCategoryPlot() {
109         this(new NumberAxis());
110     }
111     
112     /**
113      * Creates a new plot.
114      *
115      * @param rangeAxis the shared range axis.
116      */

117     public CombinedRangeCategoryPlot(ValueAxis rangeAxis) {
118         super(null, null, rangeAxis, null);
119         this.subplots = new java.util.ArrayList JavaDoc();
120         this.totalWeight = 0;
121         this.gap = 5.0;
122     }
123
124     /**
125      * Returns the space between subplots.
126      *
127      * @return The gap (in Java2D units).
128      */

129     public double getGap() {
130         return this.gap;
131     }
132
133     /**
134      * Sets the amount of space between subplots and sends a
135      * {@link PlotChangeEvent} to all registered listeners.
136      *
137      * @param gap the gap between subplots (in Java2D units).
138      */

139     public void setGap(double gap) {
140         this.gap = gap;
141         notifyListeners(new PlotChangeEvent(this));
142     }
143
144     /**
145      * Adds a subplot (with a default 'weight' of 1) and sends a
146      * {@link PlotChangeEvent} to all registered listeners.
147      *
148      * @param subplot the subplot (<code>null</code> not permitted).
149      */

150     public void add(CategoryPlot subplot) {
151         // defer argument checking
152
add(subplot, 1);
153     }
154
155     /**
156      * Adds a subplot and sends a {@link PlotChangeEvent} to all registered
157      * listeners.
158      *
159      * @param subplot the subplot (<code>null</code> not permitted).
160      * @param weight the weight (must be >= 1).
161      */

162     public void add(CategoryPlot subplot, int weight) {
163         if (subplot == null) {
164             throw new IllegalArgumentException JavaDoc("Null 'subplot' argument.");
165         }
166         if (weight <= 0) {
167             throw new IllegalArgumentException JavaDoc("Require weight >= 1.");
168         }
169         // store the plot and its weight
170
subplot.setParent(this);
171         subplot.setWeight(weight);
172         subplot.setInsets(new RectangleInsets(0.0, 0.0, 0.0, 0.0));
173         subplot.setRangeAxis(null);
174         subplot.setOrientation(getOrientation());
175         subplot.addChangeListener(this);
176         this.subplots.add(subplot);
177         this.totalWeight += weight;
178         
179         // configure the range axis...
180
ValueAxis axis = getRangeAxis();
181         if (axis != null) {
182             axis.configure();
183         }
184         notifyListeners(new PlotChangeEvent(this));
185     }
186
187     /**
188      * Removes a subplot from the combined chart.
189      *
190      * @param subplot the subplot (<code>null</code> not permitted).
191      */

192     public void remove(CategoryPlot subplot) {
193         if (subplot == null) {
194             throw new IllegalArgumentException JavaDoc(" Null 'subplot' argument.");
195         }
196         int position = -1;
197         int size = this.subplots.size();
198         int i = 0;
199         while (position == -1 && i < size) {
200             if (this.subplots.get(i) == subplot) {
201                 position = i;
202             }
203             i++;
204         }
205         if (position != -1) {
206             this.subplots.remove(position);
207             subplot.setParent(null);
208             subplot.removeChangeListener(this);
209             this.totalWeight -= subplot.getWeight();
210         
211             ValueAxis range = getRangeAxis();
212             if (range != null) {
213                 range.configure();
214             }
215
216             ValueAxis range2 = getRangeAxis(1);
217             if (range2 != null) {
218                 range2.configure();
219             }
220             notifyListeners(new PlotChangeEvent(this));
221         }
222     }
223
224     /**
225      * Returns the list of subplots.
226      *
227      * @return The list (unmodifiable).
228      */

229     public List JavaDoc getSubplots() {
230         return Collections.unmodifiableList(this.subplots);
231     }
232
233     /**
234      * Calculates the space required for the axes.
235      *
236      * @param g2 the graphics device.
237      * @param plotArea the plot area.
238      *
239      * @return The space required for the axes.
240      */

241     protected AxisSpace calculateAxisSpace(Graphics2D JavaDoc g2,
242                                            Rectangle2D JavaDoc plotArea) {
243         
244         AxisSpace space = new AxisSpace();
245         PlotOrientation orientation = getOrientation();
246         
247         // work out the space required by the domain axis...
248
AxisSpace fixed = getFixedRangeAxisSpace();
249         if (fixed != null) {
250             if (orientation == PlotOrientation.VERTICAL) {
251                 space.setLeft(fixed.getLeft());
252                 space.setRight(fixed.getRight());
253             }
254             else if (orientation == PlotOrientation.HORIZONTAL) {
255                 space.setTop(fixed.getTop());
256                 space.setBottom(fixed.getBottom());
257             }
258         }
259         else {
260             ValueAxis valueAxis = getRangeAxis();
261             RectangleEdge valueEdge = Plot.resolveRangeAxisLocation(
262                 getRangeAxisLocation(), orientation
263             );
264             if (valueAxis != null) {
265                 space = valueAxis.reserveSpace(
266                     g2, this, plotArea, valueEdge, space
267                 );
268             }
269         }
270         
271         Rectangle2D JavaDoc adjustedPlotArea = space.shrink(plotArea, null);
272         // work out the maximum height or width of the non-shared axes...
273
int n = this.subplots.size();
274
275         // calculate plotAreas of all sub-plots, maximum vertical/horizontal
276
// axis width/height
277
this.subplotArea = new Rectangle2D JavaDoc[n];
278         double x = adjustedPlotArea.getX();
279         double y = adjustedPlotArea.getY();
280         double usableSize = 0.0;
281         if (orientation == PlotOrientation.VERTICAL) {
282             usableSize = adjustedPlotArea.getWidth() - this.gap * (n - 1);
283         }
284         else if (orientation == PlotOrientation.HORIZONTAL) {
285             usableSize = adjustedPlotArea.getHeight() - this.gap * (n - 1);
286         }
287
288         for (int i = 0; i < n; i++) {
289             CategoryPlot plot = (CategoryPlot) this.subplots.get(i);
290
291             // calculate sub-plot area
292
if (orientation == PlotOrientation.VERTICAL) {
293                 double w = usableSize * plot.getWeight() / this.totalWeight;
294                 this.subplotArea[i] = new Rectangle2D.Double JavaDoc(
295                     x, y, w, adjustedPlotArea.getHeight()
296                 );
297                 x = x + w + this.gap;
298             }
299             else if (orientation == PlotOrientation.HORIZONTAL) {
300                 double h = usableSize * plot.getWeight() / this.totalWeight;
301                 this.subplotArea[i] = new Rectangle2D.Double JavaDoc(
302                     x, y, adjustedPlotArea.getWidth(), h
303                 );
304                 y = y + h + this.gap;
305             }
306
307             AxisSpace subSpace = plot.calculateDomainAxisSpace(
308                 g2, this.subplotArea[i], null
309             );
310             space.ensureAtLeast(subSpace);
311
312         }
313
314         return space;
315     }
316
317     /**
318      * Draws the plot on a Java 2D graphics device (such as the screen or a
319      * printer). Will perform all the placement calculations for each
320      * sub-plots and then tell these to draw themselves.
321      *
322      * @param g2 the graphics device.
323      * @param area the area within which the plot (including axis labels)
324      * should be drawn.
325      * @param anchor the anchor point (<code>null</code> permitted).
326      * @param parentState the parent state.
327      * @param info collects information about the drawing (<code>null</code>
328      * permitted).
329      */

330     public void draw(Graphics2D JavaDoc g2, Rectangle2D JavaDoc area, Point2D JavaDoc anchor,
331                      PlotState parentState,
332                      PlotRenderingInfo info) {
333
334         // set up info collection...
335
if (info != null) {
336             info.setPlotArea(area);
337         }
338
339         // adjust the drawing area for plot insets (if any)...
340
RectangleInsets insets = getInsets();
341         insets.trim(area);
342
343         // calculate the data area...
344
AxisSpace space = calculateAxisSpace(g2, area);
345         Rectangle2D JavaDoc dataArea = space.shrink(area, null);
346
347         // set the width and height of non-shared axis of all sub-plots
348
setFixedDomainAxisSpaceForSubplots(space);
349
350         // draw the shared axis
351
ValueAxis axis = getRangeAxis();
352         RectangleEdge rangeEdge = getRangeAxisEdge();
353         double cursor = RectangleEdge.coordinate(dataArea, rangeEdge);
354         AxisState state = axis.draw(
355             g2, cursor, area, dataArea, rangeEdge, info
356         );
357         if (parentState == null) {
358             parentState = new PlotState();
359         }
360         parentState.getSharedAxisStates().put(axis, state);
361         
362         // draw all the charts
363
for (int i = 0; i < this.subplots.size(); i++) {
364             CategoryPlot plot = (CategoryPlot) this.subplots.get(i);
365             PlotRenderingInfo subplotInfo = null;
366             if (info != null) {
367                 subplotInfo = new PlotRenderingInfo(info.getOwner());
368                 info.addSubplotInfo(subplotInfo);
369             }
370             plot.draw(g2, this.subplotArea[i], null, parentState, subplotInfo);
371         }
372
373         if (info != null) {
374             info.setDataArea(dataArea);
375         }
376
377     }
378
379     /**
380      * Sets the orientation for the plot (and all the subplots).
381      *
382      * @param orientation the orientation.
383      */

384     public void setOrientation(PlotOrientation orientation) {
385
386         super.setOrientation(orientation);
387
388         Iterator JavaDoc iterator = this.subplots.iterator();
389         while (iterator.hasNext()) {
390             CategoryPlot plot = (CategoryPlot) iterator.next();
391             plot.setOrientation(orientation);
392         }
393
394     }
395     
396     /**
397       * Returns the range for the axis. This is the combined range of all the
398       * subplots.
399       *
400       * @param axis the axis.
401       *
402       * @return The range.
403       */

404      public Range getDataRange(ValueAxis axis) {
405
406          Range result = null;
407          if (this.subplots != null) {
408              Iterator JavaDoc iterator = this.subplots.iterator();
409              while (iterator.hasNext()) {
410                  CategoryPlot subplot = (CategoryPlot) iterator.next();
411                  result = Range.combine(result, subplot.getDataRange(axis));
412              }
413          }
414          return result;
415
416      }
417
418     /**
419      * Returns a collection of legend items for the plot.
420      *
421      * @return The legend items.
422      */

423     public LegendItemCollection getLegendItems() {
424         LegendItemCollection result = getFixedLegendItems();
425         if (result == null) {
426             result = new LegendItemCollection();
427             if (this.subplots != null) {
428                 Iterator JavaDoc iterator = this.subplots.iterator();
429                 while (iterator.hasNext()) {
430                     CategoryPlot plot = (CategoryPlot) iterator.next();
431                     LegendItemCollection more = plot.getLegendItems();
432                     result.addAll(more);
433                 }
434             }
435         }
436         return result;
437     }
438     
439     /**
440      * Sets the size (width or height, depending on the orientation of the
441      * plot) for the domain axis of each subplot.
442      *
443      * @param space the space.
444      */

445     protected void setFixedDomainAxisSpaceForSubplots(AxisSpace space) {
446
447         Iterator JavaDoc iterator = this.subplots.iterator();
448         while (iterator.hasNext()) {
449             CategoryPlot plot = (CategoryPlot) iterator.next();
450             plot.setFixedDomainAxisSpace(space);
451         }
452
453     }
454
455     /**
456      * Handles a 'click' on the plot by updating the anchor value.
457      *
458      * @param x x-coordinate of the click.
459      * @param y y-coordinate of the click.
460      * @param info information about the plot's dimensions.
461      *
462      */

463     public void handleClick(int x, int y, PlotRenderingInfo info) {
464
465         Rectangle2D JavaDoc dataArea = info.getDataArea();
466         if (dataArea.contains(x, y)) {
467             for (int i = 0; i < this.subplots.size(); i++) {
468                 CategoryPlot subplot = (CategoryPlot) this.subplots.get(i);
469                 PlotRenderingInfo subplotInfo = info.getSubplotInfo(i);
470                 subplot.handleClick(x, y, subplotInfo);
471             }
472         }
473
474     }
475
476     /**
477      * Receives a {@link PlotChangeEvent} and responds by notifying all
478      * listeners.
479      *
480      * @param event the event.
481      */

482     public void plotChanged(PlotChangeEvent event) {
483         notifyListeners(event);
484     }
485
486     /**
487      * Tests the plot for equality with an arbitrary object.
488      *
489      * @param obj the object (<code>null</code> permitted).
490      *
491      * @return <code>true</code> or <code>false</code>.
492      */

493     public boolean equals(Object JavaDoc obj) {
494         if (obj == this) {
495             return true;
496         }
497         if (!(obj instanceof CombinedRangeCategoryPlot)) {
498             return false;
499         }
500         if (!super.equals(obj)) {
501             return false;
502         }
503         CombinedRangeCategoryPlot that = (CombinedRangeCategoryPlot) obj;
504         if (!ObjectUtilities.equal(this.subplots, that.subplots)) {
505             return false;
506         }
507         if (this.totalWeight != that.totalWeight) {
508             return false;
509         }
510         if (this.gap != that.gap) {
511             return false;
512         }
513         return true;
514     }
515
516     /**
517      * Returns a clone of the plot.
518      *
519      * @return A clone.
520      *
521      * @throws CloneNotSupportedException this class will not throw this
522      * exception, but subclasses (if any) might.
523      */

524     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
525         CombinedRangeCategoryPlot result
526             = (CombinedRangeCategoryPlot) super.clone();
527         result.subplots = (List JavaDoc) ObjectUtilities.deepClone(this.subplots);
528         for (Iterator JavaDoc it = result.subplots.iterator(); it.hasNext();) {
529             Plot child = (Plot) it.next();
530             child.setParent(result);
531         }
532         
533         // after setting up all the subplots, the shared range axis may need
534
// reconfiguring
535
ValueAxis rangeAxis = result.getRangeAxis();
536         if (rangeAxis != null) {
537             rangeAxis.configure();
538         }
539         
540         return result;
541     }
542
543     /**
544      * Provides serialization support.
545      *
546      * @param stream the input stream.
547      *
548      * @throws IOException if there is an I/O error.
549      * @throws ClassNotFoundException if there is a classpath problem.
550      */

551     private void readObject(ObjectInputStream JavaDoc stream)
552         throws IOException JavaDoc, ClassNotFoundException JavaDoc {
553
554         stream.defaultReadObject();
555         
556         // the range axis is deserialized before the subplots, so its value
557
// range is likely to be incorrect...
558
ValueAxis rangeAxis = getRangeAxis();
559         if (rangeAxis != null) {
560             rangeAxis.configure();
561         }
562         
563     }
564
565 }
566
Popular Tags