1:
75:
76: package ;
77:
78: import ;
79: import ;
80: import ;
81: import ;
82: import ;
83: import ;
84: import ;
85: import ;
86: import ;
87: import ;
88: import ;
89: import ;
90: import ;
91: import ;
92: import ;
93:
94: import ;
95: import ;
96: import ;
97: import ;
98: import ;
99: import ;
100: import ;
101: import ;
102: import ;
103: import ;
104:
105:
110: public class SymbolAxis extends NumberAxis implements Serializable {
111:
112:
113: private static final long serialVersionUID = 7216330468770619716L;
114:
115:
116: public static final Paint DEFAULT_GRID_BAND_PAINT
117: = new Color(232, 234, 232, 128);
118:
119:
120: private List symbols;
121:
122:
123: private transient Paint gridBandPaint;
124:
125:
126: private boolean gridBandsVisible;
127:
128:
136: public SymbolAxis(String label, String[] sv) {
137: super(label);
138: this.symbols = Arrays.asList(sv);
139: this.gridBandsVisible = true;
140: this.gridBandPaint = DEFAULT_GRID_BAND_PAINT;
141:
142: setAutoTickUnitSelection(false, false);
143: setAutoRangeStickyZero(false);
144:
145: }
146:
147:
152: public String[] getSymbols() {
153: String[] result = new String[this.symbols.size()];
154: result = (String[]) this.symbols.toArray(result);
155: return result;
156: }
157:
158:
165: public Paint getGridBandPaint() {
166: return this.gridBandPaint;
167: }
168:
169:
175: public void setGridBandPaint(Paint paint) {
176: if (paint == null) {
177: throw new IllegalArgumentException("Null 'paint' argument.");
178: }
179: this.gridBandPaint = paint;
180: notifyListeners(new AxisChangeEvent(this));
181: }
182:
183:
190: public boolean isGridBandsVisible() {
191: return this.gridBandsVisible;
192: }
193:
194:
200: public void setGridBandsVisible(boolean flag) {
201: if (this.gridBandsVisible != flag) {
202: this.gridBandsVisible = flag;
203: notifyListeners(new AxisChangeEvent(this));
204: }
205: }
206:
207:
214: protected void selectAutoTickUnit(Graphics2D g2, Rectangle2D dataArea,
215: RectangleEdge edge) {
216: throw new UnsupportedOperationException();
217: }
218:
219:
235: public AxisState draw(Graphics2D g2,
236: double cursor,
237: Rectangle2D plotArea,
238: Rectangle2D dataArea,
239: RectangleEdge edge,
240: PlotRenderingInfo plotState) {
241:
242: AxisState info = new AxisState(cursor);
243: if (isVisible()) {
244: info = super.draw(g2, cursor, plotArea, dataArea, edge, plotState);
245: }
246: if (this.gridBandsVisible) {
247: drawGridBands(g2, plotArea, dataArea, edge, info.getTicks());
248: }
249: return info;
250:
251: }
252:
253:
265: protected void drawGridBands(Graphics2D g2,
266: Rectangle2D plotArea,
267: Rectangle2D dataArea,
268: RectangleEdge edge,
269: List ticks) {
270:
271: Shape savedClip = g2.getClip();
272: g2.clip(dataArea);
273: if (RectangleEdge.isTopOrBottom(edge)) {
274: drawGridBandsHorizontal(g2, plotArea, dataArea, true, ticks);
275: }
276: else if (RectangleEdge.isLeftOrRight(edge)) {
277: drawGridBandsVertical(g2, plotArea, dataArea, true, ticks);
278: }
279: g2.setClip(savedClip);
280:
281: }
282:
283:
297: protected void drawGridBandsHorizontal(Graphics2D g2,
298: Rectangle2D plotArea,
299: Rectangle2D dataArea,
300: boolean firstGridBandIsDark,
301: List ticks) {
302:
303: boolean currentGridBandIsDark = firstGridBandIsDark;
304: double yy = dataArea.getY();
305: double xx1, xx2;
306:
307:
308: double outlineStrokeWidth;
309: if (getPlot().getOutlineStroke() != null) {
310: outlineStrokeWidth
311: = ((BasicStroke) getPlot().getOutlineStroke()).getLineWidth();
312: }
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(
323: tick.getValue() - 0.5d, dataArea, RectangleEdge.BOTTOM
324: );
325: xx2 = valueToJava2D(
326: tick.getValue() + 0.5d, dataArea, RectangleEdge.BOTTOM
327: );
328: if (currentGridBandIsDark) {
329: g2.setPaint(this.gridBandPaint);
330: }
331: else {
332: g2.setPaint(Color.white);
333: }
334: band = new Rectangle2D.Double(xx1, yy + outlineStrokeWidth,
335: xx2 - xx1, dataArea.getMaxY() - yy - outlineStrokeWidth);
336: g2.fill(band);
337: currentGridBandIsDark = !currentGridBandIsDark;
338: }
339: g2.setPaintMode();
340: }
341:
342:
356: protected void drawGridBandsVertical(Graphics2D g2,
357: Rectangle2D drawArea,
358: Rectangle2D plotArea,
359: boolean firstGridBandIsDark,
360: List ticks) {
361:
362: boolean currentGridBandIsDark = firstGridBandIsDark;
363: double xx = plotArea.getX();
364: double yy1, yy2;
365:
366:
367: double outlineStrokeWidth;
368: if (getPlot().getOutlineStroke() != null) {
369: outlineStrokeWidth
370: = ((BasicStroke) getPlot().getOutlineStroke()).getLineWidth();
371: }
372: else {
373: outlineStrokeWidth = 1d;
374: }
375:
376: Iterator iterator = ticks.iterator();
377: ValueTick tick;
378: Rectangle2D band;
379: while (iterator.hasNext()) {
380: tick = (ValueTick) iterator.next();
381: yy1 = valueToJava2D(
382: tick.getValue() + 0.5d, plotArea, RectangleEdge.LEFT
383: );
384: yy2 = valueToJava2D(
385: tick.getValue() - 0.5d, plotArea, RectangleEdge.LEFT
386: );
387: if (currentGridBandIsDark) {
388: g2.setPaint(this.gridBandPaint);
389: }
390: else {
391: g2.setPaint(Color.white);
392: }
393: band = new Rectangle2D.Double(xx + outlineStrokeWidth,
394: yy1, plotArea.getMaxX() - xx - outlineStrokeWidth, yy2 - yy1);
395: g2.fill(band);
396: currentGridBandIsDark = !currentGridBandIsDark;
397: }
398: g2.setPaintMode();
399: }
400:
401:
404: protected void autoAdjustRange() {
405:
406: Plot plot = getPlot();
407: if (plot == null) {
408: return;
409: }
410:
411: if (plot instanceof ValueAxisPlot) {
412:
413:
414: double upper = this.symbols.size() - 1;
415: double lower = 0;
416: double range = upper - lower;
417:
418:
419: double minRange = getAutoRangeMinimumSize();
420: if (range < minRange) {
421: upper = (upper + lower + minRange) / 2;
422: lower = (upper + lower - minRange) / 2;
423: }
424:
425:
426: double upperMargin = 0.5;
427: double lowerMargin = 0.5;
428:
429: if (getAutoRangeIncludesZero()) {
430: if (getAutoRangeStickyZero()) {
431: if (upper <= 0.0) {
432: upper = 0.0;
433: }
434: else {
435: upper = upper + upperMargin;
436: }
437: if (lower >= 0.0) {
438: lower = 0.0;
439: }
440: else {
441: lower = lower - lowerMargin;
442: }
443: }
444: else {
445: upper = Math.max(0.0, upper + upperMargin);
446: lower = Math.min(0.0, lower - lowerMargin);
447: }
448: }
449: else {
450: if (getAutoRangeStickyZero()) {
451: if (upper <= 0.0) {
452: upper = Math.min(0.0, upper + upperMargin);
453: }
454: else {
455: upper = upper + upperMargin * range;
456: }
457: if (lower >= 0.0) {
458: lower = Math.max(0.0, lower - lowerMargin);
459: }
460: else {
461: lower = lower - lowerMargin;
462: }
463: }
464: else {
465: upper = upper + upperMargin;
466: lower = lower - lowerMargin;
467: }
468: }
469:
470: setRange(new Range(lower, upper), false, false);
471:
472: }
473:
474: }
475:
476:
487: public List refreshTicks(Graphics2D g2,
488: AxisState state,
489: Rectangle2D dataArea,
490: RectangleEdge edge) {
491: List ticks = null;
492: if (RectangleEdge.isTopOrBottom(edge)) {
493: ticks = refreshTicksHorizontal(g2, dataArea, edge);
494: }
495: else if (RectangleEdge.isLeftOrRight(edge)) {
496: ticks = refreshTicksVertical(g2, dataArea, edge);
497: }
498: return ticks;
499: }
500:
501:
511: protected List refreshTicksHorizontal(Graphics2D g2,
512: Rectangle2D dataArea,
513: RectangleEdge edge) {
514:
515: List ticks = new java.util.ArrayList();
516:
517: Font tickLabelFont = getTickLabelFont();
518: g2.setFont(tickLabelFont);
519:
520: double size = getTickUnit().getSize();
521: int count = calculateVisibleTickCount();
522: double lowestTickValue = calculateLowestVisibleTickValue();
523:
524: double previousDrawnTickLabelPos = 0.0;
525: double previousDrawnTickLabelLength = 0.0;
526:
527: if (count <= ValueAxis.MAXIMUM_TICK_COUNT) {
528: for (int i = 0; i < count; i++) {
529: double currentTickValue = lowestTickValue + (i * size);
530: double xx = valueToJava2D(currentTickValue, dataArea, edge);
531: String tickLabel;
532: NumberFormat formatter = getNumberFormatOverride();
533: if (formatter != null) {
534: tickLabel = formatter.format(currentTickValue);
535: }
536: else {
537: tickLabel = valueToString(currentTickValue);
538: }
539:
540:
541: Rectangle2D bounds = TextUtilities.getTextBounds(
542: tickLabel, g2, g2.getFontMetrics()
543: );
544: double tickLabelLength = isVerticalTickLabels()
545: ? bounds.getHeight() : bounds.getWidth();
546: boolean tickLabelsOverlapping = false;
547: if (i > 0) {
548: double avgTickLabelLength = (previousDrawnTickLabelLength
549: + tickLabelLength) / 2.0;
550: if (Math.abs(xx - previousDrawnTickLabelPos)
551: < avgTickLabelLength) {
552: tickLabelsOverlapping = true;
553: }
554: }
555: if (tickLabelsOverlapping) {
556: tickLabel = "";
557: }
558: else {
559:
560: previousDrawnTickLabelPos = xx;
561: previousDrawnTickLabelLength = tickLabelLength;
562: }
563:
564: TextAnchor anchor = null;
565: TextAnchor rotationAnchor = null;
566: double angle = 0.0;
567: if (isVerticalTickLabels()) {
568: anchor = TextAnchor.CENTER_RIGHT;
569: rotationAnchor = TextAnchor.CENTER_RIGHT;
570: if (edge == RectangleEdge.TOP) {
571: angle = Math.PI / 2.0;
572: }
573: else {
574: angle = -Math.PI / 2.0;
575: }
576: }
577: else {
578: if (edge == RectangleEdge.TOP) {
579: anchor = TextAnchor.BOTTOM_CENTER;
580: rotationAnchor = TextAnchor.BOTTOM_CENTER;
581: }
582: else {
583: anchor = TextAnchor.TOP_CENTER;
584: rotationAnchor = TextAnchor.TOP_CENTER;
585: }
586: }
587: Tick tick = new NumberTick(
588: new Double(currentTickValue), tickLabel, anchor,
589: rotationAnchor, angle
590: );
591: ticks.add(tick);
592: }
593: }
594: return ticks;
595:
596: }
597:
598:
608: protected List refreshTicksVertical(Graphics2D g2,
609: Rectangle2D dataArea,
610: RectangleEdge edge) {
611:
612: List ticks = new java.util.ArrayList();
613:
614: Font tickLabelFont = getTickLabelFont();
615: g2.setFont(tickLabelFont);
616:
617: double size = getTickUnit().getSize();
618: int count = calculateVisibleTickCount();
619: double lowestTickValue = calculateLowestVisibleTickValue();
620:
621: double previousDrawnTickLabelPos = 0.0;
622: double previousDrawnTickLabelLength = 0.0;
623:
624: if (count <= ValueAxis.MAXIMUM_TICK_COUNT) {
625: for (int i = 0; i < count; i++) {
626: double currentTickValue = lowestTickValue + (i * size);
627: double yy = valueToJava2D(currentTickValue, dataArea, edge);
628: String tickLabel;
629: NumberFormat formatter = getNumberFormatOverride();
630: if (formatter != null) {
631: tickLabel = formatter.format(currentTickValue);
632: }
633: else {
634: tickLabel = valueToString(currentTickValue);
635: }
636:
637:
638: Rectangle2D bounds = TextUtilities.getTextBounds(
639: tickLabel, g2, g2.getFontMetrics()
640: );
641: double tickLabelLength = isVerticalTickLabels()
642: ? bounds.getWidth() : bounds.getHeight();
643: boolean tickLabelsOverlapping = false;
644: if (i > 0) {
645: double avgTickLabelLength =
646: (previousDrawnTickLabelLength + tickLabelLength) / 2.0;
647: if (Math.abs(yy - previousDrawnTickLabelPos)
648: < avgTickLabelLength) {
649: tickLabelsOverlapping = true;
650: }
651: if (tickLabelsOverlapping) {
652: tickLabel = "";
653: }
654: else {
655:
656: previousDrawnTickLabelPos = yy;
657: previousDrawnTickLabelLength = tickLabelLength;
658: }
659: }
660: TextAnchor anchor = null;
661: TextAnchor rotationAnchor = null;
662: double angle = 0.0;
663: if (isVerticalTickLabels()) {
664: anchor = TextAnchor.BOTTOM_CENTER;
665: rotationAnchor = TextAnchor.BOTTOM_CENTER;
666: if (edge == RectangleEdge.LEFT) {
667: angle = -Math.PI / 2.0;
668: }
669: else {
670: angle = Math.PI / 2.0;
671: }
672: }
673: else {
674: if (edge == RectangleEdge.LEFT) {
675: anchor = TextAnchor.CENTER_RIGHT;
676: rotationAnchor = TextAnchor.CENTER_RIGHT;
677: }
678: else {
679: anchor = TextAnchor.CENTER_LEFT;
680: rotationAnchor = TextAnchor.CENTER_LEFT;
681: }
682: }
683: Tick tick = new NumberTick(
684: new Double(currentTickValue), tickLabel, anchor,
685: rotationAnchor, angle
686: );
687: ticks.add(tick);
688: }
689: }
690: return ticks;
691:
692: }
693:
694:
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:
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: if (!super.equals(obj)) {
737: return false;
738: }
739: return true;
740: }
741:
742:
749: private void writeObject(ObjectOutputStream stream) throws IOException {
750: stream.defaultWriteObject();
751: SerialUtilities.writePaint(this.gridBandPaint, stream);
752: }
753:
754:
762: private void readObject(ObjectInputStream stream)
763: throws IOException, ClassNotFoundException {
764: stream.defaultReadObject();
765: this.gridBandPaint = SerialUtilities.readPaint(stream);
766: }
767:
768: }