KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > axis > SubCategoryAxis


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2006, 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
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22  * USA.
23  *
24  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
25  * in the United States and other countries.]
26  *
27  * --------------------
28  * SubCategoryAxis.java
29  * --------------------
30  * (C) Copyright 2004-2006, by Object Refinery Limited.
31  *
32  * Original Author: David Gilbert;
33  * Contributor(s): Adriaan Joubert;
34  *
35  * $Id: SubCategoryAxis.java,v 1.6.2.2 2006/08/18 14:48:31 mungady Exp $
36  *
37  * Changes
38  * -------
39  * 12-May-2004 : Version 1 (DG);
40  * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities
41  * --> TextUtilities (DG);
42  * 26-Apr-2005 : Removed logger (DG);
43  * ------------- JFREECHART 1.0.0 ---------------------------------------------
44  * 18-Aug-2006 : Fix for bug drawing category labels, thanks to Adriaan
45  * Joubert (1277726) (DG);
46  *
47  */

48
49 package org.jfree.chart.axis;
50
51 import java.awt.Color JavaDoc;
52 import java.awt.Font JavaDoc;
53 import java.awt.FontMetrics JavaDoc;
54 import java.awt.Graphics2D JavaDoc;
55 import java.awt.Paint JavaDoc;
56 import java.awt.geom.Rectangle2D JavaDoc;
57 import java.io.IOException JavaDoc;
58 import java.io.ObjectInputStream JavaDoc;
59 import java.io.ObjectOutputStream JavaDoc;
60 import java.io.Serializable JavaDoc;
61 import java.util.Iterator JavaDoc;
62 import java.util.List JavaDoc;
63
64 import org.jfree.chart.event.AxisChangeEvent;
65 import org.jfree.chart.plot.CategoryPlot;
66 import org.jfree.chart.plot.Plot;
67 import org.jfree.chart.plot.PlotRenderingInfo;
68 import org.jfree.data.category.CategoryDataset;
69 import org.jfree.io.SerialUtilities;
70 import org.jfree.text.TextUtilities;
71 import org.jfree.ui.RectangleEdge;
72 import org.jfree.ui.TextAnchor;
73
74 /**
75  * A specialised category axis that can display sub-categories.
76  */

