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 }