KickJava   Java API By Example, From Geeks To Geeks.

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


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  * CombinedDomainCategoryPlot.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: CombinedDomainCategoryPlot.java,v 1.9 2005/05/19 14:03:40 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 : Added equals() method, implemented Cloneable and
41  * Serializable (DG);
42  * 11-Sep-2003 : Fix cloning support (subplots) (NB);
43  * 15-Sep-2003 : Implemented PublicCloneable (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' attribute (DG);
47  * 12-Nov-2004 : Implemented the Zoomable interface (DG);
48  * 25-Nov-2004 : Small update to clone() implementation (DG);
49  * 21-Feb-2005 : The getLegendItems() method now returns the fixed legend
50  * items if set (DG);
51  * 05-May-2005 : Updated draw() method parameters (DG);
52  *
53  */

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

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

101     /**
102      * Default constructor.
103      */

104     public CombinedDomainCategoryPlot() {
105         this(new CategoryAxis());
106     }
107     
108     /**
109      * Creates a new plot.
110      *
111      * @param domainAxis the shared domain axis (<code>null</code> not
112      * permitted).
113      */

114     public CombinedDomainCategoryPlot(CategoryAxis domainAxis) {
115         super(null, domainAxis, null, null);
116         this.subplots = new java.util.ArrayList JavaDoc();
117         this.totalWeight = 0;
118         this.gap = 5.0;
119     }
120
121     /**
122      * Returns the space between subplots.
123      *
124      * @return The gap (in Java2D units).
125      */

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

136     public void setGap(double gap) {
137         this.gap = gap;
138         notifyListeners(new PlotChangeEvent(this));
139     }
140
141     /**
142      * Adds a subplot to the combined chart and sends a {@link PlotChangeEvent}
143      * to all registered listeners.
144      *
145      * @param subplot the subplot (<code>null</code> not permitted).
146      */

147     public void add(CategoryPlot subplot) {
148         add(subplot, 1);
149     }
150     
151     /**
152      * Adds a subplot to the combined chart and sends a {@link PlotChangeEvent}
153      * to all registered listeners.
154      *
155      * @param subplot the subplot (<code>null</code> not permitted).
156      * @param weight the weight (must be >= 1).
157      */

158     public void add(CategoryPlot subplot, int weight) {
159         if (subplot == null) {
160             throw new IllegalArgumentException JavaDoc("Null 'subplot' argument.");
161         }
162         if (weight < 1) {
163             throw new IllegalArgumentException JavaDoc("Require weight >= 1.");
164         }
165         subplot.setParent(this);
166         subplot.setWeight(weight);
167         subplot.setInsets(new RectangleInsets(0.0, 0.0, 0.0, 0.0));
168         subplot.setDomainAxis(null);
169         subplot.setOrientation(getOrientation());
170         subplot.addChangeListener(this);
171         this.subplots.add(subplot);
172         this.totalWeight += weight;
173         CategoryAxis axis = getDomainAxis();
174         if (axis != null) {
175             axis.configure();
176         }
177         notifyListeners(new PlotChangeEvent(this));
178     }
179
180     /**
181      * Removes a subplot from the combined chart. Potentially, this removes
182      * some unique categories from the overall union of the datasets...so the
183      * domain axis is reconfigured, then a {@link PlotChangeEvent} is sent to
184      * all registered listeners.
185      *
186      * @param subplot the subplot (<code>null</code> not permitted).
187      */

188     public void remove(CategoryPlot subplot) {
189         if (subplot == null) {
190             throw new IllegalArgumentException JavaDoc("Null 'subplot' argument.");
191         }
192         int position = -1;
193         int size = this.subplots.size();
194         int i = 0;
195         while (position == -1 && i < size) {
196             if (this.subplots.get(i) == subplot) {
197                 position = i;
198             }
199             i++;
200         }
201         if (position != -1) {
202             this.subplots.remove(position);
203             subplot.setParent(null);
204             subplot.removeChangeListener(this);
205             this.totalWeight -= subplot.getWeight();
206
207             CategoryAxis domain = getDomainAxis();
208             if (domain != null) {
209                 domain.configure();
210             }
211             notifyListeners(new PlotChangeEvent(this));
212         }
213     }
214
215     /**
216      * Returns the list of subplots.
217      *
218      * @return An unmodifiable list of subplots .
219      */

220     public List JavaDoc getSubplots() {
221         return Collections.unmodifiableList(this.subplots);
222     }
223
224     /**
225      * Returns the subplot (if any) that contains the (x, y) point (specified
226      * in Java2D space).
227      *
228      * @param info the chart rendering info.
229      * @param source the source point.
230      *
231      * @return A subplot (possibly <code>null</code>).
232      */

233     public CategoryPlot findSubplot(PlotRenderingInfo info, Point2D JavaDoc source) {
234         CategoryPlot result = null;
235         int subplotIndex = info.getSubplotIndex(source);
236         if (subplotIndex >= 0) {
237             result = (CategoryPlot) this.subplots.get(subplotIndex);
238         }
239         return result;
240     }
241     
242     /**
243      * Multiplies the range on the range axis/axes by the specified factor.
244      *
245      * @param factor the zoom factor.
246      * @param info the plot rendering info.
247      * @param source the source point.
248      */

249     public void zoomRangeAxes(double factor, PlotRenderingInfo info,
250                               Point2D JavaDoc source) {
251         CategoryPlot subplot = findSubplot(info, source);
252         if (subplot != null) {
253             subplot.zoomRangeAxes(factor, info, source);
254         }
255     }
256
257     /**
258      * Zooms in on the range axes.
259      *
260      * @param lowerPercent the lower bound.
261      * @param upperPercent the upper bound.
262      * @param info the plot rendering info.
263      * @param source the source point.
264      */

265     public void zoomRangeAxes(double lowerPercent, double upperPercent,
266                               PlotRenderingInfo info, Point2D JavaDoc source) {
267         CategoryPlot subplot = findSubplot(info, source);
268         if (subplot != null) {
269             subplot.zoomRangeAxes(lowerPercent, upperPercent, info, source);
270         }
271     }
272
273     /**
274      * Calculates the space required for the axes.
275      *
276      * @param g2 the graphics device.
277      * @param plotArea the plot area.
278      *
279      * @return The space required for the axes.
280      */

281     protected AxisSpace calculateAxisSpace(Graphics2D JavaDoc g2,
282                                            Rectangle2D JavaDoc plotArea) {
283         
284         AxisSpace space = new AxisSpace();
285         PlotOrientation orientation = getOrientation();
286         
287         // work out the space required by the domain axis...
288
AxisSpace fixed = getFixedDomainAxisSpace();
289         if (fixed != null) {
290             if (orientation == PlotOrientation.HORIZONTAL) {
291                 space.setLeft(fixed.getLeft());
292                 space.setRight(fixed.getRight());
293             }
294             else if (orientation == PlotOrientation.VERTICAL) {
295                 space.setTop(fixed.getTop());
296                 space.setBottom(fixed.getBottom());
297             }
298         }
299         else {
300             CategoryAxis categoryAxis = getDomainAxis();
301             RectangleEdge categoryEdge = Plot.resolveDomainAxisLocation(
302                 getDomainAxisLocation(), orientation
303             );
304             if (categoryAxis != null) {
305                 space = categoryAxis.reserveSpace(
306                     g2, this, plotArea, categoryEdge, space
307                 );
308             }
309             else {
310                 if (getDrawSharedDomainAxis()) {
311                     space = getDomainAxis().reserveSpace(
312                         g2, this, plotArea, categoryEdge, space
313                     );
314                 }
315             }
316         }
317         
318         Rectangle2D JavaDoc adjustedPlotArea = space.shrink(plotArea, null);
319         
320         // work out the maximum height or width of the non-shared axes...
321
int n = this.subplots.size();
322         this.subplotAreas = new Rectangle2D JavaDoc[n];
323         double x = adjustedPlotArea.getX();
324         double y = adjustedPlotArea.getY();
325         double usableSize = 0.0;
326         if (orientation == PlotOrientation.HORIZONTAL) {
327             usableSize = adjustedPlotArea.getWidth() - this.gap * (n - 1);
328         }
329         else if (orientation == PlotOrientation.VERTICAL) {
330             usableSize = adjustedPlotArea.getHeight() - this.gap * (n - 1);
331         }
332
333         for (int i = 0; i < n; i++) {
334             CategoryPlot plot = (CategoryPlot) this.subplots.get(i);
335
336             // calculate sub-plot area
337
if (orientation == PlotOrientation.HORIZONTAL) {
338                 double w = usableSize * plot.getWeight() / this.totalWeight;
339                 this.subplotAreas[i] = new Rectangle2D.Double JavaDoc(
340                     x, y, w, adjustedPlotArea.getHeight()
341                 );
342                 x = x + w + this.gap;
343             }
344             else if (orientation == PlotOrientation.VERTICAL) {
345                 double h = usableSize * plot.getWeight() / this.totalWeight;
346                 this.subplotAreas[i] = new Rectangle2D.Double JavaDoc(
347                     x, y, adjustedPlotArea.getWidth(), h
348                 );
349                 y = y + h + this.gap;
350             }
351
352             AxisSpace subSpace = plot.calculateRangeAxisSpace(
353                 g2, this.subplotAreas[i], null
354             );
355             space.ensureAtLeast(subSpace);
356
357         }
358
359         return space;
360     }
361
362     /**
363      * Draws the plot on a Java 2D graphics device (such as the screen or a
364      * printer). Will perform all the placement calculations for each of the
365      * sub-plots and then tell these to draw themselves.
366      *
367      * @param g2 the graphics device.
368      * @param area the area within which the plot (including axis labels)
369      * should be drawn.
370      * @param anchor the anchor point (<code>null</code> permitted).
371      * @param parentState the state from the parent plot, if there is one.
372      * @param info collects information about the drawing (<code>null</code>
373      * permitted).
374      */

375     public void draw(Graphics2D JavaDoc g2,
376                      Rectangle2D JavaDoc area,
377                      Point2D JavaDoc anchor,
378                      PlotState parentState,
379                      PlotRenderingInfo info) {
380         
381         // set up info collection...
382
if (info != null) {
383             info.setPlotArea(area);
384         }
385
386         // adjust the drawing area for plot insets (if any)...
387
RectangleInsets insets = getInsets();
388         area.setRect(
389             area.getX() + insets.getLeft(),
390             area.getY() + insets.getTop(),
391             area.getWidth() - insets.getLeft() - insets.getRight(),
392             area.getHeight() - insets.getTop() - insets.getBottom()
393         );
394
395
396         // calculate the data area...
397
setFixedRangeAxisSpaceForSubplots(null);
398         AxisSpace space = calculateAxisSpace(g2, area);
399         Rectangle2D JavaDoc dataArea = space.shrink(area, null);
400
401         // set the width and height of non-shared axis of all sub-plots
402
setFixedRangeAxisSpaceForSubplots(space);
403
404         // draw the shared axis
405
CategoryAxis axis = getDomainAxis();
406         RectangleEdge domainEdge = getDomainAxisEdge();
407         double cursor = RectangleEdge.coordinate(dataArea, domainEdge);
408         AxisState axisState = axis.draw(
409             g2, cursor, area, dataArea, domainEdge, info
410         );
411         if (parentState == null) {
412             parentState = new PlotState();
413         }
414         parentState.getSharedAxisStates().put(axis, axisState);
415         
416         // draw all the subplots
417
for (int i = 0; i < this.subplots.size(); i++) {
418             CategoryPlot plot = (CategoryPlot) this.subplots.get(i);
419             PlotRenderingInfo subplotInfo = null;
420             if (info != null) {
421                 subplotInfo = new PlotRenderingInfo(info.getOwner());
422                 info.addSubplotInfo(subplotInfo);
423             }
424             plot.draw(g2, this.subplotAreas[i], null, parentState, subplotInfo);
425         }
426
427         if (info != null) {
428             info.setDataArea(dataArea);
429         }
430
431     }
432
433     /**
434      * Sets the size (width or height, depending on the orientation of the
435      * plot) for the range axis of each subplot.
436      *
437      * @param space the space (<code>null</code> permitted).
438      */

439     protected void setFixedRangeAxisSpaceForSubplots(AxisSpace space) {
440
441         Iterator JavaDoc iterator = this.subplots.iterator();
442         while (iterator.hasNext()) {
443             CategoryPlot plot = (CategoryPlot) iterator.next();
444             plot.setFixedRangeAxisSpace(space);
445         }
446
447     }
448
449     /**
450      * Sets the orientation of the plot (and all subplots).
451      *
452      * @param orientation the orientation (<code>null</code> not permitted).
453      */

454     public void setOrientation(PlotOrientation orientation) {
455
456         super.setOrientation(orientation);
457
458         Iterator JavaDoc iterator = this.subplots.iterator();
459         while (iterator.hasNext()) {
460             CategoryPlot plot = (CategoryPlot) iterator.next();
461             plot.setOrientation(orientation);
462         }
463
464     }
465     
466     /**
467      * Returns a collection of legend items for the plot.
468      *
469      * @return The legend items.
470      */

471     public LegendItemCollection getLegendItems() {
472         LegendItemCollection result = getFixedLegendItems();
473         if (result == null) {
474             result = new LegendItemCollection();
475             if (this.subplots != null) {
476                 Iterator JavaDoc iterator = this.subplots.iterator();
477                 while (iterator.hasNext()) {
478                     CategoryPlot plot = (CategoryPlot) iterator.next();
479                     LegendItemCollection more = plot.getLegendItems();
480                     result.addAll(more);
481                 }
482             }
483         }
484         return result;
485     }
486     
487     /**
488      * Returns an unmodifiable list of the categories contained in all the
489      * subplots.
490      *
491      * @return The list.
492      */

493     public List JavaDoc getCategories() {
494         
495         List JavaDoc result = new java.util.ArrayList JavaDoc();
496
497         if (this.subplots != null) {
498             Iterator JavaDoc iterator = this.subplots.iterator();
499             while (iterator.hasNext()) {
500                 CategoryPlot plot = (CategoryPlot) iterator.next();
501                 List JavaDoc more = plot.getCategories();
502                 Iterator JavaDoc moreIterator = more.iterator();
503                 while (moreIterator.hasNext()) {
504                     Comparable JavaDoc category = (Comparable JavaDoc) moreIterator.next();
505                     if (!result.contains(category)) {
506                         result.add(category);
507                     }
508                 }
509             }
510         }
511
512         return Collections.unmodifiableList(result);
513     }
514     
515     /**
516      * Handles a 'click' on the plot.
517      *
518      * @param x x-coordinate of the click.
519      * @param y y-coordinate of the click.
520      * @param info information about the plot's dimensions.
521      *
522      */

523     public void handleClick(int x, int y, PlotRenderingInfo info) {
524
525         Rectangle2D JavaDoc dataArea = info.getDataArea();
526         if (dataArea.contains(x, y)) {
527             for (int i = 0; i < this.subplots.size(); i++) {
528                 CategoryPlot subplot = (CategoryPlot) this.subplots.get(i);
529                 PlotRenderingInfo subplotInfo = info.getSubplotInfo(i);
530                 subplot.handleClick(x, y, subplotInfo);
531             }
532         }
533
534     }
535     
536     /**
537      * Receives a {@link PlotChangeEvent} and responds by notifying all
538      * listeners.
539      *
540      * @param event the event.
541      */

542     public void plotChanged(PlotChangeEvent event) {
543         notifyListeners(event);
544     }
545
546     /**
547      * Tests the plot for equality with an arbitrary object.
548      *
549      * @param obj the object (<code>null</code> permitted).
550      *
551      * @return A boolean.
552      */

553     public boolean equals(Object JavaDoc obj) {
554         if (obj == this) {
555             return true;
556         }
557         if (!(obj instanceof CombinedDomainCategoryPlot)) {
558             return false;
559         }
560         if (!super.equals(obj)) {
561             return false;
562         }
563         CombinedDomainCategoryPlot plot = (CombinedDomainCategoryPlot) obj;
564         if (!ObjectUtilities.equal(this.subplots, plot.subplots)) {
565             return false;
566         }
567         if (this.totalWeight != plot.totalWeight) {
568             return false;
569         }
570         if (this.gap != plot.gap) {
571             return false;
572         }
573         return true;
574     }
575
576     /**
577      * Returns a clone of the plot.
578      *
579      * @return A clone.
580      *
581      * @throws CloneNotSupportedException this class will not throw this
582      * exception, but subclasses (if any) might.
583      */

584     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
585         
586         CombinedDomainCategoryPlot result
587             = (CombinedDomainCategoryPlot) super.clone();
588         result.subplots = (List JavaDoc) ObjectUtilities.deepClone(this.subplots);
589         for (Iterator JavaDoc it = result.subplots.iterator(); it.hasNext();) {
590             Plot child = (Plot) it.next();
591             child.setParent(result);
592         }
593         return result;
594         
595     }
596     
597 }
598
Popular Tags