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