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