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