KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > axis > PeriodAxis


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2006, 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  * PeriodAxis.java
29  * ---------------
30  * (C) Copyright 2004-2006, by Object Refinery Limited and Contributors.
31  *
32  * Original Author: David Gilbert (for Object Refinery Limited);
33  * Contributor(s): -;
34  *
35  * $Id: PeriodAxis.java,v 1.16.2.5 2006/10/06 14:00:06 mungady Exp $
36  *
37  * Changes
38  * -------
39  * 01-Jun-2004 : Version 1 (DG);
40  * 16-Sep-2004 : Fixed bug in equals() method, added clone() method and
41  * PublicCloneable interface (DG);
42  * 25-Nov-2004 : Updates to support major and minor tick marks (DG);
43  * 25-Feb-2005 : Fixed some tick mark bugs (DG);
44  * 15-Apr-2005 : Fixed some more tick mark bugs (DG);
45  * 26-Apr-2005 : Removed LOGGER (DG);
46  * 16-Jun-2005 : Fixed zooming (DG);
47  * 15-Sep-2005 : Changed configure() method to check autoRange flag,
48  * and added ticks to state (DG);
49  * ------------- JFREECHART 1.0.x ---------------------------------------------
50  * 06-Oct-2006 : Updated for deprecations in RegularTimePeriod and
51  * subclasses (DG);
52  *
53  */

54
55 package org.jfree.chart.axis;
56
57 import java.awt.BasicStroke JavaDoc;
58 import java.awt.Color JavaDoc;
59 import java.awt.FontMetrics JavaDoc;
60 import java.awt.Graphics2D JavaDoc;
61 import java.awt.Paint JavaDoc;
62 import java.awt.Stroke JavaDoc;
63 import java.awt.geom.Line2D JavaDoc;
64 import java.awt.geom.Rectangle2D JavaDoc;
65 import java.io.IOException JavaDoc;
66 import java.io.ObjectInputStream JavaDoc;
67 import java.io.ObjectOutputStream JavaDoc;
68 import java.io.Serializable JavaDoc;
69 import java.lang.reflect.Constructor JavaDoc;
70 import java.text.DateFormat JavaDoc;
71 import java.text.SimpleDateFormat JavaDoc;
72 import java.util.ArrayList JavaDoc;
73 import java.util.Arrays JavaDoc;
74 import java.util.Calendar JavaDoc;
75 import java.util.Collections JavaDoc;
76 import java.util.Date JavaDoc;
77 import java.util.List JavaDoc;
78 import java.util.TimeZone JavaDoc;
79
80 import org.jfree.chart.event.AxisChangeEvent;
81 import org.jfree.chart.plot.Plot;
82 import org.jfree.chart.plot.PlotRenderingInfo;
83 import org.jfree.chart.plot.ValueAxisPlot;
84 import org.jfree.data.Range;
85 import org.jfree.data.time.Day;
86 import org.jfree.data.time.Month;
87 import org.jfree.data.time.RegularTimePeriod;
88 import org.jfree.data.time.Year;
89 import org.jfree.io.SerialUtilities;
90 import org.jfree.text.TextUtilities;
91 import org.jfree.ui.RectangleEdge;
92 import org.jfree.ui.TextAnchor;
93 import org.jfree.util.PublicCloneable;
94
95 /**
96  * An axis that displays a date scale based on a
97  * {@link org.jfree.data.time.RegularTimePeriod}. This axis works when
98  * displayed across the bottom or top of a plot, but is broken for display at
99  * the left or right of charts.
100  */

