    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2011, 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     * CategoryTextAnnotation.java
    029     * ---------------------------
    030     * (C) Copyright 2003-2011, by Object Refinery Limited and Contributors.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   Peter Kolb (patch 2809117);
    034     *
    035     * Changes:
    036     * --------
    037     * 02-Apr-2003 : Version 1 (DG);
    038     * 02-Jul-2003 : Added new text alignment and rotation options (DG);
    039     * 04-Jul-2003 : Added a category anchor option (DG);
    040     * 19-Aug-2003 : Added equals() method and implemented Cloneable (DG);
    041     * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
    042     * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities
    043     *               --> TextUtilities (DG);
    044     * ------------- JFREECHART 1.0.x -------------------------------------------
    045     * 06-Mar-2007 : Implemented hashCode() (DG);
    046     * 23-Apr-2008 : Implemented PublicCloneable (DG);
    047     * 24-Jun-2009 : Fire change events (see patch 2809117 by PK) (DG);
    048     *
    049     */
    050
    051    package org.jfree.chart.annotations;
    052
    053    import java.awt.Graphics2D;
    054    import java.awt.geom.Rectangle2D;
    055    import java.io.Serializable;
    056
    057    import org.jfree.chart.axis.CategoryAnchor;
    058    import org.jfree.chart.axis.CategoryAxis;
    059    import org.jfree.chart.axis.ValueAxis;
    060    import org.jfree.chart.event.AnnotationChangeEvent;
    061    import org.jfree.chart.plot.CategoryPlot;
    062    import org.jfree.chart.plot.Plot;
    063    import org.jfree.chart.plot.PlotOrientation;
    064    import org.jfree.data.category.CategoryDataset;
    065    import org.jfree.text.TextUtilities;
    066    import org.jfree.ui.RectangleEdge;
    067    import org.jfree.util.PublicCloneable;
    068
    069    /**
    070     * A text annotation that can be placed on a {@link CategoryPlot}.
    071     */
    072    public class CategoryTextAnnotation extends TextAnnotation
    073            implements CategoryAnnotation, Cloneable, PublicCloneable,
    074                       Serializable {
    075
    076        /** For serialization. */
    077        private static final long serialVersionUID = 3333360090781320147L;
    078
    079        /** The category. */
    080        private Comparable category;
    081
    082        /** The category anchor (START, MIDDLE, or END). */
    083        private CategoryAnchor categoryAnchor;
    084
    085        /** The value. */
    086        private double value;
    087
    088        /**
    089         * Creates a new annotation to be displayed at the given location.
    090         *
    091         * @param text  the text (<code>null</code> not permitted).
    092         * @param category  the category (<code>null</code> not permitted).
    093         * @param value  the value.
    094         */
    095        public CategoryTextAnnotation(String text, Comparable category,
    096                                      double value) {
    097            super(text);
    098            if (category == null) {
    099                throw new IllegalArgumentException("Null 'category' argument.");
    100            }
    101            this.category = category;
    102            this.value = value;
    103            this.categoryAnchor = CategoryAnchor.MIDDLE;
    104        }
    105
    106        /**
    107         * Returns the category.
    108         *
    109         * @return The category (never <code>null</code>).
    110         *
    111         * @see #setCategory(Comparable)
    112         */
    113        public Comparable getCategory() {
    114            return this.category;
    115        }
    116
    117        /**
    118         * Sets the category that the annotation attaches to and sends an
    119         * {@link AnnotationChangeEvent} to all registered listeners.
    120         *
    121         * @param category  the category (<code>null</code> not permitted).
    122         *
    123         * @see #getCategory()
    124         */
    125        public void setCategory(Comparable category) {
    126            if (category == null) {
    127                throw new IllegalArgumentException("Null 'category' argument.");
    128            }
    129            this.category = category;
    130            fireAnnotationChanged();
    131        }
    132
    133        /**
    134         * Returns the category anchor point.
    135         *
    136         * @return The category anchor point.
    137         *
    138         * @see #setCategoryAnchor(CategoryAnchor)
    139         */
    140        public CategoryAnchor getCategoryAnchor() {
    141            return this.categoryAnchor;
    142        }
    143
    144        /**
    145         * Sets the category anchor point and sends an
    146         * {@link AnnotationChangeEvent} to all registered listeners.
    147         *
    148         * @param anchor  the anchor point (<code>null</code> not permitted).
    149         *
    150         * @see #getCategoryAnchor()
    151         */
    152        public void setCategoryAnchor(CategoryAnchor anchor) {
    153            if (anchor == null) {
    154                throw new IllegalArgumentException("Null 'anchor' argument.");
    155            }
    156            this.categoryAnchor = anchor;
    157            fireAnnotationChanged();
    158        }
    159
    160        /**
    161         * Returns the value that the annotation attaches to.
    162         *
    163         * @return The value.
    164         *
    165         * @see #setValue(double)
    166         */
    167        public double getValue() {
    168            return this.value;
    169        }
    170
    171        /**
    172         * Sets the value and sends an
    173         * {@link AnnotationChangeEvent} to all registered listeners.
    174         *
    175         * @param value  the value.
    176         *
    177         * @see #getValue()
    178         */
    179        public void setValue(double value) {
    180            this.value = value;
    181            fireAnnotationChanged();
    182        }
    183
    184        /**
    185         * Draws the annotation.
    186         *
    187         * @param g2  the graphics device.
    188         * @param plot  the plot.
    189         * @param dataArea  the data area.
    190         * @param domainAxis  the domain axis.
    191         * @param rangeAxis  the range axis.
    192         */
    193        public void draw(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea,
    194                         CategoryAxis domainAxis, ValueAxis rangeAxis) {
    195
    196            CategoryDataset dataset = plot.getDataset();
    197            int catIndex = dataset.getColumnIndex(this.category);
    198            int catCount = dataset.getColumnCount();
    199
    200            float anchorX = 0.0f;
    201            float anchorY = 0.0f;
    202            PlotOrientation orientation = plot.getOrientation();
    203            RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
    204                    plot.getDomainAxisLocation(), orientation);
    205            RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
    206                    plot.getRangeAxisLocation(), orientation);
    207
    208            if (orientation == PlotOrientation.HORIZONTAL) {
    209                anchorY = (float) domainAxis.getCategoryJava2DCoordinate(
    210                        this.categoryAnchor, catIndex, catCount, dataArea,
    211                        domainEdge);
    212                anchorX = (float) rangeAxis.valueToJava2D(this.value, dataArea,
    213                        rangeEdge);
    214            }
    215            else if (orientation == PlotOrientation.VERTICAL) {
    216                anchorX = (float) domainAxis.getCategoryJava2DCoordinate(
    217                        this.categoryAnchor, catIndex, catCount, dataArea,
    218                        domainEdge);
    219                anchorY = (float) rangeAxis.valueToJava2D(this.value, dataArea,
    220                        rangeEdge);
    221            }
    222            g2.setFont(getFont());
    223            g2.setPaint(getPaint());
    224            TextUtilities.drawRotatedString(getText(), g2, anchorX, anchorY,
    225                    getTextAnchor(), getRotationAngle(), getRotationAnchor());
    226
    227        }
    228
    229        /**
    230         * Tests this object for equality with another.
    231         *
    232         * @param obj  the object (<code>null</code> permitted).
    233         *
    234         * @return <code>true</code> or <code>false</code>.
    235         */
    236        public boolean equals(Object obj) {
    237            if (obj == this) {
    238                return true;
    239            }
    240            if (!(obj instanceof CategoryTextAnnotation)) {
    241                return false;
    242            }
    243            CategoryTextAnnotation that = (CategoryTextAnnotation) obj;
    244            if (!super.equals(obj)) {
    245                return false;
    246            }
    247            if (!this.category.equals(that.getCategory())) {
    248                return false;
    249            }
    250            if (!this.categoryAnchor.equals(that.getCategoryAnchor())) {
    251                return false;
    252            }
    253            if (this.value != that.getValue()) {
    254                return false;
    255            }
    256            return true;
    257        }
    258
    259        /**
    260         * Returns a hash code for this instance.
    261         *
    262         * @return A hash code.
    263         */
    264        public int hashCode() {
    265            int result = super.hashCode();
    266            result = 37 * result + this.category.hashCode();
    267            result = 37 * result + this.categoryAnchor.hashCode();
    268            long temp = Double.doubleToLongBits(this.value);
    269            result = 37 * result + (int) (temp ^ (temp >>> 32));
    270            return result;
    271        }
    272
    273        /**
    274         * Returns a clone of the annotation.
    275         *
    276         * @return A clone.
    277         *
    278         * @throws CloneNotSupportedException  this class will not throw this
    279         *         exception, but subclasses (if any) might.
    280         */
    281        public Object clone() throws CloneNotSupportedException {
    282            return super.clone();
    283        }
    284
    285    }