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