001    package org.shiftone.jrat.provider.tree.ui.trace.graph;
002    
003    
004    import org.shiftone.jrat.core.MethodKey;
005    import org.shiftone.jrat.provider.tree.ui.TraceTreeNode;
006    import org.shiftone.jrat.provider.tree.ui.trace.PercentColorLookup;
007    import org.shiftone.jrat.util.log.Logger;
008    
009    import javax.swing.*;
010    import java.awt.*;
011    import java.awt.geom.Rectangle2D;
012    import java.text.DecimalFormat;
013    
014    
015    /**
016     * @author jeff@shiftone.org (Jeff Drost)
017     */
018    public class TreeGraphComponent extends BufferedJComponent implements Scrollable {
019    
020        private static final Logger LOG = Logger.getLogger(TreeGraphComponent.class);
021        private TraceTreeNode node;
022        private Color LINE_COLOR = Color.LIGHT_GRAY;
023        private PercentColorLookup colorLookup = new PercentColorLookup();
024        private DecimalFormat pctDecimalFormat = new DecimalFormat("#,###.#'%'");
025        private Font font = new Font("SansSerif", Font.PLAIN, 9);
026        private int rowHeight = 12;
027    
028        public TreeGraphComponent() {
029            setDoubleBuffered(false);
030            setBackground(Color.WHITE);
031        }
032    
033    
034        protected void paintBuffer(Graphics2D g) {
035    
036            Graphics2D g2d = (Graphics2D) g;
037    
038            g.setColor(getBackground());
039            g.fillRect(0, 0, getWidth(), getHeight());
040    
041            if ((node != null) && !node.isRootNode() && (node.getTotalExits() != 0)) {
042                paint(g2d, node, 0, 0, getWidth());
043            }
044        }
045    
046    
047        /**
048         * @todo clean up this ugly code
049         */
050        private void paint(Graphics2D g, TraceTreeNode node, int x, int row, int width) {
051    
052            g.setFont(font);
053    
054            FontMetrics metrics = g.getFontMetrics();
055            Color color = colorLookup.getColor(node.getPctOfAvgParentDuration());
056            int height = metrics.getHeight() + metrics.getDescent();
057            int y = row * height;
058    
059            rowHeight = height;
060    
061            g.setColor(color);
062            g.fill3DRect(x, y, width, height, true);
063    
064            // print the text on the node
065            Graphics gg = g.create(x, y, width, height);
066    
067            gg.setColor(Color.BLACK);
068    
069            MethodKey methodKey = node.getMethodKey();
070            String text = methodKey.getClassName()
071                    + "." + methodKey.getMethodName()
072                    + " " + pctDecimalFormat.format(node.getPctOfAvgRootDuration());
073    
074            Rectangle2D stringBounds = metrics.getStringBounds(text, g);
075    
076            if (stringBounds.getWidth() < width) {
077                gg.drawString(text, (int) (width / 2 - stringBounds.getWidth() / 2), (int) (stringBounds.getHeight()));
078            } else {
079                text = methodKey.getMethodName() + " " + pctDecimalFormat.format(node.getPctOfAvgRootDuration());
080                stringBounds = metrics.getStringBounds(text, g);
081    
082                if (stringBounds.getWidth() < width) {
083                    gg.drawString(text, (int) (width / 2 - stringBounds.getWidth() / 2), (int) (stringBounds.getHeight()));
084                }
085            }
086    
087            // print the children
088            long total = node.getTotalDuration();
089    
090            if ((total > 0) && (node.getChildCount() > 0)) {
091                int childX = 0;
092    
093                for (int i = 0; i < node.getChildCount(); i++) {
094                    TraceTreeNode child = (TraceTreeNode) node.getChildAt(i);
095                    long part = child.getTotalDuration();
096                    int partWidth = (int) ((part * (long) width) / total);
097    
098                    if (partWidth > 1) {
099                        paint(g, child, x + childX, row + 1, partWidth);
100                    } else {
101                        g.setColor(LINE_COLOR);
102                        g.drawLine(x + childX, (row + 1) * height, x + childX, (row + 1 + node.getMaxDepth()) * height);
103                    }
104    
105                    childX += partWidth;
106                }
107            }
108        }
109    
110    
111        public synchronized void setStackTreeNode(TraceTreeNode node) {
112    
113            //LOG.info("setStackTreeNode " + node + " " + node.getMaxDepth());
114            this.node = node;
115    
116            dataChanged();
117            setPreferredSize(new Dimension(getWidth(), (int) (rowHeight * node.getMaxDepth())));
118            setSize(getPreferredSize());
119            // LOG.info("getPreferredSize " + getPreferredSize());
120            if (isVisible()) {
121                repaint();
122            }
123        }
124    
125    
126        public Dimension getPreferredScrollableViewportSize() {
127            return getPreferredSize();
128        }
129    
130    
131        public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
132            return (int) rowHeight;
133        }
134    
135    
136        public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
137            return (int) rowHeight;
138        }
139    
140    
141        public boolean getScrollableTracksViewportWidth() {
142            return true;
143        }
144    
145    
146        public boolean getScrollableTracksViewportHeight() {
147            return false;
148        }
149    }