Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jfreechart/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * ------------------------- 28: * CombinedDomainXYPlot.java 29: * ------------------------- 30: * (C) Copyright 2001-2008, by Bill Kelemen and Contributors. 31: * 32: * Original Author: Bill Kelemen; 33: * Contributor(s): David Gilbert (for Object Refinery Limited); 34: * Anthony Boulestreau; 35: * David Basten; 36: * Kevin Frechette (for ISTI); 37: * Nicolas Brodu; 38: * Petr Kubanek (bug 1606205); 39: * 40: * Changes: 41: * -------- 42: * 06-Dec-2001 : Version 1 (BK); 43: * 12-Dec-2001 : Removed unnecessary 'throws' clause from constructor (DG); 44: * 18-Dec-2001 : Added plotArea attribute and get/set methods (BK); 45: * 22-Dec-2001 : Fixed bug in chartChanged with multiple combinations of 46: * CombinedPlots (BK); 47: * 08-Jan-2002 : Moved to new package com.jrefinery.chart.combination (DG); 48: * 25-Feb-2002 : Updated import statements (DG); 49: * 28-Feb-2002 : Readded "this.plotArea = plotArea" that was deleted from 50: * draw() method (BK); 51: * 26-Mar-2002 : Added an empty zoom method (this method needs to be written so 52: * that combined plots will support zooming (DG); 53: * 29-Mar-2002 : Changed the method createCombinedAxis adding the creation of 54: * OverlaidSymbolicAxis and CombinedSymbolicAxis(AB); 55: * 23-Apr-2002 : Renamed CombinedPlot-->MultiXYPlot, and simplified the 56: * structure (DG); 57: * 23-May-2002 : Renamed (again) MultiXYPlot-->CombinedXYPlot (DG); 58: * 19-Jun-2002 : Added get/setGap() methods suggested by David Basten (DG); 59: * 25-Jun-2002 : Removed redundant imports (DG); 60: * 16-Jul-2002 : Draws shared axis after subplots (to fix missing gridlines), 61: * added overrides of 'setSeriesPaint()' and 'setXYItemRenderer()' 62: * that pass changes down to subplots (KF); 63: * 09-Oct-2002 : Added add(XYPlot) method (DG); 64: * 26-Mar-2003 : Implemented Serializable (DG); 65: * 16-May-2003 : Renamed CombinedXYPlot --> CombinedDomainXYPlot (DG); 66: * 04-Aug-2003 : Removed leftover code that was causing domain axis drawing 67: * problem (DG); 68: * 08-Aug-2003 : Adjusted totalWeight in remove() method (DG); 69: * 21-Aug-2003 : Implemented Cloneable (DG); 70: * 11-Sep-2003 : Fix cloning support (subplots) (NB); 71: * 15-Sep-2003 : Fixed error in cloning (DG); 72: * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 73: * 17-Sep-2003 : Updated handling of 'clicks' (DG); 74: * 12-Nov-2004 : Implemented the new Zoomable interface (DG); 75: * 25-Nov-2004 : Small update to clone() implementation (DG); 76: * 21-Feb-2005 : The getLegendItems() method now returns the fixed legend 77: * items if set (DG); 78: * 05-May-2005 : Removed unused draw() method (DG); 79: * ------------- JFREECHART 1.0.x --------------------------------------------- 80: * 23-Aug-2006 : Override setFixedRangeAxisSpace() to update subplots (DG); 81: * 06-Feb-2007 : Fixed bug 1606205, draw shared axis after subplots (DG); 82: * 23-Mar-2007 : Reverted previous patch (bug fix 1606205) (DG); 83: * 17-Apr-2007 : Added null argument checks to findSubplot() (DG); 84: * 27-Nov-2007 : Modified setFixedRangeAxisSpaceForSubplots() so as not to 85: * trigger change event in subplots (DG); 86: * 28-Jan-2008 : Reset fixed range axis space in subplots for each call to 87: * draw() (DG); 88: * 27-Mar-2008 : Add documentation for getDataRange() method (DG); 89: * 31-Mar-2008 : Updated getSubplots() to return EMPTY_LIST for null 90: * subplots, as suggested by Richard West (DG); 91: * 28-Apr-2008 : Fixed zooming problem (see bug 1950037) (DG); 92: * 93: */ 94: 95: package org.jfree.chart.plot; 96: 97: import java.awt.Graphics2D; 98: import java.awt.geom.Point2D; 99: 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: }