| 1 130 131 package org.jfree.chart.plot; 132 133 import java.awt.AlphaComposite ; 134 import java.awt.BasicStroke ; 135 import java.awt.Color ; 136 import java.awt.Composite ; 137 import java.awt.Font ; 138 import java.awt.Graphics2D ; 139 import java.awt.Paint ; 140 import java.awt.Shape ; 141 import java.awt.Stroke ; 142 import java.awt.geom.Arc2D ; 143 import java.awt.geom.Line2D ; 144 import java.awt.geom.Point2D ; 145 import java.awt.geom.Rectangle2D ; 146 import java.io.IOException ; 147 import java.io.ObjectInputStream ; 148 import java.io.ObjectOutputStream ; 149 import java.io.Serializable ; 150 import java.util.Iterator ; 151 import java.util.List ; 152 import java.util.ResourceBundle ; 153 154 import org.jfree.chart.LegendItem; 155 import org.jfree.chart.LegendItemCollection; 156 import org.jfree.chart.entity.EntityCollection; 157 import org.jfree.chart.entity.PieSectionEntity; 158 import org.jfree.chart.event.PlotChangeEvent; 159 import org.jfree.chart.labels.PieSectionLabelGenerator; 160 import org.jfree.chart.labels.PieToolTipGenerator; 161 import org.jfree.chart.labels.StandardPieItemLabelGenerator; 162 import org.jfree.chart.labels.StandardPieSectionLabelGenerator; 163 import org.jfree.chart.urls.PieURLGenerator; 164 import org.jfree.data.DefaultKeyedValues; 165 import org.jfree.data.KeyedValues; 166 import org.jfree.data.general.DatasetChangeEvent; 167 import org.jfree.data.general.DatasetUtilities; 168 import org.jfree.data.general.PieDataset; 169 import org.jfree.io.SerialUtilities; 170 import org.jfree.text.G2TextMeasurer; 171 import org.jfree.text.TextBlock; 172 import org.jfree.text.TextBox; 173 import org.jfree.text.TextUtilities; 174 import org.jfree.ui.RectangleAnchor; 175 import org.jfree.ui.RectangleInsets; 176 import org.jfree.util.ObjectList; 177 import org.jfree.util.ObjectUtilities; 178 import org.jfree.util.PaintList; 179 import org.jfree.util.Rotation; 180 import org.jfree.util.ShapeUtilities; 181 import org.jfree.util.StrokeList; 182 183 199 public class PiePlot extends Plot implements Cloneable , Serializable { 200 201 202 private static final long serialVersionUID = -795612466005590431L; 203 204 205 public static final double DEFAULT_INTERIOR_GAP = 0.25; 206 207 208 public static final double MAX_INTERIOR_GAP = 0.40; 209 210 211 public static final double DEFAULT_START_ANGLE = 90.0; 212 213 214 public static final Font DEFAULT_LABEL_FONT 215 = new Font ("SansSerif", Font.PLAIN, 10); 216 217 218 public static final Paint DEFAULT_LABEL_PAINT = Color.black; 219 220 221 public static final Paint DEFAULT_LABEL_BACKGROUND_PAINT 222 = new Color (255, 255, 192); 223 224 225 public static final Paint DEFAULT_LABEL_OUTLINE_PAINT = Color.black; 226 227 228 public static final Stroke DEFAULT_LABEL_OUTLINE_STROKE 229 = new BasicStroke (0.5f); 230 231 232 public static final Paint DEFAULT_LABEL_SHADOW_PAINT = Color.lightGray; 233 234 235 public static final double DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW = 0.00001; 236 237 238 private PieDataset dataset; 239 240 241 private int pieIndex; 242 243 247 private double interiorGap; 248 249 250 private boolean circular; 251 252 253 private double startAngle; 254 255 256 private Rotation direction; 257 258 259 private transient Paint sectionPaint; 260 261 262 private PaintList sectionPaintList; 263 264 265 private transient Paint baseSectionPaint; 266 267 268 private transient Paint sectionOutlinePaint; 269 270 271 private PaintList sectionOutlinePaintList; 272 273 274 private transient Paint baseSectionOutlinePaint; 275 276 277 private transient Stroke sectionOutlineStroke; 278 279 280 private StrokeList sectionOutlineStrokeList; 281 282 283 private transient Stroke baseSectionOutlineStroke; 284 285 286 private transient Paint shadowPaint = Color.gray; 287 288 289 private double shadowXOffset = 4.0f; 290 291 292 private double shadowYOffset = 4.0f; 293 294 295 private ObjectList explodePercentages; 296 297 298 private PieSectionLabelGenerator labelGenerator; 299 300 301 private Font labelFont; 302 303 304 private transient Paint labelPaint; 305 306 307 private transient Paint labelBackgroundPaint; 308 309 313 private transient Paint labelOutlinePaint; 314 315 319 private transient Stroke labelOutlineStroke; 320 321 325 private transient Paint labelShadowPaint; 326 327 328 private double maximumLabelWidth = 0.20; 329 330 334 private double labelGap = 0.05; 335 336 337 private boolean labelLinksVisible; 338 339 340 private double labelLinkMargin = 0.05; 341 342 343 private transient Paint labelLinkPaint = Color.black; 344 345 346 private transient Stroke labelLinkStroke = new BasicStroke (0.5f); 347 348 349 private PieToolTipGenerator toolTipGenerator; 350 351 352 private PieURLGenerator urlGenerator; 353 354 355 private PieSectionLabelGenerator legendLabelGenerator; 356 357 358 private PieSectionLabelGenerator legendLabelToolTipGenerator; 359 360 363 private boolean ignoreNullValues; 364 365 368 private boolean ignoreZeroValues; 369 370 371 private transient Shape legendItemShape; 372 373 384 private double minimumArcAngleToDraw; 385 386 387 protected static ResourceBundle localizationResources = 388 ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle"); 389 390 393 public PiePlot() { 394 this(null); 395 } 396 397 402 public PiePlot(PieDataset dataset) { 403 super(); 404 this.dataset = dataset; 405 if (dataset != null) { 406 dataset.addChangeListener(this); 407 } 408 this.pieIndex = 0; 409 410 this.interiorGap = DEFAULT_INTERIOR_GAP; 411 this.circular = true; 412 this.startAngle = DEFAULT_START_ANGLE; 413 this.direction = Rotation.CLOCKWISE; 414 this.minimumArcAngleToDraw = DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW; 415 416 this.sectionPaint = null; 417 this.sectionPaintList = new PaintList(); 418 this.baseSectionPaint = null; 419 420 this.sectionOutlinePaint = null; 421 this.sectionOutlinePaintList = new PaintList(); 422 this.baseSectionOutlinePaint = DEFAULT_OUTLINE_PAINT; 423 424 this.sectionOutlineStroke = null; 425 this.sectionOutlineStrokeList = new StrokeList(); 426 this.baseSectionOutlineStroke = DEFAULT_OUTLINE_STROKE; 427 428 this.explodePercentages = new ObjectList(); 429 430 this.labelGenerator = new StandardPieItemLabelGenerator(); 431 this.labelFont = DEFAULT_LABEL_FONT; 432 this.labelPaint = DEFAULT_LABEL_PAINT; 433 this.labelBackgroundPaint = DEFAULT_LABEL_BACKGROUND_PAINT; 434 this.labelOutlinePaint = DEFAULT_LABEL_OUTLINE_PAINT; 435 this.labelOutlineStroke = DEFAULT_LABEL_OUTLINE_STROKE; 436 this.labelShadowPaint = DEFAULT_LABEL_SHADOW_PAINT; 437 this.labelLinksVisible = true; 438 439 this.toolTipGenerator = null; 440 this.urlGenerator = null; 441 this.legendLabelGenerator = new StandardPieSectionLabelGenerator(); 442 this.legendLabelToolTipGenerator = null; 443 this.legendItemShape = Plot.DEFAULT_LEGEND_ITEM_CIRCLE; 444 445 this.ignoreNullValues = false; 446 this.ignoreZeroValues = false; 447 } 448 449 454 public PieDataset getDataset() { 455 return this.dataset; 456 } 457 458 463 public void setDataset(PieDataset dataset) { 464 PieDataset existing = this.dataset; 467 if (existing != null) { 468 existing.removeChangeListener(this); 469 } 470 471 this.dataset = dataset; 473 if (dataset != null) { 474 setDatasetGroup(dataset.getGroup()); 475 dataset.addChangeListener(this); 476 } 477 478 DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); 480 datasetChanged(event); 481 } 482 483 489 public int getPieIndex() { 490 return this.pieIndex; 491 } 492 493 499 public void setPieIndex(int index) { 500 this.pieIndex = index; 501 } 502 503 509 public double getStartAngle() { 510 return this.startAngle; 511 } 512 513 521 public void setStartAngle(double angle) { 522 this.startAngle = angle; 523 notifyListeners(new PlotChangeEvent(this)); 524 } 525 526 532 public Rotation getDirection() { 533 return this.direction; 534 } 535 536 542 public void setDirection(Rotation direction) { 543 if (direction == null) { 544 throw new IllegalArgumentException ("Null 'direction' argument."); 545 } 546 this.direction = direction; 547 notifyListeners(new PlotChangeEvent(this)); 548 549 } 550 551 557 public double getInteriorGap() { 558 return this.interiorGap; 559 } 560 561 569 public void setInteriorGap(double percent) { 570 571 if ((percent < 0.0) || (percent > MAX_INTERIOR_GAP)) { 573 throw new IllegalArgumentException ( 574 "Invalid 'percent' (" + percent + ") argument."); 575 } 576 577 if (this.interiorGap != percent) { 579 this.interiorGap = percent; 580 notifyListeners(new PlotChangeEvent(this)); 581 } 582 583 } 584 585 591 public boolean isCircular() { 592 return this.circular; 593 } 594 595 601 public void setCircular(boolean flag) { 602 setCircular(flag, true); 603 } 604 605 612 public void setCircular(boolean circular, boolean notify) { 613 this.circular = circular; 614 if (notify) { 615 notifyListeners(new PlotChangeEvent(this)); 616 } 617 } 618 619 625 public boolean getIgnoreNullValues() { 626 return this.ignoreNullValues; 627 } 628 629 637 public void setIgnoreNullValues(boolean flag) { 638 this.ignoreNullValues = flag; 639 notifyListeners(new PlotChangeEvent(this)); 640 } 641 642 648 public boolean getIgnoreZeroValues() { 649 return this.ignoreZeroValues; 650 } 651 652 660 public void setIgnoreZeroValues(boolean flag) { 661 this.ignoreZeroValues = flag; 662 notifyListeners(new PlotChangeEvent(this)); 663 } 664 665 667 672 public Paint getSectionPaint() { 673 return this.sectionPaint; 674 } 675 676 683 public void setSectionPaint(Paint paint) { 684 this.sectionPaint = paint; 685 notifyListeners(new PlotChangeEvent(this)); 686 } 687 688 695 public Paint getSectionPaint(int section) { 696 697 if (this.sectionPaint != null) { 699 return this.sectionPaint; 700 } 701 702 Paint result = this.sectionPaintList.getPaint(section); 704 if (result == null) { 705 DrawingSupplier supplier = getDrawingSupplier(); 706 if (supplier != null) { 707 Paint p = supplier.getNextPaint(); 708 this.sectionPaintList.setPaint(section, p); 709 result = p; 710 } 711 else { 712 result = this.baseSectionPaint; 713 } 714 } 715 return result; 716 717 } 718 719 726 public void setSectionPaint(int section, Paint paint) { 727 this.sectionPaintList.setPaint(section, paint); 728 notifyListeners(new PlotChangeEvent(this)); 729 } 730 731 737 public Paint getBaseSectionPaint() { 738 return this.baseSectionPaint; 739 } 740 741 746 public void setBaseSectionPaint(Paint paint) { 747 if (paint == null) { 748 throw new IllegalArgumentException ("Null 'paint' argument."); 749 } 750 this.baseSectionPaint = paint; 751 notifyListeners(new PlotChangeEvent(this)); 752 } 753 754 756 761 public Paint getSectionOutlinePaint() { 762 return this.sectionOutlinePaint; 763 } 764 765 772 public void setSectionOutlinePaint(Paint paint) { 773 this.sectionOutlinePaint = paint; 774 notifyListeners(new PlotChangeEvent(this)); 775 } 776 777 784 public Paint getSectionOutlinePaint(int section) { 785 786 if (this.sectionOutlinePaint != null) { 788 return this.sectionOutlinePaint; 789 } 790 791 Paint result = this.sectionOutlinePaintList.getPaint(section); 793 if (result == null) { 794 result = this.baseSectionOutlinePaint; 795 } 796 return result; 797 798 } 799 800 807 public void setSectionOutlinePaint(int section, Paint paint) { 808 this.sectionOutlinePaintList.setPaint(section, paint); 809 notifyListeners(new PlotChangeEvent(this)); 810 } 811 812 818 public Paint getBaseSectionOutlinePaint() { 819 return this.baseSectionOutlinePaint; 820 } 821 822 827 public void setBaseSectionOutlinePaint(Paint paint) { 828 if (paint == null) { 829 throw new IllegalArgumentException ("Null 'paint' argument."); 830 } 831 this.baseSectionOutlinePaint = paint; 832 notifyListeners(new PlotChangeEvent(this)); 833 } 834 835 837 842 public Stroke getSectionOutlineStroke() { 843 return this.sectionOutlineStroke; 844 } 845 846 853 public void setSectionOutlineStroke(Stroke stroke) { 854 this.sectionOutlineStroke = stroke; 855 notifyListeners(new PlotChangeEvent(this)); 856 } 857 858 865 public Stroke getSectionOutlineStroke(int section) { 866 867 if (this.sectionOutlineStroke != null) { 869 return this.sectionOutlineStroke; 870 } 871 872 Stroke result = this.sectionOutlineStrokeList.getStroke(section); 874 if (result == null) { 875 result = this.baseSectionOutlineStroke; 876 } 877 return result; 878 879 } 880 881 888 public void setSectionOutlineStroke(int section, Stroke stroke) { 889 this.sectionOutlineStrokeList.setStroke(section, stroke); 890 notifyListeners(new PlotChangeEvent(this)); 891 } 892 893 899 public Stroke getBaseSectionOutlineStroke() { 900 return this.baseSectionOutlineStroke; 901 } 902 903 908 public void setBaseSectionOutlineStroke(Stroke stroke) { 909 if (stroke == null) { 910 throw new IllegalArgumentException ("Null 'stroke' argument."); 911 } 912 this.baseSectionOutlineStroke = stroke; 913 notifyListeners(new PlotChangeEvent(this)); 914 } 915 916 921 public Paint getShadowPaint() { 922 return this.shadowPaint; 923 } 924 925 931 public void setShadowPaint(Paint paint) { 932 this.shadowPaint = paint; 933 notifyListeners(new |