1 19 20 package org.apache.excalibur.instrument.client; 21 22 import java.awt.Color ; 23 import java.awt.Dimension ; 24 import java.awt.FontMetrics ; 25 import java.awt.Graphics ; 26 import java.awt.Graphics2D ; 27 import java.awt.Insets ; 28 import java.awt.RenderingHints ; 29 import java.awt.event.MouseEvent ; 30 import java.awt.event.MouseListener ; 31 import java.awt.event.MouseMotionListener ; 32 import java.text.DecimalFormat ; 33 import java.text.MessageFormat ; 34 import java.util.Calendar ; 35 import java.util.Date ; 36 37 import javax.swing.JComponent ; 38 39 44 public class LineChart 45 extends JComponent 46 implements MouseListener , MouseMotionListener 47 { 48 49 private static long m_zoneOffset; 50 51 private Color m_lineColor = Color.red; 52 private Color m_lightLineColor = new Color ( 255, 128, 128 ); 53 private Color m_lightGridColor = new Color ( 192, 192, 192 ); 54 private Color m_darkGridColor = new Color ( 128, 128, 128 ); 55 private Color m_frameColor = Color.black; 56 private Color m_crossColor = Color.blue; 57 private Color m_maskFrameColor = new Color ( 128, 128, 255, 192 ); 58 private Color m_maskColor = new Color ( 224, 224, 255, 192 ); 59 60 61 private int m_lineSampleInterval; 62 63 64 private long m_sampleInterval; 65 66 70 private String m_format; 71 72 76 private String m_dFormat; 77 78 79 private int m_averageWindow; 80 81 82 private boolean m_antialias; 83 84 85 private long m_time; 86 87 88 private int[] m_values; 89 90 91 private float[] m_averageWindowValues; 92 93 94 private int m_min; 95 96 97 private int m_max; 98 99 100 private DecimalFormat m_intFormat = new DecimalFormat ( "###,###,###,##0" ); 101 102 103 private DecimalFormat m_floatFormat = new DecimalFormat ( "###,###,###,##0.00" ); 104 105 private boolean m_mouseOver; 106 private boolean m_mousePressed; 107 private int m_mouseX; 108 private int m_mouseY; 109 110 113 static 114 { 115 Calendar now = Calendar.getInstance(); 116 m_zoneOffset = now.get( Calendar.ZONE_OFFSET ); 117 } 118 119 122 141 public LineChart( int lineSampleInterval, 142 long sampleInterval, 143 String format, 144 String detailFormat, 145 int averageWindow, 146 boolean antialias ) 147 { 148 m_lineSampleInterval = lineSampleInterval; 149 m_sampleInterval = sampleInterval; 150 m_format = format; 151 m_dFormat = detailFormat; 152 m_averageWindow = averageWindow; 153 m_antialias = antialias; 154 155 setBackground( Color.white ); 156 setValues( new int[ 0 ], System.currentTimeMillis() ); 157 addMouseListener( this ); 158 addMouseMotionListener( this ); 159 } 160 161 164 170 public void setAntialias( boolean antialias ) 171 { 172 m_antialias = antialias; 173 } 174 175 181 public void setValues( int[] values, long time ) 182 { 183 int max = 0; 184 int min = Integer.MAX_VALUE; 185 for( int i = 0; i < values.length; i++ ) 186 { 187 int v = values[ i ]; 188 if( v > max ) 189 { 190 max = v; 191 } 192 if( v < min ) 193 { 194 min = v; 195 } 196 } 197 if( ( max - min ) < 10 ) 198 { 199 max += ( ( 10 - ( max - min ) ) / 2 ); 200 min -= ( 10 - ( max - min ) ); 201 } 202 if( min < 0 ) 203 { 204 max += -min; 205 min = 0; 206 } 207 208 float[] averageValues = new float[ values.length ]; 209 if( m_averageWindow > 1 ) 210 { 211 int total = 0; 212 for( int i = 0; i < values.length; i++ ) 213 { 214 total += values[ i ]; 215 if( i >= m_averageWindow - 1 ) 216 { 217 if( i >= m_averageWindow ) 218 { 219 total -= values[ i - m_averageWindow ]; 220 } 221 averageValues[ i ] = (float)total / m_averageWindow; 222 } 223 else 224 { 225 averageValues[ i ] = Float.MIN_VALUE; 226 } 227 } 228 } 229 230 synchronized( this ) 231 { 232 m_time = time; 233 m_values = values; 234 m_averageWindowValues = averageValues; 235 m_min = min; 236 m_max = max; 237 } 238 239 repaint(); 240 } 241 242 private String getFormattedTime( Date dTime, boolean detailed ) 243 { 244 Calendar calendar = Calendar.getInstance(); 245 calendar.setTime( dTime ); 246 247 int v; 248 String year, month, day, hour, minute, second, hundreths; 249 250 v = calendar.get( Calendar.YEAR ); 252 year = Integer.toString( v ); 253 254 v = calendar.get( Calendar.MONTH ) - Calendar.JANUARY + 1; 256 if( v < 10 ) 257 { 258 month = "0" + Integer.toString( v ); 259 } 260 else 261 { 262 month = Integer.toString( v ); 263 } 264 265 v = calendar.get( Calendar.DAY_OF_MONTH ); 267 if( v < 10 ) 268 { 269 day = "0" + Integer.toString( v ); 270 } 271 else 272 { 273 day = Integer.toString( v ); 274 } 275 276 v = calendar.get( Calendar.HOUR_OF_DAY ); 278 if( v < 10 ) 279 { 280 hour = "0" + Integer.toString( v ); 281 } 282 else 283 { 284 hour = Integer.toString( v ); 285 } 286 287 v = calendar.get( Calendar.MINUTE ); 289 if( v < 10 ) 290 { 291 minute = "0" + Integer.toString( v ); 292 } 293 else 294 { 295 minute = Integer.toString( v ); 296 } 297 298 v = calendar.get( Calendar.SECOND ); 300 if( v < 10 ) 301 { 302 second = "0" + Integer.toString( v ); 303 } 304 else 305 { 306 second = Integer.toString( v ); 307 } 308 309 v = calendar.get( Calendar.MILLISECOND ) / 10; 311 if( v < 10 ) 312 { 313 hundreths = "0" + Integer.toString( v ); 314 } 315 else 316 { 317 hundreths = Integer.toString( v ); 318 } 319 320 String format; 321 if( detailed ) 322 { 323 format = MessageFormat.format( m_dFormat, 324 new Object []{year, month, day, hour, minute, second, hundreths} ); 325 } 326 else 327 { 328 format = MessageFormat.format( m_format, 329 new Object []{year, month, day, hour, minute, second, hundreths} ); 330 } 331 return format; 332 } 333 334 345 private void paintHorizontalGrid( Graphics g, 346 int yLabelInterval, 347 int fontHeight, 348 int chartLeft, 349 int chartTop, 350 int chartWidth, 351 int chartHeight ) 352 { 353 if( chartHeight > 0 ) 354 { 355 int horizonalLineLabeledInterval = (int)Math.ceil( (float)fontHeight 356 / ( (long)yLabelInterval * chartHeight / ( m_max - m_min ) ) ); 357 int horizontalLineNumber = 0; 358 for( int i = ( (int)Math.ceil( (float)m_min / yLabelInterval ) ) * yLabelInterval; 359 i < m_max; i += yLabelInterval ) 360 { 361 int y = chartTop + chartHeight - 364 (int)( (long)chartHeight * ( i - m_min ) / ( m_max - m_min ) ); 365 366 if( horizontalLineNumber >= horizonalLineLabeledInterval ) 367 { 368 horizontalLineNumber = 0; 369 } 370 if( horizontalLineNumber == 0 ) 371 { 372 String lbl = m_intFormat.format( i ); 373 g.setColor( m_frameColor ); 374 g.drawString( lbl, chartLeft - 5 - g.getFontMetrics().stringWidth( lbl ), 375 y + ( fontHeight >> 1 ) ); 376 377 g.setColor( m_darkGridColor ); 378 } 379 else 380 { 381 g.setColor( m_lightGridColor ); 382 } 383 horizontalLineNumber++; 384 385 if( chartWidth > 0 ) 386 { 387 g.drawLine( chartLeft, y, chartLeft + chartWidth, y ); 388 } 389 } 390 } 391 } 392 393 403 private void paintVerticalGrid( Graphics g, 404 int fontHeight, 405 int chartLeft, 406 int chartTop, 407 int chartWidth, 408 int chartHeight ) 409 { 410 if( chartWidth > 0 ) 411 { 412 FontMetrics fontMetrics = g.getFontMetrics(); 413 414 String format = MessageFormat.format( m_format, new String []{"00", "00", "00", "00"} ); 416 int fw = fontMetrics.stringWidth( format ) + 10; 417 418 int verticalLineLabeledInterval; 420 if( ( m_values.length > 0 ) && ( chartWidth > 0 ) ) 421 { 422 verticalLineLabeledInterval = (int)Math.ceil( 423 (float)fw / ( m_lineSampleInterval * chartWidth / ( m_values.length ) ) ); 424 } 425 else 426 { 427 verticalLineLabeledInterval = 1; 428 } 429 430 long baseTime = ( ( m_time - m_values.length * m_sampleInterval ) / 432 ( m_sampleInterval * m_lineSampleInterval ) ) * 433 ( m_sampleInterval * m_lineSampleInterval ) - m_zoneOffset; 434 435 int verticalLineNumber = 0; 437 for( int i = 0; i < m_values.length; i++ ) 438 { 439 long time = m_time - ( m_values.length - i - 1 ) * m_sampleInterval; 440 if( ( ( ( time - baseTime ) / m_sampleInterval ) % m_lineSampleInterval ) == 0 ) 441 { 442 int x = chartLeft + i * chartWidth / ( m_values.length - 1 ); 443 444 if( ( verticalLineNumber >= verticalLineLabeledInterval ) || 446 ( verticalLineNumber == 0 ) ) 447 { 448 format = getFormattedTime( new Date ( time ), false ); 449 450 if( x - fontMetrics.stringWidth( format ) / 2 >= chartLeft ) 451 { 452 g.setColor( m_frameColor ); 453 g.drawString( format, x - fontMetrics.stringWidth( format ) / 2, 454 chartTop + chartHeight + fontHeight ); 455 456 g.setColor( m_darkGridColor ); 457 verticalLineNumber = 1; 458 } 459 else 460 { 461 g.setColor( m_lightGridColor ); 462 } 463 } 464 else 465 { 466 g.setColor( m_lightGridColor ); 467 verticalLineNumber++; 468 } 469 470 if( chartHeight > 0 ) 472 { 473 g.drawLine( x, chartTop, x, chartTop + chartHeight ); 474 } 475 } 476 } 477 } 478 } 479 480 489 private void paintFrame( Graphics g, 490 int chartLeft, 491 int chartTop, 492 int chartWidth, 493 int chartHeight ) 494 { 495 if( ( chartWidth > 0 ) && ( chartHeight > 0 ) ) 496 { 497 g.setColor( m_frameColor ); 498 g.drawLine( chartLeft, chartTop, chartLeft, chartTop + chartHeight ); 499 g.drawLine( chartLeft, chartTop + chartHeight, chartLeft + chartWidth, 500 chartTop + chartHeight ); 501 } 502 } 503 504 513 private void paintValues( Graphics g, 514 int chartLeft, 515 int chartTop, 516 int chartWidth, 517 int chartHeight ) 518 { 519 if( ( m_averageWindow > 0 ) && ( m_mousePressed ) ) 520 { 521 g.setColor( m_lightLineColor ); 522 } 523 else 524 { 525 g.setColor( m_lineColor ); 526 } 527 528 int lastX = 0; 529 int lastY = 0; 530 for( int i = 0; i < m_values.length; i++ ) 531 { 532 int x = chartLeft + i * chartWidth / ( m_values.length - 1 ); 534 535 int y = chartTop + chartHeight - 538 (int)( (long)chartHeight * ( m_values[ i ] - m_min ) / ( m_max - m_min ) ); 539 540 if( i > 0 ) 541 { 542 g.drawLine( lastX, lastY, x, y ); 543 } 544 545 lastX = x; 546 lastY = y; 547 } 548 549 if( ( m_averageWindow > 0 ) && ( m_mousePressed ) ) 551 { 552 g.setColor( m_lineColor ); 553 lastX = 0; 554 lastY = 0; 555 for( int i = m_averageWindow; i < m_averageWindowValues.length; i++ ) 556 { 557 int x = chartLeft + i * chartWidth / ( m_averageWindowValues.length - 1 ); 559 560 int y = chartTop + chartHeight - 563 (int)( chartHeight * ( m_averageWindowValues[ i ] - m_min ) / 564 ( m_max - m_min ) ); 565 566 if( i > m_averageWindow ) 567 { 568 g.drawLine( lastX, lastY, x, y ); 569 } 570 571 lastX = x; 572 lastY = y; 573 } 574 } 575 } 576 577 587 private void paintOverlayAt( Graphics g, 588 int fontHeight, 589 int chartLeft, 590 int chartTop, 591 int chartWidth, 592 int chartHeight, 593 int mouseDataPointX, 594 int mouseDataPointY, 595 String mouseDataPointValue, 596 long mouseDataPointTime ) 597 { 598 g.setColor( m_crossColor ); 600 g.drawLine( 601 mouseDataPointX, chartTop, mouseDataPointX, chartTop + chartHeight ); 602 g.drawLine( 603 chartLeft, mouseDataPointY, chartLeft + chartWidth, mouseDataPointY ); 604 605 String mouseDataPointLabel = mouseDataPointValue + " : " + 607 getFormattedTime( new Date ( mouseDataPointTime ), true ); 608 int mouseDataPointLabelWidth = 609 g.getFontMetrics().stringWidth( mouseDataPointLabel ); 610 int mouseDataPointLabelLeft; 611 int mouseDataPointLabelTop; 612 613 if( mouseDataPointX + 5 + mouseDataPointLabelWidth < chartLeft + chartWidth ) 618 { 619 mouseDataPointLabelLeft = mouseDataPointX + 4; 621 if( mouseDataPointY + 5 + fontHeight < chartTop + chartHeight ) 622 { 623 mouseDataPointLabelTop = mouseDataPointY + 4; 625 } 626 else 627 { 628 mouseDataPointLabelTop = mouseDataPointY - 4 - fontHeight; 630 } 631 } 632 else 633 { 634 mouseDataPointLabelLeft = mouseDataPointX - 4 - mouseDataPointLabelWidth; 636 if( mouseDataPointY + 5 + fontHeight < chartTop + chartHeight ) 637 { 638 mouseDataPointLabelTop = mouseDataPointY + 4; 640 } 641 else 642 { 643 mouseDataPointLabelTop = mouseDataPointY - 4 - fontHeight; 645 } 646 } 647 648 g.setColor( m_maskFrameColor ); 650 g.drawRect( mouseDataPointLabelLeft - 1, mouseDataPointLabelTop - 1, 651 mouseDataPointLabelWidth + 2, fontHeight + 2 ); 652 653 g.setColor( m_maskColor ); 656 g.fillRect( mouseDataPointLabelLeft - 1, mouseDataPointLabelTop - 1, 657 mouseDataPointLabelWidth + 2, fontHeight + 2 ); 658 659 g.setColor( m_crossColor ); 661 g.drawString( mouseDataPointLabel, mouseDataPointLabelLeft, 662 mouseDataPointLabelTop + fontHeight ); 663 } 664 665 675 private void paintOverlay( Graphics g, 676 int fontHeight, 677 int chartLeft, 678 int chartTop, 679 int chartWidth, 680 int chartHeight ) 681 { 682 if( ( m_mouseOver ) && ( m_mouseX >= chartLeft ) && 683 ( m_mouseX <= chartLeft + chartWidth ) ) 684 { 685 int index = (int)Math.round( 687 (float)( m_values.length - 1 ) * ( m_mouseX - chartLeft ) / chartWidth ); 688 689 int mouseDataPointX = 0; 691 int mouseDataPointY = 0; 692 String mouseDataPointValue = null; 693 long mouseDataPointTime = 0; 694 boolean showLabel = false; 695 696 if( ( m_averageWindow > 0 ) && ( m_mousePressed ) ) 697 { 698 if( ( index >= m_averageWindow ) && ( index < m_averageWindowValues.length ) ) 699 { 700 702 mouseDataPointX = chartLeft + index * chartWidth / 704 ( m_averageWindowValues.length - 1 ); 705 706 mouseDataPointY = chartTop + chartHeight - 710 (int)( chartHeight * ( m_averageWindowValues[ index ] - m_min ) / 711 ( m_max - m_min ) ); 712 713 mouseDataPointValue = 715 m_floatFormat.format( m_averageWindowValues[ index ] ); 716 717 mouseDataPointTime = m_time - 719 ( m_averageWindowValues.length - index - 1 ) * m_sampleInterval; 720 721 showLabel = true; 722 } 723 } 724 else 725 { 726 if( ( index >= 0 ) && ( index < m_values.length ) ) 727 { 728 730 mouseDataPointX = 732 chartLeft + index * chartWidth / ( m_values.length - 1 ); 733 734 mouseDataPointY = chartTop + chartHeight - 737 (int)( (long)chartHeight * ( m_values[ index ] - m_min ) / 738 ( m_max - m_min ) ); 739 740 mouseDataPointValue = m_intFormat.format( m_values[ index ] ); 742 743 mouseDataPointTime = m_time - ( m_values.length - index - 1 ) * 745 m_sampleInterval; 746 747 showLabel = true; 748 } 749 } 750 751 if( showLabel ) 752 { 753 paintOverlayAt( g, fontHeight, chartLeft, chartTop, chartWidth, 754 chartHeight, mouseDataPointX, mouseDataPointY, mouseDataPointValue, 755 mouseDataPointTime ); 756 } 757 } 758 } 759 760 765 public synchronized void paintComponent( Graphics g ) 766 { 767 Dimension size = getSize(); 768 Insets insets = getInsets(); 769 770 if ( g.getClass().getName().indexOf( "Graphics2D" ) >= 0 ) 773 { 774 Graphics2D g2 = (Graphics2D )g; 775 if ( m_antialias ) 776 { 777 g2.setRenderingHint( 778 RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); 779 } 780 else 781 { 782 g2.setRenderingHint( 783 RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF ); 784 } 785 } 786 787 g.setColor( getBackground() ); 788 g.fillRect( insets.left, insets.top, size.width - insets.left - insets.right, 789 size.height - insets.top - insets.bottom ); 790 791 int yLabelInterval = 1; 793 while( ( m_max - m_min ) / yLabelInterval > 20 ) 794 { 795 yLabelInterval *= 10; 796 } 797 798 FontMetrics fontMetrics = g.getFontMetrics(); 799 int maxYLabelWidth = fontMetrics.stringWidth( m_intFormat.format( m_max ) ); 800 int fontHeight = fontMetrics.getAscent(); 801 int fontHalfHeight = fontHeight / 2; 802 803 int chartLeft = insets.left + maxYLabelWidth + 5; 804 int chartTop = insets.top + 5; 805 int chartWidth = size.width - chartLeft - insets.right - 1 - 5; 806 int chartHeight = size.height - chartTop - insets.bottom - 1 - fontHeight; 807 808 paintHorizontalGrid( g, yLabelInterval, fontHeight, chartLeft, chartTop, 810 chartWidth, chartHeight ); 811 812 if ( m_values.length > 1 ) 814 { 815 paintVerticalGrid( g, fontHeight, chartLeft, chartTop, chartWidth, chartHeight ); 816 } 817 818 paintFrame( g, chartLeft, chartTop, chartWidth, chartHeight ); 820 821 if( ( chartWidth > 0 ) && ( chartHeight > 0 ) && ( m_values.length > 1 ) ) 822 { 823 paintValues( g, chartLeft, chartTop, chartWidth, chartHeight ); 825 826 827 paintOverlay( g, fontHeight, chartLeft, chartTop, chartWidth, chartHeight ); 829 } 830 } 831 832 835 840 public void mouseClicked( MouseEvent event ) 841 { 842 } 843 844 849 public void mousePressed( MouseEvent event ) 850 { 851 m_mousePressed = true; 852 } 853 854 859 public void mouseReleased( MouseEvent event ) 860 { 861 m_mousePressed = false; 862 } 863 864 869 public void mouseEntered( MouseEvent event ) 870 { 871 m_mouseOver = true; 872 repaint(); 873 } 874 875 880 public void mouseExited( MouseEvent event ) 881 { 882 m_mouseOver = false; 883 repaint(); 884 } 885 886 889 894 public void mouseDragged( MouseEvent event ) 895 { 896 m_mouseX = event.getX(); 897 m_mouseY = event.getY(); 898 repaint(); 899 } 900 901 906 public void mouseMoved( MouseEvent event ) 907 { 908 m_mouseX = event.getX(); 909 m_mouseY = event.getY(); 910 repaint(); 911 } 912 } 913 914 | Popular Tags |