001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2008, 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     * ExtendedCategoryAxis.java
029     * -------------------------
030     * (C) Copyright 2003-2008, by Object Refinery Limited and Contributors.
031     *
032     * Original Author:  David Gilbert (for Object Refinery Limited);
033     * Contributor(s):   -;
034     *
035     * Changes
036     * -------
037     * 07-Nov-2003 : Version 1 (DG);
038     * 07-Jan-2004 : Updated the createLabel() method (DG);
039     * 29-Jan-2004 : Added paint attribute (DG);
040     * ------------- JFREECHART 1.0.x ---------------------------------------------
041     * 21-Mar-2007 : Implemented equals(), clone() and fixed serialization (DG);
042     *
043     */
044    
045    package org.jfree.chart.axis;
046    
047    import java.awt.Color;
048    import java.awt.Font;
049    import java.awt.Graphics2D;
050    import java.awt.Paint;
051    import java.io.IOException;
052    import java.io.ObjectInputStream;
053    import java.io.ObjectOutputStream;
054    import java.util.HashMap;
055    import java.util.Map;
056    
057    import org.jfree.chart.event.AxisChangeEvent;
058    import org.jfree.io.SerialUtilities;
059    import org.jfree.text.TextBlock;
060    import org.jfree.text.TextFragment;
061    import org.jfree.text.TextLine;
062    import org.jfree.ui.RectangleEdge;
063    import org.jfree.util.PaintUtilities;
064    
065    /**
066     * An extended version of the {@link CategoryAxis} class that supports
067     * sublabels on the axis.
068     */
069    public class ExtendedCategoryAxis extends CategoryAxis {
070    
071        /** For serialization. */
072        static final long serialVersionUID = -3004429093959826567L;
073    
074        /** Storage for the sublabels. */
075        private Map sublabels;
076    
077        /** The sublabel font. */
078        private Font sublabelFont;
079    
080        /** The sublabel paint. */
081        private transient Paint sublabelPaint;
082    
083        /**
084         * Creates a new axis.
085         *
086         * @param label  the axis label.
087         */
088        public ExtendedCategoryAxis(String label) {
089            super(label);
090            this.sublabels = new HashMap();
091            this.sublabelFont = new Font("SansSerif", Font.PLAIN, 10);
092            this.sublabelPaint = Color.black;
093        }
094    
095        /**
096         * Returns the font for the sublabels.
097         *
098         * @return The font (never <code>null</code>).
099         *
100         * @see #setSubLabelFont(Font)
101         */
102        public Font getSubLabelFont() {
103            return this.sublabelFont;
104        }
105    
106        /**
107         * Sets the font for the sublabels and sends an {@link AxisChangeEvent} to
108         * all registered listeners.
109         *
110         * @param font  the font (<code>null</code> not permitted).
111         *
112         * @see #getSubLabelFont()
113         */
114        public void setSubLabelFont(Font font) {
115            if (font == null) {
116                throw new IllegalArgumentException("Null 'font' argument.");
117            }
118            this.sublabelFont = font;
119            notifyListeners(new AxisChangeEvent(this));
120        }
121    
122        /**
123         * Returns the paint for the sublabels.
124         *
125         * @return The paint (never <code>null</code>).
126         *
127         * @see #setSubLabelPaint(Paint)
128         */
129        public Paint getSubLabelPaint() {
130            return this.sublabelPaint;
131        }
132    
133        /**
134         * Sets the paint for the sublabels and sends an {@link AxisChangeEvent}
135         * to all registered listeners.
136         *
137         * @param paint  the paint (<code>null</code> not permitted).
138         *
139         * @see #getSubLabelPaint()
140         */
141        public void setSubLabelPaint(Paint paint) {
142            if (paint == null) {
143                throw new IllegalArgumentException("Null 'paint' argument.");
144            }
145            this.sublabelPaint = paint;
146            notifyListeners(new AxisChangeEvent(this));
147        }
148    
149        /**
150         * Adds a sublabel for a category.
151         *
152         * @param category  the category.
153         * @param label  the label.
154         */
155        public void addSubLabel(Comparable category, String label) {
156            this.sublabels.put(category, label);
157        }
158    
159        /**
160         * Overrides the default behaviour by adding the sublabel to the text
161         * block that is used for the category label.
162         *
163         * @param category  the category.
164         * @param width  the width (not used yet).
165         * @param edge  the location of the axis.
166         * @param g2  the graphics device.
167         *
168         * @return A label.
169         */
170        protected TextBlock createLabel(Comparable category, float width,
171                                        RectangleEdge edge, Graphics2D g2) {
172            TextBlock label = super.createLabel(category, width, edge, g2);
173            String s = (String) this.sublabels.get(category);
174            if (s != null) {
175                if (edge == RectangleEdge.TOP || edge == RectangleEdge.BOTTOM) {
176                    TextLine line = new TextLine(s, this.sublabelFont,
177                            this.sublabelPaint);
178                    label.addLine(line);
179                }
180                else if (edge == RectangleEdge.LEFT
181                        || edge == RectangleEdge.RIGHT) {
182                    TextLine line = label.getLastLine();
183                    if (line != null) {
184                        line.addFragment(new TextFragment("  " + s,
185                                this.sublabelFont, this.sublabelPaint));
186                    }
187                }
188            }
189            return label;
190        }
191    
192        /**
193         * Tests this axis for equality with an arbitrary object.
194         *
195         * @param obj  the object (<code>null</code> permitted).
196         *
197         * @return A boolean.
198         */
199        public boolean equals(Object obj) {
200            if (obj == this) {
201                return true;
202            }
203            if (!(obj instanceof ExtendedCategoryAxis)) {
204                return false;
205            }
206            ExtendedCategoryAxis that = (ExtendedCategoryAxis) obj;
207            if (!this.sublabelFont.equals(that.sublabelFont)) {
208                return false;
209            }
210            if (!PaintUtilities.equal(this.sublabelPaint, that.sublabelPaint)) {
211                return false;
212            }
213            if (!this.sublabels.equals(that.sublabels)) {
214                return false;
215            }
216            return super.equals(obj);
217        }
218    
219        /**
220         * Returns a clone of this axis.
221         *
222         * @return A clone.
223         *
224         * @throws CloneNotSupportedException if there is a problem cloning.
225         */
226        public Object clone() throws CloneNotSupportedException {
227            ExtendedCategoryAxis clone = (ExtendedCategoryAxis) super.clone();
228            clone.sublabels = new HashMap(this.sublabels);
229            return clone;
230        }
231    
232        /**
233         * Provides serialization support.
234         *
235         * @param stream  the output stream.
236         *
237         * @throws IOException  if there is an I/O error.
238         */
239        private void writeObject(ObjectOutputStream stream) throws IOException {
240            stream.defaultWriteObject();
241            SerialUtilities.writePaint(this.sublabelPaint, stream);
242        }
243    
244        /**
245         * Provides serialization support.
246         *
247         * @param stream  the input stream.
248         *
249         * @throws IOException  if there is an I/O error.
250         * @throws ClassNotFoundException  if there is a classpath problem.
251         */
252        private void readObject(ObjectInputStream stream)
253            throws IOException, ClassNotFoundException {
254            stream.defaultReadObject();
255            this.sublabelPaint = SerialUtilities.readPaint(stream);
256        }
257    
258    }