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