001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
006 *
007 * Project Info: http://www.jfree.org/jfreechart/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it
010 * under the terms of the GNU Lesser General Public License as published by
011 * the Free Software Foundation; either version 2.1 of the License, or
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022 * USA.
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025 * in the United States and other countries.]
026 *
027 * -------------------------
028 * LineAndShapeRenderer.java
029 * -------------------------
030 * (C) Copyright 2001-2006, by Object Refinery Limited and Contributors.
031 *
032 * Original Author: David Gilbert (for Object Refinery Limited);
033 * Contributor(s): Mark Watson (www.markwatson.com);
034 * Jeremy Bowman;
035 * Richard Atkinson;
036 * Christian W. Zuckschwerdt;
037 *
038 * $Id: LineAndShapeRenderer.java,v 1.18.2.7 2006/10/06 15:02:16 mungady Exp $
039 *
040 * Changes
041 * -------
042 * 23-Oct-2001 : Version 1 (DG);
043 * 15-Nov-2001 : Modified to allow for null data values (DG);
044 * 16-Jan-2002 : Renamed HorizontalCategoryItemRenderer.java
045 * --> CategoryItemRenderer.java (DG);
046 * 05-Feb-2002 : Changed return type of the drawCategoryItem method from void
047 * to Shape, as part of the tooltips implementation (DG);
048 * 11-May-2002 : Support for value label drawing (JB);
049 * 29-May-2002 : Now extends AbstractCategoryItemRenderer (DG);
050 * 25-Jun-2002 : Removed redundant import (DG);
051 * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs
052 * for HTML image maps (RA);
053 * 26-Sep-2002 : Fixed errors reported by Checkstyle (DG);
054 * 11-Oct-2002 : Added new constructor to incorporate tool tip and URL
055 * generators (DG);
056 * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and
057 * CategoryToolTipGenerator interface (DG);
058 * 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG);
059 * 06-Nov-2002 : Renamed drawCategoryItem() --> drawItem() and now using axis
060 * for category spacing (DG);
061 * 17-Jan-2003 : Moved plot classes to a separate package (DG);
062 * 10-Apr-2003 : Changed CategoryDataset to KeyedValues2DDataset in drawItem()
063 * method (DG);
064 * 12-May-2003 : Modified to take into account the plot orientation (DG);
065 * 29-Jul-2003 : Amended code that doesn't compile with JDK 1.2.2 (DG);
066 * 30-Jul-2003 : Modified entity constructor (CZ);
067 * 22-Sep-2003 : Fixed cloning (DG);
068 * 10-Feb-2004 : Small change to drawItem() method to make cut-and-paste
069 * override easier (DG);
070 * 16-Jun-2004 : Fixed bug (id=972454) with label positioning on horizontal
071 * charts (DG);
072 * 15-Oct-2004 : Updated equals() method (DG);
073 * 05-Nov-2004 : Modified drawItem() signature (DG);
074 * 11-Nov-2004 : Now uses ShapeUtilities class to translate shapes (DG);
075 * 27-Jan-2005 : Changed attribute names, modified constructor and removed
076 * constants (DG);
077 * 01-Feb-2005 : Removed unnecessary constants (DG);
078 * 15-Mar-2005 : Fixed bug 1163897, concerning outlines for shapes (DG);
079 * 13-Apr-2005 : Check flags that control series visibility (DG);
080 * 20-Apr-2005 : Use generators for legend labels, tooltips and URLs (DG);
081 * 09-Jun-2005 : Use addItemEntity() method (DG);
082 * ------------- JFREECHART 1.0.x ---------------------------------------------
083 * 25-May-2006 : Added check to drawItem() to detect when both the line and
084 * the shape are not visible (DG);
085 *
086 */
087
088 package org.jfree.chart.renderer.category;
089
090 import java.awt.Graphics2D;
091 import java.awt.Paint;
092 import java.awt.Shape;
093 import java.awt.Stroke;
094 import java.awt.geom.Line2D;
095 import java.awt.geom.Rectangle2D;
096 import java.io.Serializable;
097
098 import org.jfree.chart.LegendItem;
099 import org.jfree.chart.axis.CategoryAxis;
100 import org.jfree.chart.axis.ValueAxis;
101 import org.jfree.chart.entity.EntityCollection;
102 import org.jfree.chart.event.RendererChangeEvent;
103 import org.jfree.chart.plot.CategoryPlot;
104 import org.jfree.chart.plot.PlotOrientation;
105 import org.jfree.data.category.CategoryDataset;
106 import org.jfree.util.BooleanList;
107 import org.jfree.util.BooleanUtilities;
108 import org.jfree.util.ObjectUtilities;
109 import org.jfree.util.PublicCloneable;
110 import org.jfree.util.ShapeUtilities;
111
112 /**
113 * A renderer that draws shapes for each data item, and lines between data
114 * items (for use with the {@link CategoryPlot} class).
115 */
116 public class LineAndShapeRenderer extends AbstractCategoryItemRenderer
117 implements Cloneable, PublicCloneable,
118 Serializable {
119
120 /** For serialization. */
121 private static final long serialVersionUID = -197749519869226398L;
122
123 /** A flag that controls whether or not lines are visible for ALL series. */
124 private Boolean linesVisible;
125
126 /**
127 * A table of flags that control (per series) whether or not lines are
128 * visible.
129 */
130 private BooleanList seriesLinesVisible;
131
132 /**
133 * A flag indicating whether or not lines are drawn between non-null
134 * points.
135 */
136 private boolean baseLinesVisible;
137
138 /**
139 * A flag that controls whether or not shapes are visible for ALL series.
140 */
141 private Boolean shapesVisible;
142
143 /**
144 * A table of flags that control (per series) whether or not shapes are
145 * visible.
146 */
147 private BooleanList seriesShapesVisible;
148
149 /** The default value returned by the getShapeVisible() method. */
150 private boolean baseShapesVisible;
151
152 /** A flag that controls whether or not shapes are filled for ALL series. */
153 private Boolean shapesFilled;
154
155 /**
156 * A table of flags that control (per series) whether or not shapes are
157 * filled.
158 */
159 private BooleanList seriesShapesFilled;
160
161 /** The default value returned by the getShapeFilled() method. */
162 private boolean baseShapesFilled;
163
164 /**
165 * A flag that controls whether the fill paint is used for filling
166 * shapes.
167 */
168 private boolean useFillPaint;
169
170 /** A flag that controls whether outlines are drawn for shapes. */
171 private boolean drawOutlines;
172
173 /**
174 * A flag that controls whether the outline paint is used for drawing shape
175 * outlines - if not, the regular series paint is used.
176 */
177 private boolean useOutlinePaint;
178
179 /**
180 * Creates a renderer with both lines and shapes visible by default.
181 */
182 public LineAndShapeRenderer() {
183 this(true, true);
184 }
185
186 /**
187 * Creates a new renderer with lines and/or shapes visible.
188 *
189 * @param lines draw lines?
190 * @param shapes draw shapes?
191 */
192 public LineAndShapeRenderer(boolean lines, boolean shapes) {
193 super();
194 this.linesVisible = null;
195 this.seriesLinesVisible = new BooleanList();
196 this.baseLinesVisible = lines;
197 this.shapesVisible = null;
198 this.seriesShapesVisible = new BooleanList();
199 this.baseShapesVisible = shapes;
200 this.shapesFilled = null;
201 this.seriesShapesFilled = new BooleanList();
202 this.baseShapesFilled = true;
203 this.useFillPaint = false;
204 this.drawOutlines = true;
205 this.useOutlinePaint = false;
206 }
207
208 // LINES VISIBLE
209
210 /**
211 * Returns the flag used to control whether or not the line for an item is
212 * visible.
213 *
214 * @param series the series index (zero-based).
215 * @param item the item index (zero-based).
216 *
217 * @return A boolean.
218 */
219 public boolean getItemLineVisible(int series, int item) {
220 Boolean flag = this.linesVisible;
221 if (flag == null) {
222 flag = getSeriesLinesVisible(series);
223 }
224 if (flag != null) {
225 return flag.booleanValue();
226 }
227 else {
228 return this.baseLinesVisible;
229 }
230 }
231
232 /**
233 * Returns a flag that controls whether or not lines are drawn for ALL
234 * series. If this flag is <code>null</code>, then the "per series"
235 * settings will apply.
236 *
237 * @return A flag (possibly <code>null</code>).
238 */
239 public Boolean getLinesVisible() {
240 return this.linesVisible;
241 }
242
243 /**
244 * Sets a flag that controls whether or not lines are drawn between the
245 * items in ALL series, and sends a {@link RendererChangeEvent} to all
246 * registered listeners. You need to set this to <code>null</code> if you
247 * want the "per series" settings to apply.
248 *
249 * @param visible the flag (<code>null</code> permitted).
250 */
251 public void setLinesVisible(Boolean visible) {
252 this.linesVisible = visible;
253 notifyListeners(new RendererChangeEvent(this));
254 }
255
256 /**
257 * Sets a flag that controls whether or not lines are drawn between the
258 * items in ALL series, and sends a {@link RendererChangeEvent} to all
259 * registered listeners.
260 *
261 * @param visible the flag.
262 */
263 public void setLinesVisible(boolean visible) {
264 setLinesVisible(BooleanUtilities.valueOf(visible));
265 }
266
267 /**
268 * Returns the flag used to control whether or not the lines for a series
269 * are visible.
270 *
271 * @param series the series index (zero-based).
272 *
273 * @return The flag (possibly <code>null</code>).
274 */
275 public Boolean getSeriesLinesVisible(int series) {
276 return this.seriesLinesVisible.getBoolean(series);
277 }
278
279 /**
280 * Sets the 'lines visible' flag for a series.
281 *
282 * @param series the series index (zero-based).
283 * @param flag the flag (<code>null</code> permitted).
284 */
285 public void setSeriesLinesVisible(int series, Boolean flag) {
286 this.seriesLinesVisible.setBoolean(series, flag);
287 notifyListeners(new RendererChangeEvent(this));
288 }
289
290 /**
291 * Sets the 'lines visible' flag for a series.
292 *
293 * @param series the series index (zero-based).
294 * @param visible the flag.
295 */
296 public void setSeriesLinesVisible(int series, boolean visible) {
297 setSeriesLinesVisible(series, BooleanUtilities.valueOf(visible));
298 }
299
300 /**
301 * Returns the base 'lines visible' attribute.
302 *
303 * @return The base flag.
304 */
305 public boolean getBaseLinesVisible() {
306 return this.baseLinesVisible;
307 }
308
309 /**
310 * Sets the base 'lines visible' flag.
311 *
312 * @param flag the flag.
313 */
314 public void setBaseLinesVisible(boolean flag) {
315 this.baseLinesVisible = flag;
316 notifyListeners(new RendererChangeEvent(this));
317 }
318
319 // SHAPES VISIBLE
320
321 /**
322 * Returns the flag used to control whether or not the shape for an item is
323 * visible.
324 *
325 * @param series the series index (zero-based).
326 * @param item the item index (zero-based).
327 *
328 * @return A boolean.
329 */
330 public boolean getItemShapeVisible(int series, int item) {
331 Boolean flag = this.shapesVisible;
332 if (flag == null) {
333 flag = getSeriesShapesVisible(series);
334 }
335 if (flag != null) {
336 return flag.booleanValue();
337 }
338 else {
339 return this.baseShapesVisible;
340 }
341 }
342
343 /**
344 * Returns the flag that controls whether the shapes are visible for the
345 * items in ALL series.
346 *
347 * @return The flag (possibly <code>null</code>).
348 */
349 public Boolean getShapesVisible() {
350 return this.shapesVisible;
351 }
352
353 /**
354 * Sets the 'shapes visible' for ALL series and sends a
355 * {@link RendererChangeEvent} to all registered listeners.
356 *
357 * @param visible the flag (<code>null</code> permitted).
358 */
359 public void setShapesVisible(Boolean visible) {
360 this.shapesVisible = visible;
361 notifyListeners(new RendererChangeEvent(this));
362 }
363
364 /**
365 * Sets the 'shapes visible' for ALL series and sends a
366 * {@link RendererChangeEvent} to all registered listeners.
367 *
368 * @param visible the flag.
369 */
370 public void setShapesVisible(boolean visible) {
371 setShapesVisible(BooleanUtilities.valueOf(visible));
372 }
373
374 /**
375 * Returns the flag used to control whether or not the shapes for a series
376 * are visible.
377 *
378 * @param series the series index (zero-based).
379 *
380 * @return A boolean.
381 */
382 public Boolean getSeriesShapesVisible(int series) {
383 return this.seriesShapesVisible.getBoolean(series);
384 }
385
386 /**
387 * Sets the 'shapes visible' flag for a series and sends a
388 * {@link RendererChangeEvent} to all registered listeners.
389 *
390 * @param series the series index (zero-based).
391 * @param visible the flag.
392 */
393 public void setSeriesShapesVisible(int series, boolean visible) {
394 setSeriesShapesVisible(series, BooleanUtilities.valueOf(visible));
395 }
396
397 /**
398 * Sets the 'shapes visible' flag for a series and sends a
399 * {@link RendererChangeEvent} to all registered listeners.
400 *
401 * @param series the series index (zero-based).
402 * @param flag the flag.
403 */
404 public void setSeriesShapesVisible(int series, Boolean flag) {
405 this.seriesShapesVisible.setBoolean(series, flag);
406 notifyListeners(new RendererChangeEvent(this));
407 }
408
409 /**
410 * Returns the base 'shape visible' attribute.
411 *
412 * @return The base flag.
413 */
414 public boolean getBaseShapesVisible() {
415 return this.baseShapesVisible;
416 }
417
418 /**
419 * Sets the base 'shapes visible' flag.
420 *
421 * @param flag the flag.
422 */
423 public void setBaseShapesVisible(boolean flag) {
424 this.baseShapesVisible = flag;
425 notifyListeners(new RendererChangeEvent(this));
426 }
427
428 /**
429 * Returns <code>true</code> if outlines should be drawn for shapes, and
430 * <code>false</code> otherwise.
431 *
432 * @return A boolean.
433 */
434 public boolean getDrawOutlines() {
435 return this.drawOutlines;
436 }
437
438 /**
439 * Sets the flag that controls whether outlines are drawn for
440 * shapes, and sends a {@link RendererChangeEvent} to all registered
441 * listeners.
442 * <P>
443 * In some cases, shapes look better if they do NOT have an outline, but
444 * this flag allows you to set your own preference.
445 *
446 * @param flag the flag.
447 */
448 public void setDrawOutlines(boolean flag) {
449 this.drawOutlines = flag;
450 notifyListeners(new RendererChangeEvent(this));
451 }
452
453 /**
454 * Returns the flag that controls whether the outline paint is used for
455 * shape outlines. If not, the regular series paint is used.
456 *
457 * @return A boolean.
458 */
459 public boolean getUseOutlinePaint() {
460 return this.useOutlinePaint;
461 }
462
463 /**
464 * Sets the flag that controls whether the outline paint is used for shape
465 * outlines.
466 *
467 * @param use the flag.
468 */
469 public void setUseOutlinePaint(boolean use) {
470 this.useOutlinePaint = use;
471 }
472
473 // SHAPES FILLED
474
475 /**
476 * Returns the flag used to control whether or not the shape for an item
477 * is filled. The default implementation passes control to the
478 * <code>getSeriesShapesFilled</code> method. You can override this method
479 * if you require different behaviour.
480 *
481 * @param series the series index (zero-based).
482 * @param item the item index (zero-based).
483 *
484 * @return A boolean.
485 */
486 public boolean getItemShapeFilled(int series, int item) {
487 return getSeriesShapesFilled(series);
488 }
489
490 /**
491 * Returns the flag used to control whether or not the shapes for a series
492 * are filled.
493 *
494 * @param series the series index (zero-based).
495 *
496 * @return A boolean.
497 */
498 public boolean getSeriesShapesFilled(int series) {
499
500 // return the overall setting, if there is one...
501 if (this.shapesFilled != null) {
502 return this.shapesFilled.booleanValue();
503 }
504
505 // otherwise look up the paint table
506 Boolean flag = this.seriesShapesFilled.getBoolean(series);
507 if (flag != null) {
508 return flag.booleanValue();
509 }
510 else {
511 return this.baseShapesFilled;
512 }
513
514 }
515
516 /**
517 * Returns the flag that controls whether or not shapes are filled for
518 * ALL series.
519 *
520 * @return A Boolean.
521 */
522 public Boolean getShapesFilled() {
523 return this.shapesFilled;
524 }
525
526 /**
527 * Sets the 'shapes filled' for ALL series.
528 *
529 * @param filled the flag.
530 */
531 public void setShapesFilled(boolean filled) {
532 if (filled) {
533 setShapesFilled(Boolean.TRUE);
534 }
535 else {
536 setShapesFilled(Boolean.FALSE);
537 }
538 }
539
540 /**
541 * Sets the 'shapes filled' for ALL series.
542 *
543 * @param filled the flag (<code>null</code> permitted).
544 */
545 public void setShapesFilled(Boolean filled) {
546 this.shapesFilled = filled;
547 }
548
549 /**
550 * Sets the 'shapes filled' flag for a series.
551 *
552 * @param series the series index (zero-based).
553 * @param filled the flag.
554 */
555 public void setSeriesShapesFilled(int series, Boolean filled) {
556 this.seriesShapesFilled.setBoolean(series, filled);
557 }
558
559 /**
560 * Sets the 'shapes filled' flag for a series.
561 *
562 * @param series the series index (zero-based).
563 * @param filled the flag.
564 */
565 public void setSeriesShapesFilled(int series, boolean filled) {
566 this.seriesShapesFilled.setBoolean(
567 series, BooleanUtilities.valueOf(filled)
568 );
569 }
570
571 /**
572 * Returns the base 'shape filled' attribute.
573 *
574 * @return The base flag.
575 */
576 public boolean getBaseShapesFilled() {
577 return this.baseShapesFilled;
578 }
579
580 /**
581 * Sets the base 'shapes filled' flag.
582 *
583 * @param flag the flag.
584 */
585 public void setBaseShapesFilled(boolean flag) {
586 this.baseShapesFilled = flag;
587 }
588
589 /**
590 * Returns <code>true</code> if the renderer should use the fill paint
591 * setting to fill shapes, and <code>false</code> if it should just
592 * use the regular paint.
593 *
594 * @return A boolean.
595 */
596 public boolean getUseFillPaint() {
597 return this.useFillPaint;
598 }
599
600 /**
601 * Sets the flag that controls whether the fill paint is used to fill
602 * shapes, and sends a {@link RendererChangeEvent} to all
603 * registered listeners.
604 *
605 * @param flag the flag.
606 */
607 public void setUseFillPaint(boolean flag) {
608 this.useFillPaint = flag;
609 notifyListeners(new RendererChangeEvent(this));
610 }
611
612 /**
613 * Returns a legend item for a series.
614 *
615 * @param datasetIndex the dataset index (zero-based).
616 * @param series the series index (zero-based).
617 *
618 * @return The legend item.
619 */
620 public LegendItem getLegendItem(int datasetIndex, int series) {
621
622 CategoryPlot cp = getPlot();
623 if (cp == null) {
624 return null;
625 }
626
627 if (isSeriesVisible(series) && isSeriesVisibleInLegend(series)) {
628 CategoryDataset dataset;
629 dataset = cp.getDataset(datasetIndex);
630 String label = getLegendItemLabelGenerator().generateLabel(
631 dataset, series);
632 String description = label;
633 String toolTipText = null;
634 if (getLegendItemToolTipGenerator() != null) {
635 toolTipText = getLegendItemToolTipGenerator().generateLabel(
636 dataset, series);
637 }
638 String urlText = null;
639 if (getLegendItemURLGenerator() != null) {
640 urlText = getLegendItemURLGenerator().generateLabel(
641 dataset, series);
642 }
643 Shape shape = getSeriesShape(series);
644 Paint paint = getSeriesPaint(series);
645 Paint fillPaint = (this.useFillPaint
646 ? getItemFillPaint(series, 0) : paint);
647 boolean shapeOutlineVisible = this.drawOutlines;
648 Paint outlinePaint = (this.useOutlinePaint
649 ? getItemOutlinePaint(series, 0) : paint);
650 Stroke outlineStroke = getSeriesOutlineStroke(series);
651 boolean lineVisible = getItemLineVisible(series, 0);
652 boolean shapeVisible = getItemShapeVisible(series, 0);
653 return new LegendItem(label, description, toolTipText,
654 urlText, shapeVisible, shape, getItemShapeFilled(series, 0),
655 fillPaint, shapeOutlineVisible, outlinePaint, outlineStroke,
656 lineVisible, new Line2D.Double(-7.0, 0.0, 7.0, 0.0),
657 getItemStroke(series, 0), getItemPaint(series, 0));
658 }
659 return null;
660
661 }
662
663 /**
664 * This renderer uses two passes to draw the data.
665 *
666 * @return The pass count (<code>2</code> for this renderer).
667 */
668 public int getPassCount() {
669 return 2;
670 }
671
672 /**
673 * Draw a single data item.
674 *
675 * @param g2 the graphics device.
676 * @param state the renderer state.
677 * @param dataArea the area in which the data is drawn.
678 * @param plot the plot.
679 * @param domainAxis the domain axis.
680 * @param rangeAxis the range axis.
681 * @param dataset the dataset.
682 * @param row the row index (zero-based).
683 * @param column the column index (zero-based).
684 * @param pass the pass index.
685 */
686 public void drawItem(Graphics2D g2, CategoryItemRendererState state,
687 Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis,
688 ValueAxis rangeAxis, CategoryDataset dataset, int row, int column,
689 int pass) {
690
691 // do nothing if item is not visible
692 if (!getItemVisible(row, column)) {
693 return;
694 }
695
696 // do nothing if both the line and shape are not visible
697 if (!getItemLineVisible(row, column)
698 && !getItemShapeVisible(row, column)) {
699 return;
700 }
701
702 // nothing is drawn for null...
703 Number v = dataset.getValue(row, column);
704 if (v == null) {
705 return;
706 }
707
708 PlotOrientation orientation = plot.getOrientation();
709
710 // current data point...
711 double x1 = domainAxis.getCategoryMiddle(column, getColumnCount(),
712 dataArea, plot.getDomainAxisEdge());
713 double value = v.doubleValue();
714 double y1 = rangeAxis.valueToJava2D(value, dataArea,
715 plot.getRangeAxisEdge());
716
717 if (pass == 0 && getItemLineVisible(row, column)) {
718 if (column != 0) {
719 Number previousValue = dataset.getValue(row, column - 1);
720 if (previousValue != null) {
721 // previous data point...
722 double previous = previousValue.doubleValue();
723 double x0 = domainAxis.getCategoryMiddle(column - 1,
724 getColumnCount(), dataArea,
725 plot.getDomainAxisEdge());
726 double y0 = rangeAxis.valueToJava2D(previous, dataArea,
727 plot.getRangeAxisEdge());
728
729 Line2D line = null;
730 if (orientation == PlotOrientation.HORIZONTAL) {
731 line = new Line2D.Double(y0, x0, y1, x1);
732 }
733 else if (orientation == PlotOrientation.VERTICAL) {
734 line = new Line2D.Double(x0, y0, x1, y1);
735 }
736 g2.setPaint(getItemPaint(row, column));
737 g2.setStroke(getItemStroke(row, column));
738 g2.draw(line);
739 }
740 }
741 }
742
743 if (pass == 1) {
744 Shape shape = getItemShape(row, column);
745 if (orientation == PlotOrientation.HORIZONTAL) {
746 shape = ShapeUtilities.createTranslatedShape(shape, y1, x1);
747 }
748 else if (orientation == PlotOrientation.VERTICAL) {
749 shape = ShapeUtilities.createTranslatedShape(shape, x1, y1);
750 }
751
752 if (getItemShapeVisible(row, column)) {
753 if (getItemShapeFilled(row, column)) {
754 if (this.useFillPaint) {
755 g2.setPaint(getItemFillPaint(row, column));
756 }
757 else {
758 g2.setPaint(getItemPaint(row, column));
759 }
760 g2.fill(shape);
761 }
762 if (this.drawOutlines) {
763 if (this.useOutlinePaint) {
764 g2.setPaint(getItemOutlinePaint(row, column));
765 }
766 else {
767 g2.setPaint(getItemPaint(row, column));
768 }
769 g2.setStroke(getItemOutlineStroke(row, column));
770 g2.draw(shape);
771 }
772 }
773
774 // draw the item label if there is one...
775 if (isItemLabelVisible(row, column)) {
776 if (orientation == PlotOrientation.HORIZONTAL) {
777 drawItemLabel(g2, orientation, dataset, row, column, y1,
778 x1, (value < 0.0));
779 }
780 else if (orientation == PlotOrientation.VERTICAL) {
781 drawItemLabel(g2, orientation, dataset, row, column, x1,
782 y1, (value < 0.0));
783 }
784 }
785
786 // add an item entity, if this information is being collected
787 EntityCollection entities = state.getEntityCollection();
788 if (entities != null) {
789 addItemEntity(entities, dataset, row, column, shape);
790 }
791 }
792
793 }
794
795 /**
796 * Tests this renderer for equality with an arbitrary object.
797 *
798 * @param obj the object (<code>null</code> permitted).
799 *
800 * @return A boolean.
801 */
802 public boolean equals(Object obj) {
803
804 if (obj == this) {
805 return true;
806 }
807 if (!(obj instanceof LineAndShapeRenderer)) {
808 return false;
809 }
810
811 LineAndShapeRenderer that = (LineAndShapeRenderer) obj;
812 if (this.baseLinesVisible != that.baseLinesVisible) {
813 return false;
814 }
815 if (!ObjectUtilities.equal(this.seriesLinesVisible,
816 that.seriesLinesVisible)) {
817 return false;
818 }
819 if (!ObjectUtilities.equal(this.linesVisible, that.linesVisible)) {
820 return false;
821 }
822 if (this.baseShapesVisible != that.baseShapesVisible) {
823 return false;
824 }
825 if (!ObjectUtilities.equal(this.seriesShapesVisible,
826 that.seriesShapesVisible)) {
827 return false;
828 }
829 if (!ObjectUtilities.equal(this.shapesVisible, that.shapesVisible)) {
830 return false;
831 }
832 if (!ObjectUtilities.equal(this.shapesFilled, that.shapesFilled)) {
833 return false;
834 }
835 if (!ObjectUtilities.equal(this.seriesShapesFilled,
836 that.seriesShapesFilled)) {
837 return false;
838 }
839 if (this.baseShapesFilled != that.baseShapesFilled) {
840 return false;
841 }
842 if (this.useOutlinePaint != that.useOutlinePaint) {
843 return false;
844 }
845 if (!super.equals(obj)) {
846 return false;
847 }
848 return true;
849 }
850
851 /**
852 * Returns an independent copy of the renderer.
853 *
854 * @return A clone.
855 *
856 * @throws CloneNotSupportedException should not happen.
857 */
858 public Object clone() throws CloneNotSupportedException {
859 LineAndShapeRenderer clone = (LineAndShapeRenderer) super.clone();
860 clone.seriesLinesVisible
861 = (BooleanList) this.seriesLinesVisible.clone();
862 clone.seriesShapesVisible
863 = (BooleanList) this.seriesLinesVisible.clone();
864 clone.seriesShapesFilled
865 = (BooleanList) this.seriesShapesFilled.clone();
866 return clone;
867 }
868
869 }