gt:         else if (edge == RectangleEdge.RIGHT) {
context: /* ===========================================================
   * JFreeChart : a free chart library for the Java(tm) platform
   * ===========================================================
   *
   * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
   *
   * Project Info:  http://www.jfree.org/jfreechart/index.html
   *
   * This library is free software; you can redistribute it and/or modify it 
   * under the terms of the GNU Lesser General Public License as published by 
   * the Free Software Foundation; either version 2.1 of the License, or 
   * (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful, but 
   * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
   * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
   * License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
   * USA.  
   *
   * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
   * in the United States and other countries.]
   *
   * --------------
   * ValueAxis.java
   * --------------
   * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
   *
   * Original Author:  David Gilbert (for Object Refinery Limited);
   * Contributor(s):   Jonathan Nash;
   *                   Nicolas Brodu (for Astrium and EADS Corporate Research 
   *                   Center);
   *
   * $Id: ValueAxis.java,v 1.10.2.1 2005/10/25 20:37:34 mungady Exp $
   *
   * Changes (from 18-Sep-2001)
   * --------------------------
   * 18-Sep-2001 : Added standard header and fixed DOS encoding problem (DG);
   * 23-Nov-2001 : Overhauled standard tick unit code (DG);
   * 04-Dec-2001 : Changed constructors to protected, and tidied up default 
   *               values (DG);
   * 12-Dec-2001 : Fixed vertical gridlines bug (DG);
   * 16-Jan-2002 : Added an optional crosshair, based on the implementation by 
   *               Jonathan Nash (DG);
   * 23-Jan-2002 : Moved the minimum and maximum values to here from NumberAxis, 
   *               and changed the type from Number to double (DG);
   * 25-Feb-2002 : Added default value for autoRange. Changed autoAdjustRange 
   *               from public to protected. Updated import statements (DG);
   * 23-Apr-2002 : Added setRange() method (DG);
   * 29-Apr-2002 : Added range adjustment methods (DG);
   * 13-Jun-2002 : Modified setCrosshairValue() to notify listeners only when the
   *               crosshairs are visible, to avoid unnecessary repaints, as 
   *               suggested by Kees Kuip (DG);
   * 25-Jul-2002 : Moved lower and upper margin attributes from the NumberAxis 
   *               class (DG);
   * 05-Sep-2002 : Updated constructor for changes in Axis class (DG);
   * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG);
   * 04-Oct-2002 : Moved standardTickUnits from NumberAxis --> ValueAxis (DG);
   * 08-Nov-2002 : Moved to new package com.jrefinery.chart.axis (DG);
   * 19-Nov-2002 : Removed grid settings (now controlled by the plot) (DG);
   * 27-Nov-2002 : Moved the 'inverted' attributed from NumberAxis to 
   *               ValueAxis (DG);
   * 03-Jan-2003 : Small fix to ensure auto-range minimum is observed 
   *               immediately (DG);
   * 14-Jan-2003 : Changed autoRangeMinimumSize from Number --> double (DG);
   * 20-Jan-2003 : Replaced monolithic constructor (DG);
   * 26-Mar-2003 : Implemented Serializable (DG);
   * 09-May-2003 : Added AxisLocation parameter to translation methods (DG);
   * 13-Aug-2003 : Implemented Cloneable (DG);
   * 01-Sep-2003 : Fixed bug 793167 (setMaximumAxisValue exception) (DG);
   * 02-Sep-2003 : Fixed bug 795366 (zooming on inverted axes) (DG);
   * 08-Sep-2003 : Completed Serialization support (NB);
   * 08-Sep-2003 : Renamed get/setMinimumValue --> get/setLowerBound,
   *               and get/setMaximumValue --> get/setUpperBound (DG);
   * 27-Oct-2003 : Changed DEFAULT_AUTO_RANGE_MINIMUM_SIZE value - see bug ID 
   *               829606 (DG);
   * 07-Nov-2003 : Changes to tick mechanism (DG);
   * 06-Jan-2004 : Moved axis line attributes to Axis class (DG);
   * 21-Jan-2004 : Removed redundant axisLineVisible attribute.  Renamed 
   *               translateJava2DToValue --> java2DToValue, and 
   *               translateValueToJava2D --> valueToJava2D (DG); 
   * 23-Jan-2004 : Fixed setAxisLinePaint() and setAxisLineStroke() which had no 
   *               effect (andreas.gawecki@coremedia.com);
   * 07-Apr-2004 : Changed text bounds calculation (DG);
   * 26-Apr-2004 : Added getter/setter methods for arrow shapes (DG);
   * 18-May-2004 : Added methods to set axis range *including* current 
   *               margins (DG);
   * 02-Jun-2004 : Fixed bug in setRangeWithMargins() method (DG);
   * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities 
   *               --> TextUtilities (DG);
   * 11-Jan-2005 : Removed deprecated methods in preparation for 1.0.0 
   *               release (DG);
   * 21-Apr-2005 : Replaced Insets with RectangleInsets (DG);
   *
   */
  
  package org.jfree.chart.axis;
  
  import java.awt.Font;
  import java.awt.FontMetrics;
  import java.awt.Graphics2D;
  import java.awt.Polygon;
  import java.awt.Shape;
  import java.awt.font.LineMetrics;
  import java.awt.geom.AffineTransform;
  import java.awt.geom.Line2D;
  import java.awt.geom.Rectangle2D;
  import java.io.IOException;
  import java.io.ObjectInputStream;
  import java.io.ObjectOutputStream;
  import java.io.Serializable;
  import java.util.Iterator;
  import java.util.List;
  
  import org.jfree.chart.event.AxisChangeEvent;
  import org.jfree.chart.plot.Plot;
  import org.jfree.data.Range;
  import org.jfree.io.SerialUtilities;
  import org.jfree.text.TextUtilities;
  import org.jfree.ui.RectangleEdge;
  import org.jfree.ui.RectangleInsets;
  import org.jfree.util.ObjectUtilities;
  import org.jfree.util.PublicCloneable;
  
  /**
   * The base class for axes that display value data, where values are measured 
   * using the <code>double</code> primitive.  The two key subclasses are 
   * {@link DateAxis} and {@link NumberAxis}.
   */
  public abstract class ValueAxis extends Axis 
                                  implements Cloneable, PublicCloneable, 
                                             Serializable {
  
      /** For serialization. */
      private static final long serialVersionUID = 3698345477322391456L;
      
      /** The default axis range. */
      public static final Range DEFAULT_RANGE = new Range(0.0, 1.0);
  
      /** The default auto-range value. */
      public static final boolean DEFAULT_AUTO_RANGE = true;
  
      /** The default inverted flag setting. */
      public static final boolean DEFAULT_INVERTED = false;
  
      /** The default minimum auto range. */
      public static final double DEFAULT_AUTO_RANGE_MINIMUM_SIZE = 0.00000001;
  
      /** The default value for the lower margin (0.05 = 5%). */
      public static final double DEFAULT_LOWER_MARGIN = 0.05;
  
      /** The default value for the upper margin (0.05 = 5%). */
      public static final double DEFAULT_UPPER_MARGIN = 0.05;
  
      /** The default lower bound for the axis. */
      public static final double DEFAULT_LOWER_BOUND = 0.0;
  
      /** The default upper bound for the axis. */
      public static final double DEFAULT_UPPER_BOUND = 1.0;
  
      /** The default auto-tick-unit-selection value. */
      public static final boolean DEFAULT_AUTO_TICK_UNIT_SELECTION = true;
  
      /** The maximum tick count. */
      public static final int MAXIMUM_TICK_COUNT = 500;
      
      /** 
       * A flag that controls whether an arrow is drawn at the positive end of 
       * the axis line. 
       */
      private boolean positiveArrowVisible;
      
      /** 
       * A flag that controls whether an arrow is drawn at the negative end of 
       * the axis line. 
       */
      private boolean negativeArrowVisible;
      
      /** The shape used for an up arrow. */
      private transient Shape upArrow;
      
      /** The shape used for a down arrow. */
      private transient Shape downArrow;
      
      /** The shape used for a left arrow. */
      private transient Shape leftArrow;
      
      /** The shape used for a right arrow. */
      private transient Shape rightArrow;
      
      /** A flag that affects the orientation of the values on the axis. */
      private boolean inverted;
  
      /** The axis range. */
      private Range range;
  
      /** 
       * Flag that indicates whether the axis automatically scales to fit the 
       * chart data. 
       */
      private boolean autoRange;
  
      /** The minimum size for the 'auto' axis range (excluding margins). */
      private double autoRangeMinimumSize;
  
      /**
       * The upper margin percentage.  This indicates the amount by which the 
       * maximum axis value exceeds the maximum data value (as a percentage of 
       * the range on the axis) when the axis range is determined automatically.
       */
      private double upperMargin;
  
      /**
       * The lower margin.  This is a percentage that indicates the amount by
       * which the minimum axis value is "less than" the minimum data value when
       * the axis range is determined automatically.
       */
      private double lowerMargin;
  
      /**
       * If this value is positive, the amount is subtracted from the maximum
       * data value to determine the lower axis range.  This can be used to
       * provide a fixed "window" on dynamic data.
       */
      private double fixedAutoRange;
  
      /** 
       * Flag that indicates whether or not the tick unit is selected 
       * automatically. 
       */
      private boolean autoTickUnitSelection;
  
      /** The standard tick units for the axis. */
      private TickUnitSource standardTickUnits;
  
      /** An index into an array of standard tick values. */
      private int autoTickIndex;
      
      /** A flag indicating whether or not tick labels are rotated to vertical. */
      private boolean verticalTickLabels;
  
      /**
       * Constructs a value axis.
       *
       * @param label  the axis label.
       * @param standardTickUnits  the source for standard tick units 
       *                           (<code>null</code> permitted).
       */
      protected ValueAxis(String label, TickUnitSource standardTickUnits) {
  
          super(label);
  
          this.positiveArrowVisible = false;
          this.negativeArrowVisible = false;
  
          this.range = DEFAULT_RANGE;
          this.autoRange = DEFAULT_AUTO_RANGE;
  
          this.inverted = DEFAULT_INVERTED;
          this.autoRangeMinimumSize = DEFAULT_AUTO_RANGE_MINIMUM_SIZE;
  
          this.lowerMargin = DEFAULT_LOWER_MARGIN;
          this.upperMargin = DEFAULT_UPPER_MARGIN;
  
          this.fixedAutoRange = 0.0;
  
          this.autoTickUnitSelection = DEFAULT_AUTO_TICK_UNIT_SELECTION;
          this.standardTickUnits = standardTickUnits;
          
          Polygon p1 = new Polygon();
          p1.addPoint(0, 0);
          p1.addPoint(-2, 2);
          p1.addPoint(2, 2);
          
          this.upArrow = p1;
  
          Polygon p2 = new Polygon();
          p2.addPoint(0, 0);
          p2.addPoint(-2, -2);
          p2.addPoint(2, -2);
  
          this.downArrow = p2;
  
          Polygon p3 = new Polygon();
          p3.addPoint(0, 0);
          p3.addPoint(-2, -2);
          p3.addPoint(-2, 2);
          
          this.rightArrow = p3;
  
          Polygon p4 = new Polygon();
          p4.addPoint(0, 0);
          p4.addPoint(2, -2);
          p4.addPoint(2, 2);
  
          this.leftArrow = p4;
          
          this.verticalTickLabels = false;
          
      }
  
      /**
       * Returns <code>true</code> if the tick labels should be rotated (to 
       * vertical), and <code>false</code> otherwise.
       *
       * @return <code>true</code> or <code>false</code>.
       */
      public boolean isVerticalTickLabels() {
          return this.verticalTickLabels;
      }
  
      /**
       * Sets the flag that controls whether the tick labels are displayed 
       * vertically (that is, rotated 90 degrees from horizontal).  If the flag 
       * is changed, an {@link AxisChangeEvent} is sent to all registered 
       * listeners.
       *
       * @param flag  the flag.
       */
      public void setVerticalTickLabels(boolean flag) {
          if (this.verticalTickLabels != flag) {
              this.verticalTickLabels = flag;
              notifyListeners(new AxisChangeEvent(this));
          }
      }
  
      /**
       * Returns a flag that controls whether or not the axis line has an arrow 
       * drawn that points in the positive direction for the axis.
       * 
       * @return A boolean.
       */
      public boolean isPositiveArrowVisible() {
          return this.positiveArrowVisible;
      }
      
      /**
       * Sets a flag that controls whether or not the axis lines has an arrow 
       * drawn that points in the positive direction for the axis, and sends an 
       * {@link AxisChangeEvent} to all registered listeners.
       * 
       * @param visible  the flag.
       */
      public void setPositiveArrowVisible(boolean visible) {
          this.positiveArrowVisible = visible;
          notifyListeners(new AxisChangeEvent(this));
      }
      
      /**
       * Returns a flag that controls whether or not the axis line has an arrow 
       * drawn that points in the negative direction for the axis.
       * 
       * @return A boolean.
       */
      public boolean isNegativeArrowVisible() {
          return this.negativeArrowVisible;
      }
      
      /**
       * Sets a flag that controls whether or not the axis lines has an arrow 
       * drawn that points in the negative direction for the axis, and sends an 
       * {@link AxisChangeEvent} to all registered listeners.
       * 
       * @param visible  the flag.
       */
      public void setNegativeArrowVisible(boolean visible) {
          this.negativeArrowVisible = visible;
          notifyListeners(new AxisChangeEvent(this));
      }
      
      /**
       * Returns a shape that can be displayed as an arrow pointing upwards at 
       * the end of an axis line.
       * 
       * @return A shape (never <code>null</code>).
       */
      public Shape getUpArrow() {
          return this.upArrow;   
      }
      
      /**
       * Sets the shape that can be displayed as an arrow pointing upwards at 
       * the end of an axis line and sends an {@link AxisChangeEvent} to all 
       * registered listeners.
       * 
       * @param arrow  the arrow shape (<code>null</code> not permitted).
       */
      public void setUpArrow(Shape arrow) {
          if (arrow == null) {
              throw new IllegalArgumentException("Null 'arrow' argument.");   
          }
          this.upArrow = arrow;
          notifyListeners(new AxisChangeEvent(this));
      }
      
      /**
       * Returns a shape that can be displayed as an arrow pointing downwards at 
       * the end of an axis line.
       * 
       * @return A shape (never <code>null</code>).
       */
      public Shape getDownArrow() {
          return this.downArrow;   
      }
      
      /**
       * Sets the shape that can be displayed as an arrow pointing downwards at 
       * the end of an axis line and sends an {@link AxisChangeEvent} to all 
       * registered listeners.
       * 
       * @param arrow  the arrow shape (<code>null</code> not permitted).
       */
      public void setDownArrow(Shape arrow) {
          if (arrow == null) {
              throw new IllegalArgumentException("Null 'arrow' argument.");   
          }
          this.downArrow = arrow;
          notifyListeners(new AxisChangeEvent(this));
      }
      
      /**
       * Returns a shape that can be displayed as an arrow pointing left at the 
       * end of an axis line.
       * 
       * @return A shape (never <code>null</code>).
       */
      public Shape getLeftArrow() {
          return this.leftArrow;   
      }
      
      /**
       * Sets the shape that can be displayed as an arrow pointing left at the 
       * end of an axis line and sends an {@link AxisChangeEvent} to all 
       * registered listeners.
       * 
       * @param arrow  the arrow shape (<code>null</code> not permitted).
       */
      public void setLeftArrow(Shape arrow) {
          if (arrow == null) {
              throw new IllegalArgumentException("Null 'arrow' argument.");   
          }
          this.leftArrow = arrow;
          notifyListeners(new AxisChangeEvent(this));
      }
      
      /**
       * Returns a shape that can be displayed as an arrow pointing right at the 
       * end of an axis line.
       * 
       * @return A shape (never <code>null</code>).
       */
      public Shape getRightArrow() {
          return this.rightArrow;   
      }
      
      /**
       * Sets the shape that can be displayed as an arrow pointing rightwards at 
       * the end of an axis line and sends an {@link AxisChangeEvent} to all 
       * registered listeners.
       * 
       * @param arrow  the arrow shape (<code>null</code> not permitted).
       */
      public void setRightArrow(Shape arrow) {
          if (arrow == null) {
              throw new IllegalArgumentException("Null 'arrow' argument.");   
          }
          this.rightArrow = arrow;
          notifyListeners(new AxisChangeEvent(this));
      }
      
      /**
       * Draws an axis line at the current cursor position and edge.
       * 
       * @param g2  the graphics device.
       * @param cursor  the cursor position.
       * @param dataArea  the data area.
       * @param edge  the edge.
       */
      protected void drawAxisLine(Graphics2D g2, double cursor,
                                  Rectangle2D dataArea, RectangleEdge edge) {
          Line2D axisLine = null;
          if (edge == RectangleEdge.TOP) {
              axisLine = new Line2D.Double(
                  dataArea.getX(), cursor, dataArea.getMaxX(), cursor
              );  
          }
          else if (edge == RectangleEdge.BOTTOM) {
              axisLine = new Line2D.Double(
                  dataArea.getX(), cursor, dataArea.getMaxX(), cursor
              );  
          }
          else if (edge == RectangleEdge.LEFT) {
              axisLine = new Line2D.Double(
                  cursor, dataArea.getY(), cursor, dataArea.getMaxY()
              );  
          }
          else if (edge == RectangleEdge.RIGHT) {
              axisLine = new Line2D.Double(
                  cursor, dataArea.getY(), cursor, dataArea.getMaxY()
              );  
          }
          g2.setPaint(getAxisLinePaint());
          g2.setStroke(getAxisLineStroke());
          g2.draw(axisLine);
          
          boolean drawUpOrRight = false;  
          boolean drawDownOrLeft = false;
          if (this.positiveArrowVisible) {
              if (this.inverted) {
                  drawDownOrLeft = true;   
              }
              else {
                  drawUpOrRight = true;   
              }
          }
          if (this.negativeArrowVisible) {
              if (this.inverted) {
                  drawUpOrRight = true;   
              }
              else {
                  drawDownOrLeft = true;   
              }
          }
          if (drawUpOrRight) {
              double x = 0.0;
              double y = 0.0;
              Shape arrow = null;
              if (edge == RectangleEdge.TOP || edge == RectangleEdge.BOTTOM) {
                  x = dataArea.getMaxX();
                  y = cursor;
                  arrow = this.rightArrow; 
              }
              else if (edge == RectangleEdge.LEFT 
                      || edge == RectangleEdge.RIGHT) {
                  x = cursor;
                  y = dataArea.getMinY();
                  arrow = this.upArrow; 
              }
  
              // draw the arrow...
              AffineTransform transformer = new AffineTransform();
              transformer.setToTranslation(x, y);
              Shape shape = transformer.createTransformedShape(arrow);
              g2.fill(shape);
              g2.draw(shape);
          }
          
          if (drawDownOrLeft) {
              double x = 0.0;
              double y = 0.0;
              Shape arrow = null;
              if (edge == RectangleEdge.TOP || edge == RectangleEdge.BOTTOM) {
                  x = dataArea.getMinX();
                  y = cursor;
                  arrow = this.leftArrow; 
              }
              else if (edge == RectangleEdge.LEFT 
                      || edge == RectangleEdge.RIGHT) {
                  x = cursor;
                  y = dataArea.getMaxY();
                  arrow = this.downArrow; 
              }
  
              // draw the arrow...
              AffineTransform transformer = new AffineTransform();
              transformer.setToTranslation(x, y);
              Shape shape = transformer.createTransformedShape(arrow);
              g2.fill(shape);
              g2.draw(shape);
          }
          
      }
      
      /**
       * Calculates the anchor point for a tick label.
       * 
       * @param tick  the tick.
       * @param cursor  the cursor.
       * @param dataArea  the data area.
       * @param edge  the edge on which the axis is drawn.
       * 
       * @return The x and y coordinates of the anchor point.
       */
      protected float[] calculateAnchorPoint(ValueTick tick, 
                                             double cursor, 
                                             Rectangle2D dataArea, 
                                             RectangleEdge edge) {
      
          RectangleInsets insets = getTickLabelInsets();
          float[] result = new float[2];
          if (edge == RectangleEdge.TOP) {
              result[0] = (float) valueToJava2D(tick.getValue(), dataArea, edge);
              result[1] = (float) (cursor - insets.getBottom() - 2.0);
          }
          else if (edge == RectangleEdge.BOTTOM) {
              result[0] = (float) valueToJava2D(tick.getValue(), dataArea, edge);
              result[1] = (float) (cursor + insets.getTop() + 2.0); 
          }
          else if (edge == RectangleEdge.LEFT) {
              result[0] = (float) (cursor - insets.getLeft() - 2.0);    
              result[1] = (float) valueToJava2D(tick.getValue(), dataArea, edge);
          }
  