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