| 1 129 130 package org.jfree.chart.plot; 131 132 import java.awt.AlphaComposite ; 133 import java.awt.BasicStroke ; 134 import java.awt.Color ; 135 import java.awt.Composite ; 136 import java.awt.Font ; 137 import java.awt.Graphics2D ; 138 import java.awt.Paint ; 139 import java.awt.Shape ; 140 import java.awt.Stroke ; 141 import java.awt.geom.Line2D ; 142 import java.awt.geom.Point2D ; 143 import java.awt.geom.Rectangle2D ; 144 import java.io.IOException ; 145 import java.io.ObjectInputStream ; 146 import java.io.ObjectOutputStream ; 147 import java.io.Serializable ; 148 import java.util.ArrayList ; 149 import java.util.Collection ; 150 import java.util.Collections ; 151 import java.util.HashMap ; 152 import java.util.Iterator ; 153 import java.util.List ; 154 import java.util.Map ; 155 import java.util.ResourceBundle ; 156 157 import org.jfree.chart.LegendItem; 158 import org.jfree.chart.LegendItemCollection; 159 import org.jfree.chart.annotations.CategoryAnnotation; 160 import org.jfree.chart.axis.Axis; 161 import org.jfree.chart.axis.AxisCollection; 162 import org.jfree.chart.axis.AxisLocation; 163 import org.jfree.chart.axis.AxisSpace; 164 import org.jfree.chart.axis.AxisState; 165 import org.jfree.chart.axis.CategoryAnchor; 166 import org.jfree.chart.axis.CategoryAxis; 167 import org.jfree.chart.axis.ValueAxis; 168 import org.jfree.chart.axis.ValueTick; 169 import org.jfree.chart.event.ChartChangeEventType; 170 import org.jfree.chart.event.PlotChangeEvent; 171 import org.jfree.chart.event.RendererChangeEvent; 172 import org.jfree.chart.event.RendererChangeListener; 173 import org.jfree.chart.renderer.category.CategoryItemRenderer; 174 import org.jfree.chart.renderer.category.CategoryItemRendererState; 175 import org.jfree.data.Range; 176 import org.jfree.data.category.CategoryDataset; 177 import org.jfree.data.general.Dataset; 178 import org.jfree.data.general.DatasetChangeEvent; 179 import org.jfree.data.general.DatasetUtilities; 180 import org.jfree.io.SerialUtilities; 181 import org.jfree.ui.Layer; 182 import org.jfree.ui.RectangleEdge; 183 import org.jfree.ui.RectangleInsets; 184 import org.jfree.util.ObjectList; 185 import org.jfree.util.ObjectUtilities; 186 import org.jfree.util.PublicCloneable; 187 import org.jfree.util.SortOrder; 188 189 193 public class CategoryPlot extends Plot 194 implements ValueAxisPlot, 195 Zoomable, 196 RendererChangeListener, 197 Cloneable , PublicCloneable, Serializable { 198 199 200 private static final long serialVersionUID = -3537691700434728188L; 201 202 206 public static final boolean DEFAULT_DOMAIN_GRIDLINES_VISIBLE = false; 207 208 212 public static final boolean DEFAULT_RANGE_GRIDLINES_VISIBLE = true; 213 214 215 public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke (0.5f, 216 BasicStroke.CAP_BUTT, 217 BasicStroke.JOIN_BEVEL, 218 0.0f, 219 new float[] {2.0f, 2.0f}, 220 0.0f); 221 222 223 public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray; 224 225 226 public static final Font DEFAULT_VALUE_LABEL_FONT 227 = new Font ("SansSerif", Font.PLAIN, 10); 228 229 230 protected static ResourceBundle localizationResources 231 = ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle"); 232 233 234 private PlotOrientation orientation; 235 236 237 private RectangleInsets axisOffset; 238 239 240 private ObjectList domainAxes; 241 242 243 private ObjectList domainAxisLocations; 244 245 249 private boolean drawSharedDomainAxis; 250 251 252 private ObjectList rangeAxes; 253 254 255 private ObjectList rangeAxisLocations; 256 257 258 private ObjectList datasets; 259 260 261 private ObjectList datasetToDomainAxisMap; 262 263 264 private ObjectList datasetToRangeAxisMap; 265 266 267 private ObjectList renderers; 268 269 270 private DatasetRenderingOrder renderingOrder 271 = DatasetRenderingOrder.REVERSE; 272 273 277 private SortOrder columnRenderingOrder = SortOrder.ASCENDING; 278 279 283 private SortOrder rowRenderingOrder = SortOrder.ASCENDING; 284 285 289 private boolean domainGridlinesVisible; 290 291 292 private CategoryAnchor domainGridlinePosition; 293 294 295 private transient Stroke domainGridlineStroke; 296 297 298 private transient Paint domainGridlinePaint; 299 300 304 private boolean rangeGridlinesVisible; 305 306 307 private transient Stroke rangeGridlineStroke; 308 309 310 private transient Paint rangeGridlinePaint; 311 312 313 private double anchorValue; 314 315 316 private boolean rangeCrosshairVisible; 317 318 319 private double rangeCrosshairValue; 320 321 322 private transient Stroke rangeCrosshairStroke; 323 324 325 private transient Paint rangeCrosshairPaint; 326 327 331 private boolean rangeCrosshairLockedOnData = true; 332 333 334 private Map foregroundDomainMarkers; 335 336 337 private Map backgroundDomainMarkers; 338 339 340 private Map foregroundRangeMarkers; 341 342 343 private Map backgroundRangeMarkers; 344 345 350 private List annotations; 351 352 356 private int weight; 357 358 359 private AxisSpace fixedDomainAxisSpace; 360 361 362 private AxisSpace fixedRangeAxisSpace; 363 364 368 private LegendItemCollection fixedLegendItems; 369 370 373 public CategoryPlot() { 374 this(null, null, null, null); 375 } 376 377 386 public CategoryPlot(CategoryDataset dataset, 387 CategoryAxis domainAxis, 388 ValueAxis rangeAxis, 389 CategoryItemRenderer renderer) { 390 391 super(); 392 393 this.orientation = PlotOrientation.VERTICAL; 394 395 this.domainAxes = new ObjectList(); 397 this.domainAxisLocations = new ObjectList(); 398 this.rangeAxes = new ObjectList(); 399 this.rangeAxisLocations = new ObjectList(); 400 401 this.datasetToDomainAxisMap = new ObjectList(); 402 this.datasetToRangeAxisMap = new ObjectList(); 403 404 this.renderers = new ObjectList(); 405 406 this.datasets = new ObjectList(); 407 this.datasets.set(0, dataset); 408 if (dataset != null) { 409 dataset.addChangeListener(this); 410 } 411 412 this.axisOffset = RectangleInsets.ZERO_INSETS; 413 414 setDomainAxisLocation(AxisLocation.BOTTOM_OR_LEFT, false); 415 setRangeAxisLocation(AxisLocation.TOP_OR_LEFT, false); 416 417 this.renderers.set(0, renderer); 418 if (renderer != null) { 419 renderer.setPlot(this); 420 renderer.addChangeListener(this); 421 } 422 423 this.domainAxes.set(0, domainAxis); 424 this.mapDatasetToDomainAxis(0, 0); 425 if (domainAxis != null) { 426 domainAxis.setPlot(this); 427 domainAxis.addChangeListener(this); 428 } 429 this.drawSharedDomainAxis = false; 430 431 this.rangeAxes.set(0, rangeAxis); 432 this.mapDatasetToRangeAxis(0, 0); 433 if (rangeAxis != null) { 434 rangeAxis.setPlot(this); 435 rangeAxis.addChangeListener(this); 436 } 437 438 configureDomainAxes(); 439 configureRangeAxes(); 440 441 this.domainGridlinesVisible = DEFAULT_DOMAIN_GRIDLINES_VISIBLE; 442 this.domainGridlinePosition = CategoryAnchor.MIDDLE; 443 this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE; 444 this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT; 445 446 this.rangeGridlinesVisible = DEFAULT_RANGE_GRIDLINES_VISIBLE; 447 this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE; 448 this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT; 449 450 this.foregroundDomainMarkers = new HashMap (); 451 this.backgroundDomainMarkers = new HashMap (); 452 this.foregroundRangeMarkers = new HashMap (); 453 this.backgroundRangeMarkers = new HashMap (); 454 455 Marker baseline = new ValueMarker( 456 0.0, new Color (0.8f, 0.8f, 0.8f, 0.5f), new BasicStroke (1.0f), 457 new Color (0.85f, 0.85f, 0.95f, 0.5f), new BasicStroke (1.0f), 0.6f 458 ); 459 addRangeMarker(baseline, Layer.BACKGROUND); 460 461 this.anchorValue = 0.0; 462 this.annotations = new java.util.ArrayList (); 463 464 } 465 466 471 public String getPlotType() { 472 return localizationResources.getString("Category_Plot"); 473 } 474 475 480 public PlotOrientation getOrientation() { 481 return this.orientation; 482 } 483 484 490 public void setOrientation(PlotOrientation orientation) { 491 if (orientation == null) { 492 throw new IllegalArgumentException ("Null 'orientation' argument."); 493 } 494 this.orientation = orientation; 495 notifyListeners(new PlotChangeEvent(this)); 496 } 497 498 503 public RectangleInsets getAxisOffset() { 504 return this.axisOffset; 505 } 506 507 512 public void setAxisOffset(RectangleInsets offset) { 513 if (offset == null) { 514 throw new IllegalArgumentException ("Null 'offset' argument."); 515 } 516 this.axisOffset = offset; 517 notifyListeners(new PlotChangeEvent(this)); 518 } 519 520 521 528 public CategoryAxis getDomainAxis() { 529 return getDomainAxis(0); 530 } 531 532 539 public CategoryAxis getDomainAxis(int index) { 540 CategoryAxis result = null; 541 if (index < this.domainAxes.size()) { 542 result = (CategoryAxis) this.domainAxes.get(index); 543 } 544 if (result == null) { 545 Plot parent = getParent(); 546 if (parent instanceof CategoryPlot) { 547 CategoryPlot cp = (CategoryPlot) parent; 548 result = cp.getDomainAxis(index); 549 } 550 } 551 return result; 552 } 553 554 560 public void setDomainAxis(CategoryAxis axis) { 561 setDomainAxis(0, axis); 562 } 563 564 571 public void setDomainAxis(int index, CategoryAxis axis) { 572 setDomainAxis(index, axis, true); 573 } 574 575 583 public void setDomainAxis(int index, CategoryAxis axis, boolean notify) { 584 CategoryAxis existing = (CategoryAxis) this.domainAxes.get(index); 585 if (existing != null) { 586 existing.removeChangeListener(this); 587 } 588 if (axis != null) { 589 axis.setPlot(this); 590 } 591 this.domainAxes.set(index, axis); 592 if (axis != null) { 593 axis.configure(); 594 axis.addChangeListener(this); 595 } 596 if (notify) { 597 notifyListeners(new PlotChangeEvent(this)); 598 } 599 } 600 601 607 public void setDomainAxes(CategoryAxis[] axes) { 608 for (int i = 0; i < axes.length; i++) { 609 setDomainAxis(i, axes[i], false); 610 } 611 notifyListeners(new PlotChangeEvent(this)); 612 } 613 614 619 public AxisLocation getDomainAxisLocation() { 620 return getDomainAxisLocation(0); 621 } 622 623 630 public AxisLocation getDomainAxisLocation(int index) { 631 AxisLocation result = null; 632 if (index < this.domainAxisLocations.size()) { 633 result = (AxisLocation) this.domainAxisLocations.get(index); 634 } 635 if (result == null) { 636 result = AxisLocation.getOpposite(getDomainAxisLocation(0)); 637 } 638 return result; 639 640 } 641 642 648 public void setDomainAxisLocation(AxisLocation location) { 649 setDomainAxisLocation(location, true); 651 } 652 653 659 public void setDomainAxisLocation(AxisLocation location, boolean notify) { 660 if (location == null) { 661 throw new IllegalArgumentException ("Null 'location' argument."); 662 } 663 setDomainAxisLocation(0, location); 664 } 665 666 673 public void setDomainAxisLocation(int index, AxisLocation location) { 674 this.domainAxisLocations.set(index, location); 677 notifyListeners(new PlotChangeEvent(this)); 678 } 679 680 686 public RectangleEdge getDomainAxisEdge() { 687 return getDomainAxisEdge(0); 688 } 689 690 697 public RectangleEdge getDomainAxisEdge(int index) { 698 RectangleEdge result = null; 699 AxisLocation location = getDomainAxisLocation(index); 700 if (location != null) { 701 result = Plot.resolveDomainAxisLocation(location, this.orientation); 702 } 703 else { 704 result = RectangleEdge.opposite(getDomainAxisEdge(0)); 705 } 706 return result; 707 } 708 709 713 public void clearDomainAxes() { 714 for (int i = 0; i < this.domainAxes.size(); i++) { 715 CategoryAxis axis = (CategoryAxis) this.domainAxes.get(i); 716 if (axis != null) { 717 axis.removeChangeListener(this); 718 } 719 } 720 this.domainAxes.clear(); 721 notifyListeners(new PlotChangeEvent(this)); 722 } 723 724 727 public void configureDomainAxes() { 728 for (int i = 0; i < this.domainAxes.size(); i++) { 729 CategoryAxis axis = (CategoryAxis) this.domainAxes.get(i); 730 if (axis != null) { 731 axis.configure(); 732 } 733 } 734 } 735 736 743 public ValueAxis getRangeAxis() { 744 return getRangeAxis(0); 745 } 746 747 754 public ValueAxis getRangeAxis(int index) { 755 ValueAxis result = null; 756 if (index < this.rangeAxes.size()) { 757 result = (ValueAxis) this.rangeAxes.get(index); 758 } 759 if (result == null) { 760 Plot parent = getParent(); 761 if (parent instanceof CategoryPlot) { 762 CategoryPlot cp = (CategoryPlot) parent; 763 result = cp.getRangeAxis(index); 764 } 765 } 766 return result; 767 } 768 769 775 public void setRangeAxis(ValueAxis axis) { 776 setRangeAxis(0, axis); 777 } 778 779 786 public void setRangeAxis(int index, ValueAxis axis) { 787 setRangeAxis(index, axis, true); 788 } 789 790 798 public void setRangeAxis(int index, ValueAxis axis, boolean notify) { 799 ValueAxis existing = (ValueAxis) this.rangeAxes.get(index); 800 if (existing != null) { 801 existing.removeChangeListener(this); 802 } 803 if (axis != null) { 804 axis.setPlot(this); 805 } 806 this.rangeAxes.set(index, axis); 807 if (axis != null) { 808 axis.configure(); 809 axis.addChangeListener(this); 810 } 811 if (notify) { 812 notifyListeners(new PlotChangeEvent(this)); 813 } 814 } 815 816 822 public void setRangeAxes(ValueAxis[] axes) { 823 for (int i = 0; i < axes.length; i++) { 824 setRangeAxis(i, axes[i], false); 825 } 826 notifyListeners(new PlotChangeEvent(this)); 827 } 828 829 834 public AxisLocation getRangeAxisLocation() { 835 return getRangeAxisLocation(0); 836 } 837 838 845 public AxisLocation getRangeAxisLocation(int index) { 846 AxisLocation result = null; 847 if (index < this.rangeAxisLocations.size()) { 848 result = (AxisLocation) this.rangeAxisLocations.get(index); 849 } 850 if (result == null) { 851 result = AxisLocation.getOpposite(getRangeAxisLocation(0)); 852 } 853 return result; 854 } 855 856 862 public void setRangeAxisLocation(AxisLocation location) { 863 setRangeAxisLocation(location, true); 865 } 866 867 874 public void setRangeAxisLocation(AxisLocation location, boolean notify) { 875 setRangeAxisLocation(0, location, notify); 876 } 877 878 885 public void setRangeAxisLocation(int index, AxisLocation location) { 886 setRangeAxisLocation(index, location, true); 887 } 888 889 897 public void setRangeAxisLocation(int index, AxisLocation location, 898 boolean notify) { 899 this.rangeAxisLocations.set(index, location); 901 if (notify) { 902 notifyListeners(new PlotChangeEvent(this)); 903 } 904 } 905 906 911 public RectangleEdge getRangeAxisEdge() { 912 return getRangeAxisEdge(0); 913 } 914 915 |