1 75 76 package org.jfree.chart.axis; 77 78 import java.awt.BasicStroke ; 79 import java.awt.Color ; 80 import java.awt.Font ; 81 import java.awt.Graphics2D ; 82 import java.awt.Paint ; 83 import java.awt.Shape ; 84 import java.awt.geom.Rectangle2D ; 85 import java.io.IOException ; 86 import java.io.ObjectInputStream ; 87 import java.io.ObjectOutputStream ; 88 import java.io.Serializable ; 89 import java.text.NumberFormat ; 90 import java.util.Arrays ; 91 import java.util.Iterator ; 92 import java.util.List ; 93 94 import org.jfree.chart.event.AxisChangeEvent; 95 import org.jfree.chart.plot.Plot; 96 import org.jfree.chart.plot.PlotRenderingInfo; 97 import org.jfree.chart.plot.ValueAxisPlot; 98 import org.jfree.data.Range; 99 import org.jfree.io.SerialUtilities; 100 import org.jfree.text.TextUtilities; 101 import org.jfree.ui.RectangleEdge; 102 import org.jfree.ui.TextAnchor; 103 import org.jfree.util.PaintUtilities; 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 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 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; } 410 411 if (plot instanceof ValueAxisPlot) { 412 413 double upper = this.symbols.size() - 1; 415 double lower = 0; 416 double range = upper - lower; 417 418 double minRange = getAutoRangeMinimumSize(); 420 if (range < minRange) { 421 upper = (upper + lower + minRange) / 2; 422 lower = (upper + lower - minRange) / 2; 423 } 424 425 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 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 = ""; } 558 else { 559 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 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 = ""; } 654 else { 655 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 } 769 | Popular Tags |