001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, 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 * CombinedDomainXYPlot.java 029 * ------------------------- 030 * (C) Copyright 2001-2008, by Bill Kelemen and Contributors. 031 * 032 * Original Author: Bill Kelemen; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * Anthony Boulestreau; 035 * David Basten; 036 * Kevin Frechette (for ISTI); 037 * Nicolas Brodu; 038 * Petr Kubanek (bug 1606205); 039 * 040 * Changes: 041 * -------- 042 * 06-Dec-2001 : Version 1 (BK); 043 * 12-Dec-2001 : Removed unnecessary 'throws' clause from constructor (DG); 044 * 18-Dec-2001 : Added plotArea attribute and get/set methods (BK); 045 * 22-Dec-2001 : Fixed bug in chartChanged with multiple combinations of 046 * CombinedPlots (BK); 047 * 08-Jan-2002 : Moved to new package com.jrefinery.chart.combination (DG); 048 * 25-Feb-2002 : Updated import statements (DG); 049 * 28-Feb-2002 : Readded "this.plotArea = plotArea" that was deleted from 050 * draw() method (BK); 051 * 26-Mar-2002 : Added an empty zoom method (this method needs to be written so 052 * that combined plots will support zooming (DG); 053 * 29-Mar-2002 : Changed the method createCombinedAxis adding the creation of 054 * OverlaidSymbolicAxis and CombinedSymbolicAxis(AB); 055 * 23-Apr-2002 : Renamed CombinedPlot-->MultiXYPlot, and simplified the 056 * structure (DG); 057 * 23-May-2002 : Renamed (again) MultiXYPlot-->CombinedXYPlot (DG); 058 * 19-Jun-2002 : Added get/setGap() methods suggested by David Basten (DG); 059 * 25-Jun-2002 : Removed redundant imports (DG); 060 * 16-Jul-2002 : Draws shared axis after subplots (to fix missing gridlines), 061 * added overrides of 'setSeriesPaint()' and 'setXYItemRenderer()' 062 * that pass changes down to subplots (KF); 063 * 09-Oct-2002 : Added add(XYPlot) method (DG); 064 * 26-Mar-2003 : Implemented Serializable (DG); 065 * 16-May-2003 : Renamed CombinedXYPlot --> CombinedDomainXYPlot (DG); 066 * 04-Aug-2003 : Removed leftover code that was causing domain axis drawing 067 * problem (DG); 068 * 08-Aug-2003 : Adjusted totalWeight in remove() method (DG); 069 * 21-Aug-2003 : Implemented Cloneable (DG); 070 * 11-Sep-2003 : Fix cloning support (subplots) (NB); 071 * 15-Sep-2003 : Fixed error in cloning (DG); 072 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 073 * 17-Sep-2003 : Updated handling of 'clicks' (DG); 074 * 12-Nov-2004 : Implemented the new Zoomable interface (DG); 075 * 25-Nov-2004 : Small update to clone() implementation (DG); 076 * 21-Feb-2005 : The getLegendItems() method now returns the fixed legend 077 * items if set (DG); 078 * 05-May-2005 : Removed unused draw() method (DG); 079 * ------------- JFREECHART 1.0.x --------------------------------------------- 080 * 23-Aug-2006 : Override setFixedRangeAxisSpace() to update subplots (DG); 081 * 06-Feb-2007 : Fixed bug 1606205, draw shared axis after subplots (DG); 082 * 23-Mar-2007 : Reverted previous patch (bug fix 1606205) (DG); 083 * 17-Apr-2007 : Added null argument checks to findSubplot() (DG); 084 * 27-Nov-2007 : Modified setFixedRangeAxisSpaceForSubplots() so as not to 085 * trigger change event in subplots (DG); 086 * 28-Jan-2008 : Reset fixed range axis space in subplots for each call to 087 * draw() (DG); 088 * 27-Mar-2008 : Add documentation for getDataRange() method (DG); 089 * 31-Mar-2008 : Updated getSubplots() to return EMPTY_LIST for null 090 * subplots, as suggested by Richard West (DG); 091 * 28-Apr-2008 : Fixed zooming problem (see bug 1950037) (DG); 092 * 093 */ 094 095 package org.jfree.chart.plot; 096 097 import java.awt.Graphics2D; 098 import java.awt.geom.Point2D; 099 import java.awt.geom.Rectangle2D; 100 import java.util.Collections; 101 import java.util.Iterator; 102 import java.util.List; 103 104 import org.jfree.chart.LegendItemCollection; 105 import org.jfree.chart.axis.AxisSpace; 106 import org.jfree.chart.axis.AxisState; 107 import org.jfree.chart.axis.NumberAxis; 108 import org.jfree.chart.axis.ValueAxis; 109 import org.jfree.chart.event.PlotChangeEvent; 110 import org.jfree.chart.event.PlotChangeListener; 111 import org.jfree.chart.renderer.xy.XYItemRenderer; 112 import org.jfree.data.Range; 113 import org.jfree.ui.RectangleEdge; 114 import org.jfree.ui.RectangleInsets; 115 import org.jfree.util.ObjectUtilities; 116 117 /** 118 * An extension of {@link XYPlot} that contains multiple subplots that share a 119 * common domain axis. 120 */ 121 public class CombinedDomainXYPlot extends XYPlot 122 implements PlotChangeListener { 123 124 /** For serialization. */ 125 private static final long serialVersionUID = -7765545541261907383L; 126 127 /** Storage for the subplot references. */ 128 private List subplots; 129 130 /** Total weight of all charts. */ 131 private int totalWeight = 0; 132 133 /** The gap between subplots. */ 134 private double gap = 5.0; 135 136 /** Temporary storage for the subplot areas. */ 137 private transient Rectangle2D[] subplotAreas; 138 // TODO: the subplot areas needs to be moved out of the plot into the plot 139 // state 140 141 /** 142 * Default constructor. 143 */ 144 public CombinedDomainXYPlot() { 145 this(new NumberAxis()); 146 } 147 148 /** 149 * Creates a new combined plot that shares a domain axis among multiple 150 * subplots. 151 * 152 * @param domainAxis the shared axis. 153 */ 154 public CombinedDomainXYPlot(ValueAxis domainAxis) { 155 156 super( 157 null, // no data in the parent plot 158 domainAxis, 159 null, // no range axis 160 null // no rendereer 161 ); 162 163 this.subplots = new java.util.ArrayList(); 164 165 } 166 167 /** 168 * Returns a string describing the type of plot. 169 * 170 * @return The type of plot. 171 */ 172 public String getPlotType() { 173 return "Combined_Domain_XYPlot"; 174 } 175 176 /** 177 * Sets the orientation for the plot (also changes the orientation for all 178 * the subplots to match). 179 * 180 * @param orientation the orientation (<code>null</code> not allowed). 181 */ 182 public void setOrientation(PlotOrientation orientation) { 183 184 super.setOrientation(orientation); 185 Iterator iterator = this.subplots.iterator(); 186 while (iterator.hasNext()) { 187 XYPlot plot = (XYPlot) iterator.next(); 188 plot.setOrientation(orientation); 189 } 190 191 } 192 193 /** 194 * Returns a range representing the extent of the data values in this plot 195 * (obtained from the subplots) that will be rendered against the specified 196 * axis. NOTE: This method is intended for internal JFreeChart use, and 197 * is public only so that code in the axis classes can call it. Since 198 * only the domain axis is shared between subplots, the JFreeChart code 199 * will only call this method for the domain values (although this is not 200 * checked/enforced). 201 * 202 * @param axis the axis. 203 * 204 * @return The range (possibly <code>null</code>). 205 */ 206 public Range getDataRange(ValueAxis axis) { 207 Range result = null; 208 if (this.subplots != null) { 209 Iterator iterator = this.subplots.iterator(); 210 while (iterator.hasNext()) { 211 XYPlot subplot = (XYPlot) iterator.next(); 212 result = Range.combine(result, subplot.getDataRange(axis)); 213 } 214 } 215 return result; 216 } 217 218 /** 219 * Returns the gap between subplots, measured in Java2D units. 220 * 221 * @return The gap (in Java2D units). 222 */ 223 public double getGap() { 224 return this.gap; 225 } 226 227 /** 228 * Sets the amount of space between subplots and sends a 229 * {@link PlotChangeEvent} to all registered listeners. 230 * 231 * @param gap the gap between subplots (in Java2D units). 232 */ 233 public void setGap(double gap) { 234 this.gap = gap; 235 fireChangeEvent(); 236 } 237 238 /** 239 * Adds a subplot (with a default 'weight' of 1) and sends a 240 * {@link PlotChangeEvent} to all registered listeners. 241 * <P> 242 * The domain axis for the subplot will be set to <code>null</code>. You 243 * must ensure that the subplot has a non-null range axis. 244 * 245 * @param subplot the subplot (<code>null</code> not permitted). 246 */ 247 public void add(XYPlot subplot) { 248 // defer argument checking 249 add(subplot, 1); 250 } 251 252 /** 253 * Adds a subplot with the specified weight and sends a 254 * {@link PlotChangeEvent} to all registered listeners. The weight 255 * determines how much space is allocated to the subplot relative to all 256 * the other subplots. 257 * <P> 258 * The domain axis for the subplot will be set to <code>null</code>. You 259 * must ensure that the subplot has a non-null range axis. 260 * 261 * @param subplot the subplot (<code>null</code> not permitted). 262 * @param weight the weight (must be >= 1). 263 */ 264 public void add(XYPlot subplot, int weight) { 265 266 if (subplot == null) { 267 throw new IllegalArgumentException("Null 'subplot' argument."); 268 } 269 if (weight <= 0) { 270 throw new IllegalArgumentException("Require weight >= 1."); 271 } 272 273 // store the plot and its weight 274 subplot.setParent(this); 275 subplot.setWeight(weight); 276 subplot.setInsets(new RectangleInsets(0.0, 0.0, 0.0, 0.0), false); 277 subplot.setDomainAxis(null); 278 subplot.addChangeListener(this); 279 this.subplots.add(subplot); 280 281 // keep track of total weights 282 this.totalWeight += weight; 283 284 ValueAxis axis = getDomainAxis(); 285 if (axis != null) { 286 axis.configure(); 287 } 288 fireChangeEvent(); 289 } 290 291 /** 292 * Removes a subplot from the combined chart and sends a 293 * {@link PlotChangeEvent} to all registered listeners. 294 * 295 * @param subplot the subplot (<code>null</code> not permitted). 296 */ 297 public void remove(XYPlot subplot) { 298 if (subplot == null) { 299 throw new IllegalArgumentException(" Null 'subplot' argument."); 300 } 301 int position = -1; 302 int size = this.subplots.size(); 303 int i = 0; 304 while (position == -1 && i < size) { 305 if (this.subplots.get(i) == subplot) { 306 position = i; 307 } 308 i++; 309 } 310 if (position != -1) { 311 this.subplots.remove(position); 312 subplot.setParent(null); 313 subplot.removeChangeListener(this); 314 this.totalWeight -= subplot.getWeight(); 315 316 ValueAxis domain = getDomainAxis(); 317 if (domain != null) { 318 domain.configure(); 319 } 320 fireChangeEvent(); 321 } 322 } 323 324 /** 325 * Returns the list of subplots. The returned list may be empty, but is 326 * never <code>null</code>. 327 * 328 * @return An unmodifiable list of subplots. 329 */ 330 public List getSubplots() { 331 if (this.subplots != null) { 332 return Collections.unmodifiableList(this.subplots); 333 } 334 else { 335 return Collections.EMPTY_LIST; 336 } 337 } 338 339 /** 340 * Calculates the axis space required. 341 * 342 * @param g2 the graphics device. 343 * @param plotArea the plot area. 344 * 345 * @return The space. 346 */ 347 protected AxisSpace calculateAxisSpace(Graphics2D g2, 348 Rectangle2D plotArea) { 349 350 AxisSpace space = new AxisSpace(); 351 PlotOrientation orientation = getOrientation(); 352 353 // work out the space required by the domain axis... 354 AxisSpace fixed = getFixedDomainAxisSpace(); 355 if (fixed != null) { 356 if (orientation == PlotOrientation.HORIZONTAL) { 357 space.setLeft(fixed.getLeft()); 358 space.setRight(fixed.getRight()); 359 } 360 else if (orientation == PlotOrientation.VERTICAL) { 361 space.setTop(fixed.getTop()); 362 space.setBottom(fixed.getBottom()); 363 } 364 } 365 else { 366 ValueAxis xAxis = getDomainAxis(); 367 RectangleEdge xEdge = Plot.resolveDomainAxisLocation( 368 getDomainAxisLocation(), orientation); 369 if (xAxis != null) { 370 space = xAxis.reserveSpace(g2, this, plotArea, xEdge, space); 371 } 372 } 373 374 Rectangle2D adjustedPlotArea = space.shrink(plotArea, null); 375 376 // work out the maximum height or width of the non-shared axes... 377 int n = this.subplots.size(); 378 this.subplotAreas = new Rectangle2D[n]; 379 double x = adjustedPlotArea.getX(); 380 double y = adjustedPlotArea.getY(); 381 double usableSize = 0.0; 382 if (orientation == PlotOrientation.HORIZONTAL) { 383 usableSize = adjustedPlotArea.getWidth() - this.gap * (n - 1); 384 } 385 else if (orientation == PlotOrientation.VERTICAL) { 386 usableSize = adjustedPlotArea.getHeight() - this.gap * (n - 1); 387 } 388 389 for (int i = 0; i < n; i++) { 390 XYPlot plot = (XYPlot) this.subplots.get(i); 391 392 // calculate sub-plot area 393 if (orientation == PlotOrientation.HORIZONTAL) { 394 double w = usableSize * plot.getWeight() / this.totalWeight; 395 this.subplotAreas[i] = new Rectangle2D.Double(x, y, w, 396 adjustedPlotArea.getHeight()); 397 x = x + w + this.gap; 398 } 399 else if (orientation == PlotOrientation.VERTICAL) { 400 double h = usableSize * plot.getWeight() / this.totalWeight; 401 this.subplotAreas[i] = new Rectangle2D.Double(x, y, 402 adjustedPlotArea.getWidth(), h); 403 y = y + h + this.gap; 404 } 405 406 AxisSpace subSpace = plot.calculateRangeAxisSpace(g2, 407 this.subplotAreas[i], null); 408 space.ensureAtLeast(subSpace); 409 410 } 411 412 return space; 413 } 414 415 /** 416 * Draws the plot within the specified area on a graphics device. 417 * 418 * @param g2 the graphics device. 419 * @param area the plot area (in Java2D space). 420 * @param anchor an anchor point in Java2D space (<code>null</code> 421 * permitted). 422 * @param parentState the state from the parent plot, if there is one 423 * (<code>null</code> permitted). 424 * @param info collects chart drawing information (<code>null</code> 425 * permitted). 426 */ 427 public void draw(Graphics2D g2, 428 Rectangle2D area, 429 Point2D anchor, 430 PlotState parentState, 431 PlotRenderingInfo info) { 432 433 // set up info collection... 434 if (info != null) { 435 info.setPlotArea(area); 436 } 437 438 // adjust the drawing area for plot insets (if any)... 439 RectangleInsets insets = getInsets(); 440 insets.trim(area); 441 442 setFixedRangeAxisSpaceForSubplots(null); 443 AxisSpace space = calculateAxisSpace(g2, area); 444 Rectangle2D dataArea = space.shrink(area, null); 445 446 // set the width and height of non-shared axis of all sub-plots 447 setFixedRangeAxisSpaceForSubplots(space); 448 449 // draw the shared axis 450 ValueAxis axis = getDomainAxis(); 451 RectangleEdge edge = getDomainAxisEdge(); 452 double cursor = RectangleEdge.coordinate(dataArea, edge); 453 AxisState axisState = axis.draw(g2, cursor, area, dataArea, edge, info); 454 if (parentState == null) { 455 parentState = new PlotState(); 456 } 457 parentState.getSharedAxisStates().put(axis, axisState); 458 459 // draw all the subplots 460 for (int i = 0; i < this.subplots.size(); i++) { 461 XYPlot plot = (XYPlot) this.subplots.get(i); 462 PlotRenderingInfo subplotInfo = null; 463 if (info != null) { 464 subplotInfo = new PlotRenderingInfo(info.getOwner()); 465 info.addSubplotInfo(subplotInfo); 466 } 467 plot.draw(g2, this.subplotAreas[i], anchor, parentState, 468 subplotInfo); 469 } 470 471 if (info != null) { 472 info.setDataArea(dataArea); 473 } 474 475 } 476 477 /** 478 * Returns a collection of legend items for the plot. 479 * 480 * @return The legend items. 481 */ 482 public LegendItemCollection getLegendItems() { 483 LegendItemCollection result = getFixedLegendItems(); 484 if (result == null) { 485 result = new LegendItemCollection(); 486 if (this.subplots != null) { 487 Iterator iterator = this.subplots.iterator(); 488 while (iterator.hasNext()) { 489 XYPlot plot = (XYPlot) iterator.next(); 490 LegendItemCollection more = plot.getLegendItems(); 491 result.addAll(more); 492 } 493 } 494 } 495 return result; 496 } 497 498 /** 499 * Multiplies the range on the range axis/axes by the specified factor. 500 * 501 * @param factor the zoom factor. 502 * @param info the plot rendering info (<code>null</code> not permitted). 503 * @param source the source point (<code>null</code> not permitted). 504 */ 505 public void zoomRangeAxes(double factor, PlotRenderingInfo info, 506 Point2D source) { 507 zoomRangeAxes(factor, info, source, false); 508 } 509 510 /** 511 * Multiplies the range on the range axis/axes by the specified factor. 512 * 513 * @param factor the zoom factor. 514 * @param state the plot state. 515 * @param source the source point (in Java2D coordinates). 516 * @param useAnchor use source point as zoom anchor? 517 */ 518 public void zoomRangeAxes(double factor, PlotRenderingInfo state, 519 Point2D source, boolean useAnchor) { 520 // delegate 'state' and 'source' argument checks... 521 XYPlot subplot = findSubplot(state, source); 522 if (subplot != null) { 523 subplot.zoomRangeAxes(factor, state, source, useAnchor); 524 } 525 else { 526 // if the source point doesn't fall within a subplot, we do the 527 // zoom on all subplots... 528 Iterator iterator = getSubplots().iterator(); 529 while (iterator.hasNext()) { 530 subplot = (XYPlot) iterator.next(); 531 subplot.zoomRangeAxes(factor, state, source, useAnchor); 532 } 533 } 534 } 535 536 /** 537 * Zooms in on the range axes. 538 * 539 * @param lowerPercent the lower bound. 540 * @param upperPercent the upper bound. 541 * @param info the plot rendering info (<code>null</code> not permitted). 542 * @param source the source point (<code>null</code> not permitted). 543 */ 544 public void zoomRangeAxes(double lowerPercent, double upperPercent, 545 PlotRenderingInfo info, Point2D source) { 546 // delegate 'info' and 'source' argument checks... 547 XYPlot subplot = findSubplot(info, source); 548 if (subplot != null) { 549 subplot.zoomRangeAxes(lowerPercent, upperPercent, info, source); 550 } 551 else { 552 // if the source point doesn't fall within a subplot, we do the 553 // zoom on all subplots... 554 Iterator iterator = getSubplots().iterator(); 555 while (iterator.hasNext()) { 556 subplot = (XYPlot) iterator.next(); 557 subplot.zoomRangeAxes(lowerPercent, upperPercent, info, source); 558 } 559 } 560 } 561 562 /** 563 * Returns the subplot (if any) that contains the (x, y) point (specified 564 * in Java2D space). 565 * 566 * @param info the chart rendering info (<code>null</code> not permitted). 567 * @param source the source point (<code>null</code> not permitted). 568 * 569 * @return A subplot (possibly <code>null</code>). 570 */ 571 public XYPlot findSubplot(PlotRenderingInfo info, Point2D source) { 572 if (info == null) { 573 throw new IllegalArgumentException("Null 'info' argument."); 574 } 575 if (source == null) { 576 throw new IllegalArgumentException("Null 'source' argument."); 577 } 578 XYPlot result = null; 579 int subplotIndex = info.getSubplotIndex(source); 580 if (subplotIndex >= 0) { 581 result = (XYPlot) this.subplots.get(subplotIndex); 582 } 583 return result; 584 } 585 586 /** 587 * Sets the item renderer FOR ALL SUBPLOTS. Registered listeners are 588 * notified that the plot has been modified. 589 * <P> 590 * Note: usually you will want to set the renderer independently for each 591 * subplot, which is NOT what this method does. 592 * 593 * @param renderer the new renderer. 594 */ 595 public void setRenderer(XYItemRenderer renderer) { 596 597 super.setRenderer(renderer); // not strictly necessary, since the 598 // renderer set for the 599 // parent plot is not used 600 601 Iterator iterator = this.subplots.iterator(); 602 while (iterator.hasNext()) { 603 XYPlot plot = (XYPlot) iterator.next(); 604 plot.setRenderer(renderer); 605 } 606 607 } 608 609 /** 610 * Sets the fixed range axis space and sends a {@link PlotChangeEvent} to 611 * all registered listeners. 612 * 613 * @param space the space (<code>null</code> permitted). 614 */ 615 public void setFixedRangeAxisSpace(AxisSpace space) { 616 super.setFixedRangeAxisSpace(space); 617 setFixedRangeAxisSpaceForSubplots(space); 618 fireChangeEvent(); 619 } 620 621 /** 622 * Sets the size (width or height, depending on the orientation of the 623 * plot) for the domain axis of each subplot. 624 * 625 * @param space the space. 626 */ 627 protected void setFixedRangeAxisSpaceForSubplots(AxisSpace space) { 628 Iterator iterator = this.subplots.iterator(); 629 while (iterator.hasNext()) { 630 XYPlot plot = (XYPlot) iterator.next(); 631 plot.setFixedRangeAxisSpace(space, false); 632 } 633 } 634 635 /** 636 * Handles a 'click' on the plot by updating the anchor values. 637 * 638 * @param x x-coordinate, where the click occured. 639 * @param y y-coordinate, where the click occured. 640 * @param info object containing information about the plot dimensions. 641 */ 642 public void handleClick(int x, int y, PlotRenderingInfo info) { 643 Rectangle2D dataArea = info.getDataArea(); 644 if (dataArea.contains(x, y)) { 645 for (int i = 0; i < this.subplots.size(); i++) { 646 XYPlot subplot = (XYPlot) this.subplots.get(i); 647 PlotRenderingInfo subplotInfo = info.getSubplotInfo(i); 648 subplot.handleClick(x, y, subplotInfo); 649 } 650 } 651 } 652 653 /** 654 * Receives a {@link PlotChangeEvent} and responds by notifying all 655 * listeners. 656 * 657 * @param event the event. 658 */ 659 public void plotChanged(PlotChangeEvent event) { 660 notifyListeners(event); 661 } 662 663 /** 664 * Tests this plot for equality with another object. 665 * 666 * @param obj the other object. 667 * 668 * @return <code>true</code> or <code>false</code>. 669 */ 670 public boolean equals(Object obj) { 671 672 if (obj == null) { 673 return false; 674 } 675 676 if (obj == this) { 677 return true; 678 } 679 680 if (!(obj instanceof CombinedDomainXYPlot)) { 681 return false; 682 } 683 if (!super.equals(obj)) { 684 return false; 685 } 686 687 CombinedDomainXYPlot p = (CombinedDomainXYPlot) obj; 688 if (this.totalWeight != p.totalWeight) { 689 return false; 690 } 691 if (this.gap != p.gap) { 692 return false; 693 } 694 if (!ObjectUtilities.equal(this.subplots, p.subplots)) { 695 return false; 696 } 697 698 return true; 699 } 700 701 /** 702 * Returns a clone of the annotation. 703 * 704 * @return A clone. 705 * 706 * @throws CloneNotSupportedException this class will not throw this 707 * exception, but subclasses (if any) might. 708 */ 709 public Object clone() throws CloneNotSupportedException { 710 711 CombinedDomainXYPlot result = (CombinedDomainXYPlot) super.clone(); 712 result.subplots = (List) ObjectUtilities.deepClone(this.subplots); 713 for (Iterator it = result.subplots.iterator(); it.hasNext();) { 714 Plot child = (Plot) it.next(); 715 child.setParent(result); 716 } 717 718 // after setting up all the subplots, the shared domain axis may need 719 // reconfiguring 720 ValueAxis domainAxis = result.getDomainAxis(); 721 if (domainAxis != null) { 722 domainAxis.configure(); 723 } 724 725 return result; 726 727 } 728 729 }