001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2007, 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-2007, 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.10 2007/05/18 10:28:27 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 * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG);
086 * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG);
087 * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
088 *
089 */
090
091 package org.jfree.chart.renderer.category;
092
093 import java.awt.Graphics2D;
094 import java.awt.Paint;
095 import java.awt.Shape;
096 import java.awt.Stroke;
097 import java.awt.geom.Line2D;
098 import java.awt.geom.Rectangle2D;
099 import java.io.Serializable;
100
101 import org.jfree.chart.LegendItem;
102 import org.jfree.chart.axis.CategoryAxis;
103 import org.jfree.chart.axis.ValueAxis;
104 import org.jfree.chart.entity.EntityCollection;
105 import org.jfree.chart.event.RendererChangeEvent;
106 import org.jfree.chart.plot.CategoryPlot;
107 import org.jfree.chart.plot.PlotOrientation;
108 import org.jfree.data.category.CategoryDataset;
109 import org.jfree.util.BooleanList;
110 import org.jfree.util.BooleanUtilities;
111 import org.jfree.util.ObjectUtilities;
112 import org.jfree.util.PublicCloneable;
113 import org.jfree.util.ShapeUtilities;
114
115 /**
116 * A renderer that draws shapes for each data item, and lines between data
117 * items (for use with the {@link CategoryPlot} class).
118 */
119 public class LineAndShapeRenderer extends AbstractCategoryItemRenderer
120 implements Cloneable, PublicCloneable,
121 Serializable {
122
123 /** For serialization. */
124 private static final long serialVersionUID = -197749519869226398L;
125
126 /** A flag that controls whether or not lines are visible for ALL series. */
127 private Boolean linesVisible;
128
129 /**
130 * A table of flags that control (per series) whether or not lines are
131 * visible.
132 */
133 private BooleanList seriesLinesVisible;
134
135 /**
136 * A flag indicating whether or not lines are drawn between non-null
137 * points.
138 */
139 private boolean baseLinesVisible;
140
141 /**
142 * A flag that controls whether or not shapes are visible for ALL series.
143 */
144 private Boolean shapesVisible;
145
146 /**
147 * A table of flags that control (per series) whether or not shapes are
148 * visible.
149 */
150 private BooleanList seriesShapesVisible;
151
152 /** The default value returned by the getShapeVisible() method. */
153 private boolean baseShapesVisible;
154
155 /** A flag that controls whether or not shapes are filled for ALL series. */
156 private Boolean shapesFilled;
157
158 /**
159 * A table of flags that control (per series) whether or not shapes are
160 * filled.
161 */
162 private BooleanList seriesShapesFilled;
163
164 /** The default value returned by the getShapeFilled() method. */
165 private boolean baseShapesFilled;
166
167 /**
168 * A flag that controls whether the fill paint is used for filling
169 * shapes.
170 */
171 private boolean useFillPaint;
172
173 /** A flag that controls whether outlines are drawn for shapes. */
174 private boolean drawOutlines;
175
176 /**
177 * A flag that controls whether the outline paint is used for drawing shape
178 * outlines - if not, the regular series paint is used.
179 */
180 private boolean useOutlinePaint;
181
182 /**
183 * Creates a renderer with both lines and shapes visible by default.
184 */
185 public LineAndShapeRenderer() {
186 this(true, true);
187 }
188
189 /**
190 * Creates a new renderer with lines and/or shapes visible.
191 *
192 * @param lines draw lines?
193 * @param shapes draw shapes?
194 */
195 public LineAndShapeRenderer(boolean lines, boolean shapes) {
196 super();
197 this.linesVisible = null;
198 this.seriesLinesVisible = new BooleanList();
199 this.baseLinesVisible = lines;
200 this.shapesVisible = null;
201 this.seriesShapesVisible = new BooleanList();
202 this.baseShapesVisible = shapes;
203 this.shapesFilled = null;
204 this.seriesShapesFilled = new BooleanList();
205 this.baseShapesFilled = true;
206 this.useFillPaint = false;
207 this.drawOutlines = true;
208 this.useOutlinePaint = false;
209 }
210
211 // LINES VISIBLE
212
213 /**
214 * Returns the flag used to control whether or not the line for an item is
215 * visible.
216 *
217 * @param series the series index (zero-based).
218 * @param item the item index (zero-based).
219 *
220 * @return A boolean.
221 */
222 public boolean getItemLineVisible(int series, int item) {
223 Boolean flag = this.linesVisible;
224 if (flag == null) {
225 flag = getSeriesLinesVisible(series);
226 }
227 if (flag != null) {
228 return flag.booleanValue();
229 }
230 else {
231 return this.baseLinesVisible;
232 }
233 }
234
235 /**
236 * Returns a flag that controls whether or not lines are drawn for ALL
237 * series. If this flag is <code>null</code>, then the "per series"
238 * settings will apply.
239 *
240 * @return A flag (possibly <code>null</code>).
241 */
242 public Boolean getLinesVisible() {
243 return this.linesVisible;
244 }
245
246 /**
247 * Sets a flag that controls whether or not lines are drawn between the
248 * items in ALL series, and sends a {@link RendererChangeEvent} to all
249 * registered listeners. You need to set this to <code>null</code> if you
250 * want the "per series" settings to apply.
251 *
252 * @param visible the flag (<code>null</code> permitted).
253 */
254 public void setLinesVisible(Boolean visible) {
255 this.linesVisible = visible;
256 notifyListeners(new RendererChangeEvent(this));
257 }
258
259 /**
260 * Sets a flag that controls whether or not lines are drawn between the
261 * items in ALL series, and sends a {@link RendererChangeEvent} to all
262 * registered listeners.
263 *
264 * @param visible the flag.
265 */
266 public void setLinesVisible(boolean visible) {
267 setLinesVisible(BooleanUtilities.valueOf(visible));
268 }
269
270 /**
271 * Returns the flag used to control whether or not the lines for a series
272 * are visible.
273 *
274 * @param series the series index (zero-based).
275 *
276 * @return The flag (possibly <code>null</code>).
277 */
278 public Boolean getSeriesLinesVisible(int series) {
279 return this.seriesLinesVisible.getBoolean(series);
280 }
281
282 /**
283 * Sets the 'lines visible' flag for a series.
284 *
285 * @param series the series index (zero-based).
286 * @param flag the flag (<code>null</code> permitted).
287 */
288 public void setSeriesLinesVisible(int series, Boolean flag) {
289 this.seriesLinesVisible.setBoolean(series, flag);
290 notifyListeners(new RendererChangeEvent(this));
291 }
292
293 /**
294 * Sets the 'lines visible' flag for a series.
295 *
296 * @param series the series index (zero-based).
297 * @param visible the flag.
298 */
299 public void setSeriesLinesVisible(int series, boolean visible) {
300 setSeriesLinesVisible(series, BooleanUtilities.valueOf(visible));
301 }
302
303 /**
304 * Returns the base 'lines visible' attribute.
305 *
306 * @return The base flag.
307 */
308 public boolean getBaseLinesVisible() {
309 return this.baseLinesVisible;
310 }
311
312 /**
313 * Sets the base 'lines visible' flag.
314 *
315 * @param flag the flag.
316 */
317 public void setBaseLinesVisible(boolean flag) {
318 this.baseLinesVisible = flag;
319 notifyListeners(new RendererChangeEvent(this));
320 }
321
322 // SHAPES VISIBLE
323
324 /**
325 * Returns the flag used to control whether or not the shape for an item is
326 * visible.
327 *
328 * @param series the series index (zero-based).
329 * @param item the item index (zero-based).
330 *
331 * @return A boolean.
332 */
333 public boolean getItemShapeVisible(int series, int item) {
334 Boolean flag = this.shapesVisible;
335 if (flag == null) {
336 flag = getSeriesShapesVisible(series);
337 }
338 if (flag != null) {
339 return flag.booleanValue();
340 }
341 else {
342 return this.baseShapesVisible;
343 }
344 }
345
346 /**
347 * Returns the flag that controls whether the shapes are visible for the
348 * items in ALL series.
349 *
350 * @return The flag (possibly <code>null</code>).
351 */
352 public Boolean getShapesVisible() {
353 return this.shapesVisible;
354 }
355
356 /**
357 * Sets the 'shapes visible' for ALL series and sends a
358 * {@link RendererChangeEvent} to all registered listeners.
359 *
360 * @param visible the flag (<code>null</code> permitted).
361 */
362 public void setShapesVisible(Boolean visible) {
363 this.shapesVisible = visible;
364 notifyListeners(new RendererChangeEvent(this));
365 }
366
367 /**
368 * Sets the 'shapes visible' for ALL series and sends a
369 * {@link RendererChangeEvent} to all registered listeners.
370 *
371 * @param visible the flag.
372 */
373 public void setShapesVisible(boolean visible) {
374 setShapesVisible(BooleanUtilities.valueOf(visible));
375 }
376
377 /**
378 * Returns the flag used to control whether or not the shapes for a series
379 * are visible.
380 *
381 * @param series the series index (zero-based).
382 *
383 * @return A boolean.
384 */
385 public Boolean getSeriesShapesVisible(int series) {
386 return this.seriesShapesVisible.getBoolean(series);
387 }
388
389 /**
390 * Sets the 'shapes visible' flag for a series and sends a
391 * {@link RendererChangeEvent} to all registered listeners.
392 *
393 * @param series the series index (zero-based).
394 * @param visible the flag.
395 */
396 public void setSeriesShapesVisible(int series, boolean visible) {
397 setSeriesShapesVisible(series, BooleanUtilities.valueOf(visible));
398 }
399
400 /**
401 * Sets the 'shapes visible' flag for a series and sends a
402 * {@link RendererChangeEvent} to all registered listeners.
403 *
404 * @param series the series index (zero-based).
405 * @param flag the flag.
406 */
407 public void setSeriesShapesVisible(int series, Boolean flag) {
408 this.seriesShapesVisible.setBoolean(series, flag);
409 notifyListeners(new RendererChangeEvent(this));
410 }
411
412 /**
413 * Returns the base 'shape visible' attribute.
414 *
415 * @return The base flag.
416 */
417 public boolean getBaseShapesVisible() {
418 return this.baseShapesVisible;
419 }
420
421 /**
422 * Sets the base 'shapes visible' flag.
423 *
424 * @param flag the flag.
425 */
426 public void setBaseShapesVisible(boolean flag) {
427 this.baseShapesVisible = flag;
428 notifyListeners(new RendererChangeEvent(this));
429 }
430
431 /**
432 * Returns <code>true</code> if outlines should be drawn for shapes, and
433 * <code>false</code> otherwise.
434 *
435 * @return A boolean.
436 */
437 public boolean getDrawOutlines() {
438 return this.drawOutlines;
439 }
440
441 /**
442 * Sets the flag that controls whether outlines are drawn for
443 * shapes, and sends a {@link RendererChangeEvent} to all registered
444 * listeners.
445 * <P>
446 * In some cases, shapes look better if they do NOT have an outline, but
447 * this flag allows you to set your own preference.
448 *
449 * @param flag the flag.
450 */
451 public void setDrawOutlines(boolean flag) {
452 this.drawOutlines = flag;
453 notifyListeners(new RendererChangeEvent(this));
454 }
455
456 /**
457 * Returns the flag that controls whether the outline paint is used for
458 * shape outlines. If not, the regular series paint is used.
459 *
460 * @return A boolean.
461 */
462 public boolean getUseOutlinePaint() {
463 return this.useOutlinePaint;
464 }
465
466 /**
467 * Sets the flag that controls whether the outline paint is used for shape
468 * outlines.
469 *
470 * @param use the flag.
471 */
472 public void setUseOutlinePaint(boolean use) {
473 this.useOutlinePaint = use;
474 }
475
476 // SHAPES FILLED
477
478 /**
479 * Returns the flag used to control whether or not the shape for an item
480 * is filled. The default implementation passes control to the
481 * <code>getSeriesShapesFilled</code> method. You can override this method
482 * if you require different behaviour.
483 *
484 * @param series the series index (zero-based).
485 * @param item the item index (zero-based).
486 *
487 * @return A boolean.
488 */
489 public boolean getItemShapeFilled(int series, int item) {
490 return getSeriesShapesFilled(series);
491 }
492
493 /**
494 * Returns the flag used to control whether or not the shapes for a series
495 * are filled.
496 *
497 * @param series the series index (zero-based).
498 *
499 * @return A boolean.
500 */
501 public boolean getSeriesShapesFilled(int series) {
502
503 // return the overall setting, if there is one...
504 if (this.shapesFilled != null) {
505 return this.shapesFilled.booleanValue();
506 }
507
508 // otherwise look up the paint table
509 Boolean flag = this.seriesShapesFilled.getBoolean(series);
510 if (flag != null) {
511 return flag.booleanValue();
512 }
513 else {
514 return this.baseShapesFilled;
515 }
516
517 }
518
519 /**
520 * Returns the flag that controls whether or not shapes are filled for
521 * ALL series.
522 *
523 * @return A Boolean.
524 */
525 public Boolean getShapesFilled() {
526 return this.shapesFilled;
527 }
528
529 /**
530 * Sets the 'shapes filled' for ALL series.
531 *
532 * @param filled the flag.
533 */
534 public void setShapesFilled(boolean filled) {
535 if (filled) {
536 setShapesFilled(Boolean.TRUE);
537 }
538 else {
539 setShapesFilled(Boolean.FALSE);
540 }
541 }
542
543 /**
544 * Sets the 'shapes filled' for ALL series.
545 *
546 * @param filled the flag (<code>null</code> permitted).
547 */
548 public void setShapesFilled(Boolean filled) {
549 this.shapesFilled = filled;
550 }
551
552 /**
553 * Sets the 'shapes filled' flag for a series.
554 *
555 * @param series the series index (zero-based).
556 * @param filled the flag.
557 */
558 public void setSeriesShapesFilled(int series, Boolean filled) {
559 this.seriesShapesFilled.setBoolean(series, filled);
560 }
561
562 /**
563 * Sets the 'shapes filled' flag for a series.
564 *
565 * @param series the series index (zero-based).
566 * @param filled the flag.
567 */
568 public void setSeriesShapesFilled(int series, boolean filled) {
569 this.seriesShapesFilled.setBoolean(
570 series, BooleanUtilities.valueOf(filled)
571 );
572 }
573
574 /**
575 * Returns the base 'shape filled' attribute.
576 *
577 * @return The base flag.
578 */
579 public boolean getBaseShapesFilled() {
580 return this.baseShapesFilled;
581 }
582
583 /**
584 * Sets the base 'shapes filled' flag.
585 *
586 * @param flag the flag.
587 */
588 public void setBaseShapesFilled(boolean flag) {
589 this.baseShapesFilled = flag;
590 }
591
592 /**
593 * Returns <code>true</code> if the renderer should use the fill paint
594 * setting to fill shapes, and <code>false</code> if it should just
595 * use the regular paint.
596 *
597 * @return A boolean.
598 */
599 public boolean getUseFillPaint() {
600 return this.useFillPaint;
601 }
602
603 /**
604 * Sets the flag that controls whether the fill paint is used to fill
605 * shapes, and sends a {@link RendererChangeEvent} to all
606 * registered listeners.
607 *
608 * @param flag the flag.
609 */
610 public void setUseFillPaint(boolean flag) {
611 this.useFillPaint = flag;
612 notifyListeners(new RendererChangeEvent(this));
613 }
614
615 /**
616 * Returns a legend item for a series.
617 *
618 * @param datasetIndex the dataset index (zero-based).
619 * @param series the series index (zero-based).
620 *
621 * @return The legend item.
622 */
623 public LegendItem getLegendItem(int datasetIndex, int series) {
624
625 CategoryPlot cp = getPlot();
626 if (cp == null) {
627 return null;
628 }
629
630 if (isSeriesVisible(series) && isSeriesVisibleInLegend(series)) {
631 CategoryDataset dataset = cp.getDataset(datasetIndex);
632 String label = getLegendItemLabelGenerator().generateLabel(
633 dataset, series);
634 String description = label;
635 String toolTipText = null;
636 if (getLegendItemToolTipGenerator() != null) {
637 toolTipText = getLegendItemToolTipGenerator().generateLabel(
638 dataset, series);
639 }
640 String urlText = null;
641 if (getLegendItemURLGenerator() != null) {
642 urlText = getLegendItemURLGenerator().generateLabel(
643 dataset, series);
644 }
645 Shape shape = lookupSeriesShape(series);
646 Paint paint = lookupSeriesPaint(series);
647 Paint fillPaint = (this.useFillPaint
648 ? getItemFillPaint(series, 0) : paint);
649 boolean shapeOutlineVisible = this.drawOutlines;
650 Paint outlinePaint = (this.useOutlinePaint
651 ? getItemOutlinePaint(series, 0) : paint);
652 Stroke outlineStroke = lookupSeriesOutlineStroke(series);
653 boolean lineVisible = getItemLineVisible(series, 0);
654 boolean shapeVisible = getItemShapeVisible(series, 0);
655 LegendItem result = new LegendItem(label, description, toolTipText,
656 urlText, shapeVisible, shape, getItemShapeFilled(series, 0),
657 fillPaint, shapeOutlineVisible, outlinePaint, outlineStroke,
658 lineVisible, new Line2D.Double(-7.0, 0.0, 7.0, 0.0),
659 getItemStroke(series, 0), getItemPaint(series, 0));
660 result.setDataset(dataset);
661 result.setDatasetIndex(datasetIndex);
662 result.setSeriesKey(dataset.getRowKey(series));
663 result.setSeriesIndex(series);
664 return result;
665 }
666 return null;
667
668 }
669
670 /**
671 * This renderer uses two passes to draw the data.
672 *
673 * @return The pass count (<code>2</code> for this renderer).
674 */
675 public int getPassCount() {
676 return 2;
677 }
678
679 /**
680 * Draw a single data item.
681 *
682 * @param g2 the graphics device.
683 * @param state the renderer state.
684 * @param dataArea the area in which the data is drawn.
685 * @param plot the plot.
686 * @param domainAxis the domain axis.
687 * @param rangeAxis the range axis.
688 * @param dataset the dataset.
689 * @param row the row index (zero-based).
690 * @param column the column index (zero-based).
691 * @param pass the pass index.
692 */
693 public void drawItem(Graphics2D g2, CategoryItemRendererState state,
694 Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis,
695 ValueAxis rangeAxis, CategoryDataset dataset, int row, int column,
696 int pass) {
697
698 // do nothing if item is not visible
699 if (!getItemVisible(row, column)) {
700 return;
701 }
702
703 // do nothing if both the line and shape are not visible
704 if (!getItemLineVisible(row, column)
705 && !getItemShapeVisible(row, column)) {
706 return;
707 }
708
709 // nothing is drawn for null...
710 Number v = dataset.getValue(row, column);
711 if (v == null) {
712 return;
713 }
714
715 PlotOrientation orientation = plot.getOrientation();
716
717 // current data point...
718 double x1 = domainAxis.getCategoryMiddle(column, getColumnCount(),
719 dataArea, plot.getDomainAxisEdge());
720 double value = v.doubleValue();
721 double y1 = rangeAxis.valueToJava2D(value, dataArea,
722 plot.getRangeAxisEdge());
723
724 if (pass == 0 && getItemLineVisible(row, column)) {
725 if (column != 0) {
726 Number previousValue = dataset.getValue(row, column - 1);
727 if (previousValue != null) {
728 // previous data point...
729 double previous = previousValue.doubleValue();
730 double x0 = domainAxis.getCategoryMiddle(column - 1,
731 getColumnCount(), dataArea,
732 plot.getDomainAxisEdge());
733 double y0 = rangeAxis.valueToJava2D(previous, dataArea,
734 plot.getRangeAxisEdge());
735
736 Line2D line = null;
737 if (orientation == PlotOrientation.HORIZONTAL) {
738 line = new Line2D.Double(y0, x0, y1, x1);
739 }
740 else if (orientation == PlotOrientation.VERTICAL) {
741 line = new Line2D.Double(x0, y0, x1, y1);
742 }
743 g2.setPaint(getItemPaint(row, column));
744 g2.setStroke(getItemStroke(row, column));
745 g2.draw(line);
746 }
747 }
748 }
749
750 if (pass == 1) {
751 Shape shape = getItemShape(row, column);
752 if (orientation == PlotOrientation.HORIZONTAL) {
753 shape = ShapeUtilities.createTranslatedShape(shape, y1, x1);
754 }
755 else if (orientation == PlotOrientation.VERTICAL) {
756 shape = ShapeUtilities.createTranslatedShape(shape, x1, y1);
757 }
758
759 if (getItemShapeVisible(row, column)) {
760 if (getItemShapeFilled(row, column)) {
761 if (this.useFillPaint) {
762 g2.setPaint(getItemFillPaint(row, column));
763 }
764 else {
765 g2.setPaint(getItemPaint(row, column));
766 }
767 g2.fill(shape);
768 }
769 if (this.drawOutlines) {
770 if (this.useOutlinePaint) {
771 g2.setPaint(getItemOutlinePaint(row, column));
772 }
773 else {
774 g2.setPaint(getItemPaint(row, column));
775 }
776 g2.setStroke(getItemOutlineStroke(row, column));
777 g2.draw(shape);
778 }
779 }
780
781 // draw the item label if there is one...
782 if (isItemLabelVisible(row, column)) {
783 if (orientation == PlotOrientation.HORIZONTAL) {
784 drawItemLabel(g2, orientation, dataset, row, column, y1,
785 x1, (value < 0.0));
786 }
787 else if (orientation == PlotOrientation.VERTICAL) {
788 drawItemLabel(g2, orientation, dataset, row, column, x1,
789 y1, (value < 0.0));
790 }
791 }
792
793 // add an item entity, if this information is being collected
794 EntityCollection entities = state.getEntityCollection();
795 if (entities != null) {
796 addItemEntity(entities, dataset, row, column, shape);
797 }
798 }
799
800 }
801
802 /**
803 * Tests this renderer for equality with an arbitrary object.
804 *
805 * @param obj the object (<code>null</code> permitted).
806 *
807 * @return A boolean.
808 */
809 public boolean equals(Object obj) {
810
811 if (obj == this) {
812 return true;
813 }
814 if (!(obj instanceof LineAndShapeRenderer)) {
815 return false;
816 }
817
818 LineAndShapeRenderer that = (LineAndShapeRenderer) obj;
819 if (this.baseLinesVisible != that.baseLinesVisible) {
820 return false;
821 }
822 if (!ObjectUtilities.equal(this.seriesLinesVisible,
823 that.seriesLinesVisible)) {
824 return false;
825 }
826 if (!ObjectUtilities.equal(this.linesVisible, that.linesVisible)) {
827 return false;
828 }
829 if (this.baseShapesVisible != that.baseShapesVisible) {
830 return false;
831 }
832 if (!ObjectUtilities.equal(this.seriesShapesVisible,
833 that.seriesShapesVisible)) {
834 return false;
835 }
836 if (!ObjectUtilities.equal(this.shapesVisible, that.shapesVisible)) {
837 return false;
838 }
839 if (!ObjectUtilities.equal(this.shapesFilled, that.shapesFilled)) {
840 return false;
841 }
842 if (!ObjectUtilities.equal(this.seriesShapesFilled,
843 that.seriesShapesFilled)) {
844 return false;
845 }
846 if (this.baseShapesFilled != that.baseShapesFilled) {
847 return false;
848 }
849 if (this.useOutlinePaint != that.useOutlinePaint) {
850 return false;
851 }
852 if (!super.equals(obj)) {
853 return false;
854 }
855 return true;
856 }
857
858 /**
859 * Returns an independent copy of the renderer.
860 *
861 * @return A clone.
862 *
863 * @throws CloneNotSupportedException should not happen.
864 */
865 public Object clone() throws CloneNotSupportedException {
866 LineAndShapeRenderer clone = (LineAndShapeRenderer) super.clone();
867 clone.seriesLinesVisible
868 = (BooleanList) this.seriesLinesVisible.clone();
869 clone.seriesShapesVisible
870 = (BooleanList) this.seriesLinesVisible.clone();
871 clone.seriesShapesFilled
872 = (BooleanList) this.seriesShapesFilled.clone();
873 return clone;
874 }
875
876 }