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 }