1:
79:
80: package ;
81:
82: import ;
83: import ;
84: import ;
85: import ;
86: import ;
87: import ;
88: import ;
89: import ;
90: import ;
91: import ;
92: import ;
93: import ;
94: import ;
95: import ;
96: import ;
97: import ;
98:
99: import ;
100: import ;
101: import ;
102: import ;
103: import ;
104: import ;
105: import ;
106: import ;
107: import ;
108: import ;
109:
110:
113: public class SymbolAxis extends NumberAxis implements Serializable {
114:
115:
116: private static final long serialVersionUID = 7216330468770619716L;
117:
118:
119: public static final Paint DEFAULT_GRID_BAND_PAINT
120: = new Color(232, 234, 232, 128);
121:
122:
127: public static final Paint DEFAULT_GRID_BAND_ALTERNATE_PAINT
128: = new Color(0, 0, 0, 0);
129:
130:
131: private List symbols;
132:
133:
134: private boolean gridBandsVisible;
135:
136:
137: private transient Paint gridBandPaint;
138:
139:
144: private transient Paint gridBandAlternatePaint;
145:
146:
154: public SymbolAxis(String label, String[] sv) {
155: super(label);
156: this.symbols = Arrays.asList(sv);
157: this.gridBandsVisible = true;
158: this.gridBandPaint = DEFAULT_GRID_BAND_PAINT;
159: this.gridBandAlternatePaint = DEFAULT_GRID_BAND_ALTERNATE_PAINT;
160: setAutoTickUnitSelection(false, false);
161: setAutoRangeStickyZero(false);
162:
163: }
164:
165:
170: public String[] getSymbols() {
171: String[] result = new String[this.symbols.size()];
172: result = (String[]) this.symbols.toArray(result);
173: return result;
174: }
175:
176:
185: public boolean isGridBandsVisible() {
186: return this.gridBandsVisible;
187: }
188:
189:
197: public void setGridBandsVisible(boolean flag) {
198: if (this.gridBandsVisible != flag) {
199: this.gridBandsVisible = flag;
200: notifyListeners(new AxisChangeEvent(this));
201: }
202: }
203:
204:
212: public Paint getGridBandPaint() {
213: return this.gridBandPaint;
214: }
215:
216:
224: public void setGridBandPaint(Paint paint) {
225: if (paint == null) {
226: throw new IllegalArgumentException("Null 'paint' argument.");
227: }
228: this.gridBandPaint = paint;
229: notifyListeners(new AxisChangeEvent(this));
230: }
231:
232:
242: public Paint getGridBandAlternatePaint() {
243: return this.gridBandAlternatePaint;
244: }
245:
246:
257: public void setGridBandAlternatePaint(Paint paint) {
258: if (paint == null) {
259: throw new IllegalArgumentException("Null 'paint' argument.");
260: }
261: this.gridBandAlternatePaint = paint;
262: notifyListeners(new AxisChangeEvent(this));
263: }
264:
265:
272: protected void selectAutoTickUnit(Graphics2D g2, Rectangle2D dataArea,
273: RectangleEdge edge) {
274: throw new UnsupportedOperationException();
275: }
276:
277:
293: public AxisState draw(Graphics2D g2,
294: double cursor,
295: Rectangle2D plotArea,
296: Rectangle2D dataArea,
297: RectangleEdge edge,
298: PlotRenderingInfo plotState) {
299:
300: AxisState info = new AxisState(cursor);
301: if (isVisible()) {
302: info = super.draw(g2, cursor, plotArea, dataArea, edge, plotState);
303: }
304: if (this.gridBandsVisible) {
305: drawGridBands(g2, plotArea, dataArea, edge, info.getTicks());
306: }
307: return info;
308:
309: }
310:
311:
323: protected void drawGridBands(Graphics2D g2,
324: Rectangle2D plotArea,
325: Rectangle2D dataArea,
326: RectangleEdge edge,
327: List ticks) {
328:
329: Shape savedClip = g2.getClip();
330: g2.clip(dataArea);
331: if (RectangleEdge.isTopOrBottom(edge)) {
332: drawGridBandsHorizontal(g2, plotArea, dataArea, true, ticks);
333: }
334: else if (RectangleEdge.isLeftOrRight(edge)) {
335: drawGridBandsVertical(g2, plotArea, dataArea, true, ticks);
336: }
337: g2.setClip(savedClip);
338:
339: }
340:
341:
355: protected void drawGridBandsHorizontal(Graphics2D g2,
356: Rectangle2D plotArea,
357: Rectangle2D dataArea,
358: boolean firstGridBandIsDark,
359: List ticks) {
360:
361: boolean currentGridBandIsDark = firstGridBandIsDark;
362: double yy = dataArea.getY();
363: double xx1, xx2;
364:
365:
366: double outlineStrokeWidth;
367: if (getPlot().getOutlineStroke() != null) {
368: outlineStrokeWidth
369: = ((BasicStroke) getPlot().getOutlineStroke()).getLineWidth();
370: }
371: else {
372: outlineStrokeWidth = 1d;
373: }
374:
375: Iterator iterator = ticks.iterator();
376: ValueTick tick;
377: Rectangle2D band;
378: while (iterator.hasNext()) {
379: tick = (ValueTick) iterator.next();
380: xx1 = valueToJava2D(tick.getValue() - 0.5d, dataArea,
381: RectangleEdge.BOTTOM);
382: xx2 = valueToJava2D(tick.getValue() + 0.5d, dataArea,
383: RectangleEdge.BOTTOM);
384: if (currentGridBandIsDark) {
385: g2.setPaint(this.gridBandPaint);
386: }
387: else {
388: g2.setPaint(Color.white);
389: }
390: band = new Rectangle2D.Double(xx1, yy + outlineStrokeWidth,
391: xx2 - xx1, dataArea.getMaxY() - yy - outlineStrokeWidth);
392: g2.fill(band);
393: currentGridBandIsDark = !currentGridBandIsDark;
394: }
395: g2.setPaintMode();
396: }
397:
398:
412: protected void drawGridBandsVertical(Graphics2D g2,
413: Rectangle2D drawArea,
414: Rectangle2D plotArea,
415: boolean firstGridBandIsDark,
416: List ticks) {
417:
418: boolean currentGridBandIsDark = firstGridBandIsDark;
419: double xx = plotArea.getX();
420: double yy1, yy2;
421:
422:
423: double outlineStrokeWidth;
424: Stroke outlineStroke = getPlot().getOutlineStroke();
425: if (outlineStroke != null && outlineStroke instanceof BasicStroke) {
426: outlineStrokeWidth = ((BasicStroke) outlineStroke).getLineWidth();
427: }
428: else {
429: outlineStrokeWidth = 1d;
430: }
431:
432: Iterator iterator = ticks.iterator();
433: ValueTick tick;
434: Rectangle2D band;
435: while (iterator.hasNext()) {
436: tick = (ValueTick) iterator.next();
437: yy1 = valueToJava2D(tick.getValue() + 0.5d, plotArea,
438: RectangleEdge.LEFT);
439: yy2 = valueToJava2D(tick.getValue() - 0.5d, plotArea,
440: RectangleEdge.LEFT);
441: if (currentGridBandIsDark) {
442: g2.setPaint(this.gridBandPaint);
443: }
444: else {
445: g2.setPaint(Color.white);
446: }
447: band = new Rectangle2D.Double(xx + outlineStrokeWidth, yy1,
448: plotArea.getMaxX() - xx - outlineStrokeWidth, yy2 - yy1);
449: g2.fill(band);
450: currentGridBandIsDark = !currentGridBandIsDark;
451: }
452: g2.setPaintMode();
453: }
454:
455:
458: protected void autoAdjustRange() {
459:
460: Plot plot = getPlot();
461: if (plot == null) {
462: return;
463: }
464:
465: if (plot instanceof ValueAxisPlot) {
466:
467:
468: double upper = this.symbols.size() - 1;
469: double lower = 0;
470: double range = upper - lower;
471:
472:
473: double minRange = getAutoRangeMinimumSize();
474: if (range < minRange) {
475: upper = (upper + lower + minRange) / 2;
476: lower = (upper + lower - minRange) / 2;
477: }
478:
479:
480: double upperMargin = 0.5;
481: double lowerMargin = 0.5;
482:
483: if (getAutoRangeIncludesZero()) {
484: if (getAutoRangeStickyZero()) {
485: if (upper <= 0.0) {
486: upper = 0.0;
487: }
488: else {
489: upper = upper + upperMargin;
490: }
491: if (lower >= 0.0) {
492: lower = 0.0;
493: }
494: else {
495: lower = lower - lowerMargin;
496: }
497: }
498: else {
499: upper = Math.max(0.0, upper + upperMargin);
500: lower = Math.min(0.0, lower - lowerMargin);
501: }
502: }
503: else {
504: if (getAutoRangeStickyZero()) {
505: if (upper <= 0.0) {
506: upper = Math.min(0.0, upper + upperMargin);
507: }
508: else {
509: upper = upper + upperMargin * range;
510: }
511: if (lower >= 0.0) {
512: lower = Math.max(0.0, lower - lowerMargin);
513: }
514: else {
515: lower = lower - lowerMargin;
516: }
517: }
518: else {
519: upper = upper + upperMargin;
520: lower = lower - lowerMargin;
521: }
522: }
523:
524: setRange(new Range(lower, upper), false, false);
525:
526: }
527:
528: }
529:
530:
541: public List refreshTicks(Graphics2D g2,
542: AxisState state,
543: Rectangle2D dataArea,
544: RectangleEdge edge) {
545: List ticks = null;
546: if (RectangleEdge.isTopOrBottom(edge)) {
547: ticks = refreshTicksHorizontal(g2, dataArea, edge);
548: }
549: else if (RectangleEdge.isLeftOrRight(edge)) {
550: ticks = refreshTicksVertical(g2, dataArea, edge);
551: }
552: return ticks;
553: }
554:
555:
565: protected List refreshTicksHorizontal(Graphics2D g2,
566: Rectangle2D dataArea,
567: RectangleEdge edge) {
568:
569: List ticks = new java.util.ArrayList();
570:
571: Font tickLabelFont = getTickLabelFont();
572: g2.setFont(tickLabelFont);
573:
574: double size = getTickUnit().getSize();
575: int count = calculateVisibleTickCount();
576: double lowestTickValue = calculateLowestVisibleTickValue();
577:
578: double previousDrawnTickLabelPos = 0.0;
579: double previousDrawnTickLabelLength = 0.0;
580:
581: if (count <= ValueAxis.MAXIMUM_TICK_COUNT) {
582: for (int i = 0; i < count; i++) {
583: double currentTickValue = lowestTickValue + (i * size);
584: double xx = valueToJava2D(currentTickValue, dataArea, edge);
585: String tickLabel;
586: NumberFormat formatter = getNumberFormatOverride();
587: if (formatter != null) {
588: tickLabel = formatter.format(currentTickValue);
589: }
590: else {
591: tickLabel = valueToString(currentTickValue);
592: }
593:
594:
595: Rectangle2D bounds = TextUtilities.getTextBounds(tickLabel, g2,
596: g2.getFontMetrics());
597: double tickLabelLength = isVerticalTickLabels()
598: ? bounds.getHeight() : bounds.getWidth();
599: boolean tickLabelsOverlapping = false;
600: if (i > 0) {
601: double avgTickLabelLength = (previousDrawnTickLabelLength
602: + tickLabelLength) / 2.0;
603: if (Math.abs(xx - previousDrawnTickLabelPos)
604: < avgTickLabelLength) {
605: tickLabelsOverlapping = true;
606: }
607: }
608: if (tickLabelsOverlapping) {
609: tickLabel = "";
610: }
611: else {
612:
613: previousDrawnTickLabelPos = xx;
614: previousDrawnTickLabelLength = tickLabelLength;
615: }
616:
617: TextAnchor anchor = null;
618: TextAnchor rotationAnchor = null;
619: double angle = 0.0;
620: if (isVerticalTickLabels()) {
621: anchor = TextAnchor.CENTER_RIGHT;
622: rotationAnchor = TextAnchor.CENTER_RIGHT;
623: if (edge == RectangleEdge.TOP) {
624: angle = Math.PI / 2.0;
625: }
626: else {
627: angle = -Math.PI / 2.0;
628: }
629: }
630: else {
631: if (edge == RectangleEdge.TOP) {
632: anchor = TextAnchor.BOTTOM_CENTER;
633: rotationAnchor = TextAnchor.BOTTOM_CENTER;
634: }
635: else {
636: anchor = TextAnchor.TOP_CENTER;
637: rotationAnchor = TextAnchor.TOP_CENTER;
638: }
639: }
640: Tick tick = new NumberTick(new Double(currentTickValue),
641: tickLabel, anchor, rotationAnchor, angle);
642: ticks.add(tick);
643: }
644: }
645: return ticks;
646:
647: }
648:
649:
659: protected List refreshTicksVertical(Graphics2D g2,
660: Rectangle2D dataArea,
661: RectangleEdge edge) {
662:
663: List ticks = new java.util.ArrayList();
664:
665: Font tickLabelFont = getTickLabelFont();
666: g2.setFont(tickLabelFont);
667:
668: double size = getTickUnit().getSize();
669: int count = calculateVisibleTickCount();
670: double lowestTickValue = calculateLowestVisibleTickValue();
671:
672: double previousDrawnTickLabelPos = 0.0;
673: double previousDrawnTickLabelLength = 0.0;
674:
675: if (count <= ValueAxis.MAXIMUM_TICK_COUNT) {
676: for (int i = 0; i < count; i++) {
677: double currentTickValue = lowestTickValue + (i * size);
678: double yy = valueToJava2D(currentTickValue, dataArea, edge);
679: String tickLabel;
680: NumberFormat formatter = getNumberFormatOverride();
681: if (formatter != null) {
682: tickLabel = formatter.format(currentTickValue);
683: }
684: else {
685: tickLabel = valueToString(currentTickValue);
686: }
687:
688:
689: Rectangle2D bounds = TextUtilities.getTextBounds(tickLabel, g2,
690: g2.getFontMetrics());
691: double tickLabelLength = isVerticalTickLabels()
692: ? bounds.getWidth() : bounds.getHeight();
693: boolean tickLabelsOverlapping = false;
694: if (i > 0) {
695: double avgTickLabelLength = (previousDrawnTickLabelLength
696: + tickLabelLength) / 2.0;
697: if (Math.abs(yy - previousDrawnTickLabelPos)
698: < avgTickLabelLength) {
699: tickLabelsOverlapping = true;
700: }
701: }
702: if (tickLabelsOverlapping) {
703: tickLabel = "";
704: }
705: else {
706:
707: previousDrawnTickLabelPos = yy;
708: previousDrawnTickLabelLength = tickLabelLength;
709: }
710:
711: TextAnchor anchor = null;
712: TextAnchor rotationAnchor = null;
713: double angle = 0.0;
714: if (isVerticalTickLabels()) {
715: anchor = TextAnchor.BOTTOM_CENTER;
716: rotationAnchor = TextAnchor.BOTTOM_CENTER;
717: if (edge == RectangleEdge.LEFT) {
718: angle = -Math.PI / 2.0;
719: }
720: else {
721: angle = Math.PI / 2.0;
722: }
723: }
724: else {
725: if (edge == RectangleEdge.LEFT) {
726: anchor = TextAnchor.CENTER_RIGHT;
727: rotationAnchor = TextAnchor.CENTER_RIGHT;
728: }
729: else {
730: anchor = TextAnchor.CENTER_LEFT;
731: rotationAnchor = TextAnchor.CENTER_LEFT;
732: }
733: }
734: Tick tick = new NumberTick(new Double(currentTickValue),
735: tickLabel, anchor, rotationAnchor, angle);
736: ticks.add(tick);
737: }
738: }
739: return ticks;
740:
741: }
742:
743:
750: public String valueToString(double value) {
751: String strToReturn;
752: try {
753: strToReturn = (String) this.symbols.get((int) value);
754: }
755: catch (IndexOutOfBoundsException ex) {
756: strToReturn = "";
757: }
758: return strToReturn;
759: }
760:
761:
768: public boolean equals(Object obj) {
769: if (obj == this) {
770: return true;
771: }
772: if (!(obj instanceof SymbolAxis)) {
773: return false;
774: }
775: SymbolAxis that = (SymbolAxis) obj;
776: if (!this.symbols.equals(that.symbols)) {
777: return false;
778: }
779: if (this.gridBandsVisible != that.gridBandsVisible) {
780: return false;
781: }
782: if (!PaintUtilities.equal(this.gridBandPaint, that.gridBandPaint)) {
783: return false;
784: }
785: if (!PaintUtilities.equal(this.gridBandAlternatePaint,
786: that.gridBandAlternatePaint)) {
787: return false;
788: }
789: return super.equals(obj);
790: }
791:
792:
799: private void writeObject(ObjectOutputStream stream) throws IOException {
800: stream.defaultWriteObject();
801: SerialUtilities.writePaint(this.gridBandPaint, stream);
802: SerialUtilities.writePaint(this.gridBandAlternatePaint, stream);
803: }
804:
805:
813: private void readObject(ObjectInputStream stream)
814: throws IOException, ClassNotFoundException {
815: stream.defaultReadObject();
816: this.gridBandPaint = SerialUtilities.readPaint(stream);
817: this.gridBandAlternatePaint = SerialUtilities.readPaint(stream);
818: }
819:
820: }