001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2020, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ----------------------------- 028 * DefaultPolarItemRenderer.java 029 * ----------------------------- 030 * (C) Copyright 2004-2020, by Solution Engineering, Inc. and 031 * Contributors. 032 * 033 * Original Author: Daniel Bridenbecker, Solution Engineering, Inc.; 034 * Contributor(s): David Gilbert (for Object Refinery Limited); 035 * Martin Hoeller (patch 2850344); 036 * 037 */ 038 039package org.jfree.chart.renderer; 040 041import java.awt.AlphaComposite; 042import java.awt.Composite; 043import java.awt.Graphics2D; 044import java.awt.Paint; 045import java.awt.Point; 046import java.awt.Shape; 047import java.awt.Stroke; 048import java.awt.geom.Ellipse2D; 049import java.awt.geom.GeneralPath; 050import java.awt.geom.Line2D; 051import java.awt.geom.PathIterator; 052import java.awt.geom.Rectangle2D; 053import java.io.IOException; 054import java.io.ObjectInputStream; 055import java.io.ObjectOutputStream; 056import java.util.Iterator; 057import java.util.List; 058 059import org.jfree.chart.LegendItem; 060import org.jfree.chart.axis.NumberTick; 061import org.jfree.chart.axis.ValueAxis; 062import org.jfree.chart.entity.EntityCollection; 063import org.jfree.chart.entity.XYItemEntity; 064import org.jfree.chart.event.RendererChangeEvent; 065import org.jfree.chart.labels.XYSeriesLabelGenerator; 066import org.jfree.chart.labels.XYToolTipGenerator; 067import org.jfree.chart.plot.DrawingSupplier; 068import org.jfree.chart.plot.PlotOrientation; 069import org.jfree.chart.plot.PlotRenderingInfo; 070import org.jfree.chart.plot.PolarPlot; 071import org.jfree.chart.text.TextUtils; 072import org.jfree.chart.urls.XYURLGenerator; 073import org.jfree.chart.util.BooleanList; 074import org.jfree.chart.util.ObjectList; 075import org.jfree.chart.util.ObjectUtils; 076import org.jfree.chart.util.Args; 077import org.jfree.chart.util.PublicCloneable; 078import org.jfree.chart.util.SerialUtils; 079import org.jfree.chart.util.ShapeUtils; 080import org.jfree.data.xy.XYDataset; 081 082/** 083 * A renderer that can be used with the {@link PolarPlot} class. 084 */ 085public class DefaultPolarItemRenderer extends AbstractRenderer 086 implements PolarItemRenderer { 087 088 /** The plot that the renderer is assigned to. */ 089 private PolarPlot plot; 090 091 /** Flags that control whether the renderer fills each series or not. */ 092 private BooleanList seriesFilled; 093 094 /** 095 * Flag that controls whether an outline is drawn for filled series or 096 * not. 097 * 098 * @since 1.0.14 099 */ 100 private boolean drawOutlineWhenFilled; 101 102 /** 103 * The composite to use when filling series. 104 * 105 * @since 1.0.14 106 */ 107 private transient Composite fillComposite; 108 109 /** 110 * A flag that controls whether the fill paint is used for filling 111 * shapes. 112 * 113 * @since 1.0.14 114 */ 115 private boolean useFillPaint; 116 117 /** 118 * The shape that is used to represent a line in the legend. 119 * 120 * @since 1.0.14 121 */ 122 private transient Shape legendLine; 123 124 /** 125 * Flag that controls whether item shapes are visible or not. 126 * 127 * @since 1.0.14 128 */ 129 private boolean shapesVisible; 130 131 /** 132 * Flag that controls if the first and last point of the dataset should be 133 * connected or not. 134 * 135 * @since 1.0.14 136 */ 137 private boolean connectFirstAndLastPoint; 138 139 /** 140 * A list of tool tip generators (one per series). 141 * 142 * @since 1.0.14 143 */ 144 private ObjectList toolTipGeneratorList; 145 146 /** 147 * The base tool tip generator. 148 * 149 * @since 1.0.14 150 */ 151 private XYToolTipGenerator baseToolTipGenerator; 152 153 /** 154 * The URL text generator. 155 * 156 * @since 1.0.14 157 */ 158 private XYURLGenerator urlGenerator; 159 160 /** 161 * The legend item tool tip generator. 162 * 163 * @since 1.0.14 164 */ 165 private XYSeriesLabelGenerator legendItemToolTipGenerator; 166 167 /** 168 * The legend item URL generator. 169 * 170 * @since 1.0.14 171 */ 172 private XYSeriesLabelGenerator legendItemURLGenerator; 173 174 /** 175 * Creates a new instance of DefaultPolarItemRenderer 176 */ 177 public DefaultPolarItemRenderer() { 178 this.seriesFilled = new BooleanList(); 179 this.drawOutlineWhenFilled = true; 180 this.fillComposite = AlphaComposite.getInstance( 181 AlphaComposite.SRC_OVER, 0.3f); 182 this.useFillPaint = false; // use item paint for fills by default 183 this.legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0); 184 this.shapesVisible = true; 185 this.connectFirstAndLastPoint = true; 186 187 this.toolTipGeneratorList = new ObjectList(); 188 this.urlGenerator = null; 189 this.legendItemToolTipGenerator = null; 190 this.legendItemURLGenerator = null; 191 } 192 193 /** 194 * Set the plot associated with this renderer. 195 * 196 * @param plot the plot. 197 * 198 * @see #getPlot() 199 */ 200 @Override 201 public void setPlot(PolarPlot plot) { 202 this.plot = plot; 203 } 204 205 /** 206 * Return the plot associated with this renderer. 207 * 208 * @return The plot. 209 * 210 * @see #setPlot(PolarPlot) 211 */ 212 @Override 213 public PolarPlot getPlot() { 214 return this.plot; 215 } 216 217 /** 218 * Returns {@code true} if the renderer will draw an outline around 219 * a filled polygon, {@code false} otherwise. 220 * 221 * @return A boolean. 222 * 223 * @since 1.0.14 224 */ 225 public boolean getDrawOutlineWhenFilled() { 226 return this.drawOutlineWhenFilled; 227 } 228 229 /** 230 * Set the flag that controls whether the outline around a filled 231 * polygon will be drawn or not and sends a {@link RendererChangeEvent} 232 * to all registered listeners. 233 * 234 * @param drawOutlineWhenFilled the flag. 235 * 236 * @since 1.0.14 237 */ 238 public void setDrawOutlineWhenFilled(boolean drawOutlineWhenFilled) { 239 this.drawOutlineWhenFilled = drawOutlineWhenFilled; 240 fireChangeEvent(); 241 } 242 243 /** 244 * Get the composite that is used for filling. 245 * 246 * @return The composite (never {@code null}). 247 * 248 * @since 1.0.14 249 */ 250 public Composite getFillComposite() { 251 return this.fillComposite; 252 } 253 254 /** 255 * Sets the composite which will be used for filling polygons and sends a 256 * {@link RendererChangeEvent} to all registered listeners. 257 * 258 * @param composite the composite to use ({@code null} not 259 * permitted). 260 * 261 * @since 1.0.14 262 */ 263 public void setFillComposite(Composite composite) { 264 Args.nullNotPermitted(composite, "composite"); 265 this.fillComposite = composite; 266 fireChangeEvent(); 267 } 268 269 /** 270 * Returns {@code true} if a shape will be drawn for every item, or 271 * {@code false} if not. 272 * 273 * @return A boolean. 274 * 275 * @since 1.0.14 276 */ 277 public boolean getShapesVisible() { 278 return this.shapesVisible; 279 } 280 281 /** 282 * Set the flag that controls whether a shape will be drawn for every 283 * item, or not and sends a {@link RendererChangeEvent} to all registered 284 * listeners. 285 * 286 * @param visible the flag. 287 * 288 * @since 1.0.14 289 */ 290 public void setShapesVisible(boolean visible) { 291 this.shapesVisible = visible; 292 fireChangeEvent(); 293 } 294 295 /** 296 * Returns {@code true} if first and last point of a series will be 297 * connected, {@code false} otherwise. 298 * 299 * @return The current status of the flag. 300 * 301 * @since 1.0.14 302 */ 303 public boolean getConnectFirstAndLastPoint() { 304 return this.connectFirstAndLastPoint; 305 } 306 307 /** 308 * Set the flag that controls whether the first and last point of a series 309 * will be connected or not and sends a {@link RendererChangeEvent} to all 310 * registered listeners. 311 * 312 * @param connect the flag. 313 * 314 * @since 1.0.14 315 */ 316 public void setConnectFirstAndLastPoint(boolean connect) { 317 this.connectFirstAndLastPoint = connect; 318 fireChangeEvent(); 319 } 320 321 /** 322 * Returns the drawing supplier from the plot. 323 * 324 * @return The drawing supplier. 325 */ 326 @Override 327 public DrawingSupplier getDrawingSupplier() { 328 DrawingSupplier result = null; 329 PolarPlot p = getPlot(); 330 if (p != null) { 331 result = p.getDrawingSupplier(); 332 } 333 return result; 334 } 335 336 /** 337 * Returns {@code true} if the renderer should fill the specified 338 * series, and {@code false} otherwise. 339 * 340 * @param series the series index (zero-based). 341 * 342 * @return A boolean. 343 */ 344 public boolean isSeriesFilled(int series) { 345 boolean result = false; 346 Boolean b = this.seriesFilled.getBoolean(series); 347 if (b != null) { 348 result = b; 349 } 350 return result; 351 } 352 353 /** 354 * Sets a flag that controls whether or not a series is filled. 355 * 356 * @param series the series index. 357 * @param filled the flag. 358 */ 359 public void setSeriesFilled(int series, boolean filled) { 360 this.seriesFilled.setBoolean(series, filled); 361 } 362 363 /** 364 * Returns {@code true} if the renderer should use the fill paint 365 * setting to fill shapes, and {@code false} if it should just 366 * use the regular paint. 367 * 368 * @return A boolean. 369 * 370 * @see #setUseFillPaint(boolean) 371 * @since 1.0.14 372 */ 373 public boolean getUseFillPaint() { 374 return this.useFillPaint; 375 } 376 377 /** 378 * Sets the flag that controls whether the fill paint is used to fill 379 * shapes, and sends a {@link RendererChangeEvent} to all 380 * registered listeners. 381 * 382 * @param flag the flag. 383 * 384 * @see #getUseFillPaint() 385 * @since 1.0.14 386 */ 387 public void setUseFillPaint(boolean flag) { 388 this.useFillPaint = flag; 389 fireChangeEvent(); 390 } 391 392 /** 393 * Returns the shape used to represent a line in the legend. 394 * 395 * @return The legend line (never {@code null}). 396 * 397 * @see #setLegendLine(Shape) 398 */ 399 public Shape getLegendLine() { 400 return this.legendLine; 401 } 402 403 /** 404 * Sets the shape used as a line in each legend item and sends a 405 * {@link RendererChangeEvent} to all registered listeners. 406 * 407 * @param line the line ({@code null} not permitted). 408 * 409 * @see #getLegendLine() 410 */ 411 public void setLegendLine(Shape line) { 412 Args.nullNotPermitted(line, "line"); 413 this.legendLine = line; 414 fireChangeEvent(); 415 } 416 417 /** 418 * Adds an entity to the collection. 419 * 420 * @param entities the entity collection being populated. 421 * @param area the entity area (if {@code null} a default will be 422 * used). 423 * @param dataset the dataset. 424 * @param series the series. 425 * @param item the item. 426 * @param entityX the entity's center x-coordinate in user space (only 427 * used if {@code area} is {@code null}). 428 * @param entityY the entity's center y-coordinate in user space (only 429 * used if {@code area} is {@code null}). 430 */ 431 protected void addEntity(EntityCollection entities, Shape area, 432 XYDataset dataset, int series, int item, 433 double entityX, double entityY) { 434 if (!getItemCreateEntity(series, item)) { 435 return; 436 } 437 Shape hotspot = area; 438 if (hotspot == null) { 439 double r = getDefaultEntityRadius(); 440 double w = r * 2; 441 if (getPlot().getOrientation() == PlotOrientation.VERTICAL) { 442 hotspot = new Ellipse2D.Double(entityX - r, entityY - r, w, w); 443 } 444 else { 445 hotspot = new Ellipse2D.Double(entityY - r, entityX - r, w, w); 446 } 447 } 448 String tip = null; 449 XYToolTipGenerator generator = getToolTipGenerator(series, item); 450 if (generator != null) { 451 tip = generator.generateToolTip(dataset, series, item); 452 } 453 String url = null; 454 if (getURLGenerator() != null) { 455 url = getURLGenerator().generateURL(dataset, series, item); 456 } 457 XYItemEntity entity = new XYItemEntity(hotspot, dataset, series, item, 458 tip, url); 459 entities.add(entity); 460 } 461 462 /** 463 * Plots the data for a given series. 464 * 465 * @param g2 the drawing surface. 466 * @param dataArea the data area. 467 * @param info collects plot rendering info. 468 * @param plot the plot. 469 * @param dataset the dataset. 470 * @param seriesIndex the series index. 471 */ 472 @Override 473 public void drawSeries(Graphics2D g2, Rectangle2D dataArea, 474 PlotRenderingInfo info, PolarPlot plot, XYDataset dataset, 475 int seriesIndex) { 476 477 final int numPoints = dataset.getItemCount(seriesIndex); 478 if (numPoints == 0) { 479 return; 480 } 481 GeneralPath poly = null; 482 ValueAxis axis = plot.getAxisForDataset(plot.indexOf(dataset)); 483 for (int i = 0; i < numPoints; i++) { 484 double theta = dataset.getXValue(seriesIndex, i); 485 double radius = dataset.getYValue(seriesIndex, i); 486 Point p = plot.translateToJava2D(theta, radius, axis, dataArea); 487 if (poly == null) { 488 poly = new GeneralPath(); 489 poly.moveTo(p.x, p.y); 490 } 491 else { 492 poly.lineTo(p.x, p.y); 493 } 494 } 495 assert poly != null; 496 if (getConnectFirstAndLastPoint()) { 497 poly.closePath(); 498 } 499 500 g2.setPaint(lookupSeriesPaint(seriesIndex)); 501 g2.setStroke(lookupSeriesStroke(seriesIndex)); 502 if (isSeriesFilled(seriesIndex)) { 503 Composite savedComposite = g2.getComposite(); 504 g2.setComposite(this.fillComposite); 505 g2.fill(poly); 506 g2.setComposite(savedComposite); 507 if (this.drawOutlineWhenFilled) { 508 // draw the outline of the filled polygon 509 g2.setPaint(lookupSeriesOutlinePaint(seriesIndex)); 510 g2.draw(poly); 511 } 512 } 513 else { 514 // just the lines, no filling 515 g2.draw(poly); 516 } 517 518 // draw the item shapes 519 if (this.shapesVisible) { 520 // setup for collecting optional entity info... 521 EntityCollection entities = null; 522 if (info != null) { 523 entities = info.getOwner().getEntityCollection(); 524 } 525 526 PathIterator pi = poly.getPathIterator(null); 527 int i = 0; 528 while (!pi.isDone()) { 529 final float[] coords = new float[6]; 530 final int segType = pi.currentSegment(coords); 531 pi.next(); 532 if (segType != PathIterator.SEG_LINETO && 533 segType != PathIterator.SEG_MOVETO) { 534 continue; 535 } 536 final int x = Math.round(coords[0]); 537 final int y = Math.round(coords[1]); 538 final Shape shape = ShapeUtils.createTranslatedShape( 539 getItemShape(seriesIndex, i++), x, y); 540 541 Paint paint; 542 if (useFillPaint) { 543 paint = lookupSeriesFillPaint(seriesIndex); 544 } 545 else { 546 paint = lookupSeriesPaint(seriesIndex); 547 } 548 g2.setPaint(paint); 549 g2.fill(shape); 550 if (isSeriesFilled(seriesIndex) && this.drawOutlineWhenFilled) { 551 g2.setPaint(lookupSeriesOutlinePaint(seriesIndex)); 552 g2.setStroke(lookupSeriesOutlineStroke(seriesIndex)); 553 g2.draw(shape); 554 } 555 556 // add an entity for the item, but only if it falls within the 557 // data area... 558 if (entities != null && ShapeUtils.isPointInRect(dataArea, x, 559 y)) { 560 addEntity(entities, shape, dataset, seriesIndex, i-1, x, y); 561 } 562 } 563 } 564 } 565 566 /** 567 * Draw the angular gridlines - the spokes. 568 * 569 * @param g2 the drawing surface. 570 * @param plot the plot ({@code null} not permitted). 571 * @param ticks the ticks ({@code null} not permitted). 572 * @param dataArea the data area. 573 */ 574 @Override 575 public void drawAngularGridLines(Graphics2D g2, PolarPlot plot, 576 List ticks, Rectangle2D dataArea) { 577 578 g2.setFont(plot.getAngleLabelFont()); 579 g2.setStroke(plot.getAngleGridlineStroke()); 580 g2.setPaint(plot.getAngleGridlinePaint()); 581 582 ValueAxis axis = plot.getAxis(); 583 double centerValue, outerValue; 584 if (axis.isInverted()) { 585 outerValue = axis.getLowerBound(); 586 centerValue = axis.getUpperBound(); 587 } else { 588 outerValue = axis.getUpperBound(); 589 centerValue = axis.getLowerBound(); 590 } 591 Point center = plot.translateToJava2D(0, centerValue, axis, dataArea); 592 Iterator iterator = ticks.iterator(); 593 while (iterator.hasNext()) { 594 NumberTick tick = (NumberTick) iterator.next(); 595 double tickVal = tick.getNumber().doubleValue(); 596 Point p = plot.translateToJava2D(tickVal, outerValue, axis, 597 dataArea); 598 g2.setPaint(plot.getAngleGridlinePaint()); 599 g2.drawLine(center.x, center.y, p.x, p.y); 600 if (plot.isAngleLabelsVisible()) { 601 int x = p.x; 602 int y = p.y; 603 g2.setPaint(plot.getAngleLabelPaint()); 604 TextUtils.drawAlignedString(tick.getText(), g2, x, y, 605 tick.getTextAnchor()); 606 } 607 } 608 } 609 610 /** 611 * Draw the radial gridlines - the rings. 612 * 613 * @param g2 the drawing surface ({@code null} not permitted). 614 * @param plot the plot ({@code null} not permitted). 615 * @param radialAxis the radial axis ({@code null} not permitted). 616 * @param ticks the ticks ({@code null} not permitted). 617 * @param dataArea the data area. 618 */ 619 @Override 620 public void drawRadialGridLines(Graphics2D g2, PolarPlot plot, 621 ValueAxis radialAxis, List ticks, Rectangle2D dataArea) { 622 623 Args.nullNotPermitted(radialAxis, "radialAxis"); 624 g2.setFont(radialAxis.getTickLabelFont()); 625 g2.setPaint(plot.getRadiusGridlinePaint()); 626 g2.setStroke(plot.getRadiusGridlineStroke()); 627 628 double centerValue; 629 if (radialAxis.isInverted()) { 630 centerValue = radialAxis.getUpperBound(); 631 } else { 632 centerValue = radialAxis.getLowerBound(); 633 } 634 Point center = plot.translateToJava2D(0, centerValue, radialAxis, dataArea); 635 636 Iterator iterator = ticks.iterator(); 637 while (iterator.hasNext()) { 638 NumberTick tick = (NumberTick) iterator.next(); 639 double angleDegrees = plot.isCounterClockwise() 640 ? plot.getAngleOffset() : -plot.getAngleOffset(); 641 Point p = plot.translateToJava2D(angleDegrees, 642 tick.getNumber().doubleValue(), radialAxis, dataArea); 643 int r = p.x - center.x; 644 int upperLeftX = center.x - r; 645 int upperLeftY = center.y - r; 646 int d = 2 * r; 647 Ellipse2D ring = new Ellipse2D.Double(upperLeftX, upperLeftY, d, d); 648 g2.setPaint(plot.getRadiusGridlinePaint()); 649 g2.draw(ring); 650 } 651 } 652 653 /** 654 * Return the legend for the given series. 655 * 656 * @param series the series index. 657 * 658 * @return The legend item. 659 */ 660 @Override 661 public LegendItem getLegendItem(int series) { 662 LegendItem result; 663 PolarPlot p = getPlot(); 664 if (p == null) { 665 return null; 666 } 667 XYDataset dataset = p.getDataset(p.getIndexOf(this)); 668 if (dataset == null) { 669 return null; 670 } 671 672 String toolTipText = null; 673 if (getLegendItemToolTipGenerator() != null) { 674 toolTipText = getLegendItemToolTipGenerator().generateLabel( 675 dataset, series); 676 } 677 String urlText = null; 678 if (getLegendItemURLGenerator() != null) { 679 urlText = getLegendItemURLGenerator().generateLabel(dataset, 680 series); 681 } 682 683 Comparable seriesKey = dataset.getSeriesKey(series); 684 String label = seriesKey.toString(); 685 String description = label; 686 Shape shape = lookupSeriesShape(series); 687 Paint paint; 688 if (this.useFillPaint) { 689 paint = lookupSeriesFillPaint(series); 690 } 691 else { 692 paint = lookupSeriesPaint(series); 693 } 694 Stroke stroke = lookupSeriesStroke(series); 695 Paint outlinePaint = lookupSeriesOutlinePaint(series); 696 Stroke outlineStroke = lookupSeriesOutlineStroke(series); 697 boolean shapeOutlined = isSeriesFilled(series) 698 && this.drawOutlineWhenFilled; 699 result = new LegendItem(label, description, toolTipText, urlText, 700 getShapesVisible(), shape, /* shapeFilled=*/ true, paint, 701 shapeOutlined, outlinePaint, outlineStroke, 702 /* lineVisible= */ true, this.legendLine, stroke, paint); 703 result.setToolTipText(toolTipText); 704 result.setURLText(urlText); 705 result.setDataset(dataset); 706 result.setSeriesKey(seriesKey); 707 result.setSeriesIndex(series); 708 709 return result; 710 } 711 712 /** 713 * Returns the tooltip generator for the specified series and item. 714 * 715 * @param series the series index. 716 * @param item the item index. 717 * 718 * @return The tooltip generator (possibly {@code null}). 719 * 720 * @since 1.0.14 721 */ 722 @Override 723 public XYToolTipGenerator getToolTipGenerator(int series, int item) { 724 XYToolTipGenerator generator 725 = (XYToolTipGenerator) this.toolTipGeneratorList.get(series); 726 if (generator == null) { 727 generator = this.baseToolTipGenerator; 728 } 729 return generator; 730 } 731 732 /** 733 * Returns the tool tip generator for the specified series. 734 * 735 * @return The tooltip generator (possibly {@code null}). 736 * 737 * @since 1.0.14 738 */ 739 @Override 740 public XYToolTipGenerator getSeriesToolTipGenerator(int series) { 741 return (XYToolTipGenerator) this.toolTipGeneratorList.get(series); 742 } 743 744 /** 745 * Sets the tooltip generator for the specified series. 746 * 747 * @param series the series index. 748 * @param generator the tool tip generator ({@code null} permitted). 749 * 750 * @since 1.0.14 751 */ 752 @Override 753 public void setSeriesToolTipGenerator(int series, 754 XYToolTipGenerator generator) { 755 this.toolTipGeneratorList.set(series, generator); 756 fireChangeEvent(); 757 } 758 759 /** 760 * Returns the default tool tip generator. 761 * 762 * @return The default tool tip generator (possibly {@code null}). 763 * 764 * @since 1.0.14 765 */ 766 @Override 767 public XYToolTipGenerator getBaseToolTipGenerator() { 768 return this.baseToolTipGenerator; 769 } 770 771 /** 772 * Sets the default tool tip generator and sends a 773 * {@link RendererChangeEvent} to all registered listeners. 774 * 775 * @param generator the generator ({@code null} permitted). 776 * 777 * @since 1.0.14 778 */ 779 @Override 780 public void setBaseToolTipGenerator(XYToolTipGenerator generator) { 781 this.baseToolTipGenerator = generator; 782 fireChangeEvent(); 783 } 784 785 /** 786 * Returns the URL generator. 787 * 788 * @return The URL generator (possibly {@code null}). 789 * 790 * @since 1.0.14 791 */ 792 @Override 793 public XYURLGenerator getURLGenerator() { 794 return this.urlGenerator; 795 } 796 797 /** 798 * Sets the URL generator. 799 * 800 * @param urlGenerator the generator ({@code null} permitted) 801 * 802 * @since 1.0.14 803 */ 804 @Override 805 public void setURLGenerator(XYURLGenerator urlGenerator) { 806 this.urlGenerator = urlGenerator; 807 fireChangeEvent(); 808 } 809 810 /** 811 * Returns the legend item tool tip generator. 812 * 813 * @return The tool tip generator (possibly {@code null}). 814 * 815 * @see #setLegendItemToolTipGenerator(XYSeriesLabelGenerator) 816 * @since 1.0.14 817 */ 818 public XYSeriesLabelGenerator getLegendItemToolTipGenerator() { 819 return this.legendItemToolTipGenerator; 820 } 821 822 /** 823 * Sets the legend item tool tip generator and sends a 824 * {@link RendererChangeEvent} to all registered listeners. 825 * 826 * @param generator the generator ({@code null} permitted). 827 * 828 * @see #getLegendItemToolTipGenerator() 829 * @since 1.0.14 830 */ 831 public void setLegendItemToolTipGenerator( 832 XYSeriesLabelGenerator generator) { 833 this.legendItemToolTipGenerator = generator; 834 fireChangeEvent(); 835 } 836 837 /** 838 * Returns the legend item URL generator. 839 * 840 * @return The URL generator (possibly {@code null}). 841 * 842 * @see #setLegendItemURLGenerator(XYSeriesLabelGenerator) 843 * @since 1.0.14 844 */ 845 public XYSeriesLabelGenerator getLegendItemURLGenerator() { 846 return this.legendItemURLGenerator; 847 } 848 849 /** 850 * Sets the legend item URL generator and sends a 851 * {@link RendererChangeEvent} to all registered listeners. 852 * 853 * @param generator the generator ({@code null} permitted). 854 * 855 * @see #getLegendItemURLGenerator() 856 * @since 1.0.14 857 */ 858 public void setLegendItemURLGenerator(XYSeriesLabelGenerator generator) { 859 this.legendItemURLGenerator = generator; 860 fireChangeEvent(); 861 } 862 863 /** 864 * Tests this renderer for equality with an arbitrary object. 865 * 866 * @param obj the object ({@code null} not permitted). 867 * 868 * @return {@code true} if this renderer is equal to {@code obj}, 869 * and {@code false} otherwise. 870 */ 871 @Override 872 public boolean equals(Object obj) { 873 if (obj == null) { 874 return false; 875 } 876 if (!(obj instanceof DefaultPolarItemRenderer)) { 877 return false; 878 } 879 DefaultPolarItemRenderer that = (DefaultPolarItemRenderer) obj; 880 if (!this.seriesFilled.equals(that.seriesFilled)) { 881 return false; 882 } 883 if (this.drawOutlineWhenFilled != that.drawOutlineWhenFilled) { 884 return false; 885 } 886 if (!ObjectUtils.equal(this.fillComposite, that.fillComposite)) { 887 return false; 888 } 889 if (this.useFillPaint != that.useFillPaint) { 890 return false; 891 } 892 if (!ShapeUtils.equal(this.legendLine, that.legendLine)) { 893 return false; 894 } 895 if (this.shapesVisible != that.shapesVisible) { 896 return false; 897 } 898 if (this.connectFirstAndLastPoint != that.connectFirstAndLastPoint) { 899 return false; 900 } 901 if (!this.toolTipGeneratorList.equals(that.toolTipGeneratorList)) { 902 return false; 903 } 904 if (!ObjectUtils.equal(this.baseToolTipGenerator, 905 that.baseToolTipGenerator)) { 906 return false; 907 } 908 if (!ObjectUtils.equal(this.urlGenerator, that.urlGenerator)) { 909 return false; 910 } 911 if (!ObjectUtils.equal(this.legendItemToolTipGenerator, 912 that.legendItemToolTipGenerator)) { 913 return false; 914 } 915 if (!ObjectUtils.equal(this.legendItemURLGenerator, 916 that.legendItemURLGenerator)) { 917 return false; 918 } 919 return super.equals(obj); 920 } 921 922 /** 923 * Returns a clone of the renderer. 924 * 925 * @return A clone. 926 * 927 * @throws CloneNotSupportedException if the renderer cannot be cloned. 928 */ 929 @Override 930 public Object clone() throws CloneNotSupportedException { 931 DefaultPolarItemRenderer clone 932 = (DefaultPolarItemRenderer) super.clone(); 933 if (this.legendLine != null) { 934 clone.legendLine = ShapeUtils.clone(this.legendLine); 935 } 936 clone.seriesFilled = (BooleanList) this.seriesFilled.clone(); 937 clone.toolTipGeneratorList 938 = (ObjectList) this.toolTipGeneratorList.clone(); 939 if (clone.baseToolTipGenerator instanceof PublicCloneable) { 940 clone.baseToolTipGenerator = (XYToolTipGenerator) 941 ObjectUtils.clone(this.baseToolTipGenerator); 942 } 943 if (clone.urlGenerator instanceof PublicCloneable) { 944 clone.urlGenerator = (XYURLGenerator) 945 ObjectUtils.clone(this.urlGenerator); 946 } 947 if (clone.legendItemToolTipGenerator instanceof PublicCloneable) { 948 clone.legendItemToolTipGenerator = (XYSeriesLabelGenerator) 949 ObjectUtils.clone(this.legendItemToolTipGenerator); 950 } 951 if (clone.legendItemURLGenerator instanceof PublicCloneable) { 952 clone.legendItemURLGenerator = (XYSeriesLabelGenerator) 953 ObjectUtils.clone(this.legendItemURLGenerator); 954 } 955 return clone; 956 } 957 958 /** 959 * Provides serialization support. 960 * 961 * @param stream the input stream. 962 * 963 * @throws IOException if there is an I/O error. 964 * @throws ClassNotFoundException if there is a classpath problem. 965 */ 966 private void readObject(ObjectInputStream stream) 967 throws IOException, ClassNotFoundException { 968 stream.defaultReadObject(); 969 this.legendLine = SerialUtils.readShape(stream); 970 this.fillComposite = SerialUtils.readComposite(stream); 971 } 972 973 /** 974 * Provides serialization support. 975 * 976 * @param stream the output stream. 977 * 978 * @throws IOException if there is an I/O error. 979 */ 980 private void writeObject(ObjectOutputStream stream) throws IOException { 981 stream.defaultWriteObject(); 982 SerialUtils.writeShape(this.legendLine, stream); 983 SerialUtils.writeComposite(this.fillComposite, stream); 984 } 985}