    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     * CategoryLabelPositions.java
    029     * ---------------------------
    030     * (C) Copyright 2004-2007, 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    }