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