001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2022, 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 * AbstractRenderer.java
029 * ---------------------
030 * (C) Copyright 2002-2022, by David Gilbert.
031 *
032 * Original Author:  David Gilbert;
033 * Contributor(s):   Nicolas Brodu;
034 *
035 */
036
037package org.jfree.chart.renderer;
038
039import java.awt.BasicStroke;
040import java.awt.Color;
041import java.awt.Font;
042import java.awt.Graphics2D;
043import java.awt.Paint;
044import java.awt.Shape;
045import java.awt.Stroke;
046import java.awt.geom.Point2D;
047import java.awt.geom.Rectangle2D;
048import java.io.IOException;
049import java.io.ObjectInputStream;
050import java.io.ObjectOutputStream;
051import java.io.Serializable;
052import java.util.Arrays;
053import java.util.EventListener;
054import java.util.HashMap;
055import java.util.List;
056import java.util.Map;
057import java.util.Objects;
058
059import javax.swing.event.EventListenerList;
060import org.jfree.chart.ChartElement;
061import org.jfree.chart.ChartElementVisitor;
062
063import org.jfree.chart.ChartHints;
064import org.jfree.chart.JFreeChart;
065import org.jfree.chart.api.PublicCloneable;
066import org.jfree.chart.event.RendererChangeEvent;
067import org.jfree.chart.event.RendererChangeListener;
068import org.jfree.chart.labels.ItemLabelAnchor;
069import org.jfree.chart.labels.ItemLabelPosition;
070import org.jfree.chart.legend.LegendTitle;
071import org.jfree.chart.plot.DrawingSupplier;
072import org.jfree.chart.plot.PlotOrientation;
073import org.jfree.chart.internal.Args;
074import org.jfree.chart.internal.CloneUtils;
075import org.jfree.chart.internal.HashUtils;
076import org.jfree.chart.internal.SerialUtils;
077import org.jfree.chart.text.TextAnchor;
078import org.jfree.chart.internal.PaintUtils;
079import org.jfree.chart.internal.ShapeUtils;
080import org.jfree.data.ItemKey;
081
082/**
083 * Base class providing common services for renderers.  Most methods that update
084 * attributes of the renderer will fire a {@link RendererChangeEvent}, which
085 * normally means the plot that owns the renderer will receive notification that
086 * the renderer has been changed (the plot will, in turn, notify the chart).
087 * 
088 * <b>Subclassing</b>
089 * If you create your own renderer that is a subclass of this, you should take
090 * care to ensure that the renderer implements cloning correctly, to ensure
091 * that {@link JFreeChart} instances that use your renderer are also
092 * cloneable.  It is recommended that you also implement the 
093 * {@link PublicCloneable} interface to provide simple access to the clone 
094 * method.
095 */
096public abstract class AbstractRenderer implements ChartElement, Cloneable, Serializable {
097
098    /** For serialization. */
099    private static final long serialVersionUID = -828267569428206075L;
100
101    /** Zero represented as a {@code double}. */
102    public static final Double ZERO = 0.0;
103
104    /** The default paint. */
105    public static final Paint DEFAULT_PAINT = Color.BLUE;
106
107    /** The default outline paint. */
108    public static final Paint DEFAULT_OUTLINE_PAINT = Color.GRAY;
109
110    /** The default stroke. */
111    public static final Stroke DEFAULT_STROKE = new BasicStroke(1.0f);
112
113    /** The default outline stroke. */
114    public static final Stroke DEFAULT_OUTLINE_STROKE = new BasicStroke(1.0f);
115
116    /** The default shape. */
117    public static final Shape DEFAULT_SHAPE
118            = new Rectangle2D.Double(-3.0, -3.0, 6.0, 6.0);
119
120    /** The default value label font. */
121    public static final Font DEFAULT_VALUE_LABEL_FONT
122            = new Font("SansSerif", Font.PLAIN, 10);
123
124    /** The default value label paint. */
125    public static final Paint DEFAULT_VALUE_LABEL_PAINT = Color.BLACK;
126
127    /** A list of flags that controls whether or not each series is visible. */
128    private Map<Integer, Boolean> seriesVisibleMap;
129
130    /** The default visibility for all series. */
131    private boolean defaultSeriesVisible;
132
133    /**
134     * A list of flags that controls whether or not each series is visible in
135     * the legend.
136     */
137    private Map<Integer, Boolean> seriesVisibleInLegendMap;
138
139    /** The default visibility for each series in the legend. */
140    private boolean defaultSeriesVisibleInLegend;
141
142    /** The paint for each series. */
143    private transient Map<Integer, Paint> seriesPaintMap;
144
145    /**
146     * A flag that controls whether or not the paintList is auto-populated
147     * in the {@link #lookupSeriesPaint(int)} method.
148     */
149    private boolean autoPopulateSeriesPaint;
150
151    /** The default paint, used when there is no paint assigned for a series. */
152    private transient Paint defaultPaint;
153
154    /** The fill paint list. */
155    private transient Map<Integer, Paint> seriesFillPaintMap;
156
157    /**
158     * A flag that controls whether or not the fillPaintList is auto-populated
159     * in the {@link #lookupSeriesFillPaint(int)} method.
160     */
161    private boolean autoPopulateSeriesFillPaint;
162
163    /** The base fill paint. */
164    private transient Paint defaultFillPaint;
165
166    /** The outline paint list. */
167    private transient Map<Integer, Paint> seriesOutlinePaintMap;
168
169    /**
170     * A flag that controls whether or not the outlinePaintList is
171     * auto-populated in the {@link #lookupSeriesOutlinePaint(int)} method.
172     */
173    private boolean autoPopulateSeriesOutlinePaint;
174
175    /** The base outline paint. */
176    private transient Paint defaultOutlinePaint;
177
178    /** The stroke list. */
179    private transient Map<Integer, Stroke> seriesStrokeMap;
180
181    /**
182     * A flag that controls whether or not the strokeList is auto-populated
183     * in the {@link #lookupSeriesStroke(int)} method.
184     */
185    private boolean autoPopulateSeriesStroke;
186
187    /** The base stroke. */
188    private transient Stroke defaultStroke;
189
190    /** The outline stroke list. */
191    private transient Map<Integer, Stroke> seriesOutlineStrokeMap;
192
193    /** The base outline stroke. */
194    private transient Stroke defaultOutlineStroke;
195
196    /**
197     * A flag that controls whether or not the outlineStrokeList is
198     * auto-populated in the {@link #lookupSeriesOutlineStroke(int)} method.
199     */
200    private boolean autoPopulateSeriesOutlineStroke;
201
202    /** The shapes to use for specific series. */
203    private Map<Integer, Shape> seriesShapeMap;
204
205    /**
206     * A flag that controls whether or not the series shapes are auto-populated
207     * in the {@link #lookupSeriesShape(int)} method.
208     */
209    private boolean autoPopulateSeriesShape;
210
211    /** The base shape. */
212    private transient Shape defaultShape;
213
214    /** Visibility of the item labels PER series. */
215    private Map<Integer, Boolean> seriesItemLabelsVisibleMap;
216
217    /** The base item labels visible. */
218    private boolean defaultItemLabelsVisible;
219
220    /** The item label font list (one font per series). */
221    private Map<Integer, Font> itemLabelFontMap;
222
223    /** The base item label font. */
224    private Font defaultItemLabelFont;
225
226    /** The item label paint list (one paint per series). */
227    private transient Map<Integer, Paint> itemLabelPaints;
228
229    /** The base item label paint. */
230    private transient Paint defaultItemLabelPaint;
231
232    /** The positive item label position (per series). */
233    private Map<Integer, ItemLabelPosition> positiveItemLabelPositionMap;
234
235    /** The fallback positive item label position. */
236    private ItemLabelPosition defaultPositiveItemLabelPosition;
237
238    /** The negative item label position (per series). */
239    private Map<Integer, ItemLabelPosition> negativeItemLabelPositionMap;
240
241    /** The fallback negative item label position. */
242    private ItemLabelPosition defaultNegativeItemLabelPosition;
243
244    /** The item label anchor offset. */
245    private double itemLabelAnchorOffset = 2.0;
246
247    /**
248     * Flags that control whether or not entities are generated for each
249     * series.  This will be overridden by 'createEntities'.
250     */
251    private Map<Integer, Boolean> seriesCreateEntitiesMap;
252
253    /**
254     * The default flag that controls whether or not entities are generated.
255     * This flag is used when both the above flags return null.
256     */
257    private boolean defaultCreateEntities;
258
259    /** The per-series legend shape settings. */
260    private Map<Integer, Shape> seriesLegendShapes;
261
262    /**
263     * The base shape for legend items.  If this is {@code null}, the
264     * series shape will be used.
265     */
266    private transient Shape defaultLegendShape;
267
268    /**
269     * A special flag that, if true, will cause the getLegendItem() method
270     * to configure the legend shape as if it were a line.
271     */
272    private boolean treatLegendShapeAsLine;
273
274    /** The per-series legend text font. */
275    private Map<Integer, Font> legendTextFontMap;
276
277    /** The base legend font. */
278    private Font defaultLegendTextFont;
279
280    /**
281     * The per series legend text paint settings.
282     */
283    private transient Map<Integer, Paint> legendTextPaints;
284
285    /**
286     * The default paint for the legend text items (if this is
287     * {@code null}, the {@link LegendTitle} class will determine the
288     * text paint to use.
289     */
290    private transient Paint defaultLegendTextPaint;
291
292    /**
293     * A flag that controls whether or not the renderer will include the
294     * non-visible series when calculating the data bounds.
295     */
296    private boolean dataBoundsIncludesVisibleSeriesOnly = true;
297
298    /** The default radius for the entity 'hotspot' */
299    private int defaultEntityRadius;
300
301    /** Storage for registered change listeners. */
302    private transient EventListenerList listenerList;
303
304    /** An event for re-use. */
305    private transient RendererChangeEvent event;
306
307    /**
308     * Default constructor.
309     */
310    public AbstractRenderer() {
311        this.seriesVisibleMap = new HashMap<>();
312        this.defaultSeriesVisible = true;
313
314        this.seriesVisibleInLegendMap = new HashMap<>();
315        this.defaultSeriesVisibleInLegend = true;
316
317        this.seriesPaintMap = new HashMap<>();
318        this.defaultPaint = DEFAULT_PAINT;
319        this.autoPopulateSeriesPaint = true;
320
321        this.seriesFillPaintMap = new HashMap<>();
322        this.defaultFillPaint = Color.WHITE;
323        this.autoPopulateSeriesFillPaint = false;
324
325        this.seriesOutlinePaintMap = new HashMap<>();
326        this.defaultOutlinePaint = DEFAULT_OUTLINE_PAINT;
327        this.autoPopulateSeriesOutlinePaint = false;
328
329        this.seriesStrokeMap = new HashMap<>();
330        this.defaultStroke = DEFAULT_STROKE;
331        this.autoPopulateSeriesStroke = true;
332
333        this.seriesOutlineStrokeMap = new HashMap<>();
334        this.defaultOutlineStroke = DEFAULT_OUTLINE_STROKE;
335        this.autoPopulateSeriesOutlineStroke = false;
336
337        this.seriesShapeMap = new HashMap<>();
338        this.defaultShape = DEFAULT_SHAPE;
339        this.autoPopulateSeriesShape = true;
340
341        this.seriesItemLabelsVisibleMap = new HashMap<>();
342        this.defaultItemLabelsVisible = false;
343
344        this.itemLabelFontMap = new HashMap<>();
345        this.defaultItemLabelFont = new Font("SansSerif", Font.PLAIN, 10);
346
347        this.itemLabelPaints = new HashMap<>();
348        this.defaultItemLabelPaint = Color.BLACK;
349
350        this.positiveItemLabelPositionMap = new HashMap<>();
351        this.defaultPositiveItemLabelPosition = new ItemLabelPosition(
352                ItemLabelAnchor.OUTSIDE12, TextAnchor.BOTTOM_CENTER);
353
354        this.negativeItemLabelPositionMap = new HashMap<>();
355        this.defaultNegativeItemLabelPosition = new ItemLabelPosition(
356                ItemLabelAnchor.OUTSIDE6, TextAnchor.TOP_CENTER);
357
358        this.seriesCreateEntitiesMap = new HashMap<>();
359        this.defaultCreateEntities = true;
360
361        this.defaultEntityRadius = 3;
362
363        this.seriesLegendShapes = new HashMap<>();
364        this.defaultLegendShape = null;
365
366        this.treatLegendShapeAsLine = false;
367
368        this.legendTextFontMap = new HashMap<>();
369        this.defaultLegendTextFont = null;
370
371        this.legendTextPaints = new HashMap<>();
372        this.defaultLegendTextPaint = null;
373
374        this.listenerList = new EventListenerList();
375    }
376
377    /**
378     * Receives a chart element visitor.
379     * 
380     * @param visitor  the visitor ({@code null} not permitted).
381     */
382    @Override
383    public void receive(ChartElementVisitor visitor) {
384        visitor.visit(this);
385    }
386    
387    /**
388     * Returns the drawing supplier from the plot.
389     *
390     * @return The drawing supplier.
391     */
392    public abstract DrawingSupplier getDrawingSupplier();
393
394    /**
395     * Adds a {@code KEY_BEGIN_ELEMENT} hint to the graphics target.  This
396     * hint is recognised by <b>JFreeSVG</b> (in theory it could be used by 
397     * other {@code Graphics2D} implementations also).
398     * 
399     * @param g2  the graphics target ({@code null} not permitted).
400     * @param key  the key ({@code null} not permitted).
401     * 
402     * @see #endElementGroup(java.awt.Graphics2D)
403     */
404    protected void beginElementGroup(Graphics2D g2, ItemKey key) {
405        Args.nullNotPermitted(key, "key");
406        Map<String, String> m = new HashMap<>(1);
407        m.put("ref", key.toJSONString());
408        g2.setRenderingHint(ChartHints.KEY_BEGIN_ELEMENT, m);        
409    }
410    
411    /**
412     * Adds a {@code KEY_END_ELEMENT} hint to the graphics target.
413     * 
414     * @param g2  the graphics target ({@code null} not permitted).
415     * 
416     * @see #beginElementGroup(java.awt.Graphics2D, org.jfree.data.ItemKey)
417     */
418    protected void endElementGroup(Graphics2D g2) {
419        g2.setRenderingHint(ChartHints.KEY_END_ELEMENT, Boolean.TRUE);
420    }
421
422    // SERIES VISIBLE (not yet respected by all renderers)
423
424    /**
425     * Returns a boolean that indicates whether or not the specified item
426     * should be drawn.
427     *
428     * @param series  the series index.
429     * @param item  the item index.
430     *
431     * @return A boolean.
432     */
433    public boolean getItemVisible(int series, int item) {
434        return isSeriesVisible(series);
435    }
436
437    /**
438     * Returns a boolean that indicates whether or not the specified series
439     * should be drawn.  In fact this method should be named 
440     * lookupSeriesVisible() to be consistent with the other series
441     * attributes and avoid confusion with the getSeriesVisible() method.
442     *
443     * @param series  the series index.
444     *
445     * @return A boolean.
446     */
447    public boolean isSeriesVisible(int series) {
448        boolean result = this.defaultSeriesVisible;
449        Boolean b = this.seriesVisibleMap.get(series);
450        if (b != null) {
451            result = b;
452        }
453        return result;
454    }
455
456    /**
457     * Returns the flag that controls whether a series is visible.
458     *
459     * @param series  the series index (zero-based).
460     *
461     * @return The flag (possibly {@code null}).
462     *
463     * @see #setSeriesVisible(int, Boolean)
464     */
465    public Boolean getSeriesVisible(int series) {
466        return this.seriesVisibleMap.get(series);
467    }
468
469    /**
470     * Sets the flag that controls whether a series is visible and sends a
471     * {@link RendererChangeEvent} to all registered listeners.
472     *
473     * @param series  the series index (zero-based).
474     * @param visible  the flag ({@code null} permitted).
475     *
476     * @see #getSeriesVisible(int)
477     */
478    public void setSeriesVisible(int series, Boolean visible) {
479        setSeriesVisible(series, visible, true);
480    }
481
482    /**
483     * Sets the flag that controls whether a series is visible and, if
484     * requested, sends a {@link RendererChangeEvent} to all registered
485     * listeners.
486     *
487     * @param series  the series index.
488     * @param visible  the flag ({@code null} permitted).
489     * @param notify  notify listeners?
490     *
491     * @see #getSeriesVisible(int)
492     */
493    public void setSeriesVisible(int series, Boolean visible, boolean notify) {
494        this.seriesVisibleMap.put(series, visible);
495        if (notify) {
496            // we create an event with a special flag set...the purpose of
497            // this is to communicate to the plot (the default receiver of
498            // the event) that series visibility has changed so the axis
499            // ranges might need updating...
500            RendererChangeEvent e = new RendererChangeEvent(this, true);
501            notifyListeners(e);
502        }
503    }
504
505    /**
506     * Returns the default visibility for all series.
507     *
508     * @return The default visibility.
509     *
510     * @see #setDefaultSeriesVisible(boolean)
511     */
512    public boolean getDefaultSeriesVisible() {
513        return this.defaultSeriesVisible;
514    }
515
516    /**
517     * Sets the default series visibility and sends a 
518     * {@link RendererChangeEvent} to all registered listeners.
519     *
520     * @param visible  the flag.
521     *
522     * @see #getDefaultSeriesVisible()
523     */
524    public void setDefaultSeriesVisible(boolean visible) {
525        // defer argument checking...
526        setDefaultSeriesVisible(visible, true);
527    }
528
529    /**
530     * Sets the default series visibility and, if requested, sends
531     * a {@link RendererChangeEvent} to all registered listeners.
532     *
533     * @param visible  the visibility.
534     * @param notify  notify listeners?
535     *
536     * @see #getDefaultSeriesVisible()
537     */
538    public void setDefaultSeriesVisible(boolean visible, boolean notify) {
539        this.defaultSeriesVisible = visible;
540        if (notify) {
541            // we create an event with a special flag set...the purpose of
542            // this is to communicate to the plot (the default receiver of
543            // the event) that series visibility has changed so the axis
544            // ranges might need updating...
545            RendererChangeEvent e = new RendererChangeEvent(this, true);
546            notifyListeners(e);
547        }
548    }
549
550    // SERIES VISIBLE IN LEGEND (not yet respected by all renderers)
551
552    /**
553     * Returns {@code true} if the series should be shown in the legend,
554     * and {@code false} otherwise.
555     *
556     * @param series  the series index.
557     *
558     * @return A boolean.
559     */
560    public boolean isSeriesVisibleInLegend(int series) {
561        boolean result = this.defaultSeriesVisibleInLegend;
562        Boolean b = this.seriesVisibleInLegendMap.get(series);
563        if (b != null) {
564            result = b;
565        }
566        return result;
567    }
568
569    /**
570     * Returns the flag that controls whether a series is visible in the
571     * legend.  This method returns only the "per series" settings - to
572     * incorporate the default settings as well, you need to use the
573     * {@link #isSeriesVisibleInLegend(int)} method.
574     *
575     * @param series  the series index (zero-based).
576     *
577     * @return The flag (possibly {@code null}).
578     *
579     * @see #setSeriesVisibleInLegend(int, Boolean)
580     */
581    public Boolean getSeriesVisibleInLegend(int series) {
582        return this.seriesVisibleInLegendMap.get(series);
583    }
584
585    /**
586     * Sets the flag that controls whether a series is visible in the legend
587     * and sends a {@link RendererChangeEvent} to all registered listeners.
588     *
589     * @param series  the series index (zero-based).
590     * @param visible  the flag ({@code null} permitted).
591     *
592     * @see #getSeriesVisibleInLegend(int)
593     */
594    public void setSeriesVisibleInLegend(int series, Boolean visible) {
595        setSeriesVisibleInLegend(series, visible, true);
596    }
597
598    /**
599     * Sets the flag that controls whether a series is visible in the legend
600     * and, if requested, sends a {@link RendererChangeEvent} to all registered
601     * listeners.
602     *
603     * @param series  the series index.
604     * @param visible  the flag ({@code null} permitted).
605     * @param notify  notify listeners?
606     *
607     * @see #getSeriesVisibleInLegend(int)
608     */
609    public void setSeriesVisibleInLegend(int series, Boolean visible, boolean notify) {
610        this.seriesVisibleInLegendMap.put(series, visible);
611        if (notify) {
612            fireChangeEvent();
613        }
614    }
615
616    /**
617     * Returns the default visibility in the legend for all series.
618     *
619     * @return The default visibility.
620     *
621     * @see #setDefaultSeriesVisibleInLegend(boolean)
622     */
623    public boolean getDefaultSeriesVisibleInLegend() {
624        return this.defaultSeriesVisibleInLegend;
625    }
626
627    /**
628     * Sets the default visibility in the legend and sends a
629     * {@link RendererChangeEvent} to all registered listeners.
630     *
631     * @param visible  the flag.
632     *
633     * @see #getDefaultSeriesVisibleInLegend()
634     */
635    public void setDefaultSeriesVisibleInLegend(boolean visible) {
636        // defer argument checking...
637        setDefaultSeriesVisibleInLegend(visible, true);
638    }
639
640    /**
641     * Sets the default visibility in the legend and, if requested, sends
642     * a {@link RendererChangeEvent} to all registered listeners.
643     *
644     * @param visible  the visibility.
645     * @param notify  notify listeners?
646     *
647     * @see #getDefaultSeriesVisibleInLegend()
648     */
649    public void setDefaultSeriesVisibleInLegend(boolean visible, 
650            boolean notify) {
651        this.defaultSeriesVisibleInLegend = visible;
652        if (notify) {
653            fireChangeEvent();
654        }
655    }
656
657    // PAINT
658
659    /**
660     * Returns the paint used to fill data items as they are drawn.
661     * (this is typically the same for an entire series).
662     * <p>
663     * The default implementation passes control to the
664     * {@code lookupSeriesPaint()} method. You can override this method
665     * if you require different behaviour.
666     *
667     * @param row  the row (or series) index (zero-based).
668     * @param column  the column (or category) index (zero-based).
669     *
670     * @return The paint (never {@code null}).
671     */
672    public Paint getItemPaint(int row, int column) {
673        return lookupSeriesPaint(row);
674    }
675
676    /**
677     * Returns the paint used to fill an item drawn by the renderer.
678     *
679     * @param series  the series index (zero-based).
680     *
681     * @return The paint (never {@code null}).
682     */
683    public Paint lookupSeriesPaint(int series) {
684
685        Paint seriesPaint = getSeriesPaint(series);
686        if (seriesPaint == null && this.autoPopulateSeriesPaint) {
687            DrawingSupplier supplier = getDrawingSupplier();
688            if (supplier != null) {
689                seriesPaint = supplier.getNextPaint();
690                setSeriesPaint(series, seriesPaint, false);
691            }
692        }
693        if (seriesPaint == null) {
694            seriesPaint = this.defaultPaint;
695        }
696        return seriesPaint;
697
698    }
699
700    /**
701     * Returns the paint used to fill an item drawn by the renderer.
702     *
703     * @param series  the series index (zero-based).
704     *
705     * @return The paint (possibly {@code null}).
706     *
707     * @see #setSeriesPaint(int, Paint)
708     */
709    public Paint getSeriesPaint(int series) {
710        return this.seriesPaintMap.get(series);
711    }
712
713    /**
714     * Sets the paint used for a series and sends a {@link RendererChangeEvent}
715     * to all registered listeners.
716     *
717     * @param series  the series index (zero-based).
718     * @param paint  the paint ({@code null} permitted).
719     *
720     * @see #getSeriesPaint(int)
721     */
722    public void setSeriesPaint(int series, Paint paint) {
723        setSeriesPaint(series, paint, true);
724    }
725
726    /**
727     * Sets the paint used for a series and, if requested, sends a
728     * {@link RendererChangeEvent} to all registered listeners.
729     *
730     * @param series  the series index.
731     * @param paint  the paint ({@code null} permitted).
732     * @param notify  notify listeners?
733     *
734     * @see #getSeriesPaint(int)
735     */
736    public void setSeriesPaint(int series, Paint paint, boolean notify) {
737        this.seriesPaintMap.put(series, paint);
738        if (notify) {
739            fireChangeEvent();
740        }
741    }
742
743    /**
744     * Clears the series paint settings for this renderer and, if requested,
745     * sends a {@link RendererChangeEvent} to all registered listeners.
746     *
747     * @param notify  notify listeners?
748     */
749    public void clearSeriesPaints(boolean notify) {
750        this.seriesPaintMap.clear();
751        if (notify) {
752            fireChangeEvent();
753        }
754    }
755
756    /**
757     * Returns the default paint.
758     *
759     * @return The default paint (never {@code null}).
760     *
761     * @see #setDefaultPaint(Paint)
762     */
763    public Paint getDefaultPaint() {
764        return this.defaultPaint;
765    }
766
767    /**
768     * Sets the default paint and sends a {@link RendererChangeEvent} to all
769     * registered listeners.
770     *
771     * @param paint  the paint ({@code null} not permitted).
772     *
773     * @see #getDefaultPaint()
774     */
775    public void setDefaultPaint(Paint paint) {
776        // defer argument checking...
777        setDefaultPaint(paint, true);
778    }
779
780    /**
781     * Sets the default series paint and, if requested, sends a
782     * {@link RendererChangeEvent} to all registered listeners.
783     *
784     * @param paint  the paint ({@code null} not permitted).
785     * @param notify  notify listeners?
786     *
787     * @see #getDefaultPaint()
788     */
789    public void setDefaultPaint(Paint paint, boolean notify) {
790        this.defaultPaint = paint;
791        if (notify) {
792            fireChangeEvent();
793        }
794    }
795
796    /**
797     * Returns the flag that controls whether or not the series paint list is
798     * automatically populated when {@link #lookupSeriesPaint(int)} is called.
799     *
800     * @return A boolean.
801     *
802     * @see #setAutoPopulateSeriesPaint(boolean)
803     */
804    public boolean getAutoPopulateSeriesPaint() {
805        return this.autoPopulateSeriesPaint;
806    }
807
808    /**
809     * Sets the flag that controls whether or not the series paint list is
810     * automatically populated when {@link #lookupSeriesPaint(int)} is called.
811     *
812     * @param auto  the new flag value.
813     *
814     * @see #getAutoPopulateSeriesPaint()
815     */
816    public void setAutoPopulateSeriesPaint(boolean auto) {
817        this.autoPopulateSeriesPaint = auto;
818    }
819
820    //// FILL PAINT //////////////////////////////////////////////////////////
821
822    /**
823     * Returns the paint used to fill data items as they are drawn.  The
824     * default implementation passes control to the
825     * {@link #lookupSeriesFillPaint(int)} method - you can override this
826     * method if you require different behaviour.
827     *
828     * @param row  the row (or series) index (zero-based).
829     * @param column  the column (or category) index (zero-based).
830     *
831     * @return The paint (never {@code null}).
832     */
833    public Paint getItemFillPaint(int row, int column) {
834        return lookupSeriesFillPaint(row);
835    }
836
837    /**
838     * Returns the paint used to fill an item drawn by the renderer.
839     *
840     * @param series  the series (zero-based index).
841     *
842     * @return The paint (never {@code null}).
843     */
844    public Paint lookupSeriesFillPaint(int series) {
845
846        Paint seriesFillPaint = getSeriesFillPaint(series);
847        if (seriesFillPaint == null && this.autoPopulateSeriesFillPaint) {
848            DrawingSupplier supplier = getDrawingSupplier();
849            if (supplier != null) {
850                seriesFillPaint = supplier.getNextFillPaint();
851                setSeriesFillPaint(series, seriesFillPaint, false);
852            }
853        }
854        if (seriesFillPaint == null) {
855            seriesFillPaint = this.defaultFillPaint;
856        }
857        return seriesFillPaint;
858
859    }
860
861    /**
862     * Returns the paint used to fill an item drawn by the renderer.
863     *
864     * @param series  the series (zero-based index).
865     *
866     * @return The paint (never {@code null}).
867     *
868     * @see #setSeriesFillPaint(int, Paint)
869     */
870    public Paint getSeriesFillPaint(int series) {
871        return this.seriesFillPaintMap.get(series);
872    }
873
874    /**
875     * Sets the paint used for a series fill and sends a
876     * {@link RendererChangeEvent} to all registered listeners.
877     *
878     * @param series  the series index (zero-based).
879     * @param paint  the paint ({@code null} permitted).
880     *
881     * @see #getSeriesFillPaint(int)
882     */
883    public void setSeriesFillPaint(int series, Paint paint) {
884        setSeriesFillPaint(series, paint, true);
885    }
886
887    /**
888     * Sets the paint used to fill a series and, if requested,
889     * sends a {@link RendererChangeEvent} to all registered listeners.
890     *
891     * @param series  the series index (zero-based).
892     * @param paint  the paint ({@code null} permitted).
893     * @param notify  notify listeners?
894     *
895     * @see #getSeriesFillPaint(int)
896     */
897    public void setSeriesFillPaint(int series, Paint paint, boolean notify) {
898        this.seriesFillPaintMap.put(series, paint);
899        if (notify) {
900            fireChangeEvent();
901        }
902    }
903
904    /**
905     * Returns the default fill paint.
906     *
907     * @return The paint (never {@code null}).
908     *
909     * @see #setDefaultFillPaint(Paint)
910     */
911    public Paint getDefaultFillPaint() {
912        return this.defaultFillPaint;
913    }
914
915    /**
916     * Sets the default fill paint and sends a {@link RendererChangeEvent} to
917     * all registered listeners.
918     *
919     * @param paint  the paint ({@code null} not permitted).
920     *
921     * @see #getDefaultFillPaint()
922     */
923    public void setDefaultFillPaint(Paint paint) {
924        // defer argument checking...
925        setDefaultFillPaint(paint, true);
926    }
927
928    /**
929     * Sets the default fill paint and, if requested, sends a
930     * {@link RendererChangeEvent} to all registered listeners.
931     *
932     * @param paint  the paint ({@code null} not permitted).
933     * @param notify  notify listeners?
934     *
935     * @see #getDefaultFillPaint()
936     */
937    public void setDefaultFillPaint(Paint paint, boolean notify) {
938        Args.nullNotPermitted(paint, "paint");
939        this.defaultFillPaint = paint;
940        if (notify) {
941            fireChangeEvent();
942        }
943    }
944
945    /**
946     * Returns the flag that controls whether or not the series fill paint list
947     * is automatically populated when {@link #lookupSeriesFillPaint(int)} is
948     * called.
949     *
950     * @return A boolean.
951     *
952     * @see #setAutoPopulateSeriesFillPaint(boolean)
953     */
954    public boolean getAutoPopulateSeriesFillPaint() {
955        return this.autoPopulateSeriesFillPaint;
956    }
957
958    /**
959     * Sets the flag that controls whether or not the series fill paint list is
960     * automatically populated when {@link #lookupSeriesFillPaint(int)} is
961     * called.
962     *
963     * @param auto  the new flag value.
964     *
965     * @see #getAutoPopulateSeriesFillPaint()
966     */
967    public void setAutoPopulateSeriesFillPaint(boolean auto) {
968        this.autoPopulateSeriesFillPaint = auto;
969    }
970
971    // OUTLINE PAINT //////////////////////////////////////////////////////////
972
973    /**
974     * Returns the paint used to outline data items as they are drawn.
975     * (this is typically the same for an entire series).
976     * <p>
977     * The default implementation passes control to the
978     * {@link #lookupSeriesOutlinePaint} method.  You can override this method
979     * if you require different behaviour.
980     *
981     * @param row  the row (or series) index (zero-based).
982     * @param column  the column (or category) index (zero-based).
983     *
984     * @return The paint (never {@code null}).
985     */
986    public Paint getItemOutlinePaint(int row, int column) {
987        return lookupSeriesOutlinePaint(row);
988    }
989
990    /**
991     * Returns the paint used to outline an item drawn by the renderer.
992     *
993     * @param series  the series (zero-based index).
994     *
995     * @return The paint (never {@code null}).
996     */
997    public Paint lookupSeriesOutlinePaint(int series) {
998
999        Paint seriesOutlinePaint = getSeriesOutlinePaint(series);
1000        if (seriesOutlinePaint == null && this.autoPopulateSeriesOutlinePaint) {
1001            DrawingSupplier supplier = getDrawingSupplier();
1002            if (supplier != null) {
1003                seriesOutlinePaint = supplier.getNextOutlinePaint();
1004                setSeriesOutlinePaint(series, seriesOutlinePaint, false);
1005            }
1006        }
1007        if (seriesOutlinePaint == null) {
1008            seriesOutlinePaint = this.defaultOutlinePaint;
1009        }
1010        return seriesOutlinePaint;
1011
1012    }
1013
1014    /**
1015     * Returns the paint used to outline an item drawn by the renderer.
1016     *
1017     * @param series  the series (zero-based index).
1018     *
1019     * @return The paint (possibly {@code null}).
1020     *
1021     * @see #setSeriesOutlinePaint(int, Paint)
1022     */
1023    public Paint getSeriesOutlinePaint(int series) {
1024        return this.seriesOutlinePaintMap.get(series);
1025    }
1026
1027    /**
1028     * Sets the paint used for a series outline and sends a
1029     * {@link RendererChangeEvent} to all registered listeners.
1030     *
1031     * @param series  the series index (zero-based).
1032     * @param paint  the paint ({@code null} permitted).
1033     *
1034     * @see #getSeriesOutlinePaint(int)
1035     */
1036    public void setSeriesOutlinePaint(int series, Paint paint) {
1037        setSeriesOutlinePaint(series, paint, true);
1038    }
1039
1040    /**
1041     * Sets the paint used to draw the outline for a series and, if requested,
1042     * sends a {@link RendererChangeEvent} to all registered listeners.
1043     *
1044     * @param series  the series index (zero-based).
1045     * @param paint  the paint ({@code null} permitted).
1046     * @param notify  notify listeners?
1047     *
1048     * @see #getSeriesOutlinePaint(int)
1049     */
1050    public void setSeriesOutlinePaint(int series, Paint paint, boolean notify) {
1051        this.seriesOutlinePaintMap.put(series, paint);
1052        if (notify) {
1053            fireChangeEvent();
1054        }
1055    }
1056
1057    /**
1058     * Returns the default outline paint.
1059     *
1060     * @return The paint (never {@code null}).
1061     *
1062     * @see #setDefaultOutlinePaint(Paint)
1063     */
1064    public Paint getDefaultOutlinePaint() {
1065        return this.defaultOutlinePaint;
1066    }
1067
1068    /**
1069     * Sets the default outline paint and sends a {@link RendererChangeEvent} to
1070     * all registered listeners.
1071     *
1072     * @param paint  the paint ({@code null} not permitted).
1073     *
1074     * @see #getDefaultOutlinePaint()
1075     */
1076    public void setDefaultOutlinePaint(Paint paint) {
1077        // defer argument checking...
1078        setDefaultOutlinePaint(paint, true);
1079    }
1080
1081    /**
1082     * Sets the default outline paint and, if requested, sends a
1083     * {@link RendererChangeEvent} to all registered listeners.
1084     *
1085     * @param paint  the paint ({@code null} not permitted).
1086     * @param notify  notify listeners?
1087     *
1088     * @see #getDefaultOutlinePaint()
1089     */
1090    public void setDefaultOutlinePaint(Paint paint, boolean notify) {
1091        Args.nullNotPermitted(paint, "paint");
1092        this.defaultOutlinePaint = paint;
1093        if (notify) {
1094            fireChangeEvent();
1095        }
1096    }
1097
1098    /**
1099     * Returns the flag that controls whether or not the series outline paint
1100     * list is automatically populated when
1101     * {@link #lookupSeriesOutlinePaint(int)} is called.
1102     *
1103     * @return A boolean.
1104     *
1105     * @see #setAutoPopulateSeriesOutlinePaint(boolean)
1106     */
1107    public boolean getAutoPopulateSeriesOutlinePaint() {
1108        return this.autoPopulateSeriesOutlinePaint;
1109    }
1110
1111    /**
1112     * Sets the flag that controls whether or not the series outline paint list
1113     * is automatically populated when {@link #lookupSeriesOutlinePaint(int)}
1114     * is called.
1115     *
1116     * @param auto  the new flag value.
1117     *
1118     * @see #getAutoPopulateSeriesOutlinePaint()
1119     */
1120    public void setAutoPopulateSeriesOutlinePaint(boolean auto) {
1121        this.autoPopulateSeriesOutlinePaint = auto;
1122    }
1123
1124    // STROKE
1125
1126    /**
1127     * Returns the stroke used to draw data items.
1128     * <p>
1129     * The default implementation passes control to the getSeriesStroke method.
1130     * You can override this method if you require different behaviour.
1131     *
1132     * @param row  the row (or series) index (zero-based).
1133     * @param column  the column (or category) index (zero-based).
1134     *
1135     * @return The stroke (never {@code null}).
1136     */
1137    public Stroke getItemStroke(int row, int column) {
1138        return lookupSeriesStroke(row);
1139    }
1140
1141    /**
1142     * Returns the stroke used to draw the items in a series.
1143     *
1144     * @param series  the series (zero-based index).
1145     *
1146     * @return The stroke (never {@code null}).
1147     */
1148    public Stroke lookupSeriesStroke(int series) {
1149
1150        Stroke result = getSeriesStroke(series);
1151        if (result == null && this.autoPopulateSeriesStroke) {
1152            DrawingSupplier supplier = getDrawingSupplier();
1153            if (supplier != null) {
1154                result = supplier.getNextStroke();
1155                setSeriesStroke(series, result, false);
1156            }
1157        }
1158        if (result == null) {
1159            result = this.defaultStroke;
1160        }
1161        return result;
1162
1163    }
1164
1165    /**
1166     * Returns the stroke used to draw the items in a series.
1167     *
1168     * @param series  the series (zero-based index).
1169     *
1170     * @return The stroke (possibly {@code null}).
1171     *
1172     * @see #setSeriesStroke(int, Stroke)
1173     */
1174    public Stroke getSeriesStroke(int series) {
1175        return this.seriesStrokeMap.get(series);
1176    }
1177
1178    /**
1179     * Sets the stroke used for a series and sends a {@link RendererChangeEvent}
1180     * to all registered listeners.
1181     *
1182     * @param series  the series index (zero-based).
1183     * @param stroke  the stroke ({@code null} permitted).
1184     *
1185     * @see #getSeriesStroke(int)
1186     */
1187    public void setSeriesStroke(int series, Stroke stroke) {
1188        setSeriesStroke(series, stroke, true);
1189    }
1190
1191    /**
1192     * Sets the stroke for a series and, if requested, sends a
1193     * {@link RendererChangeEvent} to all registered listeners.
1194     *
1195     * @param series  the series index (zero-based).
1196     * @param stroke  the stroke ({@code null} permitted).
1197     * @param notify  notify listeners?
1198     *
1199     * @see #getSeriesStroke(int)
1200     */
1201    public void setSeriesStroke(int series, Stroke stroke, boolean notify) {
1202        this.seriesStrokeMap.put(series, stroke);
1203        if (notify) {
1204            fireChangeEvent();
1205        }
1206    }
1207
1208    /**
1209     * Clears the series stroke settings for this renderer and, if requested,
1210     * sends a {@link RendererChangeEvent} to all registered listeners.
1211     *
1212     * @param notify  notify listeners?
1213     */
1214    public void clearSeriesStrokes(boolean notify) {
1215        this.seriesStrokeMap.clear();
1216        if (notify) {
1217            fireChangeEvent();
1218        }
1219    }
1220
1221    /**
1222     * Returns the default stroke.
1223     *
1224     * @return The default stroke (never {@code null}).
1225     *
1226     * @see #setDefaultStroke(Stroke)
1227     */
1228    public Stroke getDefaultStroke() {
1229        return this.defaultStroke;
1230    }
1231
1232    /**
1233     * Sets the default stroke and sends a {@link RendererChangeEvent} to all
1234     * registered listeners.
1235     *
1236     * @param stroke  the stroke ({@code null} not permitted).
1237     *
1238     * @see #getDefaultStroke()
1239     */
1240    public void setDefaultStroke(Stroke stroke) {
1241        // defer argument checking...
1242        setDefaultStroke(stroke, true);
1243    }
1244
1245    /**
1246     * Sets the base stroke and, if requested, sends a
1247     * {@link RendererChangeEvent} to all registered listeners.
1248     *
1249     * @param stroke  the stroke ({@code null} not permitted).
1250     * @param notify  notify listeners?
1251     *
1252     * @see #getDefaultStroke()
1253     */
1254    public void setDefaultStroke(Stroke stroke, boolean notify) {
1255        Args.nullNotPermitted(stroke, "stroke");
1256        this.defaultStroke = stroke;
1257        if (notify) {
1258            fireChangeEvent();
1259        }
1260    }
1261
1262    /**
1263     * Returns the flag that controls whether or not the series stroke list is
1264     * automatically populated when {@link #lookupSeriesStroke(int)} is called.
1265     *
1266     * @return A boolean.
1267     *
1268     * @see #setAutoPopulateSeriesStroke(boolean)
1269     */
1270    public boolean getAutoPopulateSeriesStroke() {
1271        return this.autoPopulateSeriesStroke;
1272    }
1273
1274    /**
1275     * Sets the flag that controls whether or not the series stroke list is
1276     * automatically populated when {@link #lookupSeriesStroke(int)} is called.
1277     *
1278     * @param auto  the new flag value.
1279     *
1280     * @see #getAutoPopulateSeriesStroke()
1281     */
1282    public void setAutoPopulateSeriesStroke(boolean auto) {
1283        this.autoPopulateSeriesStroke = auto;
1284    }
1285
1286    // OUTLINE STROKE
1287
1288    /**
1289     * Returns the stroke used to outline data items.  The default
1290     * implementation passes control to the
1291     * {@link #lookupSeriesOutlineStroke(int)} method. You can override this
1292     * method if you require different behaviour.
1293     *
1294     * @param row  the row (or series) index (zero-based).
1295     * @param column  the column (or category) index (zero-based).
1296     *
1297     * @return The stroke (never {@code null}).
1298     */
1299    public Stroke getItemOutlineStroke(int row, int column) {
1300        return lookupSeriesOutlineStroke(row);
1301    }
1302
1303    /**
1304     * Returns the stroke used to outline the items in a series.
1305     *
1306     * @param series  the series (zero-based index).
1307     *
1308     * @return The stroke (never {@code null}).
1309     */
1310    public Stroke lookupSeriesOutlineStroke(int series) {
1311
1312        Stroke result = getSeriesOutlineStroke(series);
1313        if (result == null && this.autoPopulateSeriesOutlineStroke) {
1314            DrawingSupplier supplier = getDrawingSupplier();
1315            if (supplier != null) {
1316                result = supplier.getNextOutlineStroke();
1317                setSeriesOutlineStroke(series, result, false);
1318            }
1319        }
1320        if (result == null) {
1321            result = this.defaultOutlineStroke;
1322        }
1323        return result;
1324
1325    }
1326
1327    /**
1328     * Returns the stroke used to outline the items in a series.
1329     *
1330     * @param series  the series (zero-based index).
1331     *
1332     * @return The stroke (possibly {@code null}).
1333     *
1334     * @see #setSeriesOutlineStroke(int, Stroke)
1335     */
1336    public Stroke getSeriesOutlineStroke(int series) {
1337        return this.seriesOutlineStrokeMap.get(series);
1338    }
1339
1340    /**
1341     * Sets the outline stroke used for a series and sends a
1342     * {@link RendererChangeEvent} to all registered listeners.
1343     *
1344     * @param series  the series index (zero-based).
1345     * @param stroke  the stroke ({@code null} permitted).
1346     *
1347     * @see #getSeriesOutlineStroke(int)
1348     */
1349    public void setSeriesOutlineStroke(int series, Stroke stroke) {
1350        setSeriesOutlineStroke(series, stroke, true);
1351    }
1352
1353    /**
1354     * Sets the outline stroke for a series and, if requested, sends a
1355     * {@link RendererChangeEvent} to all registered listeners.
1356     *
1357     * @param series  the series index.
1358     * @param stroke  the stroke ({@code null} permitted).
1359     * @param notify  notify listeners?
1360     *
1361     * @see #getSeriesOutlineStroke(int)
1362     */
1363    public void setSeriesOutlineStroke(int series, Stroke stroke, boolean notify) {
1364        this.seriesOutlineStrokeMap.put(series, stroke);
1365        if (notify) {
1366            fireChangeEvent();
1367        }
1368    }
1369
1370    /**
1371     * Returns the default outline stroke.
1372     *
1373     * @return The stroke (never {@code null}).
1374     *
1375     * @see #setDefaultOutlineStroke(Stroke)
1376     */
1377    public Stroke getDefaultOutlineStroke() {
1378        return this.defaultOutlineStroke;
1379    }
1380
1381    /**
1382     * Sets the default outline stroke and sends a {@link RendererChangeEvent} 
1383     * to all registered listeners.
1384     *
1385     * @param stroke  the stroke ({@code null} not permitted).
1386     *
1387     * @see #getDefaultOutlineStroke()
1388     */
1389    public void setDefaultOutlineStroke(Stroke stroke) {
1390        setDefaultOutlineStroke(stroke, true);
1391    }
1392
1393    /**
1394     * Sets the default outline stroke and, if requested, sends a
1395     * {@link RendererChangeEvent} to all registered listeners.
1396     *
1397     * @param stroke  the stroke ({@code null} not permitted).
1398     * @param notify  a flag that controls whether or not listeners are
1399     *                notified.
1400     *
1401     * @see #getDefaultOutlineStroke()
1402     */
1403    public void setDefaultOutlineStroke(Stroke stroke, boolean notify) {
1404        Args.nullNotPermitted(stroke, "stroke");
1405        this.defaultOutlineStroke = stroke;
1406        if (notify) {
1407            fireChangeEvent();
1408        }
1409    }
1410
1411    /**
1412     * Returns the flag that controls whether or not the series outline stroke
1413     * list is automatically populated when
1414     * {@link #lookupSeriesOutlineStroke(int)} is called.
1415     *
1416     * @return A boolean.
1417     *
1418     * @see #setAutoPopulateSeriesOutlineStroke(boolean)
1419     */
1420    public boolean getAutoPopulateSeriesOutlineStroke() {
1421        return this.autoPopulateSeriesOutlineStroke;
1422    }
1423
1424    /**
1425     * Sets the flag that controls whether or not the series outline stroke list
1426     * is automatically populated when {@link #lookupSeriesOutlineStroke(int)}
1427     * is called.
1428     *
1429     * @param auto  the new flag value.
1430     *
1431     * @see #getAutoPopulateSeriesOutlineStroke()
1432     */
1433    public void setAutoPopulateSeriesOutlineStroke(boolean auto) {
1434        this.autoPopulateSeriesOutlineStroke = auto;
1435    }
1436
1437    // SHAPE
1438
1439    /**
1440     * Returns a shape used to represent a data item.
1441     * <p>
1442     * The default implementation passes control to the 
1443     * {@link #lookupSeriesShape(int)} method. You can override this method if 
1444     * you require different behaviour.
1445     *
1446     * @param row  the row (or series) index (zero-based).
1447     * @param column  the column (or category) index (zero-based).
1448     *
1449     * @return The shape (never {@code null}).
1450     */
1451    public Shape getItemShape(int row, int column) {
1452        return lookupSeriesShape(row);
1453    }
1454
1455    /**
1456     * Returns a shape used to represent the items in a series.
1457     *
1458     * @param series  the series (zero-based index).
1459     *
1460     * @return The shape (never {@code null}).
1461     */
1462    public Shape lookupSeriesShape(int series) {
1463
1464        Shape result = getSeriesShape(series);
1465        if (result == null && this.autoPopulateSeriesShape) {
1466            DrawingSupplier supplier = getDrawingSupplier();
1467            if (supplier != null) {
1468                result = supplier.getNextShape();
1469                setSeriesShape(series, result, false);
1470            }
1471        }
1472        if (result == null) {
1473            result = this.defaultShape;
1474        }
1475        return result;
1476
1477    }
1478
1479    /**
1480     * Returns a shape used to represent the items in a series.
1481     *
1482     * @param series  the series (zero-based index).
1483     *
1484     * @return The shape (possibly {@code null}).
1485     *
1486     * @see #setSeriesShape(int, Shape)
1487     */
1488    public Shape getSeriesShape(int series) {
1489        return this.seriesShapeMap.get(series);
1490    }
1491
1492    /**
1493     * Sets the shape used for a series and sends a {@link RendererChangeEvent}
1494     * to all registered listeners.
1495     *
1496     * @param series  the series index (zero-based).
1497     * @param shape  the shape ({@code null} permitted).
1498     *
1499     * @see #getSeriesShape(int)
1500     */
1501    public void setSeriesShape(int series, Shape shape) {
1502        setSeriesShape(series, shape, true);
1503    }
1504
1505    /**
1506     * Sets the shape for a series and, if requested, sends a
1507     * {@link RendererChangeEvent} to all registered listeners.
1508     *
1509     * @param series  the series index (zero based).
1510     * @param shape  the shape ({@code null} permitted).
1511     * @param notify  notify listeners?
1512     *
1513     * @see #getSeriesShape(int)
1514     */
1515    public void setSeriesShape(int series, Shape shape, boolean notify) {
1516        this.seriesShapeMap.put(series, shape);
1517        if (notify) {
1518            fireChangeEvent();
1519        }
1520    }
1521
1522    /**
1523     * Returns the default shape.
1524     *
1525     * @return The shape (never {@code null}).
1526     *
1527     * @see #setDefaultShape(Shape)
1528     */
1529    public Shape getDefaultShape() {
1530        return this.defaultShape;
1531    }
1532
1533    /**
1534     * Sets the default shape and sends a {@link RendererChangeEvent} to all
1535     * registered listeners.
1536     *
1537     * @param shape  the shape ({@code null} not permitted).
1538     *
1539     * @see #getDefaultShape()
1540     */
1541    public void setDefaultShape(Shape shape) {
1542        // defer argument checking...
1543        setDefaultShape(shape, true);
1544    }
1545
1546    /**
1547     * Sets the default shape and, if requested, sends a
1548     * {@link RendererChangeEvent} to all registered listeners.
1549     *
1550     * @param shape  the shape ({@code null} not permitted).
1551     * @param notify  notify listeners?
1552     *
1553     * @see #getDefaultShape()
1554     */
1555    public void setDefaultShape(Shape shape, boolean notify) {
1556        Args.nullNotPermitted(shape, "shape");
1557        this.defaultShape = shape;
1558        if (notify) {
1559            fireChangeEvent();
1560        }
1561    }
1562
1563    /**
1564     * Returns the flag that controls whether or not the series shape list is
1565     * automatically populated when {@link #lookupSeriesShape(int)} is called.
1566     *
1567     * @return A boolean.
1568     *
1569     * @see #setAutoPopulateSeriesShape(boolean)
1570     */
1571    public boolean getAutoPopulateSeriesShape() {
1572        return this.autoPopulateSeriesShape;
1573    }
1574
1575    /**
1576     * Sets the flag that controls whether or not the series shape list is
1577     * automatically populated when {@link #lookupSeriesShape(int)} is called.
1578     *
1579     * @param auto  the new flag value.
1580     *
1581     * @see #getAutoPopulateSeriesShape()
1582     */
1583    public void setAutoPopulateSeriesShape(boolean auto) {
1584        this.autoPopulateSeriesShape = auto;
1585    }
1586
1587    // ITEM LABEL VISIBILITY...
1588
1589    /**
1590     * Returns {@code true} if an item label is visible, and
1591     * {@code false} otherwise.
1592     *
1593     * @param row  the row (or series) index (zero-based).
1594     * @param column  the column (or category) index (zero-based).
1595     *
1596     * @return A boolean.
1597     */
1598    public boolean isItemLabelVisible(int row, int column) {
1599        return isSeriesItemLabelsVisible(row);
1600    }
1601
1602    /**
1603     * Returns {@code true} if the item labels for a series are visible,
1604     * and {@code false} otherwise.
1605     *
1606     * @param series  the series index (zero-based).
1607     *
1608     * @return A boolean.
1609     */
1610    public boolean isSeriesItemLabelsVisible(int series) {
1611        Boolean b = this.seriesItemLabelsVisibleMap.get(series);
1612        if (b == null) {
1613            return this.defaultItemLabelsVisible;
1614        }
1615        return b;
1616    }
1617
1618    /**
1619     * Sets a flag that controls the visibility of the item labels for a series,
1620     * and sends a {@link RendererChangeEvent} to all registered listeners.
1621     *
1622     * @param series  the series index (zero-based).
1623     * @param visible  the flag.
1624     */
1625    public void setSeriesItemLabelsVisible(int series, boolean visible) {
1626        setSeriesItemLabelsVisible(series, Boolean.valueOf(visible));
1627    }
1628
1629    /**
1630     * Sets the visibility of the item labels for a series and sends a
1631     * {@link RendererChangeEvent} to all registered listeners.
1632     *
1633     * @param series  the series index (zero-based).
1634     * @param visible  the flag ({@code null} permitted).
1635     */
1636    public void setSeriesItemLabelsVisible(int series, Boolean visible) {
1637        setSeriesItemLabelsVisible(series, visible, true);
1638    }
1639
1640    /**
1641     * Sets the visibility of item labels for a series and, if requested, sends
1642     * a {@link RendererChangeEvent} to all registered listeners.
1643     *
1644     * @param series  the series index (zero-based).
1645     * @param visible  the visible flag.
1646     * @param notify  a flag that controls whether or not listeners are
1647     *                notified.
1648     */
1649    public void setSeriesItemLabelsVisible(int series, Boolean visible, boolean notify) {
1650        this.seriesItemLabelsVisibleMap.put(series, visible);
1651        if (notify) {
1652            fireChangeEvent();
1653        }
1654    }
1655
1656    /**
1657     * Returns the base setting for item label visibility.  A {@code null}
1658     * result should be interpreted as equivalent to {@code Boolean.FALSE}.
1659     *
1660     * @return A flag (possibly {@code null}).
1661     *
1662     * @see #setDefaultItemLabelsVisible(boolean)
1663     */
1664    public boolean getDefaultItemLabelsVisible() {
1665        return this.defaultItemLabelsVisible;
1666    }
1667
1668    /**
1669     * Sets the base flag that controls whether or not item labels are visible,
1670     * and sends a {@link RendererChangeEvent} to all registered listeners.
1671     *
1672     * @param visible  the flag.
1673     *
1674     * @see #getDefaultItemLabelsVisible()
1675     */
1676    public void setDefaultItemLabelsVisible(boolean visible) {
1677        setDefaultItemLabelsVisible(visible, true);
1678    }
1679
1680    /**
1681     * Sets the base visibility for item labels and, if requested, sends a
1682     * {@link RendererChangeEvent} to all registered listeners.
1683     *
1684     * @param visible  the flag ({@code null} is permitted, and viewed
1685     *     as equivalent to {@code Boolean.FALSE}).
1686     * @param notify  a flag that controls whether or not listeners are
1687     *                notified.
1688     *
1689     * @see #getDefaultItemLabelsVisible() 
1690     */
1691    public void setDefaultItemLabelsVisible(boolean visible, boolean notify) {
1692        this.defaultItemLabelsVisible = visible;
1693        if (notify) {
1694            fireChangeEvent();
1695        }
1696    }
1697
1698    //// ITEM LABEL FONT //////////////////////////////////////////////////////
1699
1700    /**
1701     * Returns the font for an item label.
1702     *
1703     * @param row  the row (or series) index (zero-based).
1704     * @param column  the column (or category) index (zero-based).
1705     *
1706     * @return The font (never {@code null}).
1707     */
1708    public Font getItemLabelFont(int row, int column) {
1709        Font result = getSeriesItemLabelFont(row);
1710        if (result == null) {
1711            result = this.defaultItemLabelFont;
1712        }
1713        return result;
1714    }
1715
1716    /**
1717     * Returns the font for all the item labels in a series.
1718     *
1719     * @param series  the series index (zero-based).
1720     *
1721     * @return The font (possibly {@code null}).
1722     *
1723     * @see #setSeriesItemLabelFont(int, Font)
1724     */
1725    public Font getSeriesItemLabelFont(int series) {
1726        return this.itemLabelFontMap.get(series);
1727    }
1728
1729    /**
1730     * Sets the item label font for a series and sends a
1731     * {@link RendererChangeEvent} to all registered listeners.
1732     *
1733     * @param series  the series index (zero-based).
1734     * @param font  the font ({@code null} permitted).
1735     *
1736     * @see #getSeriesItemLabelFont(int)
1737     */
1738    public void setSeriesItemLabelFont(int series, Font font) {
1739        setSeriesItemLabelFont(series, font, true);
1740    }
1741
1742    /**
1743     * Sets the item label font for a series and, if requested, sends a
1744     * {@link RendererChangeEvent} to all registered listeners.
1745     *
1746     * @param series  the series index (zero based).
1747     * @param font  the font ({@code null} permitted).
1748     * @param notify  a flag that controls whether or not listeners are
1749     *                notified.
1750     *
1751     * @see #getSeriesItemLabelFont(int)
1752     */
1753    public void setSeriesItemLabelFont(int series, Font font, boolean notify) {
1754        this.itemLabelFontMap.put(series, font);
1755        if (notify) {
1756            fireChangeEvent();
1757        }
1758    }
1759
1760    /**
1761     * Returns the default item label font (this is used when no other font
1762     * setting is available).
1763     *
1764     * @return The font (never {@code null}).
1765     *
1766     * @see #setDefaultItemLabelFont(Font)
1767     */
1768    public Font getDefaultItemLabelFont() {
1769        return this.defaultItemLabelFont;
1770    }
1771
1772    /**
1773     * Sets the default item label font and sends a {@link RendererChangeEvent} 
1774     * to all registered listeners.
1775     *
1776     * @param font  the font ({@code null} not permitted).
1777     *
1778     * @see #getDefaultItemLabelFont()
1779     */
1780    public void setDefaultItemLabelFont(Font font) {
1781        Args.nullNotPermitted(font, "font");
1782        setDefaultItemLabelFont(font, true);
1783    }
1784
1785    /**
1786     * Sets the base item label font and, if requested, sends a
1787     * {@link RendererChangeEvent} to all registered listeners.
1788     *
1789     * @param font  the font ({@code null} not permitted).
1790     * @param notify  a flag that controls whether or not listeners are
1791     *                notified.
1792     *
1793     * @see #getDefaultItemLabelFont()
1794     */
1795    public void setDefaultItemLabelFont(Font font, boolean notify) {
1796        this.defaultItemLabelFont = font;
1797        if (notify) {
1798            fireChangeEvent();
1799        }
1800    }
1801
1802    //// ITEM LABEL PAINT  ////////////////////////////////////////////////////
1803
1804    /**
1805     * Returns the paint used to draw an item label.
1806     *
1807     * @param row  the row index (zero based).
1808     * @param column  the column index (zero based).
1809     *
1810     * @return The paint (never {@code null}).
1811     */
1812    public Paint getItemLabelPaint(int row, int column) {
1813        Paint result = getSeriesItemLabelPaint(row);
1814        if (result == null) {
1815            result = this.defaultItemLabelPaint;
1816        }
1817        return result;
1818    }
1819
1820    /**
1821     * Returns the paint used to draw the item labels for a series.
1822     *
1823     * @param series  the series index (zero based).
1824     *
1825     * @return The paint (possibly {@code null}).
1826     *
1827     * @see #setSeriesItemLabelPaint(int, Paint)
1828     */
1829    public Paint getSeriesItemLabelPaint(int series) {
1830        return this.itemLabelPaints.get(series);
1831    }
1832
1833    /**
1834     * Sets the item label paint for a series and sends a
1835     * {@link RendererChangeEvent} to all registered listeners.
1836     *
1837     * @param series  the series (zero based index).
1838     * @param paint  the paint ({@code null} permitted).
1839     *
1840     * @see #getSeriesItemLabelPaint(int)
1841     */
1842    public void setSeriesItemLabelPaint(int series, Paint paint) {
1843        setSeriesItemLabelPaint(series, paint, true);
1844    }
1845
1846    /**
1847     * Sets the item label paint for a series and, if requested, sends a
1848     * {@link RendererChangeEvent} to all registered listeners.
1849     *
1850     * @param series  the series index (zero based).
1851     * @param paint  the paint ({@code null} permitted).
1852     * @param notify  a flag that controls whether or not listeners are
1853     *                notified.
1854     *
1855     * @see #getSeriesItemLabelPaint(int)
1856     */
1857    public void setSeriesItemLabelPaint(int series, Paint paint, boolean notify) {
1858        this.itemLabelPaints.put(series, paint);
1859        if (notify) {
1860            fireChangeEvent();
1861        }
1862    }
1863
1864    /**
1865     * Returns the default item label paint.
1866     *
1867     * @return The paint (never {@code null}).
1868     *
1869     * @see #setDefaultItemLabelPaint(Paint)
1870     */
1871    public Paint getDefaultItemLabelPaint() {
1872        return this.defaultItemLabelPaint;
1873    }
1874
1875    /**
1876     * Sets the default item label paint and sends a {@link RendererChangeEvent}
1877     * to all registered listeners.
1878     *
1879     * @param paint  the paint ({@code null} not permitted).
1880     *
1881     * @see #getDefaultItemLabelPaint()
1882     */
1883    public void setDefaultItemLabelPaint(Paint paint) {
1884        // defer argument checking...
1885        setDefaultItemLabelPaint(paint, true);
1886    }
1887
1888    /**
1889     * Sets the default item label paint and, if requested, sends a
1890     * {@link RendererChangeEvent} to all registered listeners..
1891     *
1892     * @param paint  the paint ({@code null} not permitted).
1893     * @param notify  a flag that controls whether or not listeners are
1894     *                notified.
1895     *
1896     * @see #getDefaultItemLabelPaint()
1897     */
1898    public void setDefaultItemLabelPaint(Paint paint, boolean notify) {
1899        Args.nullNotPermitted(paint, "paint");
1900        this.defaultItemLabelPaint = paint;
1901        if (notify) {
1902            fireChangeEvent();
1903        }
1904    }
1905
1906    // POSITIVE ITEM LABEL POSITION...
1907
1908    /**
1909     * Returns the item label position for positive values.
1910     *
1911     * @param row  the row (or series) index (zero-based).
1912     * @param column  the column (or category) index (zero-based).
1913     *
1914     * @return The item label position (never {@code null}).
1915     *
1916     * @see #getNegativeItemLabelPosition(int, int)
1917     */
1918    public ItemLabelPosition getPositiveItemLabelPosition(int row, int column) {
1919        return getSeriesPositiveItemLabelPosition(row);
1920    }
1921
1922    /**
1923     * Returns the item label position for all positive values in a series.
1924     *
1925     * @param series  the series index (zero-based).
1926     *
1927     * @return The item label position (never {@code null}).
1928     *
1929     * @see #setSeriesPositiveItemLabelPosition(int, ItemLabelPosition)
1930     */
1931    public ItemLabelPosition getSeriesPositiveItemLabelPosition(int series) {
1932        // otherwise look up the position table
1933        ItemLabelPosition position = this.positiveItemLabelPositionMap.get(series);
1934        if (position == null) {
1935            position = this.defaultPositiveItemLabelPosition;
1936        }
1937        return position;
1938    }
1939
1940    /**
1941     * Sets the item label position for all positive values in a series and
1942     * sends a {@link RendererChangeEvent} to all registered listeners.
1943     *
1944     * @param series  the series index (zero-based).
1945     * @param position  the position ({@code null} permitted).
1946     *
1947     * @see #getSeriesPositiveItemLabelPosition(int)
1948     */
1949    public void setSeriesPositiveItemLabelPosition(int series,
1950                                                   ItemLabelPosition position) {
1951        setSeriesPositiveItemLabelPosition(series, position, true);
1952    }
1953
1954    /**
1955     * Sets the item label position for all positive values in a series and (if
1956     * requested) sends a {@link RendererChangeEvent} to all registered
1957     * listeners.
1958     *
1959     * @param series  the series index (zero-based).
1960     * @param position  the position ({@code null} permitted).
1961     * @param notify  notify registered listeners?
1962     *
1963     * @see #getSeriesPositiveItemLabelPosition(int)
1964     */
1965    public void setSeriesPositiveItemLabelPosition(int series,
1966            ItemLabelPosition position, boolean notify) {
1967        this.positiveItemLabelPositionMap.put(series, position);
1968        if (notify) {
1969            fireChangeEvent();
1970        }
1971    }
1972
1973    /**
1974     * Returns the default positive item label position.
1975     *
1976     * @return The position (never {@code null}).
1977     *
1978     * @see #setDefaultPositiveItemLabelPosition(ItemLabelPosition)
1979     */
1980    public ItemLabelPosition getDefaultPositiveItemLabelPosition() {
1981        return this.defaultPositiveItemLabelPosition;
1982    }
1983
1984    /**
1985     * Sets the default positive item label position.
1986     *
1987     * @param position  the position ({@code null} not permitted).
1988     *
1989     * @see #getDefaultPositiveItemLabelPosition()
1990     */
1991    public void setDefaultPositiveItemLabelPosition(
1992            ItemLabelPosition position) {
1993        // defer argument checking...
1994        setDefaultPositiveItemLabelPosition(position, true);
1995    }
1996
1997    /**
1998     * Sets the default positive item label position and, if requested, sends a
1999     * {@link RendererChangeEvent} to all registered listeners.
2000     *
2001     * @param position  the position ({@code null} not permitted).
2002     * @param notify  notify registered listeners?
2003     *
2004     * @see #getDefaultPositiveItemLabelPosition()
2005     */
2006    public void setDefaultPositiveItemLabelPosition(ItemLabelPosition position,
2007            boolean notify) {
2008        Args.nullNotPermitted(position, "position");
2009        this.defaultPositiveItemLabelPosition = position;
2010        if (notify) {
2011            fireChangeEvent();
2012        }
2013    }
2014
2015    // NEGATIVE ITEM LABEL POSITION...
2016
2017    /**
2018     * Returns the item label position for negative values.  This method can be
2019     * overridden to provide customisation of the item label position for
2020     * individual data items.
2021     *
2022     * @param row  the row (or series) index (zero-based).
2023     * @param column  the column (or category) index (zero-based).
2024     *
2025     * @return The item label position (never {@code null}).
2026     *
2027     * @see #getPositiveItemLabelPosition(int, int)
2028     */
2029    public ItemLabelPosition getNegativeItemLabelPosition(int row, int column) {
2030        return getSeriesNegativeItemLabelPosition(row);
2031    }
2032
2033    /**
2034     * Returns the item label position for all negative values in a series.
2035     *
2036     * @param series  the series index (zero-based).
2037     *
2038     * @return The item label position (never {@code null}).
2039     *
2040     * @see #setSeriesNegativeItemLabelPosition(int, ItemLabelPosition)
2041     */
2042    public ItemLabelPosition getSeriesNegativeItemLabelPosition(int series) {
2043        // otherwise look up the position list
2044        ItemLabelPosition position 
2045                = this.negativeItemLabelPositionMap.get(series);
2046        if (position == null) {
2047            position = this.defaultNegativeItemLabelPosition;
2048        }
2049        return position;
2050    }
2051
2052    /**
2053     * Sets the item label position for negative values in a series and sends a
2054     * {@link RendererChangeEvent} to all registered listeners.
2055     *
2056     * @param series  the series index (zero-based).
2057     * @param position  the position ({@code null} permitted).
2058     *
2059     * @see #getSeriesNegativeItemLabelPosition(int)
2060     */
2061    public void setSeriesNegativeItemLabelPosition(int series,
2062                                                   ItemLabelPosition position) {
2063        setSeriesNegativeItemLabelPosition(series, position, true);
2064    }
2065
2066    /**
2067     * Sets the item label position for negative values in a series and (if
2068     * requested) sends a {@link RendererChangeEvent} to all registered
2069     * listeners.
2070     *
2071     * @param series  the series index (zero-based).
2072     * @param position  the position ({@code null} permitted).
2073     * @param notify  notify registered listeners?
2074     *
2075     * @see #getSeriesNegativeItemLabelPosition(int)
2076     */
2077    public void setSeriesNegativeItemLabelPosition(int series,
2078            ItemLabelPosition position, boolean notify) {
2079        this.negativeItemLabelPositionMap.put(series, position);
2080        if (notify) {
2081            fireChangeEvent();
2082        }
2083    }
2084
2085    /**
2086     * Returns the base item label position for negative values.
2087     *
2088     * @return The position (never {@code null}).
2089     *
2090     * @see #setDefaultNegativeItemLabelPosition(ItemLabelPosition)
2091     */
2092    public ItemLabelPosition getDefaultNegativeItemLabelPosition() {
2093        return this.defaultNegativeItemLabelPosition;
2094    }
2095
2096    /**
2097     * Sets the default item label position for negative values and sends a
2098     * {@link RendererChangeEvent} to all registered listeners.
2099     *
2100     * @param position  the position ({@code null} not permitted).
2101     *
2102     * @see #getDefaultNegativeItemLabelPosition()
2103     */
2104    public void setDefaultNegativeItemLabelPosition(
2105            ItemLabelPosition position) {
2106        setDefaultNegativeItemLabelPosition(position, true);
2107    }
2108
2109    /**
2110     * Sets the default negative item label position and, if requested, sends a
2111     * {@link RendererChangeEvent} to all registered listeners.
2112     *
2113     * @param position  the position ({@code null} not permitted).
2114     * @param notify  notify registered listeners?
2115     *
2116     * @see #getDefaultNegativeItemLabelPosition()
2117     */
2118    public void setDefaultNegativeItemLabelPosition(ItemLabelPosition position,
2119            boolean notify) {
2120        Args.nullNotPermitted(position, "position");
2121        this.defaultNegativeItemLabelPosition = position;
2122        if (notify) {
2123            fireChangeEvent();
2124        }
2125    }
2126
2127    /**
2128     * Returns the item label anchor offset.
2129     *
2130     * @return The offset.
2131     *
2132     * @see #setItemLabelAnchorOffset(double)
2133     */
2134    public double getItemLabelAnchorOffset() {
2135        return this.itemLabelAnchorOffset;
2136    }
2137
2138    /**
2139     * Sets the item label anchor offset.
2140     *
2141     * @param offset  the offset.
2142     *
2143     * @see #getItemLabelAnchorOffset()
2144     */
2145    public void setItemLabelAnchorOffset(double offset) {
2146        this.itemLabelAnchorOffset = offset;
2147        fireChangeEvent();
2148    }
2149
2150    /**
2151     * Returns a boolean that indicates whether or not the specified item
2152     * should have a chart entity created for it.
2153     *
2154     * @param series  the series index.
2155     * @param item  the item index.
2156     *
2157     * @return A boolean.
2158     */
2159    public boolean getItemCreateEntity(int series, int item) {
2160        Boolean b = getSeriesCreateEntities(series);
2161        if (b != null) {
2162            return b;
2163        }
2164        // otherwise...
2165        return this.defaultCreateEntities;
2166    }
2167
2168    /**
2169     * Returns the flag that controls whether entities are created for a
2170     * series.
2171     *
2172     * @param series  the series index (zero-based).
2173     *
2174     * @return The flag (possibly {@code null}).
2175     *
2176     * @see #setSeriesCreateEntities(int, Boolean)
2177     */
2178    public Boolean getSeriesCreateEntities(int series) {
2179        return this.seriesCreateEntitiesMap.get(series);
2180    }
2181
2182    /**
2183     * Sets the flag that controls whether entities are created for a series,
2184     * and sends a {@link RendererChangeEvent} to all registered listeners.
2185     *
2186     * @param series  the series index (zero-based).
2187     * @param create  the flag ({@code null} permitted).
2188     *
2189     * @see #getSeriesCreateEntities(int)
2190     */
2191    public void setSeriesCreateEntities(int series, Boolean create) {
2192        setSeriesCreateEntities(series, create, true);
2193    }
2194
2195    /**
2196     * Sets the flag that controls whether entities are created for a series
2197     * and, if requested, sends a {@link RendererChangeEvent} to all registered
2198     * listeners.
2199     *
2200     * @param series  the series index.
2201     * @param create  the flag ({@code null} permitted).
2202     * @param notify  notify listeners?
2203     *
2204     * @see #getSeriesCreateEntities(int)
2205     */
2206    public void setSeriesCreateEntities(int series, Boolean create,
2207                                        boolean notify) {
2208        this.seriesCreateEntitiesMap.put(series, create);
2209        if (notify) {
2210            fireChangeEvent();
2211        }
2212    }
2213
2214    /**
2215     * Returns the default flag for creating entities.
2216     *
2217     * @return The default flag for creating entities.
2218     *
2219     * @see #setDefaultCreateEntities(boolean)
2220     */
2221    public boolean getDefaultCreateEntities() {
2222        return this.defaultCreateEntities;
2223    }
2224
2225    /**
2226     * Sets the default flag that controls whether entities are created
2227     * for a series, and sends a {@link RendererChangeEvent}
2228     * to all registered listeners.
2229     *
2230     * @param create  the flag.
2231     *
2232     * @see #getDefaultCreateEntities()
2233     */
2234    public void setDefaultCreateEntities(boolean create) {
2235        // defer argument checking...
2236        setDefaultCreateEntities(create, true);
2237    }
2238
2239    /**
2240     * Sets the default flag that controls whether entities are created and,
2241     * if requested, sends a {@link RendererChangeEvent} to all registered
2242     * listeners.
2243     *
2244     * @param create  the visibility.
2245     * @param notify  notify listeners?
2246     *
2247     * @see #getDefaultCreateEntities()
2248     */
2249    public void setDefaultCreateEntities(boolean create, boolean notify) {
2250        this.defaultCreateEntities = create;
2251        if (notify) {
2252            fireChangeEvent();
2253        }
2254    }
2255
2256    /**
2257     * Returns the radius of the circle used for the default entity area
2258     * when no area is specified.
2259     *
2260     * @return A radius.
2261     *
2262     * @see #setDefaultEntityRadius(int)
2263     */
2264    public int getDefaultEntityRadius() {
2265        return this.defaultEntityRadius;
2266    }
2267
2268    /**
2269     * Sets the radius of the circle used for the default entity area
2270     * when no area is specified.
2271     *
2272     * @param radius  the radius.
2273     *
2274     * @see #getDefaultEntityRadius()
2275     */
2276    public void setDefaultEntityRadius(int radius) {
2277        this.defaultEntityRadius = radius;
2278    }
2279
2280    /**
2281     * Performs a lookup for the legend shape.
2282     *
2283     * @param series  the series index.
2284     *
2285     * @return The shape (possibly {@code null}).
2286     */
2287    public Shape lookupLegendShape(int series) {
2288        Shape result = getLegendShape(series);
2289        if (result == null) {
2290            result = this.defaultLegendShape;
2291        }
2292        if (result == null) {
2293            result = lookupSeriesShape(series);
2294        }
2295        return result;
2296    }
2297
2298    /**
2299     * Returns the legend shape defined for the specified series (possibly
2300     * {@code null}).
2301     *
2302     * @param series  the series index.
2303     *
2304     * @return The shape (possibly {@code null}).
2305     *
2306     * @see #lookupLegendShape(int)
2307     */
2308    public Shape getLegendShape(int series) {
2309        return this.seriesLegendShapes.get(series);
2310    }
2311
2312    /**
2313     * Sets the shape used for the legend item for the specified series, and
2314     * sends a {@link RendererChangeEvent} to all registered listeners.
2315     *
2316     * @param series  the series index.
2317     * @param shape  the shape ({@code null} permitted).
2318     */
2319    public void setLegendShape(int series, Shape shape) {
2320        this.seriesLegendShapes.put(series, shape);
2321        fireChangeEvent();
2322    }
2323
2324    /**
2325     * Returns the default legend shape, which may be {@code null}.
2326     *
2327     * @return The default legend shape.
2328     */
2329    public Shape getDefaultLegendShape() {
2330        return this.defaultLegendShape;
2331    }
2332
2333    /**
2334     * Sets the default legend shape and sends a
2335     * {@link RendererChangeEvent} to all registered listeners.
2336     *
2337     * @param shape  the shape ({@code null} permitted).
2338     */
2339    public void setDefaultLegendShape(Shape shape) {
2340        this.defaultLegendShape = shape;
2341        fireChangeEvent();
2342    }
2343
2344    /**
2345     * Returns the flag that controls whether or not the legend shape is
2346     * treated as a line when creating legend items.
2347     * 
2348     * @return A boolean.
2349     */
2350    protected boolean getTreatLegendShapeAsLine() {
2351        return this.treatLegendShapeAsLine;
2352    }
2353
2354    /**
2355     * Sets the flag that controls whether or not the legend shape is
2356     * treated as a line when creating legend items.
2357     *
2358     * @param treatAsLine  the new flag value.
2359     */
2360    protected void setTreatLegendShapeAsLine(boolean treatAsLine) {
2361        if (this.treatLegendShapeAsLine != treatAsLine) {
2362            this.treatLegendShapeAsLine = treatAsLine;
2363            fireChangeEvent();
2364        }
2365    }
2366
2367    /**
2368     * Performs a lookup for the legend text font.
2369     *
2370     * @param series  the series index.
2371     *
2372     * @return The font (possibly {@code null}).
2373     */
2374    public Font lookupLegendTextFont(int series) {
2375        Font result = getLegendTextFont(series);
2376        if (result == null) {
2377            result = this.defaultLegendTextFont;
2378        }
2379        return result;
2380    }
2381
2382    /**
2383     * Returns the legend text font defined for the specified series (possibly
2384     * {@code null}).
2385     *
2386     * @param series  the series index.
2387     *
2388     * @return The font (possibly {@code null}).
2389     *
2390     * @see #lookupLegendTextFont(int)
2391     */
2392    public Font getLegendTextFont(int series) {
2393        return this.legendTextFontMap.get(series);
2394    }
2395
2396    /**
2397     * Sets the font used for the legend text for the specified series, and
2398     * sends a {@link RendererChangeEvent} to all registered listeners.
2399     *
2400     * @param series  the series index.
2401     * @param font  the font ({@code null} permitted).
2402     */
2403    public void setLegendTextFont(int series, Font font) {
2404        this.legendTextFontMap.put(series, font);
2405        fireChangeEvent();
2406    }
2407
2408    /**
2409     * Returns the default legend text font, which may be {@code null}.
2410     *
2411     * @return The default legend text font.
2412     */
2413    public Font getDefaultLegendTextFont() {
2414        return this.defaultLegendTextFont;
2415    }
2416
2417    /**
2418     * Sets the default legend text font and sends a
2419     * {@link RendererChangeEvent} to all registered listeners.
2420     *
2421     * @param font  the font ({@code null} permitted).
2422     */
2423    public void setDefaultLegendTextFont(Font font) {
2424        Args.nullNotPermitted(font, "font");
2425        this.defaultLegendTextFont = font;
2426        fireChangeEvent();
2427    }
2428
2429    /**
2430     * Performs a lookup for the legend text paint.
2431     *
2432     * @param series  the series index.
2433     *
2434     * @return The paint (possibly {@code null}).
2435     */
2436    public Paint lookupLegendTextPaint(int series) {
2437        Paint result = getLegendTextPaint(series);
2438        if (result == null) {
2439            result = this.defaultLegendTextPaint;
2440        }
2441        return result;
2442    }
2443
2444    /**
2445     * Returns the legend text paint defined for the specified series (possibly
2446     * {@code null}).
2447     *
2448     * @param series  the series index.
2449     *
2450     * @return The paint (possibly {@code null}).
2451     *
2452     * @see #lookupLegendTextPaint(int)
2453     */
2454    public Paint getLegendTextPaint(int series) {
2455        return this.legendTextPaints.get(series);
2456    }
2457
2458    /**
2459     * Sets the paint used for the legend text for the specified series, and
2460     * sends a {@link RendererChangeEvent} to all registered listeners.
2461     *
2462     * @param series  the series index.
2463     * @param paint  the paint ({@code null} permitted).
2464     */
2465    public void setLegendTextPaint(int series, Paint paint) {
2466        this.legendTextPaints.put(series, paint);
2467        fireChangeEvent();
2468    }
2469
2470    /**
2471     * Returns the default legend text paint, which may be {@code null}.
2472     *
2473     * @return The default legend text paint.
2474     */
2475    public Paint getDefaultLegendTextPaint() {
2476        return this.defaultLegendTextPaint;
2477    }
2478
2479    /**
2480     * Sets the default legend text paint and sends a
2481     * {@link RendererChangeEvent} to all registered listeners.
2482     *
2483     * @param paint  the paint ({@code null} permitted).
2484     */
2485    public void setDefaultLegendTextPaint(Paint paint) {
2486        this.defaultLegendTextPaint = paint;
2487        fireChangeEvent();
2488    }
2489
2490    /**
2491     * Returns the flag that controls whether or not the data bounds reported
2492     * by this renderer will exclude non-visible series.
2493     *
2494     * @return A boolean.
2495     */
2496    public boolean getDataBoundsIncludesVisibleSeriesOnly() {
2497        return this.dataBoundsIncludesVisibleSeriesOnly;
2498    }
2499
2500    /**
2501     * Sets the flag that controls whether or not the data bounds reported
2502     * by this renderer will exclude non-visible series and sends a
2503     * {@link RendererChangeEvent} to all registered listeners.
2504     *
2505     * @param visibleOnly  include only visible series.
2506     */
2507    public void setDataBoundsIncludesVisibleSeriesOnly(boolean visibleOnly) {
2508        this.dataBoundsIncludesVisibleSeriesOnly = visibleOnly;
2509        notifyListeners(new RendererChangeEvent(this, true));
2510    }
2511
2512    /** The adjacent offset. */
2513    private static final double ADJ = Math.cos(Math.PI / 6.0);
2514
2515    /** The opposite offset. */
2516    private static final double OPP = Math.sin(Math.PI / 6.0);
2517
2518    /**
2519     * Calculates the item label anchor point.
2520     *
2521     * @param anchor  the anchor.
2522     * @param x  the x coordinate.
2523     * @param y  the y coordinate.
2524     * @param orientation  the plot orientation.
2525     *
2526     * @return The anchor point (never {@code null}).
2527     */
2528    protected Point2D calculateLabelAnchorPoint(ItemLabelAnchor anchor,
2529            double x, double y, PlotOrientation orientation) {
2530        Args.nullNotPermitted(anchor, "anchor");
2531        Point2D result = null;
2532        switch (anchor) {
2533            case CENTER:
2534                result = new Point2D.Double(x, y);
2535                break;
2536            case INSIDE1:
2537                result = new Point2D.Double(x + OPP * this.itemLabelAnchorOffset,
2538                        y - ADJ * this.itemLabelAnchorOffset);
2539                break;
2540            case INSIDE2:
2541                result = new Point2D.Double(x + ADJ * this.itemLabelAnchorOffset,
2542                        y - OPP * this.itemLabelAnchorOffset);
2543                break;
2544            case INSIDE3:
2545                result = new Point2D.Double(x + this.itemLabelAnchorOffset, y);
2546                break;
2547            case INSIDE4:
2548                result = new Point2D.Double(x + ADJ * this.itemLabelAnchorOffset,
2549                        y + OPP * this.itemLabelAnchorOffset);
2550                break;
2551            case INSIDE5:
2552                result = new Point2D.Double(x + OPP * this.itemLabelAnchorOffset,
2553                        y + ADJ * this.itemLabelAnchorOffset);
2554                break;
2555            case INSIDE6:
2556                result = new Point2D.Double(x, y + this.itemLabelAnchorOffset);
2557                break;
2558            case INSIDE7:
2559                result = new Point2D.Double(x - OPP * this.itemLabelAnchorOffset,
2560                        y + ADJ * this.itemLabelAnchorOffset);
2561                break;
2562            case INSIDE8:
2563                result = new Point2D.Double(x - ADJ * this.itemLabelAnchorOffset,
2564                        y + OPP * this.itemLabelAnchorOffset);
2565                break;
2566            case INSIDE9:
2567                result = new Point2D.Double(x - this.itemLabelAnchorOffset, y);
2568                break;
2569            case INSIDE10:
2570                result = new Point2D.Double(x - ADJ * this.itemLabelAnchorOffset,
2571                        y - OPP * this.itemLabelAnchorOffset);
2572                break;
2573            case INSIDE11:
2574                result = new Point2D.Double(x - OPP * this.itemLabelAnchorOffset,
2575                        y - ADJ * this.itemLabelAnchorOffset);
2576                break;
2577            case INSIDE12:
2578                result = new Point2D.Double(x, y - this.itemLabelAnchorOffset);
2579                break;
2580            case OUTSIDE1:
2581                result = new Point2D.Double(
2582                        x + 2.0 * OPP * this.itemLabelAnchorOffset,
2583                        y - 2.0 * ADJ * this.itemLabelAnchorOffset);
2584                break;
2585            case OUTSIDE2:
2586                result = new Point2D.Double(
2587                        x + 2.0 * ADJ * this.itemLabelAnchorOffset,
2588                        y - 2.0 * OPP * this.itemLabelAnchorOffset);
2589                break;
2590            case OUTSIDE3:
2591                result = new Point2D.Double(x + 2.0 * this.itemLabelAnchorOffset,
2592                        y);
2593                break;
2594            case OUTSIDE4:
2595                result = new Point2D.Double(
2596                        x + 2.0 * ADJ * this.itemLabelAnchorOffset,
2597                        y + 2.0 * OPP * this.itemLabelAnchorOffset);
2598                break;
2599            case OUTSIDE5:
2600                result = new Point2D.Double(
2601                        x + 2.0 * OPP * this.itemLabelAnchorOffset,
2602                        y + 2.0 * ADJ * this.itemLabelAnchorOffset);
2603                break;
2604            case OUTSIDE6:
2605                result = new Point2D.Double(x,
2606                        y + 2.0 * this.itemLabelAnchorOffset);
2607                break;
2608            case OUTSIDE7:
2609                result = new Point2D.Double(
2610                        x - 2.0 * OPP * this.itemLabelAnchorOffset,
2611                        y + 2.0 * ADJ * this.itemLabelAnchorOffset);
2612                break;
2613            case OUTSIDE8:
2614                result = new Point2D.Double(
2615                        x - 2.0 * ADJ * this.itemLabelAnchorOffset,
2616                        y + 2.0 * OPP * this.itemLabelAnchorOffset);
2617                break;
2618            case OUTSIDE9:
2619                result = new Point2D.Double(x - 2.0 * this.itemLabelAnchorOffset,
2620                        y);
2621                break;
2622            case OUTSIDE10:
2623                result = new Point2D.Double(
2624                        x - 2.0 * ADJ * this.itemLabelAnchorOffset,
2625                        y - 2.0 * OPP * this.itemLabelAnchorOffset);
2626                break;
2627            case OUTSIDE11:
2628                result = new Point2D.Double(
2629                        x - 2.0 * OPP * this.itemLabelAnchorOffset,
2630                        y - 2.0 * ADJ * this.itemLabelAnchorOffset);
2631                break;
2632            case OUTSIDE12:
2633                result = new Point2D.Double(x,
2634                        y - 2.0 * this.itemLabelAnchorOffset);
2635                break;
2636            default:
2637                throw new IllegalStateException("Unexpected anchor value.");
2638        }
2639        return result;
2640    }
2641
2642    /**
2643     * Registers an object to receive notification of changes to the renderer.
2644     *
2645     * @param listener  the listener ({@code null} not permitted).
2646     *
2647     * @see #removeChangeListener(RendererChangeListener)
2648     */
2649    public void addChangeListener(RendererChangeListener listener) {
2650        Args.nullNotPermitted(listener, "listener");
2651        this.listenerList.add(RendererChangeListener.class, listener);
2652    }
2653
2654    /**
2655     * Deregisters an object so that it no longer receives
2656     * notification of changes to the renderer.
2657     *
2658     * @param listener  the object ({@code null} not permitted).
2659     *
2660     * @see #addChangeListener(RendererChangeListener)
2661     */
2662    public void removeChangeListener(RendererChangeListener listener) {
2663        Args.nullNotPermitted(listener, "listener");
2664        this.listenerList.remove(RendererChangeListener.class, listener);
2665    }
2666
2667    /**
2668     * Returns {@code true} if the specified object is registered with
2669     * the dataset as a listener.  Most applications won't need to call this
2670     * method, it exists mainly for use by unit testing code.
2671     *
2672     * @param listener  the listener.
2673     *
2674     * @return A boolean.
2675     */
2676    public boolean hasListener(EventListener listener) {
2677        List<Object> list = Arrays.asList(this.listenerList.getListenerList());
2678        return list.contains(listener);
2679    }
2680
2681    /**
2682     * Sends a {@link RendererChangeEvent} to all registered listeners.
2683     */
2684    protected void fireChangeEvent() {
2685        notifyListeners(new RendererChangeEvent(this));
2686    }
2687
2688    /**
2689     * Notifies all registered listeners that the renderer has been modified.
2690     *
2691     * @param event  information about the change event.
2692     */
2693    public void notifyListeners(RendererChangeEvent event) {
2694        Object[] ls = this.listenerList.getListenerList();
2695        for (int i = ls.length - 2; i >= 0; i -= 2) {
2696            if (ls[i] == RendererChangeListener.class) {
2697                ((RendererChangeListener) ls[i + 1]).rendererChanged(event);
2698            }
2699        }
2700    }
2701
2702    /**
2703     * Tests this renderer for equality with another object.
2704     *
2705     * @param obj  the object ({@code null} permitted).
2706     *
2707     * @return {@code true} or {@code false}.
2708     */
2709    @Override
2710    public boolean equals(Object obj) {
2711        if (obj == this) {
2712            return true;
2713        }
2714        if (!(obj instanceof AbstractRenderer)) {
2715            return false;
2716        }
2717        AbstractRenderer that = (AbstractRenderer) obj;
2718        if (this.dataBoundsIncludesVisibleSeriesOnly
2719                != that.dataBoundsIncludesVisibleSeriesOnly) {
2720            return false;
2721        }
2722        if (this.treatLegendShapeAsLine != that.treatLegendShapeAsLine) {
2723            return false;
2724        }
2725        if (this.defaultEntityRadius != that.defaultEntityRadius) {
2726            return false;
2727        }
2728        if (!this.seriesVisibleMap.equals(that.seriesVisibleMap)) {
2729            return false;
2730        }
2731        if (this.defaultSeriesVisible != that.defaultSeriesVisible) {
2732            return false;
2733        }
2734        if (!this.seriesVisibleInLegendMap.equals(that.seriesVisibleInLegendMap)) {
2735            return false;
2736        }
2737        if (this.defaultSeriesVisibleInLegend
2738                != that.defaultSeriesVisibleInLegend) {
2739            return false;
2740        }
2741        if (!PaintUtils.equal(this.seriesPaintMap, that.seriesPaintMap)) {
2742            return false;
2743        }
2744        if (this.autoPopulateSeriesPaint != that.autoPopulateSeriesPaint) {
2745            return false;
2746        }
2747        if (!PaintUtils.equal(this.defaultPaint, that.defaultPaint)) {
2748            return false;
2749        }
2750        if (!PaintUtils.equal(this.seriesFillPaintMap, that.seriesFillPaintMap)) {
2751            return false;
2752        }
2753        if (this.autoPopulateSeriesFillPaint != that.autoPopulateSeriesFillPaint) {
2754            return false;
2755        }
2756        if (!PaintUtils.equal(this.defaultFillPaint, that.defaultFillPaint)) {
2757            return false;
2758        }
2759        if (!PaintUtils.equal(this.seriesOutlinePaintMap, that.seriesOutlinePaintMap)) {
2760            return false;
2761        }
2762        if (this.autoPopulateSeriesOutlinePaint != that.autoPopulateSeriesOutlinePaint) {
2763            return false;
2764        }
2765        if (!PaintUtils.equal(this.defaultOutlinePaint, that.defaultOutlinePaint)) {
2766            return false;
2767        }
2768        if (!Objects.equals(this.seriesStrokeMap, that.seriesStrokeMap)) {
2769            return false;
2770        }
2771        if (this.autoPopulateSeriesStroke != that.autoPopulateSeriesStroke) {
2772            return false;
2773        }
2774        if (!Objects.equals(this.defaultStroke, that.defaultStroke)) {
2775            return false;
2776        }
2777        if (!Objects.equals(this.seriesOutlineStrokeMap, that.seriesOutlineStrokeMap)) {
2778            return false;
2779        }
2780        if (this.autoPopulateSeriesOutlineStroke != that.autoPopulateSeriesOutlineStroke) {
2781            return false;
2782        }
2783        if (!Objects.equals(this.defaultOutlineStroke, that.defaultOutlineStroke)) {
2784            return false;
2785        }
2786        if (!ShapeUtils.equal(this.seriesShapeMap, that.seriesShapeMap)) {
2787            return false;
2788        }
2789        if (this.autoPopulateSeriesShape != that.autoPopulateSeriesShape) {
2790            return false;
2791        }
2792        if (!ShapeUtils.equal(this.defaultShape, that.defaultShape)) {
2793            return false;
2794        }
2795        if (!Objects.equals(this.seriesItemLabelsVisibleMap, that.seriesItemLabelsVisibleMap)) {
2796            return false;
2797        }
2798        if (!Objects.equals(this.defaultItemLabelsVisible, that.defaultItemLabelsVisible)) {
2799            return false;
2800        }
2801        if (!Objects.equals(this.itemLabelFontMap, that.itemLabelFontMap)) {
2802            return false;
2803        }
2804        if (!Objects.equals(this.defaultItemLabelFont, that.defaultItemLabelFont)) {
2805            return false;
2806        }
2807
2808        if (!PaintUtils.equal(this.itemLabelPaints, that.itemLabelPaints)) {
2809            return false;
2810        }
2811        if (!PaintUtils.equal(this.defaultItemLabelPaint,
2812                that.defaultItemLabelPaint)) {
2813            return false;
2814        }
2815
2816        if (!Objects.equals(this.positiveItemLabelPositionMap, that.positiveItemLabelPositionMap)) {
2817            return false;
2818        }
2819        if (!Objects.equals(this.defaultPositiveItemLabelPosition, that.defaultPositiveItemLabelPosition)) {
2820            return false;
2821        }
2822
2823        if (!Objects.equals(this.negativeItemLabelPositionMap, that.negativeItemLabelPositionMap)) {
2824            return false;
2825        }
2826        if (!Objects.equals(this.defaultNegativeItemLabelPosition, that.defaultNegativeItemLabelPosition)) {
2827            return false;
2828        }
2829        if (this.itemLabelAnchorOffset != that.itemLabelAnchorOffset) {
2830            return false;
2831        }
2832        if (!Objects.equals(this.seriesCreateEntitiesMap, that.seriesCreateEntitiesMap)) {
2833            return false;
2834        }
2835        if (this.defaultCreateEntities != that.defaultCreateEntities) {
2836            return false;
2837        }
2838        if (!ShapeUtils.equal(this.seriesLegendShapes, that.seriesLegendShapes)) {
2839            return false;
2840        }
2841        if (!ShapeUtils.equal(this.defaultLegendShape, that.defaultLegendShape)) {
2842            return false;
2843        }
2844        if (!Objects.equals(this.legendTextFontMap, that.legendTextFontMap)) {
2845            return false;
2846        }
2847        if (!Objects.equals(this.defaultLegendTextFont, that.defaultLegendTextFont)) {
2848            return false;
2849        }
2850        if (!PaintUtils.equal(this.legendTextPaints, that.legendTextPaints)) {
2851            return false;
2852        }
2853        if (!PaintUtils.equal(this.defaultLegendTextPaint,
2854                that.defaultLegendTextPaint)) {
2855            return false;
2856        }
2857        return true;
2858    }
2859
2860    /**
2861     * Returns a hashcode for the renderer.
2862     *
2863     * @return The hashcode.
2864     */
2865    @Override
2866    public int hashCode() {
2867        int result = 193;
2868        result = HashUtils.hashCode(result, this.seriesVisibleMap);
2869        result = HashUtils.hashCode(result, this.defaultSeriesVisible);
2870        result = HashUtils.hashCode(result, this.seriesVisibleInLegendMap);
2871        result = HashUtils.hashCode(result, this.defaultSeriesVisibleInLegend);
2872        result = HashUtils.hashCode(result, this.seriesPaintMap);
2873        result = HashUtils.hashCode(result, this.defaultPaint);
2874        result = HashUtils.hashCode(result, this.seriesFillPaintMap);
2875        result = HashUtils.hashCode(result, this.defaultFillPaint);
2876        result = HashUtils.hashCode(result, this.seriesOutlinePaintMap);
2877        result = HashUtils.hashCode(result, this.defaultOutlinePaint);
2878        result = HashUtils.hashCode(result, this.seriesStrokeMap);
2879        result = HashUtils.hashCode(result, this.defaultStroke);
2880        result = HashUtils.hashCode(result, this.seriesOutlineStrokeMap);
2881        result = HashUtils.hashCode(result, this.defaultOutlineStroke);
2882        // shapeList
2883        // baseShape
2884        result = HashUtils.hashCode(result, this.seriesItemLabelsVisibleMap);
2885        result = HashUtils.hashCode(result, this.defaultItemLabelsVisible);
2886        // itemLabelFontList
2887        // baseItemLabelFont
2888        // itemLabelPaintList
2889        // baseItemLabelPaint
2890        // positiveItemLabelPositionList
2891        // basePositiveItemLabelPosition
2892        // negativeItemLabelPositionList
2893        // baseNegativeItemLabelPosition
2894        // itemLabelAnchorOffset
2895        // createEntityList
2896        // baseCreateEntities
2897        return result;
2898    }
2899
2900    /**
2901     * Returns an independent copy of the renderer.
2902     *
2903     * @return A clone.
2904     *
2905     * @throws CloneNotSupportedException if some component of the renderer
2906     *         does not support cloning.
2907     */
2908    @Override
2909    protected Object clone() throws CloneNotSupportedException {
2910        AbstractRenderer clone = (AbstractRenderer) super.clone();
2911
2912        if (this.seriesVisibleMap != null) {
2913            clone.seriesVisibleMap = new HashMap<>(this.seriesVisibleMap);
2914        }
2915
2916        if (this.seriesVisibleInLegendMap != null) {
2917            clone.seriesVisibleInLegendMap = new HashMap<>(this.seriesVisibleInLegendMap);
2918        }
2919
2920        // 'paint' : immutable, no need to clone reference
2921        if (this.seriesPaintMap != null) {
2922            clone.seriesPaintMap = new HashMap<>(this.seriesPaintMap);
2923        }
2924        // 'basePaint' : immutable, no need to clone reference
2925
2926        if (this.seriesFillPaintMap != null) {
2927            clone.seriesFillPaintMap = new HashMap<>(this.seriesFillPaintMap);
2928        }
2929        // 'outlinePaint' : immutable, no need to clone reference
2930        if (this.seriesOutlinePaintMap != null) {
2931            clone.seriesOutlinePaintMap = new HashMap<>(this.seriesOutlinePaintMap);
2932        }
2933        // 'baseOutlinePaint' : immutable, no need to clone reference
2934
2935        // 'stroke' : immutable, no need to clone reference
2936        if (this.seriesStrokeMap != null) {
2937            clone.seriesStrokeMap = CloneUtils.cloneMapValues(this.seriesStrokeMap);
2938        }
2939        // 'baseStroke' : immutable, no need to clone reference
2940
2941        // 'outlineStroke' : immutable, no need to clone reference
2942        if (this.seriesOutlineStrokeMap != null) {
2943            clone.seriesOutlineStrokeMap = CloneUtils.cloneMapValues(this.seriesOutlineStrokeMap);
2944        }
2945        // 'baseOutlineStroke' : immutable, no need to clone reference
2946
2947        if (this.seriesShapeMap != null) {
2948            clone.seriesShapeMap = ShapeUtils.cloneMap(this.seriesShapeMap);
2949        }
2950        clone.defaultShape = CloneUtils.clone(this.defaultShape);
2951
2952
2953        // 'seriesItemLabelsVisibleMap' : immutable, no need to clone reference
2954        if (this.seriesItemLabelsVisibleMap != null) {
2955            clone.seriesItemLabelsVisibleMap = new HashMap<>(this.seriesItemLabelsVisibleMap);
2956        }
2957        // 'basePaint' : immutable, no need to clone reference
2958
2959        // 'itemLabelFont' : immutable, no need to clone reference
2960        if (this.itemLabelFontMap != null) {
2961            clone.itemLabelFontMap = new HashMap<>(this.itemLabelFontMap);
2962        }
2963        // 'baseItemLabelFont' : immutable, no need to clone reference
2964
2965        // 'itemLabelPaint' : immutable, no need to clone reference
2966        if (this.itemLabelPaints != null) {
2967            clone.itemLabelPaints  = new HashMap<>(this.itemLabelPaints);
2968        }
2969        // 'baseItemLabelPaint' : immutable, no need to clone reference
2970
2971        if (this.positiveItemLabelPositionMap != null) {
2972            clone.positiveItemLabelPositionMap 
2973                    = new HashMap<>(this.positiveItemLabelPositionMap);
2974        }
2975
2976        if (this.negativeItemLabelPositionMap != null) {
2977            clone.negativeItemLabelPositionMap 
2978                    = new HashMap<>(this.negativeItemLabelPositionMap);
2979        }
2980
2981        if (this.seriesCreateEntitiesMap != null) {
2982            clone.seriesCreateEntitiesMap = new HashMap<>(this.seriesCreateEntitiesMap);
2983        }
2984
2985        if (this.seriesLegendShapes != null) {
2986            clone.seriesLegendShapes = ShapeUtils.cloneMap(this.seriesLegendShapes);
2987        }
2988        if (this.legendTextFontMap != null) {
2989            // Font objects are immutable so just shallow copy the map
2990            clone.legendTextFontMap = new HashMap<>(this.legendTextFontMap);
2991        }
2992        if (this.legendTextPaints != null) {
2993            clone.legendTextPaints = new HashMap<>(this.legendTextPaints);
2994        }
2995        clone.listenerList = new EventListenerList();
2996        clone.event = null;
2997        return clone;
2998    }
2999
3000    /**
3001     * Provides serialization support.
3002     *
3003     * @param stream  the output stream.
3004     *
3005     * @throws IOException  if there is an I/O error.
3006     */
3007    private void writeObject(ObjectOutputStream stream) throws IOException {
3008        stream.defaultWriteObject();
3009        SerialUtils.writeMapOfPaint(this.seriesPaintMap, stream);
3010        SerialUtils.writePaint(this.defaultPaint, stream);
3011        SerialUtils.writeMapOfPaint(this.seriesFillPaintMap, stream);
3012        SerialUtils.writePaint(this.defaultFillPaint, stream);
3013        SerialUtils.writeMapOfPaint(this.seriesOutlinePaintMap, stream);
3014        SerialUtils.writePaint(this.defaultOutlinePaint, stream);
3015        SerialUtils.writeMapOfStroke(this.seriesStrokeMap, stream);
3016        SerialUtils.writeStroke(this.defaultStroke, stream);
3017        SerialUtils.writeMapOfStroke(this.seriesOutlineStrokeMap, stream);
3018        SerialUtils.writeStroke(this.defaultOutlineStroke, stream);
3019        SerialUtils.writeShape(this.defaultShape, stream);
3020        SerialUtils.writeMapOfPaint(this.itemLabelPaints, stream);        
3021        SerialUtils.writePaint(this.defaultItemLabelPaint, stream);
3022        SerialUtils.writeShape(this.defaultLegendShape, stream);
3023        SerialUtils.writeMapOfPaint(this.legendTextPaints, stream);        
3024        SerialUtils.writePaint(this.defaultLegendTextPaint, stream);
3025    }
3026
3027    /**
3028     * Provides serialization support.
3029     *
3030     * @param stream  the input stream.
3031     *
3032     * @throws IOException  if there is an I/O error.
3033     * @throws ClassNotFoundException  if there is a classpath problem.
3034     */
3035    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
3036        stream.defaultReadObject();
3037        this.seriesPaintMap = SerialUtils.readMapOfPaint(stream);
3038        this.defaultPaint = SerialUtils.readPaint(stream);
3039        this.seriesFillPaintMap = SerialUtils.readMapOfPaint(stream);
3040        this.defaultFillPaint = SerialUtils.readPaint(stream);
3041        this.seriesOutlinePaintMap = SerialUtils.readMapOfPaint(stream);
3042        this.defaultOutlinePaint = SerialUtils.readPaint(stream);
3043        this.seriesStrokeMap = SerialUtils.readMapOfStroke(stream);
3044        this.defaultStroke = SerialUtils.readStroke(stream);
3045        this.seriesOutlineStrokeMap = SerialUtils.readMapOfStroke(stream);
3046        this.defaultOutlineStroke = SerialUtils.readStroke(stream);
3047        this.defaultShape = SerialUtils.readShape(stream);
3048        this.itemLabelPaints = SerialUtils.readMapOfPaint(stream);
3049        this.defaultItemLabelPaint = SerialUtils.readPaint(stream);
3050        this.defaultLegendShape = SerialUtils.readShape(stream);
3051        this.legendTextPaints = SerialUtils.readMapOfPaint(stream);
3052        this.defaultLegendTextPaint = SerialUtils.readPaint(stream);
3053
3054        // listeners are not restored automatically, but storage must be
3055        // provided...
3056        this.listenerList = new EventListenerList();
3057    }
3058
3059}