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