001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2005, 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     * CategoryLabelPositions.java
029     * ---------------------------
030     * (C) Copyright 2004, 2005, by Object Refinery Limited and Contributors.
031     *
032     * Original Author:  David Gilbert (for Object Refinery Limited);
033     * Contributor(s):   -;
034     *
035     * $Id: CategoryLabelPositions.java,v 1.5.2.1 2005/10/25 20:37:34 mungady Exp $
036     *
037     * Changes
038     * -------
039     * 06-Jan-2004 : Version 1 (DG);
040     * 17-Feb-2004 : Added equals() method (DG);
041     * 05-Nov-2004 : Adjusted settings for UP_90 and DOWN_90 (DG);
042     *
043     */
044    
045    package org.jfree.chart.axis;
046    
047    import java.io.Serializable;
048    
049    import org.jfree.text.TextBlockAnchor;
050    import org.jfree.ui.RectangleAnchor;
051    import org.jfree.ui.RectangleEdge;
052    import org.jfree.ui.TextAnchor;
053    
054    /**
055     * Records the label positions for a category axis.  Instances of this class 
056     * are immutable.
057     */
058    public class CategoryLabelPositions implements Serializable {
059    
060        /** For serialization. */
061        private static final long serialVersionUID = -8999557901920364580L;
062        
063        /** STANDARD category label positions. */
064        public static final CategoryLabelPositions 
065            STANDARD = new CategoryLabelPositions(
066                new CategoryLabelPosition(
067                    RectangleAnchor.BOTTOM, TextBlockAnchor.BOTTOM_CENTER
068                ), // TOP
069                new CategoryLabelPosition(
070                    RectangleAnchor.TOP, TextBlockAnchor.TOP_CENTER
071                ), // BOTTOM
072                new CategoryLabelPosition(
073                    RectangleAnchor.RIGHT, TextBlockAnchor.CENTER_RIGHT, 
074                    CategoryLabelWidthType.RANGE, 0.30f
075                ), // LEFT
076                new CategoryLabelPosition(
077                    RectangleAnchor.LEFT, TextBlockAnchor.CENTER_LEFT, 
078                    CategoryLabelWidthType.RANGE, 0.30f
079                ) // RIGHT
080            );
081        
082        /** UP_90 category label positions. */
083        public static final CategoryLabelPositions 
084            UP_90 = new CategoryLabelPositions(
085                new CategoryLabelPosition(
086                    RectangleAnchor.BOTTOM, TextBlockAnchor.CENTER_LEFT, 
087                    TextAnchor.CENTER_LEFT, -Math.PI / 2.0,
088                    CategoryLabelWidthType.RANGE, 0.30f
089                ), // TOP
090                new CategoryLabelPosition(
091                    RectangleAnchor.TOP, TextBlockAnchor.CENTER_RIGHT, 
092                    TextAnchor.CENTER_RIGHT, -Math.PI / 2.0,
093                    CategoryLabelWidthType.RANGE, 0.30f
094                ), // BOTTOM
095                new CategoryLabelPosition(
096                    RectangleAnchor.RIGHT, TextBlockAnchor.BOTTOM_CENTER, 
097                    TextAnchor.BOTTOM_CENTER, -Math.PI / 2.0,
098                    CategoryLabelWidthType.CATEGORY, 0.9f
099                ), // LEFT
100                new CategoryLabelPosition(
101                    RectangleAnchor.LEFT, TextBlockAnchor.TOP_CENTER, 
102                    TextAnchor.TOP_CENTER, -Math.PI / 2.0,
103                    CategoryLabelWidthType.CATEGORY, 0.90f
104                ) // RIGHT
105            );
106        
107        /** DOWN_90 category label positions. */
108        public static final CategoryLabelPositions 
109            DOWN_90 = new CategoryLabelPositions(
110                new CategoryLabelPosition(
111                    RectangleAnchor.BOTTOM, TextBlockAnchor.CENTER_RIGHT, 
112                    TextAnchor.CENTER_RIGHT, Math.PI / 2.0,
113                    CategoryLabelWidthType.RANGE, 0.30f
114                ), // TOP
115                new CategoryLabelPosition(
116                    RectangleAnchor.TOP, TextBlockAnchor.CENTER_LEFT, 
117                    TextAnchor.CENTER_LEFT, Math.PI / 2.0,
118                    CategoryLabelWidthType.RANGE, 0.30f
119                ), // BOTTOM
120                new CategoryLabelPosition(
121                    RectangleAnchor.RIGHT, TextBlockAnchor.TOP_CENTER, 
122                    TextAnchor.TOP_CENTER, Math.PI / 2.0,
123                    CategoryLabelWidthType.CATEGORY, 0.90f
124                ), // LEFT
125                new CategoryLabelPosition(
126                    RectangleAnchor.LEFT, TextBlockAnchor.BOTTOM_CENTER, 
127                    TextAnchor.BOTTOM_CENTER, Math.PI / 2.0,
128                    CategoryLabelWidthType.CATEGORY, 0.90f
129                ) // RIGHT
130            );
131        
132        /** UP_45 category label positions. */
133        public static final CategoryLabelPositions UP_45 
134            = createUpRotationLabelPositions(Math.PI / 4.0);
135        
136        /** DOWN_45 category label positions. */
137        public static final CategoryLabelPositions DOWN_45 
138            = createDownRotationLabelPositions(Math.PI / 4.0);
139        
140        /**
141         * Creates a new instance where the category labels angled upwards by the 
142         * specified amount.
143         * 
144         * @param angle  the rotation angle (should be < Math.PI / 2.0).
145         * 
146         * @return A category label position specification.
147         */
148        public static CategoryLabelPositions createUpRotationLabelPositions(
149                double angle) {
150            return new CategoryLabelPositions(
151                new CategoryLabelPosition(
152                    RectangleAnchor.BOTTOM, TextBlockAnchor.BOTTOM_LEFT, 
153                    TextAnchor.BOTTOM_LEFT, -angle,
154                    CategoryLabelWidthType.RANGE, 0.50f
155                ), // TOP
156                new CategoryLabelPosition(
157                    RectangleAnchor.TOP, TextBlockAnchor.TOP_RIGHT, 
158                    TextAnchor.TOP_RIGHT, -angle,
159                    CategoryLabelWidthType.RANGE, 0.50f
160                ), // BOTTOM
161                new CategoryLabelPosition(
162                    RectangleAnchor.RIGHT, TextBlockAnchor.BOTTOM_RIGHT, 
163                    TextAnchor.BOTTOM_RIGHT, -angle,
164                    CategoryLabelWidthType.RANGE, 0.50f
165                ), // LEFT
166                new CategoryLabelPosition(
167                    RectangleAnchor.LEFT, TextBlockAnchor.TOP_LEFT, 
168                    TextAnchor.TOP_LEFT, -angle,
169                    CategoryLabelWidthType.RANGE, 0.50f
170                ) // RIGHT
171            );
172        }
173        
174        /**
175         * Creates a new instance where the category labels angled downwards by the
176         * specified amount.
177         * 
178         * @param angle  the rotation angle (should be < Math.PI / 2.0).
179         * 
180         * @return A category label position specification.
181         */
182        public static CategoryLabelPositions createDownRotationLabelPositions(
183                double angle) {
184            return new CategoryLabelPositions(
185                new CategoryLabelPosition(
186                    RectangleAnchor.BOTTOM, TextBlockAnchor.BOTTOM_RIGHT, 
187                    TextAnchor.BOTTOM_RIGHT, angle,
188                    CategoryLabelWidthType.RANGE, 0.50f
189                ), // TOP
190                new CategoryLabelPosition(
191                    RectangleAnchor.TOP, TextBlockAnchor.TOP_LEFT, 
192                    TextAnchor.TOP_LEFT, angle,
193                    CategoryLabelWidthType.RANGE, 0.50f
194                ), // BOTTOM
195                new CategoryLabelPosition(
196                    RectangleAnchor.RIGHT, TextBlockAnchor.TOP_RIGHT, 
197                    TextAnchor.TOP_RIGHT, angle,
198                    CategoryLabelWidthType.RANGE, 0.50f
199                ), // LEFT
200                new CategoryLabelPosition(
201                    RectangleAnchor.LEFT, TextBlockAnchor.BOTTOM_LEFT, 
202                    TextAnchor.BOTTOM_LEFT, angle,
203                    CategoryLabelWidthType.RANGE, 0.50f
204                ) // RIGHT
205            );
206        }
207        
208        /** 
209         * The label positioning details used when an axis is at the top of a 
210         * chart. 
211         */
212        private CategoryLabelPosition positionForAxisAtTop;
213        
214        /** 
215         * The label positioning details used when an axis is at the bottom of a 
216         * chart. 
217         */
218        private CategoryLabelPosition positionForAxisAtBottom;
219        
220        /** 
221         * The label positioning details used when an axis is at the left of a 
222         * chart. 
223         */
224        private CategoryLabelPosition positionForAxisAtLeft;
225        
226        /** 
227         * The label positioning details used when an axis is at the right of a 
228         * chart. 
229         */
230        private CategoryLabelPosition positionForAxisAtRight;
231     
232        /**
233         * Default constructor.
234         */
235        public CategoryLabelPositions() {
236            this.positionForAxisAtTop = new CategoryLabelPosition();
237            this.positionForAxisAtBottom = new CategoryLabelPosition();
238            this.positionForAxisAtLeft = new CategoryLabelPosition();
239            this.positionForAxisAtRight = new CategoryLabelPosition();
240        }
241        
242        /**
243         * Creates a new position specification.
244         * 
245         * @param top  the label position info used when an axis is at the top 
246         *             (<code>null</code> not permitted).
247         * @param bottom  the label position info used when an axis is at the 
248         *                bottom (<code>null</code> not permitted).
249         * @param left  the label position info used when an axis is at the left 
250         *              (<code>null</code> not permitted).
251         * @param right  the label position info used when an axis is at the right 
252         *               (<code>null</code> not permitted).
253         */
254        public CategoryLabelPositions(CategoryLabelPosition top,
255                                      CategoryLabelPosition bottom,
256                                      CategoryLabelPosition left,
257                                      CategoryLabelPosition right) {
258            
259            if (top == null) {
260                throw new IllegalArgumentException("Null 'top' argument.");
261            }
262            if (bottom == null) {
263                throw new IllegalArgumentException("Null 'bottom' argument.");
264            }
265            if (left == null) {
266                throw new IllegalArgumentException("Null 'left' argument.");
267            }
268            if (right == null) {
269                throw new IllegalArgumentException("Null 'right' argument.");
270            }
271            
272            this.positionForAxisAtTop = top;
273            this.positionForAxisAtBottom = bottom;
274            this.positionForAxisAtLeft = left;
275            this.positionForAxisAtRight = right;
276        
277        }
278        
279        /**
280         * Returns the category label position specification for an axis at the 
281         * given location.
282         * 
283         * @param edge  the axis location.
284         * 
285         * @return The category label position specification.
286         */
287        public CategoryLabelPosition getLabelPosition(RectangleEdge edge) {
288            CategoryLabelPosition result = null;
289            if (edge == RectangleEdge.TOP) {
290                result = this.positionForAxisAtTop;
291            }
292            else if (edge == RectangleEdge.BOTTOM) {
293                result = this.positionForAxisAtBottom;
294            }
295            else if (edge == RectangleEdge.LEFT) {
296                result = this.positionForAxisAtLeft;
297            }
298            else if (edge == RectangleEdge.RIGHT) {
299                result = this.positionForAxisAtRight;
300            }
301            return result;
302        }
303        
304        /**
305         * Returns a new instance based on an existing instance but with the top 
306         * position changed.
307         * 
308         * @param base  the base (<code>null</code> not permitted).
309         * @param top  the top position (<code>null</code> not permitted).
310         * 
311         * @return A new instance (never <code>null</code>).
312         */
313        public static CategoryLabelPositions replaceTopPosition(
314                CategoryLabelPositions base, CategoryLabelPosition top) {
315            
316            if (base == null) {
317                throw new IllegalArgumentException("Null 'base' argument.");
318            }
319            if (top == null) {
320                throw new IllegalArgumentException("Null 'top' argument.");
321            }
322            
323            return new CategoryLabelPositions(
324                top, 
325                base.getLabelPosition(RectangleEdge.BOTTOM),
326                base.getLabelPosition(RectangleEdge.LEFT),
327                base.getLabelPosition(RectangleEdge.RIGHT)
328            );
329        }
330        
331        /**
332         * Returns a new instance based on an existing instance but with the bottom
333         * position changed.
334         * 
335         * @param base  the base (<code>null</code> not permitted).
336         * @param bottom  the bottom position (<code>null</code> not permitted).
337         * 
338         * @return A new instance (never <code>null</code>).
339         */
340        public static CategoryLabelPositions replaceBottomPosition(
341                CategoryLabelPositions base, CategoryLabelPosition bottom) {
342            
343            if (base == null) {
344                throw new IllegalArgumentException("Null 'base' argument.");
345            }
346            if (bottom == null) {
347                throw new IllegalArgumentException("Null 'bottom' argument.");
348            }
349            
350            return new CategoryLabelPositions(
351                base.getLabelPosition(RectangleEdge.TOP),
352                bottom,
353                base.getLabelPosition(RectangleEdge.LEFT),
354                base.getLabelPosition(RectangleEdge.RIGHT)
355            );
356        }
357        
358        /**
359         * Returns a new instance based on an existing instance but with the left 
360         * position changed.
361         * 
362         * @param base  the base (<code>null</code> not permitted).
363         * @param left  the left position (<code>null</code> not permitted).
364         * 
365         * @return A new instance (never <code>null</code>).
366         */
367        public static CategoryLabelPositions replaceLeftPosition(
368                CategoryLabelPositions base, CategoryLabelPosition left) {
369            
370            if (base == null) {
371                throw new IllegalArgumentException("Null 'base' argument.");
372            }
373            if (left == null) {
374                throw new IllegalArgumentException("Null 'left' argument.");
375            }
376            
377            return new CategoryLabelPositions(
378                base.getLabelPosition(RectangleEdge.TOP),
379                base.getLabelPosition(RectangleEdge.BOTTOM),
380                left,
381                base.getLabelPosition(RectangleEdge.RIGHT)
382            );
383        }
384        
385        /**
386         * Returns a new instance based on an existing instance but with the right 
387         * position changed.
388         * 
389         * @param base  the base (<code>null</code> not permitted).
390         * @param right  the right position (<code>null</code> not permitted).
391         * 
392         * @return A new instance (never <code>null</code>).
393         */
394        public static CategoryLabelPositions replaceRightPosition(
395                CategoryLabelPositions base, CategoryLabelPosition right) {
396            
397            if (base == null) {
398                throw new IllegalArgumentException("Null 'base' argument.");
399            }
400            if (right == null) {
401                throw new IllegalArgumentException("Null 'right' argument.");
402            }
403            
404            return new CategoryLabelPositions(
405                base.getLabelPosition(RectangleEdge.TOP),
406                base.getLabelPosition(RectangleEdge.BOTTOM),
407                base.getLabelPosition(RectangleEdge.LEFT),
408                right
409            );
410        }
411        
412        /**
413         * Returns <code>true</code> if this object is equal to the specified 
414         * object, and <code>false</code> otherwise.
415         *
416         * @param obj  the other object.
417         *
418         * @return A boolean.
419         */
420        public boolean equals(Object obj) {
421    
422            if (this == obj) {
423                return true;
424            }
425            if (!(obj instanceof CategoryLabelPositions)) {
426                return false;
427            }
428    
429            CategoryLabelPositions that = (CategoryLabelPositions) obj;
430            if (!this.positionForAxisAtTop.equals(that.positionForAxisAtTop)) {
431                return false;
432            }
433            if (!this.positionForAxisAtBottom.equals(
434                    that.positionForAxisAtBottom)) {
435                return false;
436            }
437            if (!this.positionForAxisAtLeft.equals(that.positionForAxisAtLeft)) {
438                return false;
439            }
440            if (!this.positionForAxisAtRight.equals(that.positionForAxisAtRight)) {
441                return false;
442            }
443      
444            return true;
445    
446        }
447        
448        /**
449         * Returns a hash code for this object.
450         * 
451         * @return A hash code.
452         */
453        public int hashCode() {
454            int result = 19;
455            result = 37 * result + this.positionForAxisAtTop.hashCode();
456            result = 37 * result + this.positionForAxisAtBottom.hashCode();
457            result = 37 * result + this.positionForAxisAtLeft.hashCode();
458            result = 37 * result + this.positionForAxisAtRight.hashCode();
459            return result;
460        }
461    }