001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006 *
007 * Project Info: http://www.jfree.org/jfreechart/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it
010 * under the terms of the GNU Lesser General Public License as published by
011 * the Free Software Foundation; either version 2.1 of the License, or
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022 * USA.
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025 * in the United States and other countries.]
026 *
027 * ---------------
028 * SymbolAxis.java
029 * ---------------
030 * (C) Copyright 2002-2007, by Anthony Boulestreau and Contributors.
031 *
032 * Original Author: Anthony Boulestreau;
033 * Contributor(s): David Gilbert (for Object Refinery Limited);
034 *
035 *
036 * Changes
037 * -------
038 * 29-Mar-2002 : First version (AB);
039 * 19-Apr-2002 : Updated formatting and import statements (DG);
040 * 21-Jun-2002 : Make change to use the class TickUnit - remove valueToString()
041 * method and add SymbolicTickUnit (AB);
042 * 25-Jun-2002 : Removed redundant code (DG);
043 * 25-Jul-2002 : Changed order of parameters in ValueAxis constructor (DG);
044 * 05-Sep-2002 : Updated constructor to reflect changes in the Axis class (DG);
045 * 08-Nov-2002 : Moved to new package com.jrefinery.chart.axis (DG);
046 * 14-Feb-2003 : Added back missing constructor code (DG);
047 * 26-Mar-2003 : Implemented Serializable (DG);
048 * 14-May-2003 : Renamed HorizontalSymbolicAxis --> SymbolicAxis and merged in
049 * VerticalSymbolicAxis (DG);
050 * 12-Aug-2003 : Fixed bug where refreshTicks() method has different signature
051 * to super class (DG);
052 * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG);
053 * 02-Nov-2003 : Added code to avoid overlapping labels (MR);
054 * 07-Nov-2003 : Modified to use new tick classes (DG);
055 * 18-Nov-2003 : Fixed bug where symbols are not being displayed on the
056 * axis (DG);
057 * 24-Nov-2003 : Added fix for gridlines on zooming (bug id 834643) (DG);
058 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
059 * 11-Mar-2004 : Modified the way the background grid color is being drawn, see
060 * this thread:
061 * http://www.jfree.org/phpBB2/viewtopic.php?p=22973 (DG);
062 * 16-Mar-2004 : Added plotState to draw() method (DG);
063 * 07-Apr-2004 : Modified string bounds calculation (DG);
064 * 28-Mar-2005 : Renamed autoRangeIncludesZero() --> getAutoRangeIncludesZero()
065 * and autoRangeStickyZero() --> getAutoRangeStickyZero() (DG);
066 * 05-Jul-2005 : Fixed signature on refreshTicks() method - see bug report
067 * 1232264 (DG);
068 * 06-Jul-2005 : Renamed SymbolicAxis --> SymbolAxis, added equals() method,
069 * renamed getSymbolicValue() --> getSymbols(), renamed
070 * symbolicGridPaint --> gridBandPaint, fixed serialization of
071 * gridBandPaint, renamed symbolicGridLinesVisible -->
072 * gridBandsVisible, eliminated symbolicGridLineList (DG);
073 * ------------- JFREECHART 1.0.x ---------------------------------------------
074 * 02-Feb-2007 : Removed author tags all over JFreeChart sources (DG);
075 * 28-Feb-2007 : Fixed bug 1669302 (tick label overlap) (DG);
076 *
077 */
078
079 package org.jfree.chart.axis;
080
081 import java.awt.BasicStroke;
082 import java.awt.Color;
083 import java.awt.Font;
084 import java.awt.Graphics2D;
085 import java.awt.Paint;
086 import java.awt.Shape;
087 import java.awt.Stroke;
088 import java.awt.geom.Rectangle2D;
089 import java.io.IOException;
090 import java.io.ObjectInputStream;
091 import java.io.ObjectOutputStream;
092 import java.io.Serializable;
093 import java.text.NumberFormat;
094 import java.util.Arrays;
095 import java.util.Iterator;
096 import java.util.List;
097
098 import org.jfree.chart.event.AxisChangeEvent;
099 import org.jfree.chart.plot.Plot;
100 import org.jfree.chart.plot.PlotRenderingInfo;
101 import org.jfree.chart.plot.ValueAxisPlot;
102 import org.jfree.data.Range;
103 import org.jfree.io.SerialUtilities;
104 import org.jfree.text.TextUtilities;
105 import org.jfree.ui.RectangleEdge;
106 import org.jfree.ui.TextAnchor;
107 import org.jfree.util.PaintUtilities;
108
109 /**
110 * A standard linear value axis that replaces integer values with symbols.
111 */
112 public class SymbolAxis extends NumberAxis implements Serializable {
113
114 /** For serialization. */
115 private static final long serialVersionUID = 7216330468770619716L;
116
117 /** The default grid band paint. */
118 public static final Paint DEFAULT_GRID_BAND_PAINT
119 = new Color(232, 234, 232, 128);
120
121 /** The list of symbols to display instead of the numeric values. */
122 private List symbols;
123
124 /** The paint used to color the grid bands (if the bands are visible). */
125 private transient Paint gridBandPaint;
126
127 /** Flag that indicates whether or not grid bands are visible. */
128 private boolean gridBandsVisible;
129
130 /**
131 * Constructs a symbol axis, using default attribute values where
132 * necessary.
133 *
134 * @param label the axis label (<code>null</code> permitted).
135 * @param sv the list of symbols to display instead of the numeric
136 * values.
137 */
138 public SymbolAxis(String label, String[] sv) {
139 super(label);
140 this.symbols = Arrays.asList(sv);
141 this.gridBandsVisible = true;
142 this.gridBandPaint = DEFAULT_GRID_BAND_PAINT;
143
144 setAutoTickUnitSelection(false, false);
145 setAutoRangeStickyZero(false);
146
147 }
148
149 /**
150 * Returns an array of the symbols for the axis.
151 *
152 * @return The symbols.
153 */
154 public String[] getSymbols() {
155 String[] result = new String[this.symbols.size()];
156 result = (String[]) this.symbols.toArray(result);
157 return result;
158 }
159
160 /**
161 * Returns the paint used to color the grid bands.
162 *
163 * @return The grid band paint (never <code>null</code>).
164 *
165 * @see #setGridBandPaint(Paint)
166 * @see #isGridBandsVisible()
167 */
168 public Paint getGridBandPaint() {
169 return this.gridBandPaint;
170 }
171
172 /**
173 * Sets the grid band paint and sends an {@link AxisChangeEvent} to
174 * all registered listeners.
175 *
176 * @param paint the paint (<code>null</code> not permitted).
177 *
178 * @see #getGridBandPaint()
179 */
180 public void setGridBandPaint(Paint paint) {
181 if (paint == null) {
182 throw new IllegalArgumentException("Null 'paint' argument.");
183 }
184 this.gridBandPaint = paint;
185 notifyListeners(new AxisChangeEvent(this));
186 }
187
188 /**
189 * Returns <code>true</code> if the grid bands are showing, and
190 * <code>false</code> otherwise.
191 *
192 * @return <code>true</code> if the grid bands are showing, and
193 * <code>false</code> otherwise.
194 *
195 * @see #setGridBandsVisible(boolean)
196 */
197 public boolean isGridBandsVisible() {
198 return this.gridBandsVisible;
199 }
200
201 /**
202 * Sets the visibility of the grid bands and notifies registered
203 * listeners that the axis has been modified.
204 *
205 * @param flag the new setting.
206 *
207 * @see #isGridBandsVisible()
208 */
209 public void setGridBandsVisible(boolean flag) {
210 if (this.gridBandsVisible != flag) {
211 this.gridBandsVisible = flag;
212 notifyListeners(new AxisChangeEvent(this));
213 }
214 }
215
216 /**
217 * This operation is not supported by this axis.
218 *
219 * @param g2 the graphics device.
220 * @param dataArea the area in which the plot and axes should be drawn.
221 * @param edge the edge along which the axis is drawn.
222 */
223 protected void selectAutoTickUnit(Graphics2D g2, Rectangle2D dataArea,
224 RectangleEdge edge) {
225 throw new UnsupportedOperationException();
226 }
227
228 /**
229 * Draws the axis on a Java 2D graphics device (such as the screen or a
230 * printer).
231 *
232 * @param g2 the graphics device (<code>null</code> not permitted).
233 * @param cursor the cursor location.
234 * @param plotArea the area within which the plot and axes should be drawn
235 * (<code>null</code> not permitted).
236 * @param dataArea the area within which the data should be drawn
237 * (<code>null</code> not permitted).
238 * @param edge the axis location (<code>null</code> not permitted).
239 * @param plotState collects information about the plot
240 * (<code>null</code> permitted).
241 *
242 * @return The axis state (never <code>null</code>).
243 */
244 public AxisState draw(Graphics2D g2,
245 double cursor,
246 Rectangle2D plotArea,
247 Rectangle2D dataArea,
248 RectangleEdge edge,
249 PlotRenderingInfo plotState) {
250
251 AxisState info = new AxisState(cursor);
252 if (isVisible()) {
253 info = super.draw(g2, cursor, plotArea, dataArea, edge, plotState);
254 }
255 if (this.gridBandsVisible) {
256 drawGridBands(g2, plotArea, dataArea, edge, info.getTicks());
257 }
258 return info;
259
260 }
261
262 /**
263 * Draws the grid bands. Alternate bands are colored using
264 * <CODE>gridBandPaint<CODE> (<CODE>DEFAULT_GRID_BAND_PAINT</CODE> by
265 * default).
266 *
267 * @param g2 the graphics device.
268 * @param plotArea the area within which the chart should be drawn.
269 * @param dataArea the area within which the plot should be drawn (a
270 * subset of the drawArea).
271 * @param edge the axis location.
272 * @param ticks the ticks.
273 */
274 protected void drawGridBands(Graphics2D g2,
275 Rectangle2D plotArea,
276 Rectangle2D dataArea,
277 RectangleEdge edge,
278 List ticks) {
279
280 Shape savedClip = g2.getClip();
281 g2.clip(dataArea);
282 if (RectangleEdge.isTopOrBottom(edge)) {
283 drawGridBandsHorizontal(g2, plotArea, dataArea, true, ticks);
284 }
285 else if (RectangleEdge.isLeftOrRight(edge)) {
286 drawGridBandsVertical(g2, plotArea, dataArea, true, ticks);
287 }
288 g2.setClip(savedClip);
289
290 }
291
292 /**
293 * Draws the grid bands for the axis when it is at the top or bottom of
294 * the plot.
295 *
296 * @param g2 the graphics device.
297 * @param plotArea the area within which the chart should be drawn.
298 * @param dataArea the area within which the plot should be drawn
299 * (a subset of the drawArea).
300 * @param firstGridBandIsDark True: the first grid band takes the
301 * color of <CODE>gridBandPaint<CODE>.
302 * False: the second grid band takes the
303 * color of <CODE>gridBandPaint<CODE>.
304 * @param ticks the ticks.
305 */
306 protected void drawGridBandsHorizontal(Graphics2D g2,
307 Rectangle2D plotArea,
308 Rectangle2D dataArea,
309 boolean firstGridBandIsDark,
310 List ticks) {
311
312 boolean currentGridBandIsDark = firstGridBandIsDark;
313 double yy = dataArea.getY();
314 double xx1, xx2;
315
316 //gets the outline stroke width of the plot
317 double outlineStrokeWidth;
318 if (getPlot().getOutlineStroke() != null) {
319 outlineStrokeWidth
320 = ((BasicStroke) getPlot().getOutlineStroke()).getLineWidth();
321 }
322 else {
323 outlineStrokeWidth = 1d;
324 }
325
326 Iterator iterator = ticks.iterator();
327 ValueTick tick;
328 Rectangle2D band;
329 while (iterator.hasNext()) {
330 tick = (ValueTick) iterator.next();
331 xx1 = valueToJava2D(tick.getValue() - 0.5d, dataArea,
332 RectangleEdge.BOTTOM);
333 xx2 = valueToJava2D(tick.getValue() + 0.5d, dataArea,
334 RectangleEdge.BOTTOM);
335 if (currentGridBandIsDark) {
336 g2.setPaint(this.gridBandPaint);
337 }
338 else {
339 g2.setPaint(Color.white);
340 }
341 band = new Rectangle2D.Double(xx1, yy + outlineStrokeWidth,
342 xx2 - xx1, dataArea.getMaxY() - yy - outlineStrokeWidth);
343 g2.fill(band);
344 currentGridBandIsDark = !currentGridBandIsDark;
345 }
346 g2.setPaintMode();
347 }
348
349 /**
350 * Draws the grid bands for the axis when it is at the top or bottom of
351 * the plot.
352 *
353 * @param g2 the graphics device.
354 * @param drawArea the area within which the chart should be drawn.
355 * @param plotArea the area within which the plot should be drawn (a
356 * subset of the drawArea).
357 * @param firstGridBandIsDark True: the first grid band takes the
358 * color of <CODE>gridBandPaint<CODE>.
359 * False: the second grid band takes the
360 * color of <CODE>gridBandPaint<CODE>.
361 * @param ticks a list of ticks.
362 */
363 protected void drawGridBandsVertical(Graphics2D g2,
364 Rectangle2D drawArea,
365 Rectangle2D plotArea,
366 boolean firstGridBandIsDark,
367 List ticks) {
368
369 boolean currentGridBandIsDark = firstGridBandIsDark;
370 double xx = plotArea.getX();
371 double yy1, yy2;
372
373 //gets the outline stroke width of the plot
374 double outlineStrokeWidth;
375 Stroke outlineStroke = getPlot().getOutlineStroke();
376 if (outlineStroke != null && outlineStroke instanceof BasicStroke) {
377 outlineStrokeWidth = ((BasicStroke) outlineStroke).getLineWidth();
378 }
379 else {
380 outlineStrokeWidth = 1d;
381 }
382
383 Iterator iterator = ticks.iterator();
384 ValueTick tick;
385 Rectangle2D band;
386 while (iterator.hasNext()) {
387 tick = (ValueTick) iterator.next();
388 yy1 = valueToJava2D(tick.getValue() + 0.5d, plotArea,
389 RectangleEdge.LEFT);
390 yy2 = valueToJava2D(tick.getValue() - 0.5d, plotArea,
391 RectangleEdge.LEFT);
392 if (currentGridBandIsDark) {
393 g2.setPaint(this.gridBandPaint);
394 }
395 else {
396 g2.setPaint(Color.white);
397 }
398 band = new Rectangle2D.Double(xx + outlineStrokeWidth, yy1,
399 plotArea.getMaxX() - xx - outlineStrokeWidth, yy2 - yy1);
400 g2.fill(band);
401 currentGridBandIsDark = !currentGridBandIsDark;
402 }
403 g2.setPaintMode();
404 }
405
406 /**
407 * Rescales the axis to ensure that all data is visible.
408 */
409 protected void autoAdjustRange() {
410
411 Plot plot = getPlot();
412 if (plot == null) {
413 return; // no plot, no data
414 }
415
416 if (plot instanceof ValueAxisPlot) {
417
418 // ensure that all the symbols are displayed
419 double upper = this.symbols.size() - 1;
420 double lower = 0;
421 double range = upper - lower;
422
423 // ensure the autorange is at least <minRange> in size...
424 double minRange = getAutoRangeMinimumSize();
425 if (range < minRange) {
426 upper = (upper + lower + minRange) / 2;
427 lower = (upper + lower - minRange) / 2;
428 }
429
430 // this ensure that the grid bands will be displayed correctly.
431 double upperMargin = 0.5;
432 double lowerMargin = 0.5;
433
434 if (getAutoRangeIncludesZero()) {
435 if (getAutoRangeStickyZero()) {
436 if (upper <= 0.0) {
437 upper = 0.0;
438 }
439 else {
440 upper = upper + upperMargin;
441 }
442 if (lower >= 0.0) {
443 lower = 0.0;
444 }
445 else {
446 lower = lower - lowerMargin;
447 }
448 }
449 else {
450 upper = Math.max(0.0, upper + upperMargin);
451 lower = Math.min(0.0, lower - lowerMargin);
452 }
453 }
454 else {
455 if (getAutoRangeStickyZero()) {
456 if (upper <= 0.0) {
457 upper = Math.min(0.0, upper + upperMargin);
458 }
459 else {
460 upper = upper + upperMargin * range;
461 }
462 if (lower >= 0.0) {
463 lower = Math.max(0.0, lower - lowerMargin);
464 }
465 else {
466 lower = lower - lowerMargin;
467 }
468 }
469 else {
470 upper = upper + upperMargin;
471 lower = lower - lowerMargin;
472 }
473 }
474
475 setRange(new Range(lower, upper), false, false);
476
477 }
478
479 }
480
481 /**
482 * Calculates the positions of the tick labels for the axis, storing the
483 * results in the tick label list (ready for drawing).
484 *
485 * @param g2 the graphics device.
486 * @param state the axis state.
487 * @param dataArea the area in which the data should be drawn.
488 * @param edge the location of the axis.
489 *
490 * @return A list of ticks.
491 */
492 public List refreshTicks(Graphics2D g2,
493 AxisState state,
494 Rectangle2D dataArea,
495 RectangleEdge edge) {
496 List ticks = null;
497 if (RectangleEdge.isTopOrBottom(edge)) {
498 ticks = refreshTicksHorizontal(g2, dataArea, edge);
499 }
500 else if (RectangleEdge.isLeftOrRight(edge)) {
501 ticks = refreshTicksVertical(g2, dataArea, edge);
502 }
503 return ticks;
504 }
505
506 /**
507 * Calculates the positions of the tick labels for the axis, storing the
508 * results in the tick label list (ready for drawing).
509 *
510 * @param g2 the graphics device.
511 * @param dataArea the area in which the data should be drawn.
512 * @param edge the location of the axis.
513 *
514 * @return The ticks.
515 */
516 protected List refreshTicksHorizontal(Graphics2D g2,
517 Rectangle2D dataArea,
518 RectangleEdge edge) {
519
520 List ticks = new java.util.ArrayList();
521
522 Font tickLabelFont = getTickLabelFont();
523 g2.setFont(tickLabelFont);
524
525 double size = getTickUnit().getSize();
526 int count = calculateVisibleTickCount();
527 double lowestTickValue = calculateLowestVisibleTickValue();
528
529 double previousDrawnTickLabelPos = 0.0;
530 double previousDrawnTickLabelLength = 0.0;
531
532 if (count <= ValueAxis.MAXIMUM_TICK_COUNT) {
533 for (int i = 0; i < count; i++) {
534 double currentTickValue = lowestTickValue + (i * size);
535 double xx = valueToJava2D(currentTickValue, dataArea, edge);
536 String tickLabel;
537 NumberFormat formatter = getNumberFormatOverride();
538 if (formatter != null) {
539 tickLabel = formatter.format(currentTickValue);
540 }
541 else {
542 tickLabel = valueToString(currentTickValue);
543 }
544
545 // avoid to draw overlapping tick labels
546 Rectangle2D bounds = TextUtilities.getTextBounds(tickLabel, g2,
547 g2.getFontMetrics());
548 double tickLabelLength = isVerticalTickLabels()
549 ? bounds.getHeight() : bounds.getWidth();
550 boolean tickLabelsOverlapping = false;
551 if (i > 0) {
552 double avgTickLabelLength = (previousDrawnTickLabelLength
553 + tickLabelLength) / 2.0;
554 if (Math.abs(xx - previousDrawnTickLabelPos)
555 < avgTickLabelLength) {
556 tickLabelsOverlapping = true;
557 }
558 }
559 if (tickLabelsOverlapping) {
560 tickLabel = ""; // don't draw this tick label
561 }
562 else {
563 // remember these values for next comparison
564 previousDrawnTickLabelPos = xx;
565 previousDrawnTickLabelLength = tickLabelLength;
566 }
567
568 TextAnchor anchor = null;
569 TextAnchor rotationAnchor = null;
570 double angle = 0.0;
571 if (isVerticalTickLabels()) {
572 anchor = TextAnchor.CENTER_RIGHT;
573 rotationAnchor = TextAnchor.CENTER_RIGHT;
574 if (edge == RectangleEdge.TOP) {
575 angle = Math.PI / 2.0;
576 }
577 else {
578 angle = -Math.PI / 2.0;
579 }
580 }
581 else {
582 if (edge == RectangleEdge.TOP) {
583 anchor = TextAnchor.BOTTOM_CENTER;
584 rotationAnchor = TextAnchor.BOTTOM_CENTER;
585 }
586 else {
587 anchor = TextAnchor.TOP_CENTER;
588 rotationAnchor = TextAnchor.TOP_CENTER;
589 }
590 }
591 Tick tick = new NumberTick(new Double(currentTickValue),
592 tickLabel, anchor, rotationAnchor, angle);
593 ticks.add(tick);
594 }
595 }
596 return ticks;
597
598 }
599
600 /**
601 * Calculates the positions of the tick labels for the axis, storing the
602 * results in the tick label list (ready for drawing).
603 *
604 * @param g2 the graphics device.
605 * @param dataArea the area in which the plot should be drawn.
606 * @param edge the location of the axis.
607 *
608 * @return The ticks.
609 */
610 protected List refreshTicksVertical(Graphics2D g2,
611 Rectangle2D dataArea,
612 RectangleEdge edge) {
613
614 List ticks = new java.util.ArrayList();
615
616 Font tickLabelFont = getTickLabelFont();
617 g2.setFont(tickLabelFont);
618
619 double size = getTickUnit().getSize();
620 int count = calculateVisibleTickCount();
621 double lowestTickValue = calculateLowestVisibleTickValue();
622
623 double previousDrawnTickLabelPos = 0.0;
624 double previousDrawnTickLabelLength = 0.0;
625
626 if (count <= ValueAxis.MAXIMUM_TICK_COUNT) {
627 for (int i = 0; i < count; i++) {
628 double currentTickValue = lowestTickValue + (i * size);
629 double yy = valueToJava2D(currentTickValue, dataArea, edge);
630 String tickLabel;
631 NumberFormat formatter = getNumberFormatOverride();
632 if (formatter != null) {
633 tickLabel = formatter.format(currentTickValue);
634 }
635 else {
636 tickLabel = valueToString(currentTickValue);
637 }
638
639 // avoid to draw overlapping tick labels
640 Rectangle2D bounds = TextUtilities.getTextBounds(tickLabel, g2,
641 g2.getFontMetrics());
642 double tickLabelLength = isVerticalTickLabels()
643 ? bounds.getWidth() : bounds.getHeight();
644 boolean tickLabelsOverlapping = false;
645 if (i > 0) {
646 double avgTickLabelLength = (previousDrawnTickLabelLength
647 + tickLabelLength) / 2.0;
648 if (Math.abs(yy - previousDrawnTickLabelPos)
649 < avgTickLabelLength) {
650 tickLabelsOverlapping = true;
651 }
652 }
653 if (tickLabelsOverlapping) {
654 tickLabel = ""; // don't draw this tick label
655 }
656 else {
657 // remember these values for next comparison
658 previousDrawnTickLabelPos = yy;
659 previousDrawnTickLabelLength = tickLabelLength;
660 }
661
662 TextAnchor anchor = null;
663 TextAnchor rotationAnchor = null;
664 double angle = 0.0;
665 if (isVerticalTickLabels()) {
666 anchor = TextAnchor.BOTTOM_CENTER;
667 rotationAnchor = TextAnchor.BOTTOM_CENTER;
668 if (edge == RectangleEdge.LEFT) {
669 angle = -Math.PI / 2.0;
670 }
671 else {
672 angle = Math.PI / 2.0;
673 }
674 }
675 else {
676 if (edge == RectangleEdge.LEFT) {
677 anchor = TextAnchor.CENTER_RIGHT;
678 rotationAnchor = TextAnchor.CENTER_RIGHT;
679 }
680 else {
681 anchor = TextAnchor.CENTER_LEFT;
682 rotationAnchor = TextAnchor.CENTER_LEFT;
683 }
684 }
685 Tick tick = new NumberTick(new Double(currentTickValue),
686 tickLabel, anchor, rotationAnchor, angle);
687 ticks.add(tick);
688 }
689 }
690 return ticks;
691
692 }
693
694 /**
695 * Converts a value to a string, using the list of symbols.
696 *
697 * @param value value to convert.
698 *
699 * @return The symbol.
700 */
701 public String valueToString(double value) {
702 String strToReturn;
703 try {
704 strToReturn = (String) this.symbols.get((int) value);
705 }
706 catch (IndexOutOfBoundsException ex) {
707 strToReturn = "";
708 }
709 return strToReturn;
710 }
711
712 /**
713 * Tests this axis for equality with an arbitrary object.
714 *
715 * @param obj the object (<code>null</code> permitted).
716 *
717 * @return A boolean.
718 */
719 public boolean equals(Object obj) {
720 if (obj == this) {
721 return true;
722 }
723 if (!(obj instanceof SymbolAxis)) {
724 return false;
725 }
726 SymbolAxis that = (SymbolAxis) obj;
727 if (!this.symbols.equals(that.symbols)) {
728 return false;
729 }
730 if (this.gridBandsVisible != that.gridBandsVisible) {
731 return false;
732 }
733 if (!PaintUtilities.equal(this.gridBandPaint, that.gridBandPaint)) {
734 return false;
735 }
736 return super.equals(obj);
737 }
738
739 /**
740 * Provides serialization support.
741 *
742 * @param stream the output stream.
743 *
744 * @throws IOException if there is an I/O error.
745 */
746 private void writeObject(ObjectOutputStream stream) throws IOException {
747 stream.defaultWriteObject();
748 SerialUtilities.writePaint(this.gridBandPaint, stream);
749 }
750
751 /**
752 * Provides serialization support.
753 *
754 * @param stream the input stream.
755 *
756 * @throws IOException if there is an I/O error.
757 * @throws ClassNotFoundException if there is a classpath problem.
758 */
759 private void readObject(ObjectInputStream stream)
760 throws IOException, ClassNotFoundException {
761 stream.defaultReadObject();
762 this.gridBandPaint = SerialUtilities.readPaint(stream);
763 }
764
765 }