77 public class SubCategoryAxis extends CategoryAxis
78                              implements Cloneable JavaDoc, Serializable JavaDoc {
79     
80     /** For serialization. */
81     private static final long serialVersionUID = -1279463299793228344L;
82     
83     /** Storage for the sub-categories (these need to be set manually). */
84     private List JavaDoc subCategories;
85     
86     /** The font for the sub-category labels. */
87     private Font JavaDoc subLabelFont = new Font JavaDoc("SansSerif", Font.PLAIN, 10);
88     
89     /** The paint for the sub-category labels. */
90     private transient Paint JavaDoc subLabelPaint = Color.black;
91     
92     /**
93      * Creates a new axis.
94      *
95      * @param label the axis label.
96      */

97     public SubCategoryAxis(String JavaDoc label) {
98         super(label);
99         this.subCategories = new java.util.ArrayList JavaDoc();
100     }
101
102     /**
103      * Adds a sub-category to the axis.
104      *
105      * @param subCategory the sub-category.
106      */

107     public void addSubCategory(Comparable JavaDoc subCategory) {
108         this.subCategories.add(subCategory);
109     }
110     
111     /**
112      * Returns the font used to display the sub-category labels.
113      *
114      * @return The font (never <code>null</code>).
115      */

116     public Font JavaDoc getSubLabelFont() {
117         return this.subLabelFont;
118     }
119     
120     /**
121      * Sets the font used to display the sub-category labels and sends an
122      * {@link AxisChangeEvent} to all registered listeners.
123      *
124      * @param font the font (<code>null</code> not permitted).
125      */

126     public void setSubLabelFont(Font JavaDoc font) {
127         if (font == null) {
128             throw new IllegalArgumentException JavaDoc("Null 'font' argument.");
129         }
130         this.subLabelFont = font;
131         notifyListeners(new AxisChangeEvent(this));
132     }
133     
134     /**
135      * Returns the paint used to display the sub-category labels.
136      *
137      * @return The paint (never <code>null</code>).
138      */

139     public Paint JavaDoc getSubLabelPaint() {
140         return this.subLabelPaint;
141     }
142     
143     /**
144      * Sets the paint used to display the sub-category labels and sends an
145      * {@link AxisChangeEvent} to all registered listeners.
146      *
147      * @param paint the paint (<code>null</code> not permitted).
148      */

149     public void setSubLabelPaint(Paint JavaDoc paint) {
150         if (paint == null) {
151             throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
152         }
153         this.subLabelPaint = paint;
154         notifyListeners(new AxisChangeEvent(this));
155     }
156     
157     /**
158      * Estimates the space required for the axis, given a specific drawing area.
159      *
160      * @param g2 the graphics device (used to obtain font information).
161      * @param plot the plot that the axis belongs to.
162      * @param plotArea the area within which the axis should be drawn.
163      * @param edge the axis location (top or bottom).
164      * @param space the space already reserved.
165      *
166      * @return The space required to draw the axis.
167      */

168     public AxisSpace reserveSpace(Graphics2D JavaDoc g2, Plot plot,
169                                   Rectangle2D JavaDoc plotArea,
170                                   RectangleEdge edge, AxisSpace space) {
171
172         // create a new space object if one wasn't supplied...
173
if (space == null) {
174             space = new AxisSpace();
175         }
176         
177         // if the axis is not visible, no additional space is required...
178
if (!isVisible()) {
179             return space;
180         }
181
182         space = super.reserveSpace(g2, plot, plotArea, edge, space);
183         double maxdim = getMaxDim(g2, edge);
184         if (RectangleEdge.isTopOrBottom(edge)) {
185             space.add(maxdim, edge);
186         }
187         else if (RectangleEdge.isLeftOrRight(edge)) {
188             space.add(maxdim, edge);
189         }
190         return space;
191     }
192     
193     /**
194      * Returns the maximum of the relevant dimension (height or width) of the
195      * subcategory labels.
196      *
197      * @param g2 the graphics device.
198      * @param edge the edge.
199      *
200      * @return The maximum dimension.
201      */

202     private double getMaxDim(Graphics2D JavaDoc g2, RectangleEdge edge) {
203         double result = 0.0;
204         g2.setFont(this.subLabelFont);
205         FontMetrics JavaDoc fm = g2.getFontMetrics();
206         Iterator JavaDoc iterator = this.subCategories.iterator();
207         while (iterator.hasNext()) {
208             Comparable JavaDoc subcategory = (Comparable JavaDoc) iterator.next();
209             String JavaDoc label = subcategory.toString();
210             Rectangle2D JavaDoc bounds = TextUtilities.getTextBounds(label, g2, fm);
211             double dim = 0.0;
212             if (RectangleEdge.isLeftOrRight(edge)) {
213                 dim = bounds.getWidth();
214             }
215             else { // must be top or bottom
216
dim = bounds.getHeight();
217             }
218             result = Math.max(result, dim);
219         }
220         return result;
221     }
222     
223     /**
224      * Draws the axis on a Java 2D graphics device (such as the screen or a
225      * printer).
226      *
227      * @param g2 the graphics device (<code>null</code> not permitted).
228      * @param cursor the cursor location.
229      * @param plotArea the area within which the axis should be drawn
230      * (<code>null</code> not permitted).
231      * @param dataArea the area within which the plot is being drawn
232      * (<code>null</code> not permitted).
233      * @param edge the location of the axis (<code>null</code> not permitted).
234      * @param plotState collects information about the plot
235      * (<code>null</code> permitted).
236      *
237      * @return The axis state (never <code>null</code>).
238      */

239     public AxisState draw(Graphics2D JavaDoc g2,
240                           double cursor,
241                           Rectangle2D JavaDoc plotArea,
242                           Rectangle2D JavaDoc dataArea,
243                           RectangleEdge edge,
244                           PlotRenderingInfo plotState) {
245         
246         // if the axis is not visible, don't draw it...
247
if (!isVisible()) {
248             return new AxisState(cursor);
249         }
250         
251         if (isAxisLineVisible()) {
252             drawAxisLine(g2, cursor, dataArea, edge);
253         }
254
255         // draw the category labels and axis label
256
AxisState state = new AxisState(cursor);
257         state = drawSubCategoryLabels(
258             g2, plotArea, dataArea, edge, state, plotState
259         );
260         state = drawCategoryLabels(g2, plotArea, dataArea, edge, state,
261                 plotState);
262         state = drawLabel(getLabel(), g2, plotArea, dataArea, edge, state);
263     
264         return state;
265
266     }
267     
268     /**
269      * Draws the category labels and returns the updated axis state.
270      *
271      * @param g2 the graphics device (<code>null</code> not permitted).
272      * @param plotArea the plot area (<code>null</code> not permitted).
273      * @param dataArea the area inside the axes (<code>null</code> not
274      * permitted).
275      * @param edge the axis location (<code>null</code> not permitted).
276      * @param state the axis state (<code>null</code> not permitted).
277      * @param plotState collects information about the plot (<code>null</code>
278      * permitted).
279      *
280      * @return The updated axis state (never <code>null</code>).
281      */

282     protected AxisState drawSubCategoryLabels(Graphics2D JavaDoc g2,
283                                               Rectangle2D JavaDoc plotArea,
284                                               Rectangle2D JavaDoc dataArea,
285                                               RectangleEdge edge,
286                                               AxisState state,
287                                               PlotRenderingInfo plotState) {
288
289         if (state == null) {
290             throw new IllegalArgumentException JavaDoc("Null 'state' argument.");
291         }
292
293         g2.setFont(this.subLabelFont);
294         g2.setPaint(this.subLabelPaint);
295         CategoryPlot plot = (CategoryPlot) getPlot();
296         CategoryDataset dataset = plot.getDataset();
297         int categoryCount = dataset.getColumnCount();
298
299         double maxdim = getMaxDim(g2, edge);
300         for (int categoryIndex = 0; categoryIndex < categoryCount;
301              categoryIndex++) {
302
303             double x0 = 0.0;
304             double x1 = 0.0;
305             double y0 = 0.0;
306             double y1 = 0.0;
307             if (edge == RectangleEdge.TOP) {
308                 x0 = getCategoryStart(categoryIndex, categoryCount, dataArea,
309                         edge);
310                 x1 = getCategoryEnd(categoryIndex, categoryCount, dataArea,
311                         edge);
312                 y1 = state.getCursor();
313                 y0 = y1 - maxdim;
314             }
315             else if (edge == RectangleEdge.BOTTOM) {
316                 x0 = getCategoryStart(categoryIndex, categoryCount, dataArea,
317                         edge);
318                 x1 = getCategoryEnd(categoryIndex, categoryCount, dataArea,
319                         edge);
320                 y0 = state.getCursor();
321                 y1 = y0 + maxdim;
322             }
323             else if (edge == RectangleEdge.LEFT) {
324                 y0 = getCategoryStart(categoryIndex, categoryCount, dataArea,
325                         edge);
326                 y1 = getCategoryEnd(categoryIndex, categoryCount, dataArea,
327                         edge);
328                 x1 = state.getCursor();
329                 x0 = x1 - maxdim;
330             }
331             else if (edge == RectangleEdge.RIGHT) {
332                 y0 = getCategoryStart(categoryIndex, categoryCount, dataArea,
333                         edge);
334                 y1 = getCategoryEnd(categoryIndex, categoryCount, dataArea,
335                         edge);
336                 x0 = state.getCursor();
337                 x1 = x0 + maxdim;
338             }
339             Rectangle2D JavaDoc area = new Rectangle2D.Double JavaDoc(x0, y0, (x1 - x0),
340                     (y1 - y0));
341             int subCategoryCount = this.subCategories.size();
342             float width = (float) ((x1 - x0) / subCategoryCount);
343             float height = (float) ((y1 - y0) / subCategoryCount);
344             float xx = 0.0f;
345             float yy = 0.0f;
346             for (int i = 0; i < subCategoryCount; i++) {
347                 if (RectangleEdge.isTopOrBottom(edge)) {
348                     xx = (float) (x0 + (i + 0.5) * width);
349                     yy = (float) area.getCenterY();
350                 }
351                 else {
352                     xx = (float) area.getCenterX();
353                     yy = (float) (y0 + (i + 0.5) * height);
354                 }
355                 String JavaDoc label = this.subCategories.get(i).toString();
356                 TextUtilities.drawRotatedString(label, g2, xx, yy,
357                         TextAnchor.CENTER, 0.0, TextAnchor.CENTER);
358             }
359         }
360
361         if (edge.equals(RectangleEdge.TOP)) {
362             double h = maxdim;
363             state.cursorUp(h);
364         }
365         else if (edge.equals(RectangleEdge.BOTTOM)) {
366             double h = maxdim;
367             state.cursorDown(h);
368         }
369         else if (edge == RectangleEdge.LEFT) {
370             double w = maxdim;
371             state.cursorLeft(w);
372         }
373         else if (edge == RectangleEdge.RIGHT) {
374             double w = maxdim;
375             state.cursorRight(w);
376         }
377         return state;
378     }
379     
380     /**
381      * Tests the axis for equality with an arbitrary object.
382      *
383      * @param obj the object (<code>null</code> permitted).
384      *
385      * @return A boolean.
386      */

387     public boolean equals(Object JavaDoc obj) {
388         if (obj == this) {
389             return true;
390         }
391         if (obj instanceof SubCategoryAxis && super.equals(obj)) {
392             SubCategoryAxis axis = (SubCategoryAxis) obj;
393             if (!this.subCategories.equals(axis.subCategories)) {
394                 return false;
395             }
396             if (!this.subLabelFont.equals(axis.subLabelFont)) {
397                 return false;
398             }
399             if (!this.subLabelPaint.equals(axis.subLabelPaint)) {
400                 return false;
401             }
402             return true;
403         }
404         return false;
405     }
406     
407     /**
408      * Provides serialization support.
409      *
410      * @param stream the output stream.
411      *
412      * @throws IOException if there is an I/O error.
413      */

414     private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
415         stream.defaultWriteObject();
416         SerialUtilities.writePaint(this.subLabelPaint, stream);
417     }
418
419     /**
420      * Provides serialization support.
421      *
422      * @param stream the input stream.
423      *
424      * @throws IOException if there is an I/O error.
425      * @throws ClassNotFoundException if there is a classpath problem.
426      */

427     private void readObject(ObjectInputStream JavaDoc stream)
428         throws IOException JavaDoc, ClassNotFoundException JavaDoc {
429         stream.defaultReadObject();
430         this.subLabelPaint = SerialUtilities.readPaint(stream);
431     }
432   
433 }
434
Popular Tags