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