001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-present, by David Gilbert 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 * LegendItem.java
029 * ---------------
030 * (C) Copyright 2000-present, by David Gilbert and Contributors.
031 *
032 * Original Author:  David Gilbert;
033 * Contributor(s):   Andrzej Porebski;
034 *                   David Li;
035 *                   Wolfgang Irler;
036 *                   Luke Quinane;
037 *                   Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier);
038 *
039 */
040
041package org.jfree.chart;
042
043import java.awt.BasicStroke;
044import java.awt.Color;
045import java.awt.Font;
046import java.awt.Paint;
047import java.awt.Shape;
048import java.awt.Stroke;
049import java.awt.geom.Line2D;
050import java.awt.geom.Rectangle2D;
051import java.io.IOException;
052import java.io.ObjectInputStream;
053import java.io.ObjectOutputStream;
054import java.io.Serializable;
055import java.text.AttributedString;
056import java.text.CharacterIterator;
057import java.util.Objects;
058import org.jfree.chart.text.AttributedStringUtils;
059import org.jfree.chart.ui.GradientPaintTransformer;
060import org.jfree.chart.ui.StandardGradientPaintTransformer;
061import org.jfree.chart.util.PaintUtils;
062import org.jfree.chart.util.Args;
063import org.jfree.chart.util.PublicCloneable;
064import org.jfree.chart.util.SerialUtils;
065import org.jfree.chart.util.ShapeUtils;
066import org.jfree.data.general.Dataset;
067
068/**
069 * A temporary storage object for recording the properties of a legend item,
070 * without any consideration for layout issues.
071 */
072public class LegendItem implements Cloneable, Serializable {
073
074    /** For serialization. */
075    private static final long serialVersionUID = -797214582948827144L;
076
077    /**
078     * The dataset.
079     */
080    private Dataset dataset;
081
082    /**
083     * The series key.
084     */
085    private Comparable seriesKey;
086
087    /** The dataset index. */
088    private int datasetIndex;
089
090    /** The series index. */
091    private int series;
092
093    /** The label. */
094    private String label;
095
096    /**
097     * The label font ({@code null} is permitted).
098     */
099    private Font labelFont;
100
101    /**
102     * The label paint ({@code null} is permitted).
103     */
104    private transient Paint labelPaint;
105
106    /** The attributed label (if null, fall back to the regular label). */
107    private transient AttributedString attributedLabel;
108
109    /**
110     * The description (not currently used - could be displayed as a tool tip).
111     */
112    private String description;
113
114    /** The tool tip text. */
115    private String toolTipText;
116
117    /** The url text. */
118    private String urlText;
119
120    /** A flag that controls whether or not the shape is visible. */
121    private boolean shapeVisible;
122
123    /** The shape. */
124    private transient Shape shape;
125
126    /** A flag that controls whether or not the shape is filled. */
127    private boolean shapeFilled;
128
129    /** The paint. */
130    private transient Paint fillPaint;
131
132    /**
133     * A gradient paint transformer.
134     */
135    private GradientPaintTransformer fillPaintTransformer;
136
137    /** A flag that controls whether or not the shape outline is visible. */
138    private boolean shapeOutlineVisible;
139
140    /** The outline paint. */
141    private transient Paint outlinePaint;
142
143    /** The outline stroke. */
144    private transient Stroke outlineStroke;
145
146    /** A flag that controls whether or not the line is visible. */
147    private boolean lineVisible;
148
149    /** The line. */
150    private transient Shape line;
151
152    /** The stroke. */
153    private transient Stroke lineStroke;
154
155    /** The line paint. */
156    private transient Paint linePaint;
157
158    /**
159     * The shape must be non-null for a LegendItem - if no shape is required,
160     * use this.
161     */
162    private static final Shape UNUSED_SHAPE = new Line2D.Float();
163
164    /**
165     * The stroke must be non-null for a LegendItem - if no stroke is required,
166     * use this.
167     */
168    private static final Stroke UNUSED_STROKE = new BasicStroke(0.0f);
169
170    /**
171     * Creates a legend item with the specified label.  The remaining
172     * attributes take default values.
173     *
174     * @param label  the label ({@code null} not permitted).
175     */
176    public LegendItem(String label) {
177        this(label, Color.BLACK);
178    }
179
180    /**
181     * Creates a legend item with the specified label and fill paint.  The
182     * remaining attributes take default values.
183     *
184     * @param label  the label ({@code null} not permitted).
185     * @param paint  the paint ({@code null} not permitted).
186     */
187    public LegendItem(String label, Paint paint) {
188        this(label, null, null, null, new Rectangle2D.Double(-4.0, -4.0, 8.0,
189                8.0), paint);
190    }
191
192    /**
193     * Creates a legend item with a filled shape.  The shape is not outlined,
194     * and no line is visible.
195     *
196     * @param label  the label ({@code null} not permitted).
197     * @param description  the description ({@code null} permitted).
198     * @param toolTipText  the tool tip text ({@code null} permitted).
199     * @param urlText  the URL text ({@code null} permitted).
200     * @param shape  the shape ({@code null} not permitted).
201     * @param fillPaint  the paint used to fill the shape ({@code null}
202     *                   not permitted).
203     */
204    public LegendItem(String label, String description,
205                      String toolTipText, String urlText,
206                      Shape shape, Paint fillPaint) {
207
208        this(label, description, toolTipText, urlText,
209                /* shape visible = */ true, shape,
210                /* shape filled = */ true, fillPaint,
211                /* shape outlined */ false, Color.BLACK, UNUSED_STROKE,
212                /* line visible */ false, UNUSED_SHAPE, UNUSED_STROKE,
213                Color.BLACK);
214
215    }
216
217    /**
218     * Creates a legend item with a filled and outlined shape.
219     *
220     * @param label  the label ({@code null} not permitted).
221     * @param description  the description ({@code null} permitted).
222     * @param toolTipText  the tool tip text ({@code null} permitted).
223     * @param urlText  the URL text ({@code null} permitted).
224     * @param shape  the shape ({@code null} not permitted).
225     * @param fillPaint  the paint used to fill the shape ({@code null}
226     *                   not permitted).
227     * @param outlineStroke  the outline stroke ({@code null} not
228     *                       permitted).
229     * @param outlinePaint  the outline paint ({@code null} not
230     *                      permitted).
231     */
232    public LegendItem(String label, String description, String toolTipText, 
233            String urlText, Shape shape, Paint fillPaint, Stroke outlineStroke, 
234            Paint outlinePaint) {
235
236        this(label, description, toolTipText, urlText,
237                /* shape visible = */ true, shape,
238                /* shape filled = */ true, fillPaint,
239                /* shape outlined = */ true, outlinePaint, outlineStroke,
240                /* line visible */ false, UNUSED_SHAPE, UNUSED_STROKE,
241                Color.BLACK);
242
243    }
244
245    /**
246     * Creates a legend item using a line.
247     *
248     * @param label  the label ({@code null} not permitted).
249     * @param description  the description ({@code null} permitted).
250     * @param toolTipText  the tool tip text ({@code null} permitted).
251     * @param urlText  the URL text ({@code null} permitted).
252     * @param line  the line ({@code null} not permitted).
253     * @param lineStroke  the line stroke ({@code null} not permitted).
254     * @param linePaint  the line paint ({@code null} not permitted).
255     */
256    public LegendItem(String label, String description, String toolTipText, 
257            String urlText, Shape line, Stroke lineStroke, Paint linePaint) {
258
259        this(label, description, toolTipText, urlText,
260                /* shape visible = */ false, UNUSED_SHAPE,
261                /* shape filled = */ false, Color.BLACK,
262                /* shape outlined = */ false, Color.BLACK, UNUSED_STROKE,
263                /* line visible = */ true, line, lineStroke, linePaint);
264    }
265
266    /**
267     * Creates a new legend item.
268     *
269     * @param label  the label ({@code null} not permitted).
270     * @param description  the description (not currently used,
271     *        {@code null} permitted).
272     * @param toolTipText  the tool tip text ({@code null} permitted).
273     * @param urlText  the URL text ({@code null} permitted).
274     * @param shapeVisible  a flag that controls whether or not the shape is
275     *                      displayed.
276     * @param shape  the shape ({@code null} permitted).
277     * @param shapeFilled  a flag that controls whether or not the shape is
278     *                     filled.
279     * @param fillPaint  the fill paint ({@code null} not permitted).
280     * @param shapeOutlineVisible  a flag that controls whether or not the
281     *                             shape is outlined.
282     * @param outlinePaint  the outline paint ({@code null} not permitted).
283     * @param outlineStroke  the outline stroke ({@code null} not
284     *                       permitted).
285     * @param lineVisible  a flag that controls whether or not the line is
286     *                     visible.
287     * @param line  the line.
288     * @param lineStroke  the stroke ({@code null} not permitted).
289     * @param linePaint  the line paint ({@code null} not permitted).
290     */
291    public LegendItem(String label, String description,
292                      String toolTipText, String urlText,
293                      boolean shapeVisible, Shape shape,
294                      boolean shapeFilled, Paint fillPaint,
295                      boolean shapeOutlineVisible, Paint outlinePaint,
296                      Stroke outlineStroke,
297                      boolean lineVisible, Shape line,
298                      Stroke lineStroke, Paint linePaint) {
299
300        Args.nullNotPermitted(label, "label");
301        Args.nullNotPermitted(fillPaint, "fillPaint");
302        Args.nullNotPermitted(lineStroke, "lineStroke");
303        Args.nullNotPermitted(outlinePaint, "outlinePaint");
304        Args.nullNotPermitted(outlineStroke, "outlineStroke");
305        this.label = label;
306        this.labelPaint = null;
307        this.attributedLabel = null;
308        this.description = description;
309        this.shapeVisible = shapeVisible;
310        this.shape = shape;
311        this.shapeFilled = shapeFilled;
312        this.fillPaint = fillPaint;
313        this.fillPaintTransformer = new StandardGradientPaintTransformer();
314        this.shapeOutlineVisible = shapeOutlineVisible;
315        this.outlinePaint = outlinePaint;
316        this.outlineStroke = outlineStroke;
317        this.lineVisible = lineVisible;
318        this.line = line;
319        this.lineStroke = lineStroke;
320        this.linePaint = linePaint;
321        this.toolTipText = toolTipText;
322        this.urlText = urlText;
323    }
324
325    /**
326     * Creates a legend item with a filled shape.  The shape is not outlined,
327     * and no line is visible.
328     *
329     * @param label  the label ({@code null} not permitted).
330     * @param description  the description ({@code null} permitted).
331     * @param toolTipText  the tool tip text ({@code null} permitted).
332     * @param urlText  the URL text ({@code null} permitted).
333     * @param shape  the shape ({@code null} not permitted).
334     * @param fillPaint  the paint used to fill the shape ({@code null}
335     *                   not permitted).
336     */
337    public LegendItem(AttributedString label, String description,
338                      String toolTipText, String urlText,
339                      Shape shape, Paint fillPaint) {
340
341        this(label, description, toolTipText, urlText,
342                /* shape visible = */ true, shape,
343                /* shape filled = */ true, fillPaint,
344                /* shape outlined = */ false, Color.BLACK, UNUSED_STROKE,
345                /* line visible = */ false, UNUSED_SHAPE, UNUSED_STROKE,
346                Color.BLACK);
347
348    }
349
350    /**
351     * Creates a legend item with a filled and outlined shape.
352     *
353     * @param label  the label ({@code null} not permitted).
354     * @param description  the description ({@code null} permitted).
355     * @param toolTipText  the tool tip text ({@code null} permitted).
356     * @param urlText  the URL text ({@code null} permitted).
357     * @param shape  the shape ({@code null} not permitted).
358     * @param fillPaint  the paint used to fill the shape ({@code null}
359     *                   not permitted).
360     * @param outlineStroke  the outline stroke ({@code null} not
361     *                       permitted).
362     * @param outlinePaint  the outline paint ({@code null} not
363     *                      permitted).
364     */
365    public LegendItem(AttributedString label, String description,
366                      String toolTipText, String urlText,
367                      Shape shape, Paint fillPaint,
368                      Stroke outlineStroke, Paint outlinePaint) {
369
370        this(label, description, toolTipText, urlText,
371                /* shape visible = */ true, shape,
372                /* shape filled = */ true, fillPaint,
373                /* shape outlined = */ true, outlinePaint, outlineStroke,
374                /* line visible = */ false, UNUSED_SHAPE, UNUSED_STROKE,
375                Color.BLACK);
376    }
377
378    /**
379     * Creates a legend item using a line.
380     *
381     * @param label  the label ({@code null} not permitted).
382     * @param description  the description ({@code null} permitted).
383     * @param toolTipText  the tool tip text ({@code null} permitted).
384     * @param urlText  the URL text ({@code null} permitted).
385     * @param line  the line ({@code null} not permitted).
386     * @param lineStroke  the line stroke ({@code null} not permitted).
387     * @param linePaint  the line paint ({@code null} not permitted).
388     */
389    public LegendItem(AttributedString label, String description,
390                      String toolTipText, String urlText,
391                      Shape line, Stroke lineStroke, Paint linePaint) {
392
393        this(label, description, toolTipText, urlText,
394                /* shape visible = */ false, UNUSED_SHAPE,
395                /* shape filled = */ false, Color.BLACK,
396                /* shape outlined = */ false, Color.BLACK, UNUSED_STROKE,
397                /* line visible = */ true, line, lineStroke, linePaint);
398    }
399
400    /**
401     * Creates a new legend item.
402     *
403     * @param label  the label ({@code null} not permitted).
404     * @param description  the description (not currently used,
405     *        {@code null} permitted).
406     * @param toolTipText  the tool tip text ({@code null} permitted).
407     * @param urlText  the URL text ({@code null} permitted).
408     * @param shapeVisible  a flag that controls whether or not the shape is
409     *                      displayed.
410     * @param shape  the shape ({@code null} permitted).
411     * @param shapeFilled  a flag that controls whether or not the shape is
412     *                     filled.
413     * @param fillPaint  the fill paint ({@code null} not permitted).
414     * @param shapeOutlineVisible  a flag that controls whether or not the
415     *                             shape is outlined.
416     * @param outlinePaint  the outline paint ({@code null} not permitted).
417     * @param outlineStroke  the outline stroke ({@code null} not
418     *                       permitted).
419     * @param lineVisible  a flag that controls whether or not the line is
420     *                     visible.
421     * @param line  the line ({@code null} not permitted).
422     * @param lineStroke  the stroke ({@code null} not permitted).
423     * @param linePaint  the line paint ({@code null} not permitted).
424     */
425    public LegendItem(AttributedString label, String description,
426                      String toolTipText, String urlText,
427                      boolean shapeVisible, Shape shape,
428                      boolean shapeFilled, Paint fillPaint,
429                      boolean shapeOutlineVisible, Paint outlinePaint,
430                      Stroke outlineStroke,
431                      boolean lineVisible, Shape line, Stroke lineStroke,
432                      Paint linePaint) {
433
434        Args.nullNotPermitted(label, "label");
435        Args.nullNotPermitted(fillPaint, "fillPaint");
436        Args.nullNotPermitted(lineStroke, "lineStroke");
437        Args.nullNotPermitted(line, "line");
438        Args.nullNotPermitted(linePaint, "linePaint");
439        Args.nullNotPermitted(outlinePaint, "outlinePaint");
440        Args.nullNotPermitted(outlineStroke, "outlineStroke");
441        this.label = characterIteratorToString(label.getIterator());
442        this.attributedLabel = label;
443        this.description = description;
444        this.shapeVisible = shapeVisible;
445        this.shape = shape;
446        this.shapeFilled = shapeFilled;
447        this.fillPaint = fillPaint;
448        this.fillPaintTransformer = new StandardGradientPaintTransformer();
449        this.shapeOutlineVisible = shapeOutlineVisible;
450        this.outlinePaint = outlinePaint;
451        this.outlineStroke = outlineStroke;
452        this.lineVisible = lineVisible;
453        this.line = line;
454        this.lineStroke = lineStroke;
455        this.linePaint = linePaint;
456        this.toolTipText = toolTipText;
457        this.urlText = urlText;
458    }
459
460    /**
461     * Returns a string containing the characters from the given iterator.
462     *
463     * @param iterator  the iterator ({@code null} not permitted).
464     *
465     * @return A string.
466     */
467    private String characterIteratorToString(CharacterIterator iterator) {
468        int endIndex = iterator.getEndIndex();
469        int beginIndex = iterator.getBeginIndex();
470        int count = endIndex - beginIndex;
471        if (count <= 0) {
472            return "";
473        }
474        char[] chars = new char[count];
475        int i = 0;
476        char c = iterator.first();
477        while (c != CharacterIterator.DONE) {
478            chars[i] = c;
479            i++;
480            c = iterator.next();
481        }
482        return new String(chars);
483    }
484
485    /**
486     * Returns the dataset.
487     *
488     * @return The dataset.
489     *
490     * @see #setDatasetIndex(int)
491     */
492    public Dataset getDataset() {
493        return this.dataset;
494    }
495
496    /**
497     * Sets the dataset.
498     *
499     * @param dataset  the dataset.
500     */
501    public void setDataset(Dataset dataset) {
502        this.dataset = dataset;
503    }
504
505    /**
506     * Returns the dataset index for this legend item.
507     *
508     * @return The dataset index.
509     *
510     * @see #setDatasetIndex(int)
511     * @see #getDataset()
512     */
513    public int getDatasetIndex() {
514        return this.datasetIndex;
515    }
516
517    /**
518     * Sets the dataset index for this legend item.
519     *
520     * @param index  the index.
521     *
522     * @see #getDatasetIndex()
523     */
524    public void setDatasetIndex(int index) {
525        this.datasetIndex = index;
526    }
527
528    /**
529     * Returns the series key.
530     *
531     * @return The series key.
532     *
533     * @see #setSeriesKey(Comparable)
534     */
535    public Comparable getSeriesKey() {
536        return this.seriesKey;
537    }
538
539    /**
540     * Sets the series key.
541     *
542     * @param key  the series key.
543     */
544    public void setSeriesKey(Comparable key) {
545        this.seriesKey = key;
546    }
547
548    /**
549     * Returns the series index for this legend item.
550     *
551     * @return The series index.
552     */
553    public int getSeriesIndex() {
554        return this.series;
555    }
556
557    /**
558     * Sets the series index for this legend item.
559     *
560     * @param index  the index.
561     */
562    public void setSeriesIndex(int index) {
563        this.series = index;
564    }
565
566    /**
567     * Returns the label.
568     *
569     * @return The label (never {@code null}).
570     */
571    public String getLabel() {
572        return this.label;
573    }
574
575    /**
576     * Returns the label font.
577     *
578     * @return The label font (possibly {@code null}).
579     */
580    public Font getLabelFont() {
581        return this.labelFont;
582    }
583
584    /**
585     * Sets the label font.
586     *
587     * @param font  the font ({@code null} permitted).
588     */
589    public void setLabelFont(Font font) {
590        this.labelFont = font;
591    }
592
593    /**
594     * Returns the paint used to draw the label.
595     *
596     * @return The paint (possibly {@code null}).
597     */
598    public Paint getLabelPaint() {
599        return this.labelPaint;
600    }
601
602    /**
603     * Sets the paint used to draw the label.
604     *
605     * @param paint  the paint ({@code null} permitted).
606     */
607    public void setLabelPaint(Paint paint) {
608        this.labelPaint = paint;
609    }
610
611    /**
612     * Returns the attributed label.
613     *
614     * @return The attributed label (possibly {@code null}).
615     */
616    public AttributedString getAttributedLabel() {
617        return this.attributedLabel;
618    }
619
620    /**
621     * Returns the description for the legend item.
622     *
623     * @return The description (possibly {@code null}).
624     *
625     * @see #setDescription(java.lang.String) 
626     */
627    public String getDescription() {
628        return this.description;
629    }
630
631    /**
632     * Sets the description for this legend item.
633     *
634     * @param text  the description ({@code null} permitted).
635     *
636     * @see #getDescription()
637     */
638    public void setDescription(String text) {
639        this.description = text;
640    }
641
642    /**
643     * Returns the tool tip text.
644     *
645     * @return The tool tip text (possibly {@code null}).
646     *
647     * @see #setToolTipText(java.lang.String) 
648     */
649    public String getToolTipText() {
650        return this.toolTipText;
651    }
652
653    /**
654     * Sets the tool tip text for this legend item.
655     *
656     * @param text  the text ({@code null} permitted).
657     *
658     * @see #getToolTipText()
659     */
660    public void setToolTipText(String text) {
661        this.toolTipText = text;
662    }
663
664    /**
665     * Returns the URL text.
666     *
667     * @return The URL text (possibly {@code null}).
668     *
669     * @see #setURLText(java.lang.String) 
670     */
671    public String getURLText() {
672        return this.urlText;
673    }
674
675    /**
676     * Sets the URL text.
677     *
678     * @param text  the text ({@code null} permitted).
679     *
680     * @see #getURLText()
681     */
682    public void setURLText(String text) {
683        this.urlText = text;
684    }
685
686    /**
687     * Returns a flag that indicates whether or not the shape is visible.
688     *
689     * @return A boolean.
690     *
691     * @see #setShapeVisible(boolean)
692     */
693    public boolean isShapeVisible() {
694        return this.shapeVisible;
695    }
696
697    /**
698     * Sets the flag that controls whether or not the shape is visible.
699     *
700     * @param visible  the new flag value.
701     *
702     * @see #isShapeVisible()
703     * @see #isLineVisible()
704     */
705    public void setShapeVisible(boolean visible) {
706        this.shapeVisible = visible;
707    }
708
709    /**
710     * Returns the shape used to label the series represented by this legend
711     * item.
712     *
713     * @return The shape (never {@code null}).
714     *
715     * @see #setShape(java.awt.Shape) 
716     */
717    public Shape getShape() {
718        return this.shape;
719    }
720
721    /**
722     * Sets the shape for the legend item.
723     *
724     * @param shape  the shape ({@code null} not permitted).
725     *
726     * @see #getShape()
727     */
728    public void setShape(Shape shape) {
729        Args.nullNotPermitted(shape, "shape");
730        this.shape = shape;
731    }
732
733    /**
734     * Returns a flag that controls whether or not the shape is filled.
735     *
736     * @return A boolean.
737     */
738    public boolean isShapeFilled() {
739        return this.shapeFilled;
740    }
741
742    /**
743     * Returns the fill paint.
744     *
745     * @return The fill paint (never {@code null}).
746     */
747    public Paint getFillPaint() {
748        return this.fillPaint;
749    }
750
751    /**
752     * Sets the fill paint.
753     *
754     * @param paint  the paint ({@code null} not permitted).
755     */
756    public void setFillPaint(Paint paint) {
757        Args.nullNotPermitted(paint, "paint");
758        this.fillPaint = paint;
759    }
760
761    /**
762     * Returns the flag that controls whether or not the shape outline
763     * is visible.
764     *
765     * @return A boolean.
766     */
767    public boolean isShapeOutlineVisible() {
768        return this.shapeOutlineVisible;
769    }
770
771    /**
772     * Returns the line stroke for the series.
773     *
774     * @return The stroke (never {@code null}).
775     */
776    public Stroke getLineStroke() {
777        return this.lineStroke;
778    }
779    
780    /**
781     * Sets the line stroke.
782     * 
783     * @param stroke  the stroke ({@code null} not permitted).
784     */
785    public void setLineStroke(Stroke stroke) {
786        Args.nullNotPermitted(stroke, "stroke");
787        this.lineStroke = stroke;
788    }
789
790    /**
791     * Returns the paint used for lines.
792     *
793     * @return The paint (never {@code null}).
794     */
795    public Paint getLinePaint() {
796        return this.linePaint;
797    }
798
799    /**
800     * Sets the line paint.
801     *
802     * @param paint  the paint ({@code null} not permitted).
803     */
804    public void setLinePaint(Paint paint) {
805        Args.nullNotPermitted(paint, "paint");
806        this.linePaint = paint;
807    }
808
809    /**
810     * Returns the outline paint.
811     *
812     * @return The outline paint (never {@code null}).
813     */
814    public Paint getOutlinePaint() {
815        return this.outlinePaint;
816    }
817
818    /**
819     * Sets the outline paint.
820     *
821     * @param paint  the paint ({@code null} not permitted).
822     */
823    public void setOutlinePaint(Paint paint) {
824        Args.nullNotPermitted(paint, "paint");
825        this.outlinePaint = paint;
826    }
827
828    /**
829     * Returns the outline stroke.
830     *
831     * @return The outline stroke (never {@code null}).
832     *
833     * @see #setOutlineStroke(java.awt.Stroke) 
834     */
835    public Stroke getOutlineStroke() {
836        return this.outlineStroke;
837    }
838
839    /**
840     * Sets the outline stroke.
841     *
842     * @param stroke  the stroke ({@code null} not permitted).
843     *
844     * @see #getOutlineStroke()
845     */
846    public void setOutlineStroke(Stroke stroke) {
847        Args.nullNotPermitted(stroke, "stroke");
848        this.outlineStroke = stroke;
849    }
850
851    /**
852     * Returns a flag that indicates whether or not the line is visible.
853     *
854     * @return A boolean.
855     *
856     * @see #setLineVisible(boolean) 
857     */
858    public boolean isLineVisible() {
859        return this.lineVisible;
860    }
861
862    /**
863     * Sets the flag that controls whether or not the line shape is visible for
864     * this legend item.
865     *
866     * @param visible  the new flag value.
867     *
868     * @see #isLineVisible()
869     */
870    public void setLineVisible(boolean visible) {
871        this.lineVisible = visible;
872    }
873
874    /**
875     * Returns the line.
876     *
877     * @return The line (never {@code null}).
878     *
879     * @see #setLine(java.awt.Shape)
880     * @see #isLineVisible() 
881     */
882    public Shape getLine() {
883        return this.line;
884    }
885
886    /**
887     * Sets the line.
888     *
889     * @param line  the line ({@code null} not permitted).
890     *
891     * @see #getLine()
892     */
893    public void setLine(Shape line) {
894        Args.nullNotPermitted(line, "line");
895        this.line = line;
896    }
897
898    /**
899     * Returns the transformer used when the fill paint is an instance of
900     * {@code GradientPaint}.
901     *
902     * @return The transformer (never {@code null}).
903     *
904     * @see #setFillPaintTransformer(GradientPaintTransformer)
905     */
906    public GradientPaintTransformer getFillPaintTransformer() {
907        return this.fillPaintTransformer;
908    }
909
910    /**
911     * Sets the transformer used when the fill paint is an instance of
912     * {@code GradientPaint}.
913     *
914     * @param transformer  the transformer ({@code null} not permitted).
915     *
916     * @see #getFillPaintTransformer()
917     */
918    public void setFillPaintTransformer(GradientPaintTransformer transformer) {
919        Args.nullNotPermitted(transformer, "transformer");
920        this.fillPaintTransformer = transformer;
921    }
922
923    /**
924     * Tests this item for equality with an arbitrary object.
925     *
926     * @param obj  the object ({@code null} permitted).
927     *
928     * @return A boolean.
929     */
930    @Override
931    public boolean equals(Object obj) {
932        if (obj == this) {
933            return true;
934        }
935        if (!(obj instanceof LegendItem)) {
936            return false;
937        }
938        LegendItem that = (LegendItem) obj;
939
940        if (!Objects.equals(this.dataset, that.dataset)) {
941            return false;
942        }
943        if (!Objects.equals(this.seriesKey, that.seriesKey)) {
944            return false;
945        }
946        if (this.datasetIndex != that.datasetIndex) {
947            return false;
948        }
949        if (this.series != that.series) {
950            return false;
951        }
952        if (!Objects.equals(this.label, that.label)) {
953            return false;
954        }
955        if (!Objects.equals(this.labelFont, that.labelFont)) {
956            return false;
957        }
958        if (!Objects.equals(this.description, that.description)) {
959            return false;
960        }
961        if (!Objects.equals(this.toolTipText, that.toolTipText)) {
962            return false;
963        }
964        if (!Objects.equals(this.urlText, that.urlText)) {
965            return false;
966        }
967        if (this.shapeVisible != that.shapeVisible) {
968            return false;
969        }
970        if (this.shapeFilled != that.shapeFilled) {
971            return false;
972        }
973        if (!Objects.equals(this.fillPaintTransformer,
974                            that.fillPaintTransformer)) {
975            return false;
976        }
977        if (!ShapeUtils.equal(this.shape, that.shape)) {
978            return false;
979        }
980        if (!PaintUtils.equal(this.fillPaint, that.fillPaint)) {
981            return false;
982        }
983        if (!AttributedStringUtils.equal(this.attributedLabel,
984                                         that.attributedLabel)) {
985            return false;
986        }
987        if (this.shapeOutlineVisible != that.shapeOutlineVisible) {
988            return false;
989        }
990        if (!Objects.equals(this.outlineStroke, that.outlineStroke)) {
991            return false;
992        }
993        if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) {
994            return false;
995        }
996        if (!this.lineVisible == that.lineVisible) {
997            return false;
998        }
999        if (!ShapeUtils.equal(this.line, that.line)) {
1000            return false;
1001        }
1002        if (!Objects.equals(this.lineStroke, that.lineStroke)) {
1003            return false;
1004        }
1005        if (!PaintUtils.equal(this.linePaint, that.linePaint)) {
1006            return false;
1007        }
1008        if (!Objects.equals(this.labelFont, that.labelFont)) {
1009            return false;
1010        }
1011        if (!PaintUtils.equal(this.labelPaint, that.labelPaint)) {
1012            return false;
1013        }
1014        return true;
1015    }
1016
1017    @Override
1018    public int hashCode() {
1019        int hash = 7;
1020        hash = 83 * hash + Objects.hashCode(this.dataset);
1021        hash = 83 * hash + Objects.hashCode(this.seriesKey);
1022        hash = 83 * hash + this.datasetIndex;
1023        hash = 83 * hash + this.series;
1024        hash = 83 * hash + Objects.hashCode(this.label);
1025        hash = 83 * hash + Objects.hashCode(this.labelFont);
1026        hash = 83 * hash + HashUtils.hashCodeForPaint(this.labelPaint);
1027        hash = 83 * hash + Objects.hashCode(this.attributedLabel);
1028        hash = 83 * hash + Objects.hashCode(this.description);
1029        hash = 83 * hash + Objects.hashCode(this.toolTipText);
1030        hash = 83 * hash + Objects.hashCode(this.urlText);
1031        hash = 83 * hash + (this.shapeVisible ? 1 : 0);
1032        hash = 83 * hash + Objects.hashCode(this.shape);
1033        hash = 83 * hash + (this.shapeFilled ? 1 : 0);
1034        hash = 83 * hash + HashUtils.hashCodeForPaint(this.fillPaint);
1035        hash = 83 * hash + Objects.hashCode(this.fillPaintTransformer);
1036        hash = 83 * hash + (this.shapeOutlineVisible ? 1 : 0);
1037        hash = 83 * hash + HashUtils.hashCodeForPaint(this.outlinePaint);
1038        hash = 83 * hash + Objects.hashCode(this.outlineStroke);
1039        hash = 83 * hash + (this.lineVisible ? 1 : 0);
1040        hash = 83 * hash + Objects.hashCode(this.line);
1041        hash = 83 * hash + Objects.hashCode(this.lineStroke);
1042        hash = 83 * hash + HashUtils.hashCodeForPaint(this.linePaint);
1043        return hash;
1044    }
1045
1046    /**
1047     * Returns an independent copy of this object (except that the clone will
1048     * still reference the same dataset as the original {@code LegendItem}).
1049     *
1050     * @return A clone.
1051     *
1052     * @throws CloneNotSupportedException if the legend item cannot be cloned.
1053     */
1054    @Override
1055    public Object clone() throws CloneNotSupportedException {
1056        LegendItem clone = (LegendItem) super.clone();
1057        if (this.seriesKey instanceof PublicCloneable) {
1058            PublicCloneable pc = (PublicCloneable) this.seriesKey;
1059            clone.seriesKey = (Comparable) pc.clone();
1060        }
1061        // FIXME: Clone the attributed string if it is not null
1062        clone.shape = ShapeUtils.clone(this.shape);
1063        if (this.fillPaintTransformer instanceof PublicCloneable) {
1064            PublicCloneable pc = (PublicCloneable) this.fillPaintTransformer;
1065            clone.fillPaintTransformer = (GradientPaintTransformer) pc.clone();
1066
1067        }
1068        clone.line = ShapeUtils.clone(this.line);
1069        return clone;
1070    }
1071
1072    /**
1073     * Provides serialization support.
1074     *
1075     * @param stream  the output stream ({@code null} not permitted).
1076     *
1077     * @throws IOException  if there is an I/O error.
1078     */
1079    private void writeObject(ObjectOutputStream stream) throws IOException {
1080        stream.defaultWriteObject();
1081        SerialUtils.writeAttributedString(this.attributedLabel, stream);
1082        SerialUtils.writeShape(this.shape, stream);
1083        SerialUtils.writePaint(this.fillPaint, stream);
1084        SerialUtils.writeStroke(this.outlineStroke, stream);
1085        SerialUtils.writePaint(this.outlinePaint, stream);
1086        SerialUtils.writeShape(this.line, stream);
1087        SerialUtils.writeStroke(this.lineStroke, stream);
1088        SerialUtils.writePaint(this.linePaint, stream);
1089        SerialUtils.writePaint(this.labelPaint, stream);
1090    }
1091
1092    /**
1093     * Provides serialization support.
1094     *
1095     * @param stream  the input stream ({@code null} not permitted).
1096     *
1097     * @throws IOException  if there is an I/O error.
1098     * @throws ClassNotFoundException  if there is a classpath problem.
1099     */
1100    private void readObject(ObjectInputStream stream)
1101        throws IOException, ClassNotFoundException {
1102        stream.defaultReadObject();
1103        this.attributedLabel = SerialUtils.readAttributedString(stream);
1104        this.shape = SerialUtils.readShape(stream);
1105        this.fillPaint = SerialUtils.readPaint(stream);
1106        this.outlineStroke = SerialUtils.readStroke(stream);
1107        this.outlinePaint = SerialUtils.readPaint(stream);
1108        this.line = SerialUtils.readShape(stream);
1109        this.lineStroke = SerialUtils.readStroke(stream);
1110        this.linePaint = SerialUtils.readPaint(stream);
1111        this.labelPaint = SerialUtils.readPaint(stream);
1112    }
1113
1114}