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 = new Color(232,
119: 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,
224: Rectangle2D dataArea, 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, double cursor,
245: Rectangle2D plotArea, Rectangle2D dataArea,
246: RectangleEdge edge, PlotRenderingInfo plotState) {
247:
248: AxisState info = new AxisState(cursor);
249: if (isVisible()) {
250: info = super .draw(g2, cursor, plotArea, dataArea, edge,
251: plotState);
252: }
253: if (this .gridBandsVisible) {
254: drawGridBands(g2, plotArea, dataArea, edge, info.getTicks());
255: }
256: return info;
257:
258: }
259:
260: /**
261: * Draws the grid bands. Alternate bands are colored using
262: * <CODE>gridBandPaint<CODE> (<CODE>DEFAULT_GRID_BAND_PAINT</CODE> by
263: * default).
264: *
265: * @param g2 the graphics device.
266: * @param plotArea the area within which the chart should be drawn.
267: * @param dataArea the area within which the plot should be drawn (a
268: * subset of the drawArea).
269: * @param edge the axis location.
270: * @param ticks the ticks.
271: */
272: protected void drawGridBands(Graphics2D g2, Rectangle2D plotArea,
273: Rectangle2D dataArea, RectangleEdge edge, List ticks) {
274:
275: Shape savedClip = g2.getClip();
276: g2.clip(dataArea);
277: if (RectangleEdge.isTopOrBottom(edge)) {
278: drawGridBandsHorizontal(g2, plotArea, dataArea, true, ticks);
279: } else if (RectangleEdge.isLeftOrRight(edge)) {
280: drawGridBandsVertical(g2, plotArea, dataArea, true, ticks);
281: }
282: g2.setClip(savedClip);
283:
284: }
285:
286: /**
287: * Draws the grid bands for the axis when it is at the top or bottom of
288: * the plot.
289: *
290: * @param g2 the graphics device.
291: * @param plotArea the area within which the chart should be drawn.
292: * @param dataArea the area within which the plot should be drawn
293: * (a subset of the drawArea).
294: * @param firstGridBandIsDark True: the first grid band takes the
295: * color of <CODE>gridBandPaint<CODE>.
296: * False: the second grid band takes the
297: * color of <CODE>gridBandPaint<CODE>.
298: * @param ticks the ticks.
299: */
300: protected void drawGridBandsHorizontal(Graphics2D g2,
301: Rectangle2D plotArea, Rectangle2D dataArea,
302: boolean firstGridBandIsDark, List ticks) {
303:
304: boolean currentGridBandIsDark = firstGridBandIsDark;
305: double yy = dataArea.getY();
306: double xx1, xx2;
307:
308: //gets the outline stroke width of the plot
309: double outlineStrokeWidth;
310: if (getPlot().getOutlineStroke() != null) {
311: outlineStrokeWidth = ((BasicStroke) getPlot()
312: .getOutlineStroke()).getLineWidth();
313: } else {
314: outlineStrokeWidth = 1d;
315: }
316:
317: Iterator iterator = ticks.iterator();
318: ValueTick tick;
319: Rectangle2D band;
320: while (iterator.hasNext()) {
321: tick = (ValueTick) iterator.next();
322: xx1 = valueToJava2D(tick.getValue() - 0.5d, dataArea,
323: RectangleEdge.BOTTOM);
324: xx2 = valueToJava2D(tick.getValue() + 0.5d, dataArea,
325: RectangleEdge.BOTTOM);
326: if (currentGridBandIsDark) {
327: g2.setPaint(this .gridBandPaint);
328: } else {
329: g2.setPaint(Color.white);
330: }
331: band = new Rectangle2D.Double(xx1, yy + outlineStrokeWidth,
332: xx2 - xx1, dataArea.getMaxY() - yy
333: - outlineStrokeWidth);
334: g2.fill(band);
335: currentGridBandIsDark = !currentGridBandIsDark;
336: }
337: g2.setPaintMode();
338: }
339:
340: /**
341: * Draws the grid bands for the axis when it is at the top or bottom of
342: * the plot.
343: *
344: * @param g2 the graphics device.
345: * @param drawArea the area within which the chart should be drawn.
346: * @param plotArea the area within which the plot should be drawn (a
347: * subset of the drawArea).
348: * @param firstGridBandIsDark True: the first grid band takes the
349: * color of <CODE>gridBandPaint<CODE>.
350: * False: the second grid band takes the
351: * color of <CODE>gridBandPaint<CODE>.
352: * @param ticks a list of ticks.
353: */
354: protected void drawGridBandsVertical(Graphics2D g2,
355: Rectangle2D drawArea, Rectangle2D plotArea,
356: boolean firstGridBandIsDark, List ticks) {
357:
358: boolean currentGridBandIsDark = firstGridBandIsDark;
359: double xx = plotArea.getX();
360: double yy1, yy2;
361:
362: //gets the outline stroke width of the plot
363: double outlineStrokeWidth;
364: Stroke outlineStroke = getPlot().getOutlineStroke();
365: if (outlineStroke != null
366: && outlineStroke instanceof BasicStroke) {
367: outlineStrokeWidth = ((BasicStroke) outlineStroke)
368: .getLineWidth();
369: } else {
370: outlineStrokeWidth = 1d;
371: }
372:
373: Iterator iterator = ticks.iterator();
374: ValueTick tick;
375: Rectangle2D band;
376: while (iterator.hasNext()) {
377: tick = (ValueTick) iterator.next();
378: yy1 = valueToJava2D(tick.getValue() + 0.5d, plotArea,
379: RectangleEdge.LEFT);
380: yy2 = valueToJava2D(tick.getValue() - 0.5d, plotArea,
381: RectangleEdge.LEFT);
382: if (currentGridBandIsDark) {
383: g2.setPaint(this .gridBandPaint);
384: } else {
385: g2.setPaint(Color.white);
386: }
387: band = new Rectangle2D.Double(xx + outlineStrokeWidth, yy1,
388: plotArea.getMaxX() - xx - outlineStrokeWidth, yy2
389: - yy1);
390: g2.fill(band);
391: currentGridBandIsDark = !currentGridBandIsDark;
392: }
393: g2.setPaintMode();
394: }
395:
396: /**
397: * Rescales the axis to ensure that all data is visible.
398: */
399: protected void autoAdjustRange() {
400:
401: Plot plot = getPlot();
402: if (plot == null) {
403: return; // no plot, no data
404: }
405:
406: if (plot instanceof ValueAxisPlot) {
407:
408: // ensure that all the symbols are displayed
409: double upper = this .symbols.size() - 1;
410: double lower = 0;
411: double range = upper - lower;
412:
413: // ensure the autorange is at least <minRange> in size...
414: double minRange = getAutoRangeMinimumSize();
415: if (range < minRange) {
416: upper = (upper + lower + minRange) / 2;
417: lower = (upper + lower - minRange) / 2;
418: }
419:
420: // this ensure that the grid bands will be displayed correctly.
421: double upperMargin = 0.5;
422: double lowerMargin = 0.5;
423:
424: if (getAutoRangeIncludesZero()) {
425: if (getAutoRangeStickyZero()) {
426: if (upper <= 0.0) {
427: upper = 0.0;
428: } else {
429: upper = upper + upperMargin;
430: }
431: if (lower >= 0.0) {
432: lower = 0.0;
433: } else {
434: lower = lower - lowerMargin;
435: }
436: } else {
437: upper = Math.max(0.0, upper + upperMargin);
438: lower = Math.min(0.0, lower - lowerMargin);
439: }
440: } else {
441: if (getAutoRangeStickyZero()) {
442: if (upper <= 0.0) {
443: upper = Math.min(0.0, upper + upperMargin);
444: } else {
445: upper = upper + upperMargin * range;
446: }
447: if (lower >= 0.0) {
448: lower = Math.max(0.0, lower - lowerMargin);
449: } else {
450: lower = lower - lowerMargin;
451: }
452: } else {
453: upper = upper + upperMargin;
454: lower = lower - lowerMargin;
455: }
456: }
457:
458: setRange(new Range(lower, upper), false, false);
459:
460: }
461:
462: }
463:
464: /**
465: * Calculates the positions of the tick labels for the axis, storing the
466: * results in the tick label list (ready for drawing).
467: *
468: * @param g2 the graphics device.
469: * @param state the axis state.
470: * @param dataArea the area in which the data should be drawn.
471: * @param edge the location of the axis.
472: *
473: * @return A list of ticks.
474: */
475: public List refreshTicks(Graphics2D g2, AxisState state,
476: Rectangle2D dataArea, RectangleEdge edge) {
477: List ticks = null;
478: if (RectangleEdge.isTopOrBottom(edge)) {
479: ticks = refreshTicksHorizontal(g2, dataArea, edge);
480: } else if (RectangleEdge.isLeftOrRight(edge)) {
481: ticks = refreshTicksVertical(g2, dataArea, edge);
482: }
483: return ticks;
484: }
485:
486: /**
487: * Calculates the positions of the tick labels for the axis, storing the
488: * results in the tick label list (ready for drawing).
489: *
490: * @param g2 the graphics device.
491: * @param dataArea the area in which the data should be drawn.
492: * @param edge the location of the axis.
493: *
494: * @return The ticks.
495: */
496: protected List refreshTicksHorizontal(Graphics2D g2,
497: Rectangle2D dataArea, RectangleEdge edge) {
498:
499: List ticks = new java.util.ArrayList();
500:
501: Font tickLabelFont = getTickLabelFont();
502: g2.setFont(tickLabelFont);
503:
504: double size = getTickUnit().getSize();
505: int count = calculateVisibleTickCount();
506: double lowestTickValue = calculateLowestVisibleTickValue();
507:
508: double previousDrawnTickLabelPos = 0.0;
509: double previousDrawnTickLabelLength = 0.0;
510:
511: if (count <= ValueAxis.MAXIMUM_TICK_COUNT) {
512: for (int i = 0; i < count; i++) {
513: double currentTickValue = lowestTickValue + (i * size);
514: double xx = valueToJava2D(currentTickValue, dataArea,
515: edge);
516: String tickLabel;
517: NumberFormat formatter = getNumberFormatOverride();
518: if (formatter != null) {
519: tickLabel = formatter.format(currentTickValue);
520: } else {
521: tickLabel = valueToString(currentTickValue);
522: }
523:
524: // avoid to draw overlapping tick labels
525: Rectangle2D bounds = TextUtilities.getTextBounds(
526: tickLabel, g2, g2.getFontMetrics());
527: double tickLabelLength = isVerticalTickLabels() ? bounds
528: .getHeight()
529: : bounds.getWidth();
530: boolean tickLabelsOverlapping = false;
531: if (i > 0) {
532: double avgTickLabelLength = (previousDrawnTickLabelLength + tickLabelLength) / 2.0;
533: if (Math.abs(xx - previousDrawnTickLabelPos) < avgTickLabelLength) {
534: tickLabelsOverlapping = true;
535: }
536: }
537: if (tickLabelsOverlapping) {
538: tickLabel = ""; // don't draw this tick label
539: } else {
540: // remember these values for next comparison
541: previousDrawnTickLabelPos = xx;
542: previousDrawnTickLabelLength = tickLabelLength;
543: }
544:
545: TextAnchor anchor = null;
546: TextAnchor rotationAnchor = null;
547: double angle = 0.0;
548: if (isVerticalTickLabels()) {
549: anchor = TextAnchor.CENTER_RIGHT;
550: rotationAnchor = TextAnchor.CENTER_RIGHT;
551: if (edge == RectangleEdge.TOP) {
552: angle = Math.PI / 2.0;
553: } else {
554: angle = -Math.PI / 2.0;
555: }
556: } else {
557: if (edge == RectangleEdge.TOP) {
558: anchor = TextAnchor.BOTTOM_CENTER;
559: rotationAnchor = TextAnchor.BOTTOM_CENTER;
560: } else {
561: anchor = TextAnchor.TOP_CENTER;
562: rotationAnchor = TextAnchor.TOP_CENTER;
563: }
564: }
565: Tick tick = new NumberTick(
566: new Double(currentTickValue), tickLabel,
567: anchor, rotationAnchor, angle);
568: ticks.add(tick);
569: }
570: }
571: return ticks;
572:
573: }
574:
575: /**
576: * Calculates the positions of the tick labels for the axis, storing the
577: * results in the tick label list (ready for drawing).
578: *
579: * @param g2 the graphics device.
580: * @param dataArea the area in which the plot should be drawn.
581: * @param edge the location of the axis.
582: *
583: * @return The ticks.
584: */
585: protected List refreshTicksVertical(Graphics2D g2,
586: Rectangle2D dataArea, RectangleEdge edge) {
587:
588: List ticks = new java.util.ArrayList();
589:
590: Font tickLabelFont = getTickLabelFont();
591: g2.setFont(tickLabelFont);
592:
593: double size = getTickUnit().getSize();
594: int count = calculateVisibleTickCount();
595: double lowestTickValue = calculateLowestVisibleTickValue();
596:
597: double previousDrawnTickLabelPos = 0.0;
598: double previousDrawnTickLabelLength = 0.0;
599:
600: if (count <= ValueAxis.MAXIMUM_TICK_COUNT) {
601: for (int i = 0; i < count; i++) {
602: double currentTickValue = lowestTickValue + (i * size);
603: double yy = valueToJava2D(currentTickValue, dataArea,
604: edge);
605: String tickLabel;
606: NumberFormat formatter = getNumberFormatOverride();
607: if (formatter != null) {
608: tickLabel = formatter.format(currentTickValue);
609: } else {
610: tickLabel = valueToString(currentTickValue);
611: }
612:
613: // avoid to draw overlapping tick labels
614: Rectangle2D bounds = TextUtilities.getTextBounds(
615: tickLabel, g2, g2.getFontMetrics());
616: double tickLabelLength = isVerticalTickLabels() ? bounds
617: .getWidth()
618: : bounds.getHeight();
619: boolean tickLabelsOverlapping = false;
620: if (i > 0) {
621: double avgTickLabelLength = (previousDrawnTickLabelLength + tickLabelLength) / 2.0;
622: if (Math.abs(yy - previousDrawnTickLabelPos) < avgTickLabelLength) {
623: tickLabelsOverlapping = true;
624: }
625: }
626: if (tickLabelsOverlapping) {
627: tickLabel = ""; // don't draw this tick label
628: } else {
629: // remember these values for next comparison
630: previousDrawnTickLabelPos = yy;
631: previousDrawnTickLabelLength = tickLabelLength;
632: }
633:
634: TextAnchor anchor = null;
635: TextAnchor rotationAnchor = null;
636: double angle = 0.0;
637: if (isVerticalTickLabels()) {
638: anchor = TextAnchor.BOTTOM_CENTER;
639: rotationAnchor = TextAnchor.BOTTOM_CENTER;
640: if (edge == RectangleEdge.LEFT) {
641: angle = -Math.PI / 2.0;
642: } else {
643: angle = Math.PI / 2.0;
644: }
645: } else {
646: if (edge == RectangleEdge.LEFT) {
647: anchor = TextAnchor.CENTER_RIGHT;
648: rotationAnchor = TextAnchor.CENTER_RIGHT;
649: } else {
650: anchor = TextAnchor.CENTER_LEFT;
651: rotationAnchor = TextAnchor.CENTER_LEFT;
652: }
653: }
654: Tick tick = new NumberTick(
655: new Double(currentTickValue), tickLabel,
656: anchor, rotationAnchor, angle);
657: ticks.add(tick);
658: }
659: }
660: return ticks;
661:
662: }
663:
664: /**
665: * Converts a value to a string, using the list of symbols.
666: *
667: * @param value value to convert.
668: *
669: * @return The symbol.
670: */
671: public String valueToString(double value) {
672: String strToReturn;
673: try {
674: strToReturn = (String) this .symbols.get((int) value);
675: } catch (IndexOutOfBoundsException ex) {
676: strToReturn = "";
677: }
678: return strToReturn;
679: }
680:
681: /**
682: * Tests this axis for equality with an arbitrary object.
683: *
684: * @param obj the object (<code>null</code> permitted).
685: *
686: * @return A boolean.
687: */
688: public boolean equals(Object obj) {
689: if (obj == this ) {
690: return true;
691: }
692: if (!(obj instanceof SymbolAxis)) {
693: return false;
694: }
695: SymbolAxis that = (SymbolAxis) obj;
696: if (!this .symbols.equals(that.symbols)) {
697: return false;
698: }
699: if (this .gridBandsVisible != that.gridBandsVisible) {
700: return false;
701: }
702: if (!PaintUtilities.equal(this .gridBandPaint,
703: that.gridBandPaint)) {
704: return false;
705: }
706: return super .equals(obj);
707: }
708:
709: /**
710: * Provides serialization support.
711: *
712: * @param stream the output stream.
713: *
714: * @throws IOException if there is an I/O error.
715: */
716: private void writeObject(ObjectOutputStream stream)
717: throws IOException {
718: stream.defaultWriteObject();
719: SerialUtilities.writePaint(this .gridBandPaint, stream);
720: }
721:
722: /**
723: * Provides serialization support.
724: *
725: * @param stream the input stream.
726: *
727: * @throws IOException if there is an I/O error.
728: * @throws ClassNotFoundException if there is a classpath problem.
729: */
730: private void readObject(ObjectInputStream stream)
731: throws IOException, ClassNotFoundException {
732: stream.defaultReadObject();
733: this.gridBandPaint = SerialUtilities.readPaint(stream);
734: }
735:
736: }
|