gt:         RegularTimePeriod p2 = this.labelInfo[band].createInstance(
context: /* ===========================================================
   * JFreeChart : a free chart library for the Java(tm) platform
   * ===========================================================
   *
   * (C) Copyright 2000-2004, 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., 59 Temple Place, Suite 330,
   * Boston, MA 02111-1307, USA.
   *
   * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
   * in the United States and other countries.]
   *
   * ---------------
   * PeriodAxis.java
   * ---------------
   * (C) Copyright 2004, by Object Refinery Limited and Contributors.
   *
   * Original Author:  David Gilbert (for Object Refinery Limited);
   * Contributor(s):   -;
   *
   * $Id: PeriodAxis.java,v 1.1 2004/08/31 14:30:24 mungady Exp $
   *
   * Changes
   * -------
   * 01-Jun-2004 : Version 1 (DG);
   *
   */
  
  package org.jfree.chart.axis;
  
  import java.awt.FontMetrics;
  import java.awt.Graphics2D;
  import java.awt.geom.Line2D;
  import java.awt.geom.Rectangle2D;
  import java.io.Serializable;
  import java.lang.reflect.Constructor;
  import java.text.DateFormat;
  import java.util.Arrays;
  import java.util.Date;
  import java.util.List;
  import java.util.TimeZone;
  
  import org.jfree.chart.event.AxisChangeEvent;
  import org.jfree.chart.plot.Plot;
  import org.jfree.chart.plot.PlotRenderingInfo;
  import org.jfree.chart.plot.ValueAxisPlot;
  import org.jfree.data.Range;
  import org.jfree.data.time.Day;
  import org.jfree.data.time.Month;
  import org.jfree.data.time.RegularTimePeriod;
  import org.jfree.data.time.Year;
  import org.jfree.text.TextUtilities;
  import org.jfree.ui.RectangleEdge;
  import org.jfree.ui.TextAnchor;
  import org.jfree.util.Log;
  import org.jfree.util.LogContext;
  
  /**
   * An axis that displays a date scale based on a {@link org.jfree.data.time.RegularTimePeriod}.
   */
  public class PeriodAxis extends ValueAxis implements Cloneable, Serializable {
      
      /** The first time period in the overall range. */
      private RegularTimePeriod first;
      
      /** The last time period in the overall range. */
      private RegularTimePeriod last;
      
      /** The time zone used to convert 'first' and 'last' to absolute milliseconds. */
      private TimeZone timeZone;
      
      /** The {@link RegularTimePeriod} subclass used to automatically determine the axis range. */
      private Class autoRangeTimePeriodClass;
      
      /** Info for each labelling band. */
      private PeriodAxisLabelInfo[] labelInfo;
      
      /** Access to logging facilities. */
      private static final LogContext LOGGER = Log.createContext(PeriodAxis.class);
  
      /**
       * Creates a new axis.
       * 
       * @param label  the axis label.
       */
      public PeriodAxis(String label) {
          this(label, new Day(), new Day());
      }
      
      /**
       * Creates a new axis.
       * 
       * @param label  the axis label (<code>null</code> permitted).
       * @param first  the first time period in the axis range (<code>null</code> not permitted).
       * @param last  the last time period in the axis range (<code>null</code> not permitted).
       */
      public PeriodAxis(String label, RegularTimePeriod first, RegularTimePeriod last) {
          this(label, first, last, TimeZone.getDefault());
      }
      
      /**
       * Creates a new axis.
       * 
       * @param label  the axis label (<code>null</code> permitted).
       * @param first  the first time period in the axis range (<code>null</code> not permitted).
       * @param last  the last time period in the axis range (<code>null</code> not permitted).
       * @param timeZone  the time zone (<code>null</code> not permitted).
       */
      public PeriodAxis(String label, 
                        RegularTimePeriod first, RegularTimePeriod last, TimeZone timeZone) {
          super(label, null);
          this.first = first;
          this.last = last;
          this.timeZone = timeZone;
          this.autoRangeTimePeriodClass = Day.class;
          setAutoRange(true);
          this.labelInfo = new PeriodAxisLabelInfo[2];
          this.labelInfo[0] = new PeriodAxisLabelInfo(Month.class, "MMM");
          this.labelInfo[1] = new PeriodAxisLabelInfo(Year.class, "yyyy");
          
      }
      
      /**
       * Returns the first time period in the axis range.
       * 
       * @return The first time period (never <code>null</code>).
       */
      public RegularTimePeriod getFirst() {
          return this.first;
      }
      
      /**
       * Sets the first time period in the axis range and sends an {@link AxisChangeEvent} to
       * all registered listeners.
       * 
       * @param first  the time period (<code>null</code> not permitted).
       */
      public void setFirst(RegularTimePeriod first) {
          if (first == null) {
              throw new IllegalArgumentException("Null 'first' argument.");   
          }
          this.first = first;   
          notifyListeners(new AxisChangeEvent(this));
      }
      
      /**
       * Returns the last time period in the axis range.
       * 
       * @return The last time period (never <code>null</code>).
       */
      public RegularTimePeriod getLast() {
          return this.last;
      }
      
      /**
       * Sets the last time period in the axis range and sends an {@link AxisChangeEvent} to
       * all registered listeners.
       * 
       * @param last  the time period (<code>null</code> not permitted).
       */
      public void setLast(RegularTimePeriod last) {
          if (last == null) {
              throw new IllegalArgumentException("Null 'last' argument.");   
          }
          this.last = last;   
          notifyListeners(new AxisChangeEvent(this));
      }
      
      /**
       * Returns the time zone used to convert the periods defining the axis range into absolute
       * milliseconds.
       * 
       * @return The time zone (never <code>null</code>).
       */
      public TimeZone getTimeZone() {
          return this.timeZone;   
      }
      
      /**
       * Sets the time zone that is used to convert the time periods into absolute milliseconds.
       * 
       * @param zone  the time zone (<code>null</code> not permitted).
       */
      public void setTimeZone(TimeZone zone) {
          if (zone == null) {
              throw new IllegalArgumentException("Null 'zone' argument.");   
          }
          this.timeZone = zone;
          notifyListeners(new AxisChangeEvent(this));
      }
      
      /**
       * Returns the class used to create the first and last time periods for the axis range
       * when the auto-range flag is set to <code>true</code>.
       * 
       * @return The class (never <code>null</code>).
       */
      public Class getAutoRangeTimePeriodClass() {
          return this.autoRangeTimePeriodClass;   
      }
      
      /**
       * Sets the class used to create the first and last time periods for the axis range
       * when the auto-range flag is set to <code>true</code> and sends an {@link AxisChangeEvent} 
       * to all registered listeners.
       * 
       * @param c  the class (<code>null</code> not permitted).
       */
      public void setAutoRangeTimePeriodClass(Class c) {
          if (c == null) {
              throw new IllegalArgumentException("Null 'c' argument.");   
          }
          this.autoRangeTimePeriodClass = c;   
          notifyListeners(new AxisChangeEvent(this));
      }
      
      /**
       * Returns an array of label info records.
       * 
       * @return An array.
       */
      public PeriodAxisLabelInfo[] getLabelInfo() {
          return this.labelInfo;    
      }
      
      /**
       * Sets the array of label info records.
       * 
       * @param info  the info.
       */
      public void setLabelInfo(PeriodAxisLabelInfo[] info) {
          this.labelInfo = info;   
      }
      
      /**
       * Returns the range for the axis.
       *
       * @return The axis range (never <code>null</code>).
       */
      public Range getRange() {
          // TODO: find a cleaner way to do this...
          return new Range(
              this.first.getFirstMillisecond(this.timeZone), 
              this.last.getLastMillisecond(this.timeZone)
          );
      }
  
      /**
       * Configures the axis to work with the current plot.  Override this method
       * to perform any special processing (such as auto-rescaling).
       */
      public void configure() {
          autoAdjustRange();
      }
  
      /**
       * Estimates the space (height or width) required to draw the axis.
       *
       * @param g2  the graphics device.
       * @param plot  the plot that the axis belongs to.
       * @param plotArea  the area within which the plot (including axes) should be drawn.
       * @param edge  the axis location.
       * @param space  space already reserved.
       *
       * @return The space required to draw the axis (including pre-reserved space).
       */
      public AxisSpace reserveSpace(Graphics2D g2, Plot plot, 
                                    Rectangle2D plotArea, RectangleEdge edge, 
                                    AxisSpace space) {
          // create a new space object if one wasn't supplied...
          if (space == null) {
              space = new AxisSpace();
          }
          
          // if the axis is not visible, no additional space is required...
          if (!isVisible()) {
              return space;
          }
  
          // if the axis has a fixed dimension, return it...
          double dimension = getFixedDimension();
          if (dimension > 0.0) {
              space.ensureAtLeast(dimension, edge);
          }
          
          // get the axis label size and update the space object...
          Rectangle2D labelEnclosure = getLabelEnclosure(g2, edge);
          double labelHeight = 0.0;
          double labelWidth = 0.0;
          double tickLabelBandsDimension = 0.0;
          
          for (int i = 0; i < this.labelInfo.length; i++) {
              PeriodAxisLabelInfo info = this.labelInfo[i];
              FontMetrics fm = g2.getFontMetrics(info.getLabelFont());
              tickLabelBandsDimension += info.getSpacer().getAdjustedHeight(fm.getHeight());
          }
          
          if (RectangleEdge.isTopOrBottom(edge)) {
              labelHeight = labelEnclosure.getHeight();
              space.add(labelHeight + tickLabelBandsDimension, edge);
          }
          else if (RectangleEdge.isLeftOrRight(edge)) {
              labelWidth = labelEnclosure.getWidth();
              space.add(labelWidth + tickLabelBandsDimension, edge);
          }
  
          return space;
      }
  
      /**
       * Draws the axis on a Java 2D graphics device (such as the screen or a printer).
       *
       * @param g2  the graphics device (<code>null</code> not permitted).
       * @param cursor  the cursor location (determines where to draw the axis).
       * @param plotArea  the area within which the axes and plot should be drawn.
       * @param dataArea  the area within which the data should be drawn.
       * @param edge  the axis location (<code>null</code> not permitted).
       * @param plotState  collects information about the plot (<code>null</code> permitted).
       * 
       * @return The axis state (never <code>null</code>).
       */
      public AxisState draw(Graphics2D g2, 
                            double cursor,
                            Rectangle2D plotArea, 
                            Rectangle2D dataArea,
                            RectangleEdge edge,
                            PlotRenderingInfo plotState) {
          
          LOGGER.debug("In draw() method (cursor = " + cursor + ")");
          AxisState axisState = new AxisState(cursor);
          drawAxisLine(g2, cursor, dataArea, edge);
          for (int band = 0; band < this.labelInfo.length; band++) {
              axisState = drawTickLabels(band, g2, axisState, dataArea, edge);
          }
          
          // draw the axis label (note that 'state' is passed in *and* returned)...
          axisState = drawLabel(getLabel(), g2, plotArea, dataArea, edge, axisState);
          return axisState;
          
      }
      
      /**
       * Draws the tick labels for one "band" of time periods.
       * 
       * @param band  the band index (zero-based).
       * @param g2  the graphics device.
       * @param state  the axis state.
       * @param dataArea  the data area.
       * @param edge  the edge where the axis is located.
       * 
       * @return The updated axis state.
       */
      protected AxisState drawTickLabels(int band, Graphics2D g2, AxisState state, 
                                         Rectangle2D dataArea, RectangleEdge edge) {
  
          // work out the initial gap
          double delta1 = 0.0;
          FontMetrics fm = g2.getFontMetrics(this.labelInfo[band].getLabelFont());
          if (edge == RectangleEdge.BOTTOM) {
              delta1 = this.labelInfo[band].getSpacer().getTopSpace(fm.getHeight());   
          }
          else if (edge == RectangleEdge.TOP) {
              delta1 = this.labelInfo[band].getSpacer().getBottomSpace(fm.getHeight());   
          }
          state.moveCursor(delta1, edge);
          long axisMin = this.first.getFirstMillisecond(this.timeZone);
          long axisMax = this.last.getLastMillisecond(this.timeZone);
          g2.setFont(this.labelInfo[band].getLabelFont());
          g2.setPaint(this.labelInfo[band].getLabelPaint());
  
          // work out the number of periods to skip for labelling
          RegularTimePeriod p1 = this.labelInfo[band].createInstance(
              new Date(axisMin), this.timeZone
          );
  