package de.twenty11.unitprofile.domain;
import java.text.DecimalFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
public class MethodInvocation {
private int lineNumber;
private List<Clock> durations = new ArrayList<Clock>();
private static ArrayDeque<Clock> clocks = new ArrayDeque<Clock>();
private String cls;
private String method;
private List<MethodInvocation> children = new ArrayList<MethodInvocation>();
private double timeShare;
DecimalFormat df = new DecimalFormat("#.00");
DecimalFormat longFormat = new DecimalFormat("###,##0");
private MethodInvocation parent;
private double selfTimeShare;
public MethodInvocation(MethodDescriptor methodDescriptor) {
this(null, methodDescriptor);
}
public MethodInvocation(MethodInvocation parent, MethodDescriptor methodDescriptor) {
newTimer();
this.cls = methodDescriptor.getClassName();
this.method = methodDescriptor.getMethodName();
this.parent = parent;
this.lineNumber = methodDescriptor.getLineNumber();
if (parent != null) {
parent.addChild(this);
}
}
public void increment() {
newTimer();
}
private void addChild(MethodInvocation invocation) {
this.children.add(invocation);
}
public void setEnd(long currentTimeMillis) {
Clock newestTimer = clocks.pollLast();
newestTimer.stop();
}
public MethodInvocation getParent() {
return parent;
}
public String getCls() {
return cls;
}
public String getMethod() {
return method;
}
public int getLineNumber() {
return lineNumber;
}
public List<MethodInvocation> getChildren() {
return children;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(cls).append("#").append(method).append(" ")
.append((getTime()));
sb.append(", ").append(children.size()).append(" children");
return sb.toString();
}
public long getTime() {
long time = 0;
for(Clock timer : durations) {
time += timer.getElapsed();
}
return time;
}
public long getSelfTime() {
long childrenTime = 0;
for(MethodInvocation child : children) {
childrenTime += child.getTime();
}
return getTime() - childrenTime;
}
private int getCount() {
return durations.size();
}
public String dump() {
return dump(0);
}
private String dump(int indentation) {
StringBuilder sb = new StringBuilder();
// for (int i = 0; i < depth; i++) {
// sb.append(" ");
// }
sb.append(cls).append("#").append(method).append(" (").append("").append("): ")
.append((getTime()));
sb.append(" ").append(timeShare).append("%");
sb.append("\n");
if (children.size() != 0) {
for (MethodInvocation invocation : children) {
sb.append(invocation.dump(++indentation));
}
}
return sb.toString();
}
public void calc() {
calc(null);
}
private void calc(Long parentTime) {
if (parentTime == null) {
setTimeShare(100.0);
parentTime = getTime();
} else {
setTimeShare(100.0 * getTime() / parentTime);
}
setSelfTimeShare(100.0 * getSelfTime() / getTime());
for (MethodInvocation invocation : children) {
invocation.calc(parentTime);
}
}
private void setTimeShare(double d) {
this.timeShare = d;
}
private void setSelfTimeShare(double d) {
this.selfTimeShare = d;
}
public String treetable() {
StringBuilder sb = new StringBuilder();
sb.append("<table id=\"treetable1\">");
sb.append("<caption>\n");
sb.append("<a href='#' onclick=\"jQuery('#treetable1').treetable('expandAll'); return false;\">Expand all</a>\n");
sb.append("<a href='#' onclick=\"jQuery('#treetable1').treetable('collapseAll'); return false;\">Collapse all</a>\n");
sb.append("</caption>\n");
sb.append("<thead>\n");
sb.append(" <tr>\n");
sb.append(" <th>Method</th>\n");
sb.append(" <th align='right'>Time (ms)</th>\n");
sb.append(" <th align='right'>Time (%)</th>\n");
sb.append(" <th> </th>\n");
sb.append(" <th align='right'>Self Time (ms)</th>\n");
sb.append(" <th align='right'>Self Time (%)</th>\n");
sb.append(" <th align='right'>Count</th>\n");
sb.append(" <th align='right'>Time/Invocation</th>\n");
sb.append(" </tr>\n");
sb.append("</thead>\n");
sb.append("<tbody>\n");
sb.append(rowInTreeTable(this, 0, null));
sb.append("</tbody>\n");
sb.append("</table>");
return sb.toString();
}
public String rowInTreeTable(MethodInvocation inv, int indentation, Integer parentId) {
StringBuilder sb = new StringBuilder();
Integer id = indentation;
sb.append("\n<tr data-tt-id='").append(id).append("'");
if (parentId != null) {
sb.append(" data-tt-parent-id='").append(parentId).append("'");
}
sb.append(">");
sb.append("<td align='left'>").append(inv.cls).append("#").append(inv.method).append(" (").append(inv.getLineNumber()).append(")").append("</td>");
// time (ms)
sb.append("<td align='right'>").append((longFormat.format(inv.getTime()))).append("</td>");
// time (%)
sb.append("<td align='right'>").append(df.format(inv.timeShare)).append("% </td>");
// bar
sb.append("<td align='left'>");
sb.append("<span class='graph'><span style='width: ").append(Math.round(inv.timeShare)).append("px;' class='bar'></span></span>");
sb.append("</td>");
sb.append("</td>");
sb.append("<td align='right'>").append(inv.getSelfTime()).append("</td>");
sb.append("<td align='right'>").append(df.format(inv.selfTimeShare)).append("%</td>");
sb.append("<td align='right'>").append(inv.getCount()).append("</td>");
sb.append("<td align='right'>").append(inv.getTime() / inv.getCount()).append("</td>");
sb.append("</tr>");
if (inv.children.size() != 0) {
for (MethodInvocation child : inv.children) {
sb.append(rowInTreeTable(child, ++indentation, id));
sb.append("\n");
}
}
return sb.toString();
}
private void newTimer() {
Clock timer = new Clock();
durations.add(timer);
clocks.add(timer);
}
}