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