101 public class PeriodAxis extends ValueAxis
102                         implements Cloneable JavaDoc, PublicCloneable, Serializable JavaDoc {
103     
104     /** For serialization. */
105     private static final long serialVersionUID = 8353295532075872069L;
106     
107     /** The first time period in the overall range. */
108     private RegularTimePeriod first;
109     
110     /** The last time period in the overall range. */
111     private RegularTimePeriod last;
112     
113     /**
114      * The time zone used to convert 'first' and 'last' to absolute
115      * milliseconds.
116      */

117     private TimeZone JavaDoc timeZone;
118     
119     /**
120      * A calendar used for date manipulations in the current time zone.
121      */

122     private Calendar JavaDoc calendar;
123     
124     /**
125      * The {@link RegularTimePeriod} subclass used to automatically determine
126      * the axis range.
127      */

128     private Class JavaDoc autoRangeTimePeriodClass;
129     
130     /**
131      * Indicates the {@link RegularTimePeriod} subclass that is used to
132      * determine the spacing of the major tick marks.
133      */

134     private Class JavaDoc majorTickTimePeriodClass;
135     
136     /**
137      * A flag that indicates whether or not tick marks are visible for the
138      * axis.
139      */

140     private boolean minorTickMarksVisible;
141
142     /**
143      * Indicates the {@link RegularTimePeriod} subclass that is used to
144      * determine the spacing of the minor tick marks.
145      */

146     private Class JavaDoc minorTickTimePeriodClass;
147     
148     /** The length of the tick mark inside the data area (zero permitted). */
149     private float minorTickMarkInsideLength = 0.0f;
150
151     /** The length of the tick mark outside the data area (zero permitted). */
152     private float minorTickMarkOutsideLength = 2.0f;
153
154     /** The stroke used to draw tick marks. */
155     private transient Stroke JavaDoc minorTickMarkStroke = new BasicStroke JavaDoc(0.5f);
156
157     /** The paint used to draw tick marks. */
158     private transient Paint JavaDoc minorTickMarkPaint = Color.black;
159     
160     /** Info for each labelling band. */
161     private PeriodAxisLabelInfo[] labelInfo;
162
163     /**
164      * Creates a new axis.
165      *
166      * @param label the axis label.
167      */

168     public PeriodAxis(String JavaDoc label) {
169         this(label, new Day(), new Day());
170     }
171     
172     /**
173      * Creates a new axis.
174      *
175      * @param label the axis label (<code>null</code> permitted).
176      * @param first the first time period in the axis range
177      * (<code>null</code> not permitted).
178      * @param last the last time period in the axis range
179      * (<code>null</code> not permitted).
180      */

181     public PeriodAxis(String JavaDoc label,
182                       RegularTimePeriod first, RegularTimePeriod last) {
183         this(label, first, last, TimeZone.getDefault());
184     }
185     
186     /**
187      * Creates a new axis.
188      *
189      * @param label the axis label (<code>null</code> permitted).
190      * @param first the first time period in the axis range
191      * (<code>null</code> not permitted).
192      * @param last the last time period in the axis range
193      * (<code>null</code> not permitted).
194      * @param timeZone the time zone (<code>null</code> not permitted).
195      */

196     public PeriodAxis(String JavaDoc label,
197                       RegularTimePeriod first, RegularTimePeriod last,
198                       TimeZone JavaDoc timeZone) {
199         
200         super(label, null);
201         this.first = first;
202         this.last = last;
203         this.timeZone = timeZone;
204         this.calendar = Calendar.getInstance(timeZone);
205         this.autoRangeTimePeriodClass = first.getClass();
206         this.majorTickTimePeriodClass = first.getClass();
207         this.minorTickMarksVisible = false;
208         this.minorTickTimePeriodClass = RegularTimePeriod.downsize(
209                 this.majorTickTimePeriodClass);
210         setAutoRange(true);
211         this.labelInfo = new PeriodAxisLabelInfo[2];
212         this.labelInfo[0] = new PeriodAxisLabelInfo(Month.class,
213                 new SimpleDateFormat JavaDoc("MMM"));
214         this.labelInfo[1] = new PeriodAxisLabelInfo(Year.class,
215                 new SimpleDateFormat JavaDoc("yyyy"));
216         
217     }
218     
219     /**
220      * Returns the first time period in the axis range.
221      *
222      * @return The first time period (never <code>null</code>).
223      */

224     public RegularTimePeriod getFirst() {
225         return this.first;
226     }
227     
228     /**
229      * Sets the first time period in the axis range and sends an
230      * {@link AxisChangeEvent} to all registered listeners.
231      *
232      * @param first the time period (<code>null</code> not permitted).
233      */

234     public void setFirst(RegularTimePeriod first) {
235         if (first == null) {
236             throw new IllegalArgumentException JavaDoc("Null 'first' argument.");
237         }
238         this.first = first;
239         notifyListeners(new AxisChangeEvent(this));
240     }
241     
242     /**
243      * Returns the last time period in the axis range.
244      *
245      * @return The last time period (never <code>null</code>).
246      */

247     public RegularTimePeriod getLast() {
248         return this.last;
249     }
250     
251     /**
252      * Sets the last time period in the axis range and sends an
253      * {@link AxisChangeEvent} to all registered listeners.
254      *
255      * @param last the time period (<code>null</code> not permitted).
256      */

257     public void setLast(RegularTimePeriod last) {
258         if (last == null) {
259             throw new IllegalArgumentException JavaDoc("Null 'last' argument.");
260         }
261         this.last = last;
262         notifyListeners(new AxisChangeEvent(this));
263     }
264     
265     /**
266      * Returns the time zone used to convert the periods defining the axis
267      * range into absolute milliseconds.
268      *
269      * @return The time zone (never <code>null</code>).
270      */

271     public TimeZone JavaDoc getTimeZone() {
272         return this.timeZone;
273     }
274     
275     /**
276      * Sets the time zone that is used to convert the time periods into
277      * absolute milliseconds.
278      *
279      * @param zone the time zone (<code>null</code> not permitted).
280      */

281     public void setTimeZone(TimeZone JavaDoc zone) {
282         if (zone == null) {
283             throw new IllegalArgumentException JavaDoc("Null 'zone' argument.");
284         }
285         this.timeZone = zone;
286         this.calendar = Calendar.getInstance(zone);
287         notifyListeners(new AxisChangeEvent(this));
288     }
289     
290     /**
291      * Returns the class used to create the first and last time periods for
292      * the axis range when the auto-range flag is set to <code>true</code>.
293      *
294      * @return The class (never <code>null</code>).
295      */

296     public Class JavaDoc getAutoRangeTimePeriodClass() {
297         return this.autoRangeTimePeriodClass;
298     }
299     
300     /**
301      * Sets the class used to create the first and last time periods for the
302      * axis range when the auto-range flag is set to <code>true</code> and
303      * sends an {@link AxisChangeEvent} to all registered listeners.
304      *
305      * @param c the class (<code>null</code> not permitted).
306      */

307     public void setAutoRangeTimePeriodClass(Class JavaDoc c) {
308         if (c == null) {
309             throw new IllegalArgumentException JavaDoc("Null 'c' argument.");
310         }
311         this.autoRangeTimePeriodClass = c;
312         notifyListeners(new AxisChangeEvent(this));
313     }
314     
315     /**
316      * Returns the class that controls the spacing of the major tick marks.
317      *
318      * @return The class (never <code>null</code>).
319      */

320     public Class JavaDoc getMajorTickTimePeriodClass() {
321         return this.majorTickTimePeriodClass;
322     }
323     
324     /**
325      * Sets the class that controls the spacing of the major tick marks, and
326      * sends an {@link AxisChangeEvent} to all registered listeners.
327      *
328      * @param c the class (a subclass of {@link RegularTimePeriod} is
329      * expected).
330      */

331     public void setMajorTickTimePeriodClass(Class JavaDoc c) {
332         if (c == null) {
333             throw new IllegalArgumentException JavaDoc("Null 'c' argument.");
334         }
335         this.majorTickTimePeriodClass = c;
336         notifyListeners(new AxisChangeEvent(this));
337     }
338     
339     /**
340      * Returns the flag that controls whether or not minor tick marks
341      * are displayed for the axis.
342      *
343      * @return A boolean.
344      */

345     public boolean isMinorTickMarksVisible() {
346         return this.minorTickMarksVisible;
347     }
348     
349     /**
350      * Sets the flag that controls whether or not minor tick marks
351      * are displayed for the axis, and sends a {@link AxisChangeEvent}
352      * to all registered listeners.
353      *
354      * @param visible the flag.
355      */

356     public void setMinorTickMarksVisible(boolean visible) {
357         this.minorTickMarksVisible = visible;
358         notifyListeners(new AxisChangeEvent(this));
359     }
360     
361     /**
362      * Returns the class that controls the spacing of the minor tick marks.
363      *
364      * @return The class (never <code>null</code>).
365      */

366     public Class JavaDoc getMinorTickTimePeriodClass() {
367         return this.minorTickTimePeriodClass;
368     }
369     
370     /**
371      * Sets the class that controls the spacing of the minor tick marks, and
372      * sends an {@link AxisChangeEvent} to all registered listeners.
373      *
374      * @param c the class (a subclass of {@link RegularTimePeriod} is
375      * expected).
376      */

377     public void setMinorTickTimePeriodClass(Class JavaDoc c) {
378         if (c == null) {
379             throw new IllegalArgumentException JavaDoc("Null 'c' argument.");
380         }
381         this.minorTickTimePeriodClass = c;
382         notifyListeners(new AxisChangeEvent(this));
383     }
384     
385     /**
386      * Returns the stroke used to display minor tick marks, if they are
387      * visible.
388      *
389      * @return A stroke (never <code>null</code>).
390      */

391     public Stroke JavaDoc getMinorTickMarkStroke() {
392         return this.minorTickMarkStroke;
393     }
394     
395     /**
396      * Sets the stroke used to display minor tick marks, if they are
397      * visible, and sends a {@link AxisChangeEvent} to all registered
398      * listeners.
399      *
400      * @param stroke the stroke (<code>null</code> not permitted).
401      */

402     public void setMinorTickMarkStroke(Stroke JavaDoc stroke) {
403         if (stroke == null) {
404             throw new IllegalArgumentException JavaDoc("Null 'stroke' argument.");
405         }
406         this.minorTickMarkStroke = stroke;
407         notifyListeners(new AxisChangeEvent(this));
408     }
409     
410     /**
411      * Returns the paint used to display minor tick marks, if they are
412      * visible.
413      *
414      * @return A paint (never <code>null</code>).
415      */

416     public Paint JavaDoc getMinorTickMarkPaint() {
417         return this.minorTickMarkPaint;
418     }
419     
420     /**
421      * Sets the paint used to display minor tick marks, if they are
422      * visible, and sends a {@link AxisChangeEvent} to all registered
423      * listeners.
424      *
425      * @param paint the paint (<code>null</code> not permitted).
426      */

427     public void setMinorTickMarkPaint(Paint JavaDoc paint) {
428         if (paint == null) {
429             throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
430         }
431         this.minorTickMarkPaint = paint;
432         notifyListeners(new AxisChangeEvent(this));
433     }
434     
435     /**
436      * Returns the inside length for the minor tick marks.
437      *
438      * @return The length.
439      */

440     public float getMinorTickMarkInsideLength() {
441         return this.minorTickMarkInsideLength;
442     }
443     
444     /**
445      * Sets the inside length of the minor tick marks and sends an
446      * {@link AxisChangeEvent} to all registered listeners.
447      *
448      * @param length the length.
449      */

450     public void setMinorTickMarkInsideLength(float length) {
451         this.minorTickMarkInsideLength = length;
452         notifyListeners(new AxisChangeEvent(this));
453     }
454     
455     /**
456      * Returns the outside length for the minor tick marks.
457      *
458      * @return The length.
459      */

460     public float getMinorTickMarkOutsideLength() {
461         return this.minorTickMarkOutsideLength;
462     }
463     
464     /**
465      * Sets the outside length of the minor tick marks and sends an
466      * {@link AxisChangeEvent} to all registered listeners.
467      *
468      * @param length the length.
469      */

470     public void setMinorTickMarkOutsideLength(float length) {
471         this.minorTickMarkOutsideLength = length;
472         notifyListeners(new AxisChangeEvent(this));
473     }
474     
475     /**
476      * Returns an array of label info records.
477      *
478      * @return An array.
479      */

480     public PeriodAxisLabelInfo[] getLabelInfo() {
481         return this.labelInfo;
482     }
483     
484     /**
485      * Sets the array of label info records.
486      *
487      * @param info the info.
488      */

489     public void setLabelInfo(PeriodAxisLabelInfo[] info) {
490         this.labelInfo = info;
491     }
492     
493     /**
494      * Returns the range for the axis.
495      *
496      * @return The axis range (never <code>null</code>).
497      */

498     public Range getRange() {
499         // TODO: find a cleaner way to do this...
500
return new Range(this.first.getFirstMillisecond(this.calendar),
501                 this.last.getLastMillisecond(this.calendar));
502     }
503
504     /**
505      * Sets the range for the axis, if requested, sends an
506      * {@link AxisChangeEvent} to all registered listeners. As a side-effect,
507      * the auto-range flag is set to <code>false</code> (optional).
508      *
509      * @param range the range (<code>null</code> not permitted).
510      * @param turnOffAutoRange a flag that controls whether or not the auto
511      * range is turned off.
512      * @param notify a flag that controls whether or not listeners are
513      * notified.
514      */

515     public void setRange(Range range, boolean turnOffAutoRange,
516                          boolean notify) {
517         super.setRange(range, turnOffAutoRange, false);
518         long upper = Math.round(range.getUpperBound());
519         long lower = Math.round(range.getLowerBound());
520         this.first = createInstance(this.autoRangeTimePeriodClass,
521                 new Date JavaDoc(lower), this.timeZone);
522         this.last = createInstance(this.autoRangeTimePeriodClass,
523                 new Date JavaDoc(upper), this.timeZone);
524     }
525
526     /**
527      * Configures the axis to work with the current plot. Override this method
528      * to perform any special processing (such as auto-rescaling).
529      */

530     public void configure() {
531         if (this.isAutoRange()) {
532             autoAdjustRange();
533         }
534     }
535
536     /**
537      * Estimates the space (height or width) required to draw the axis.
538      *
539      * @param g2 the graphics device.
540      * @param plot the plot that the axis belongs to.
541      * @param plotArea the area within which the plot (including axes) should
542      * be drawn.
543      * @param edge the axis location.
544      * @param space space already reserved.
545      *
546      * @return The space required to draw the axis (including pre-reserved
547      * space).
548      */

549     public AxisSpace reserveSpace(Graphics2D JavaDoc g2, Plot plot,
550                                   Rectangle2D JavaDoc plotArea, RectangleEdge edge,
551                                   AxisSpace space) {
552         // create a new space object if one wasn't supplied...
553
if (space == null) {
554             space = new AxisSpace();
555         }
556         
557         // if the axis is not visible, no additional space is required...
558
if (!isVisible()) {
559             return space;
560         }
561
562         // if the axis has a fixed dimension, return it...
563
double dimension = getFixedDimension();
564         if (dimension > 0.0) {
565             space.ensureAtLeast(dimension, edge);
566         }
567         
568         // get the axis label size and update the space object...
569
Rectangle2D JavaDoc labelEnclosure = getLabelEnclosure(g2, edge);
570         double labelHeight = 0.0;
571         double labelWidth = 0.0;
572         double tickLabelBandsDimension = 0.0;
573         
574         for (int i = 0; i < this.labelInfo.length; i++) {
575             PeriodAxisLabelInfo info = this.labelInfo[i];
576             FontMetrics JavaDoc fm = g2.getFontMetrics(info.getLabelFont());
577             tickLabelBandsDimension
578                 += info.getPadding().extendHeight(fm.getHeight());
579         }
580         
581         if (RectangleEdge.isTopOrBottom(edge)) {
582             labelHeight = labelEnclosure.getHeight();
583             space.add(labelHeight + tickLabelBandsDimension, edge);
584         }
585         else if (RectangleEdge.isLeftOrRight(edge)) {
586             labelWidth = labelEnclosure.getWidth();
587             space.add(labelWidth + tickLabelBandsDimension, edge);
588         }
589
590         // add space for the outer tick labels, if any...
591
double tickMarkSpace = 0.0;
592         if (isTickMarksVisible()) {
593             tickMarkSpace = getTickMarkOutsideLength();
594         }
595         if (this.minorTickMarksVisible) {
596             tickMarkSpace = Math.max(tickMarkSpace,
597                     this.minorTickMarkOutsideLength);
598         }
599         space.add(tickMarkSpace, edge);
600         return space;
601     }
602
603     /**
604      * Draws the axis on a Java 2D graphics device (such as the screen or a
605      * printer).
606      *
607      * @param g2 the graphics device (<code>null</code> not permitted).
608      * @param cursor the cursor location (determines where to draw the axis).
609      * @param plotArea the area within which the axes and plot should be drawn.
610      * @param dataArea the area within which the data should be drawn.
611      * @param edge the axis location (<code>null</code> not permitted).
612      * @param plotState collects information about the plot
613      * (<code>null</code> permitted).
614      *
615      * @return The axis state (never <code>null</code>).
616      */

617     public AxisState draw(Graphics2D JavaDoc g2,
618                           double cursor,
619                           Rectangle2D JavaDoc plotArea,
620                           Rectangle2D JavaDoc dataArea,
621                           RectangleEdge edge,
622                           PlotRenderingInfo plotState) {
623         
624         AxisState axisState = new AxisState(cursor);
625         if (isAxisLineVisible()) {
626             drawAxisLine(g2, cursor, dataArea, edge);
627         }
628         drawTickMarks(g2, axisState, dataArea, edge);
629         for (int band = 0; band < this.labelInfo.length; band++) {
630             axisState = drawTickLabels(band, g2, axisState, dataArea, edge);
631         }
632         
633         // draw the axis label (note that 'state' is passed in *and*
634
// returned)...
635
axisState = drawLabel(getLabel(), g2, plotArea, dataArea, edge,
636                 axisState);
637         return axisState;
638         
639     }
640     
641     /**
642      * Draws the tick marks for the axis.
643      *
644      * @param g2 the graphics device.
645      * @param state the axis state.
646      * @param dataArea the data area.
647      * @param edge the edge.
648      */

649     protected void drawTickMarks(Graphics2D JavaDoc g2, AxisState state,
650                                  Rectangle2D JavaDoc dataArea,
651                                  RectangleEdge edge) {
652         if (RectangleEdge.isTopOrBottom(edge)) {
653             drawTickMarksHorizontal(g2, state, dataArea, edge);
654         }
655         else if (RectangleEdge.isLeftOrRight(edge)) {
656             drawTickMarksVertical(g2, state, dataArea, edge);
657         }
658     }
659     
660     /**
661      * Draws the major and minor tick marks for an axis that lies at the top or
662      * bottom of the plot.
663      *
664      * @param g2 the graphics device.
665      * @param state the axis state.
666      * @param dataArea the data area.
667      * @param edge the edge.
668      */

669     protected void drawTickMarksHorizontal(Graphics2D JavaDoc g2, AxisState state,
670                                            Rectangle2D JavaDoc dataArea,
671                                            RectangleEdge edge) {
672         List JavaDoc ticks = new ArrayList JavaDoc();
673         double x0 = dataArea.getX();
674         double y0 = state.getCursor();
675         double insideLength = getTickMarkInsideLength();
676         double outsideLength = getTickMarkOutsideLength();
677         RegularTimePeriod t = RegularTimePeriod.createInstance(
678                 this.majorTickTimePeriodClass, this.first.getStart(),
679                 getTimeZone());
680         long t0 = t.getFirstMillisecond(this.calendar);
681         Line2D JavaDoc inside = null;
682         Line2D JavaDoc outside = null;
683         long firstOnAxis = getFirst().getFirstMillisecond(this.calendar);
684         long lastOnAxis = getLast().getLastMillisecond(this.calendar);
685         while (t0 <= lastOnAxis) {
686             ticks.add(new NumberTick(new Double JavaDoc(t0), "", TextAnchor.CENTER,
687                     TextAnchor.CENTER, 0.0));
688             x0 = valueToJava2D(t0, dataArea, edge);
689             if (edge == RectangleEdge.TOP) {
690                 inside = new Line2D.Double JavaDoc(x0, y0, x0, y0 + insideLength);
691                 outside = new Line2D.Double JavaDoc(x0, y0, x0, y0 - outsideLength);
692             }
693             else if (edge == RectangleEdge.BOTTOM) {
694                 inside = new Line2D.Double JavaDoc(x0, y0, x0, y0 - insideLength);
695                 outside = new Line2D.Double JavaDoc(x0, y0, x0, y0 + outsideLength);
696             }
697             if (t0 > firstOnAxis) {
698                 g2.setPaint(getTickMarkPaint());
699                 g2.setStroke(getTickMarkStroke());
700                 g2.draw(inside);
701                 g2.draw(outside);
702             }
703             // draw minor tick marks
704
if (this.minorTickMarksVisible) {
705                 RegularTimePeriod tminor = RegularTimePeriod.createInstance(
706                         this.minorTickTimePeriodClass, new Date JavaDoc(t0),
707                         getTimeZone());
708                 long tt0 = tminor.getFirstMillisecond(this.calendar);
709                 while (tt0 < t.getLastMillisecond(this.calendar)
710                         && tt0 < lastOnAxis) {
711                     double xx0 = valueToJava2D(tt0, dataArea, edge);
712                     if (edge == RectangleEdge.TOP) {
713                         inside = new Line2D.Double JavaDoc(xx0, y0, xx0,
714                                 y0 + this.minorTickMarkInsideLength);
715                         outside = new Line2D.Double JavaDoc(xx0, y0, xx0,
716                                 y0 - this.minorTickMarkOutsideLength);
717                     }
718                     else if (edge == RectangleEdge.BOTTOM) {
719                         inside = new Line2D.Double JavaDoc(xx0, y0, xx0,
720                                 y0 - this.minorTickMarkInsideLength);
721                         outside = new Line2D.Double JavaDoc(xx0, y0, xx0,
722                                 y0 + this.minorTickMarkOutsideLength);
723                     }
724                     if (tt0 >= firstOnAxis) {
725                         g2.setPaint(this.minorTickMarkPaint);
726                         g2.setStroke(this.minorTickMarkStroke);
727                         g2.draw(inside);
728                         g2.draw(outside);
729                     }
730                     tminor = tminor.next();
731                     tt0 = tminor.getFirstMillisecond(this.calendar);
732                 }
733             }
734             t = t.next();
735             t0 = t.getFirstMillisecond(this.calendar);
736         }
737         if (edge == RectangleEdge.TOP) {
738             state.cursorUp(Math.max(outsideLength,
739                     this.minorTickMarkOutsideLength));
740         }
741         else if (edge == RectangleEdge.BOTTOM) {
742             state.cursorDown(Math.max(outsideLength,
743                     this.minorTickMarkOutsideLength));
744         }
745         state.setTicks(ticks);
746     }
747     
748     /**
749      * Draws the tick marks for a vertical axis.
750      *
751      * @param g2 the graphics device.
752      * @param state the axis state.
753      * @param dataArea the data area.
754      * @param edge the edge.
755      */

756     protected void drawTickMarksVertical(Graphics2D JavaDoc g2, AxisState state,
757                                          Rectangle2D JavaDoc dataArea,
758                                          RectangleEdge edge) {
759             
760     }
761     
762     /**
763      * Draws the tick labels for one "band" of time periods.
764      *
765      * @param band the band index (zero-based).
766      * @param g2 the graphics device.
767      * @param state the axis state.
768      * @param dataArea the data area.
769      * @param edge the edge where the axis is located.
770      *
771      * @return The updated axis state.
772      */

773     protected AxisState drawTickLabels(int band, Graphics2D JavaDoc g2, AxisState state,
774                                        Rectangle2D JavaDoc dataArea,
775                                        RectangleEdge edge) {
776
777         // work out the initial gap
778
double delta1 = 0.0;
779         FontMetrics JavaDoc fm = g2.getFontMetrics(this.labelInfo[band].getLabelFont());
780         if (edge == RectangleEdge.BOTTOM) {
781             delta1 = this.labelInfo[band].getPadding().calculateTopOutset(
782                     fm.getHeight());
783         }
784         else if (edge == RectangleEdge.TOP) {
785             delta1 = this.labelInfo[band].getPadding().calculateBottomOutset(
786                     fm.getHeight());
787         }
788         state.moveCursor(delta1, edge);
789         long axisMin = this.first.getFirstMillisecond(this.calendar);
790         long axisMax = this.last.getLastMillisecond(this.calendar);
791         g2.setFont(this.labelInfo[band].getLabelFont());
792         g2.setPaint(this.labelInfo[band].getLabelPaint());
793
794         // work out the number of periods to skip for labelling
795
RegularTimePeriod p1 = this.labelInfo[band].createInstance(
796                 new Date JavaDoc(axisMin), this.timeZone);
797         RegularTimePeriod p2 = this.labelInfo[band].createInstance(
798                 new Date JavaDoc(axisMax), this.timeZone);
799         String JavaDoc label1 = this.labelInfo[band].getDateFormat().format(
800                 new Date JavaDoc(p1.getMiddleMillisecond(this.calendar)));
801         String JavaDoc label2 = this.labelInfo[band].getDateFormat().format(
802                 new Date JavaDoc(p2.getMiddleMillisecond(this.calendar)));
803         Rectangle2D JavaDoc b1 = TextUtilities.getTextBounds(label1, g2,
804                 g2.getFontMetrics());
805         Rectangle2D JavaDoc b2 = TextUtilities.getTextBounds(label2, g2,
806                 g2.getFontMetrics());
807         double w = Math.max(b1.getWidth(), b2.getWidth());
808         long ww = Math.round(java2DToValue(dataArea.getX() + w + 5.0,
809                 dataArea, edge)) - axisMin;
810         long length = p1.getLastMillisecond(this.calendar)
811                       - p1.getFi