    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2006, 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     * Marker.java
    029     * -----------
    030     * (C) Copyright 2002-2006, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   Nicolas Brodu;
    034     *
    035     * $Id: Marker.java,v 1.10.2.5 2006/09/05 14:34:23 mungady Exp $
    036     *
    037     * Changes (since 2-Jul-2002)
    038     * --------------------------
    039     * 02-Jul-2002 : Added extra constructor, standard header and Javadoc
    040     *               comments (DG);
    041     * 20-Aug-2002 : Added the outline stroke attribute (DG);
    042     * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    043     * 16-Oct-2002 : Added new constructor (DG);
    044     * 26-Mar-2003 : Implemented Serializable (DG);
    045     * 21-May-2003 : Added labels (DG);
    046     * 11-Sep-2003 : Implemented Cloneable (NB);
    047     * 05-Nov-2003 : Added checks to ensure some attributes are never null (DG);
    048     * 11-Feb-2003 : Moved to org.jfree.chart.plot package, plus significant API
    049     *               changes to support IntervalMarker in plots (DG);
    050     * 14-Jun-2004 : Updated equals() method (DG);
    051     * 21-Jan-2005 : Added settings to control direction of horizontal and
    052     *               vertical label offsets (DG);
    053     * 01-Jun-2005 : Modified to use only one label offset type - this will be
    054     *               applied to the domain or range axis as appropriate (DG);
    055     * 06-Jun-2005 : Fix equals() method to handle GradientPaint (DG);
    056     * 19-Aug-2005 : Changed constructor from public --> protected (DG);
    057     * ------------- JFREECHART 1.0.0 ---------------------------------------------
    058     * 05-Sep-2006 : Added MarkerChangeListener support (DG);
    059     *
    060     */
    061
    062    package org.jfree.chart.plot;
    063
    064    import java.awt.BasicStroke;
    065    import java.awt.Color;
    066    import java.awt.Font;
    067    import java.awt.Paint;
    068    import java.awt.Stroke;
    069    import java.io.IOException;
    070    import java.io.ObjectInputStream;
    071    import java.io.ObjectOutputStream;
    072    import java.io.Serializable;
    073    import java.util.EventListener;
    074
    075    import javax.swing.event.EventListenerList;
    076
    077    import org.jfree.chart.event.MarkerChangeEvent;
    078    import org.jfree.chart.event.MarkerChangeListener;
    079    import org.jfree.io.SerialUtilities;
    080    import org.jfree.ui.LengthAdjustmentType;
    081    import org.jfree.ui.RectangleAnchor;
    082    import org.jfree.ui.RectangleInsets;
    083    import org.jfree.ui.TextAnchor;
    084    import org.jfree.util.ObjectUtilities;
    085    import org.jfree.util.PaintUtilities;
    086
    087    /**
    088     * The base class for markers that can be added to plots to highlight a value
    089     * or range of values.
    090     * <br><br>
    091     * An event notification mechanism was added to this class in JFreeChart
    092     * version 1.0.3.
    093     */
    094    public abstract class Marker implements Cloneable, Serializable {
    095
    096        /** For serialization. */
    097        private static final long serialVersionUID = -734389651405327166L;
    098
    099        /** The paint. */
    100        private transient Paint paint;
    101
    102        /** The stroke. */
    103        private transient Stroke stroke;
    104
    105        /** The outline paint. */
    106        private transient Paint outlinePaint;
    107
    108        /** The outline stroke. */
    109        private transient Stroke outlineStroke;
    110
    111        /** The alpha transparency. */
    112        private float alpha;
    113
    114        /** The label. */
    115        private String label = null;
    116
    117        /** The label font. */
    118        private Font labelFont;
    119
    120        /** The label paint. */
    121        private transient Paint labelPaint;
    122
    123        /** The label position. */
    124        private RectangleAnchor labelAnchor;
    125
    126        /** The text anchor for the label. */
    127        private TextAnchor labelTextAnchor;
    128
    129        /** The label offset from the marker rectangle. */
    130        private RectangleInsets labelOffset;
    131
    132        /**
    133         * The offset type for the domain or range axis (never <code>null</code>).
    134         */
    135        private LengthAdjustmentType labelOffsetType;
    136
    137        /** Storage for registered change listeners. */
    138        private transient EventListenerList listenerList;
    139
    140        /**
    141         * Creates a new marker with default attributes.
    142         */
    143        protected Marker() {
    144            this(Color.gray);
    145        }
    146
    147        /**
    148         * Constructs a new marker.
    149         *
    150         * @param paint  the paint (<code>null</code> not permitted).
    151         */
    152        protected Marker(Paint paint) {
    153            this(paint, new BasicStroke(0.5f), Color.gray, new BasicStroke(0.5f),
    154                    0.80f);
    155        }
    156
    157        /**
    158         * Constructs a new marker.
    159         *
    160         * @param paint  the paint (<code>null</code> not permitted).
    161         * @param stroke  the stroke (<code>null</code> not permitted).
    162         * @param outlinePaint  the outline paint (<code>null</code> permitted).
    163         * @param outlineStroke  the outline stroke (<code>null</code> permitted).
    164         * @param alpha  the alpha transparency (must be in the range 0.0f to
    165         *     1.0f).
    166         *
    167         * @throws IllegalArgumentException if <code>paint</code> or
    168         *     <code>stroke</code> is <code>null</code>, or <code>alpha</code> is
    169         *     not in the specified range.
    170         */
    171        protected Marker(Paint paint, Stroke stroke,
    172                         Paint outlinePaint, Stroke outlineStroke,
    173                         float alpha) {
    174
    175            if (paint == null) {
    176                throw new IllegalArgumentException("Null 'paint' argument.");
    177            }
    178            if (stroke == null) {
    179                throw new IllegalArgumentException("Null 'stroke' argument.");
    180            }
    181            if (alpha < 0.0f || alpha > 1.0f)
    182                throw new IllegalArgumentException(
    183                        "The 'alpha' value must be in the range 0.0f to 1.0f");
    184
    185            this.paint = paint;
    186            this.stroke = stroke;
    187            this.outlinePaint = outlinePaint;
    188            this.outlineStroke = outlineStroke;
    189            this.alpha = alpha;
    190
    191            this.labelFont = new Font("SansSerif", Font.PLAIN, 9);
    192            this.labelPaint = Color.black;
    193            this.labelAnchor = RectangleAnchor.TOP_LEFT;
    194            this.labelOffset = new RectangleInsets(3.0, 3.0, 3.0, 3.0);
    195            this.labelOffsetType = LengthAdjustmentType.CONTRACT;
    196            this.labelTextAnchor = TextAnchor.CENTER;
    197
    198            this.listenerList = new EventListenerList();
    199        }
    200
    201        /**
    202         * Returns the paint.
    203         *
    204         * @return The paint (never <code>null</code>).
    205         *
    206         * @see #setPaint(Paint)
    207         */
    208        public Paint getPaint() {
    209            return this.paint;
    210        }
    211
    212        /**
    213         * Sets the paint and sends a {@link MarkerChangeEvent} to all registered
    214         * listeners.
    215         *
    216         * @param paint  the paint (<code>null</code> not permitted).
    217         *
    218         * @see #getPaint()
    219         */
    220        public void setPaint(Paint paint) {
    221            if (paint == null) {
    222                throw new IllegalArgumentException("Null 'paint' argument.");
    223            }
    224            this.paint = paint;
    225            notifyListeners(new MarkerChangeEvent(this));
    226        }
    227
    228        /**
    229         * Returns the stroke.
    230         *
    231         * @return The stroke (never <code>null</code>).
    232         *
    233         * @see #setStroke(Stroke)
    234         */
    235        public Stroke getStroke() {
    236            return this.stroke;
    237        }
    238
    239        /**
    240         * Sets the stroke and sends a {@link MarkerChangeEvent} to all registered
    241         * listeners.
    242         *
    243         * @param stroke  the stroke (<code>null</code> not permitted).
    244         *
    245         * @see #getStroke()
    246         */
    247        public void setStroke(Stroke stroke) {
    248            if (stroke == null) {
    249                throw new IllegalArgumentException("Null 'stroke' argument.");
    250            }
    251            this.stroke = stroke;
    252            notifyListeners(new MarkerChangeEvent(this));
    253        }
    254
    255        /**
    256         * Returns the outline paint.
    257         *
    258         * @return The outline paint (possibly <code>null</code>).
    259         *
    260         * @see #setOutlinePaint(Paint)
    261         */
    262        public Paint getOutlinePaint() {
    263            return this.outlinePaint;
    264        }
    265
    266        /**
    267         * Sets the outline paint and sends a {@link MarkerChangeEvent} to all
    268         * registered listeners.
    269         *
    270         * @param paint  the paint (<code>null</code> permitted).
    271         *
    272         * @see #getOutlinePaint()
    273         */
    274        public void setOutlinePaint(Paint paint) {
    275            this.outlinePaint = paint;
    276            notifyListeners(new MarkerChangeEvent(this));
    277        }
    278
    279        /**
    280         * Returns the outline stroke.
    281         *
    282         * @return The outline stroke (possibly <code>null</code>).
    283         *
    284         * @see #setOutlineStroke(Stroke)
    285         */
    286        public Stroke getOutlineStroke() {
    287            return this.outlineStroke;
    288        }
    289
    290        /**
    291         * Sets the outline stroke and sends a {@link MarkerChangeEvent} to all
    292         * registered listeners.
    293         *
    294         * @param stroke  the stroke (<code>null</code> permitted).
    295         *
    296         * @see #getOutlineStroke()
    297         */
    298        public void setOutlineStroke(Stroke stroke) {
    299            this.outlineStroke = stroke;
    300            notifyListeners(new MarkerChangeEvent(this));
    301        }
    302
    303        /**
    304         * Returns the alpha transparency.
    305         *
    306         * @return The alpha transparency.
    307         *
    308         * @see #setAlpha(float)
    309         */
    310        public float getAlpha() {
    311            return this.alpha;
    312        }
    313
    314        /**
    315         * Sets the alpha transparency that should be used when drawing the
    316         * marker, and sends a {@link MarkerChangeEvent} to all registered
    317         * listeners.  The alpha transparency is a value in the range 0.0f
    318         * (completely transparent) to 1.0f (completely opaque).
    319         *
    320         * @param alpha  the alpha transparency (must be in the range 0.0f to
    321         *     1.0f).
    322         *
    323         * @throws IllegalArgumentException if <code>alpha</code> is not in the
    324         *     specified range.
    325         *
    326         * @see #getAlpha()
    327         */
    328        public void setAlpha(float alpha) {
    329            if (alpha < 0.0f || alpha > 1.0f)
    330                throw new IllegalArgumentException(
    331                        "The 'alpha' value must be in the range 0.0f to 1.0f");
    332            this.alpha = alpha;
    333            notifyListeners(new MarkerChangeEvent(this));
    334        }
    335
    336        /**
    337         * Returns the label (if <code>null</code> no label is displayed).
    338         *
    339         * @return The label (possibly <code>null</code>).
    340         *
    341         * @see #setLabel(String)
    342         */
    343        public String getLabel() {
    344            return this.label;
    345        }
    346
    347        /**
    348         * Sets the label (if <code>null</code> no label is displayed) and sends a
    349         * {@link MarkerChangeEvent} to all registered listeners.
    350         *
    351         * @param label  the label (<code>null</code> permitted).
    352         *
    353         * @see #getLabel()
    354         */
    355        public void setLabel(String label) {
    356            this.label = label;
    357            notifyListeners(new MarkerChangeEvent(this));
    358        }
    359
    360        /**
    361         * Returns the label font.
    362         *
    363         * @return The label font (never <code>null</code>).
    364         *
    365         * @see #setLabelFont(Font)
    366         */
    367        public Font getLabelFont() {
    368            return this.labelFont;
    369        }
    370
    371        /**
    372         * Sets the label font and sends a {@link MarkerChangeEvent} to all
    373         * registered listeners.
    374         *
    375         * @param font  the font (<code>null</code> not permitted).
    376         *
    377         * @see #getLabelFont()
    378         */
    379        public void setLabelFont(Font font) {
    380            if (font == null) {
    381                throw new IllegalArgumentException("Null 'font' argument.");
    382            }
    383            this.labelFont = font;
    384            notifyListeners(new MarkerChangeEvent(this));
    385        }
    386
    387        /**
    388         * Returns the label paint.
    389         *
    390         * @return The label paint (never </code>null</code>).
    391         *
    392         * @see #setLabelPaint(Paint)
    393         */
    394        public Paint getLabelPaint() {
    395            return this.labelPaint;
    396        }
    397
    398        /**
    399         * Sets the label paint and sends a {@link MarkerChangeEvent} to all
    400         * registered listeners.
    401         *
    402         * @param paint  the paint (<code>null</code> not permitted).
    403         *
    404         * @see #getLabelPaint()
    405         */
    406        public void setLabelPaint(Paint paint) {
    407            if (paint == null) {
    408                throw new IllegalArgumentException("Null 'paint' argument.");
    409            }
    410            this.labelPaint = paint;
    411            notifyListeners(new MarkerChangeEvent(this));
    412        }
    413
    414        /**
    415         * Returns the label anchor.  This defines the position of the label
    416         * anchor, relative to the bounds of the marker.
    417         *
    418         * @return The label anchor (never <code>null</code>).
    419         *
    420         * @see #setLabelAnchor(RectangleAnchor)
    421         */
    422        public RectangleAnchor getLabelAnchor() {
    423            return this.labelAnchor;
    424        }
    425
    426        /**
    427         * Sets the label anchor and sends a {@link MarkerChangeEvent} to all
    428         * registered listeners.  The anchor defines the position of the label
    429         * anchor, relative to the bounds of the marker.
    430         *
    431         * @param anchor  the anchor (<code>null</code> not permitted).
    432         *
    433         * @see #getLabelAnchor()
    434         */
    435        public void setLabelAnchor(RectangleAnchor anchor) {
    436            if (anchor == null) {
    437                throw new IllegalArgumentException("Null 'anchor' argument.");
    438            }
    439            this.labelAnchor = anchor;
    440            notifyListeners(new MarkerChangeEvent(this));
    441        }
    442
    443        /**
    444         * Returns the label offset.
    445         *
    446         * @return The label offset (never <code>null</code>).
    447         *
    448         * @see #setLabelOffset(RectangleInsets)
    449         */
    450        public RectangleInsets getLabelOffset() {
    451            return this.labelOffset;
    452        }
    453
    454        /**
    455         * Sets the label offset and sends a {@link MarkerChangeEvent} to all
    456         * registered listeners.
    457         *
    458         * @param offset  the label offset (<code>null</code> not permitted).
    459         *
    460         * @see #getLabelOffset()
    461         */
    462        public void setLabelOffset(RectangleInsets offset) {
    463            if (offset == null) {
    464                throw new IllegalArgumentException("Null 'offset' argument.");
    465            }
    466            this.labelOffset = offset;
    467            notifyListeners(new MarkerChangeEvent(this));
    468        }
    469
    470        /**
    471         * Returns the label offset type.
    472         *
    473         * @return The type (never <code>null</code>).
    474         *
    475         * @see #setLabelOffsetType(LengthAdjustmentType)
    476         */
    477        public LengthAdjustmentType getLabelOffsetType() {
    478            return this.labelOffsetType;
    479        }
    480
    481        /**
    482         * Sets the label offset type and sends a {@link MarkerChangeEvent} to all
    483         * registered listeners.
    484         *
    485         * @param adj  the type (<code>null</code> not permitted).
    486         *
    487         * @see #getLabelOffsetType()
    488         */
    489        public void setLabelOffsetType(LengthAdjustmentType adj) {
    490            if (adj == null) {
    491                throw new IllegalArgumentException("Null 'adj' argument.");
    492            }
    493            this.labelOffsetType = adj;
    494            notifyListeners(new MarkerChangeEvent(this));
    495        }
    496
    497        /**
    498         * Returns the label text anchor.
    499         *
    500         * @return The label text anchor (never <code>null</code>).
    501         *
    502         * @see #setLabelTextAnchor(TextAnchor)
    503         */
    504        public TextAnchor getLabelTextAnchor() {
    505            return this.labelTextAnchor;
    506        }
    507
    508        /**
    509         * Sets the label text anchor and sends a {@link MarkerChangeEvent} to
    510         * all registered listeners.
    511         *
    512         * @param anchor  the label text anchor (<code>null</code> not permitted).
    513         *
    514         * @see #getLabelTextAnchor()
    515         */
    516        public void setLabelTextAnchor(TextAnchor anchor) {
    517            if (anchor == null) {
    518                throw new IllegalArgumentException("Null 'anchor' argument.");
    519            }
    520            this.labelTextAnchor = anchor;
    521            notifyListeners(new MarkerChangeEvent(this));
    522        }
    523
    524        /**
    525         * Registers an object for notification of changes to the marker.
    526         *
    527         * @param listener  the object to be registered.
    528         *
    529         * @since 1.0.3
    530         */
    531        public void addChangeListener(MarkerChangeListener listener) {
    532            this.listenerList.add(MarkerChangeListener.class, listener);
    533        }
    534
    535        /**
    536         * Unregisters an object for notification of changes to the marker.
    537         *
    538         * @param listener  the object to be unregistered.
    539         *
    540         * @since 1.0.3
    541         */
    542        public void removeChangeListener(MarkerChangeListener listener) {
    543            this.listenerList.remove(MarkerChangeListener.class, listener);
    544        }
    545
    546        /**
    547         * Notifies all registered listeners that the marker has been modified.
    548         *
    549         * @param event  information about the change event.
    550         *
    551         * @since 1.0.3
    552         */
    553        public void notifyListeners(MarkerChangeEvent event) {
    554
    555            Object[] listeners = this.listenerList.getListenerList();
    556            for (int i = listeners.length - 2; i >= 0; i -= 2) {
    557                if (listeners[i] == MarkerChangeListener.class) {
    558                    ((MarkerChangeListener) listeners[i + 1]).markerChanged(event);
    559                }
    560            }
    561
    562        }
    563
    564        /**
    565         * Returns an array containing all the listeners of the specified type.
    566         *
    567         * @param listenerType  the listener type.
    568         *
    569         * @return The array of listeners.
    570         *
    571         * @since 1.0.3
    572         */
    573        public EventListener[] getListeners(Class listenerType) {
    574            return this.listenerList.getListeners(listenerType);
    575        }
    576
    577        /**
    578         * Tests the marker for equality with an arbitrary object.
    579         *
    580         * @param obj  the object (<code>null</code> permitted).
    581         *
    582         * @return A boolean.
    583         */
    584        public boolean equals(Object obj) {
    585            if (obj == this) {
    586                return true;
    587            }
    588            if (!(obj instanceof Marker)) {
    589                return false;
    590            }
    591            Marker that = (Marker) obj;
    592            if (!PaintUtilities.equal(this.paint, that.paint)) {
    593                return false;
    594            }
    595            if (!ObjectUtilities.equal(this.stroke, that.stroke)) {
    596                return false;
    597            }
    598            if (!PaintUtilities.equal(this.outlinePaint, that.outlinePaint)) {
    599                return false;
    600            }
    601            if (!ObjectUtilities.equal(this.outlineStroke, that.outlineStroke)) {
    602                return false;
    603            }
    604            if (this.alpha != that.alpha) {
    605                return false;
    606            }
    607            if (!ObjectUtilities.equal(this.label, that.label)) {
    608                return false;
    609            }
    610            if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) {
    611                return false;
    612            }
    613            if (!PaintUtilities.equal(this.labelPaint, that.labelPaint)) {
    614                return false;
    615            }
    616            if (this.labelAnchor != that.labelAnchor) {
    617                return false;
    618            }
    619            if (this.labelTextAnchor != that.labelTextAnchor) {
    620                return false;
    621            }
    622            if (!ObjectUtilities.equal(this.labelOffset, that.labelOffset)) {
    623                return false;
    624            }
    625            if (!this.labelOffsetType.equals(that.labelOffsetType)) {
    626                return false;
    627            }
    628            return true;
    629        }
    630
    631        /**
    632         * Creates a clone of the marker.
    633         *
    634         * @return A clone.
    635         *
    636         * @throws CloneNotSupportedException never.
    637         */
    638        public Object clone() throws CloneNotSupportedException {
    639            return super.clone();
    640        }
    641
    642        /**
    643         * Provides serialization support.
    644         *
    645         * @param stream  the output stream.
    646         *
    647         * @throws IOException  if there is an I/O error.
    648         */
    649        private void writeObject(ObjectOutputStream stream) throws IOException {
    650            stream.defaultWriteObject();
    651            SerialUtilities.writePaint(this.paint, stream);
    652            SerialUtilities.writeStroke(this.stroke, stream);
    653            SerialUtilities.writePaint(this.outlinePaint, stream);
    654            SerialUtilities.writeStroke(this.outlineStroke, stream);
    655            SerialUtilities.writePaint(this.labelPaint, stream);
    656        }
    657
    658        /**
    659         * Provides serialization support.
    660         *
    661         * @param stream  the input stream.
    662         *
    663         * @throws IOException  if there is an I/O error.
    664         * @throws ClassNotFoundException  if there is a classpath problem.
    665         */
    666        private void readObject(ObjectInputStream stream)
    667            throws IOException, ClassNotFoundException {
    668            stream.defaultReadObject();
    669            this.paint = SerialUtilities.readPaint(stream);
    670            this.stroke = SerialUtilities.readStroke(stream);
    671            this.outlinePaint = SerialUtilities.readPaint(stream);
    672            this.outlineStroke = SerialUtilities.readStroke(stream);
    673            this.labelPaint = SerialUtilities.readPaint(stream);
    674        }
    675
    676    }