StackVisualizer.java
/*
* Copyright (c) 2001, Zoltan Farkas All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.spf4j.stackmonitor;
import org.spf4j.base.HtmlUtils;
import java.io.IOException;
import java.io.Writer;
import java.util.Map;
/**
*
* @author zoly
*/
public final class StackVisualizer {
private StackVisualizer() {
}
private static final String[] COLORS = {"#CCE01B",
"#DDE01B", "#EEE01B", "#FFE01B", "#FFD01B",
"#FFC01B", "#FFA01B", "#FF901B", "#FF801B",
"#FF701B", "#FF601B", "#FF501B", "#FF401B"};
public static void generateHtmlTable(final Writer writer, final Method m,
final SampleNode node, final int tableWidth, final int maxDepth) throws IOException {
Map<Method, SampleNode> subNodes = node.getSubNodes();
writer.append("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" "
+ "style=\"overflow:hidden;table-layout:fixed;width:").
append(Integer.toString(tableWidth)).append("px\">\n");
int totalSamples = node.getSampleCount();
if (subNodes != null && maxDepth > 0) {
writer.append("<tr style=\"height:1em\">");
for (Map.Entry<Method, SampleNode> entry : subNodes.entrySet()) {
int width = entry.getValue().getSampleCount() * tableWidth / totalSamples;
writer.append("<td style=\"vertical-align:bottom; width:").
append(Integer.toString(width)).append("px\">");
generateHtmlTable(writer, entry.getKey(), entry.getValue(), width, maxDepth - 1);
writer.append("</td>");
}
writer.append("<td></td>");
writer.append("</tr>\n");
}
writer.append("<tr style=\"height:1em\" ><td ");
if (subNodes != null) {
writer.append("colspan=\"").append(Integer.toString(subNodes.size() + 1)).append("\" ");
}
writer.append(" title=\"");
m.toHtmlWriter(writer);
writer.append(":");
writer.append(Integer.toString(node.getSampleCount()));
writer.append("\" style=\"overflow:hidden;width:100%;vertical-align:bottom ;background:").
append(COLORS[(int) (Math.random() * COLORS.length)]).append("\">");
m.toHtmlWriter(writer);
writer.append(":");
writer.append(Integer.toString(totalSamples));
writer.append("</td></tr>\n");
writer.append("</table>\n");
}
public static void generateSvg(final Writer writer, final Method m,
final SampleNode node, final int x, final int y,
final int width, final int maxDepth, final String idPfx) throws IOException {
if (width < 200) {
throw new IllegalArgumentException("width must be greater than 200, it is " + width);
}
writer.append("<svg width=\"" + width + "\" height= \"" + (15 * node.height() + 15) + "\" onload=\""
+ idPfx + "init(evt)\" >\n");
writer.append("<script type=\"text/ecmascript\">\n"
+ "<![CDATA[\n"
+ "var " + idPfx + "tooltip;\n"
+ "var " + idPfx + "tooltip_bg;\n"
+ "function " + idPfx + "init(evt)\n"
+ " {\n"
+ " if ( window.svgDocument == null )\n"
+ " {\n"
+ " svgDocument = evt.target.ownerDocument;\n"
+ " }\n"
+ " " + idPfx + "tooltip = svgDocument.getElementById('" + idPfx + "tooltip');"
+ "" + idPfx + "tooltip_bg = svgDocument.getElementById('" + idPfx + "tooltip_bg');"
+ " }\n"
+ "function " + idPfx + "ss(evt, mouseovertext, xx, yy)\n"
+ "{\n"
+ " " + idPfx + "tooltip.setAttributeNS(null,\"x\",xx );\n"
+ " " + idPfx + "tooltip.setAttributeNS(null,\"y\",yy+13 );\n"
+ " " + idPfx + "tooltip.firstChild.data = mouseovertext;\n"
+ " " + idPfx + "tooltip.setAttributeNS(null,\"visibility\",\"visible\");\n"
+ "length = " + idPfx + "tooltip.getComputedTextLength();\n"
+ "" + idPfx + "tooltip_bg.setAttributeNS(null,\"width\",length+8);\n"
+ " " + idPfx + "tooltip_bg.setAttributeNS(null,\"x\",xx);\n"
+ "" + idPfx + "tooltip_bg.setAttributeNS(null,\"y\",yy);\n"
+ "" + idPfx + "tooltip_bg.setAttributeNS(null,\"visibility\",\"visibile\");"
+ "}"
+ "\n"
+ "function " + idPfx + "hh()\n"
+ "{\n"
+ " " + idPfx + "tooltip.setAttributeNS(null,\"visibility\",\"hidden\");\n"
+ "" + idPfx + "tooltip_bg.setAttributeNS(null,\"visibility\",\"hidden\");\n"
+ "}"
+ "]]>"
+ "</script>");
generateSubSvg(writer, m, node, x, y + 15, width - 100, maxDepth, idPfx);
writer.append("<rect fill=\"rgb(255,255,255)\" id=\"" + idPfx + "tooltip_bg\"\n"
+ " x=\"0\" y=\"0\" rx=\"4\" ry=\"4\"\n"
+ " width=\"55\" height=\"17\" visibility=\"hidden\"/>");
writer.append("<text font-size=\"12\" font-family=\"Verdana\" fill=\"rgb(0,0,0)\" id=\""
+ idPfx + "tooltip\" x=\"0\" y=\"0\" visibility=\"hidden\">Tooltip</text>");
writer.append("</svg>\n");
}
public static void generateSubSvg(final Writer writer, final Method m, final SampleNode node,
final int x, final int y, final int width, final int maxDepth, final String idPfx) throws IOException {
Map<Method, SampleNode> subNodes = node.getSubNodes();
int totalSamples = node.getSampleCount();
String id = idPfx + "ix" + x + "y" + y;
String content = HtmlUtils.htmlEscape(m.toString() + ":" + Integer.toString(totalSamples));
writer.append("<g onmouseover=\"" + idPfx + "ss(evt,'" + content + "'," + x + ", "
+ y + " )\" onmouseout=\"" + idPfx + "hh()\">");
writer.append("<rect id=\"" + id + "\" x=\"" + x + "\" y=\"" + y + "\" width=\"" + width
+ "\" height=\"15\" fill=\""
+ COLORS[(int) (Math.random() * COLORS.length)] + "\" />");
writer.append("<text x=\"" + x + "\" y=\"" + (y + 13)
+ "\" font-size=\"12\" font-family=\"Verdana\" fill=\"rgb(0,0,0)\" "
+ " >");
writer.append(content.substring(0, Math.min(width / 9, content.length())));
writer.append("</text>\n");
writer.append("</g>");
if (subNodes != null && maxDepth > 0) {
int rx = 0;
for (Map.Entry<Method, SampleNode> entry : subNodes.entrySet()) {
int cwidth = (int) (((long) entry.getValue().getSampleCount()) * width / totalSamples);
generateSubSvg(writer, entry.getKey(), entry.getValue(), rx + x, y + 15, cwidth, maxDepth - 1, idPfx);
rx += cwidth;
}
}
}
}