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