package com.jetbrains.actionscript.profiler.ui;
import com.intellij.notification.NotificationGroup;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.Disposer;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.PopupHandler;
import com.intellij.util.Alarm;
import com.intellij.util.EditSourceOnDoubleClickHandler;
import com.jetbrains.actionscript.profiler.ProfilerBundle;
import com.jetbrains.actionscript.profiler.base.NavigatableTree;
import com.jetbrains.actionscript.profiler.base.ProfilerActionGroup;
import com.jetbrains.actionscript.profiler.livetable.LiveModelController;
import com.jetbrains.actionscript.profiler.model.ActionScriptProfileSettings;
import com.jetbrains.actionscript.profiler.model.ProfilerDataConsumer;
import com.jetbrains.actionscript.profiler.model.ProfilingManager;
import com.jetbrains.actionscript.profiler.ui.node.CPUSnapshotNode;
import com.jetbrains.actionscript.profiler.ui.node.LiveObjectsNode;
import icons.FlexProfilerIcons;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import java.io.IOException;
import java.util.Date;
public class ActionScriptProfileControlPanel implements ProfilerActionGroup, Disposable {
public static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.balloonGroup("ActionScript Profiler");
private JPanel mainPanel;
private NavigatableTree snapshotTree;
private JLabel myStatusLabel;
private final DefaultTreeModel treeModel;
private ProfilingManager profilingManager;
private ProfilerDataConsumer profilerDataConsumer;
private Runnable connectionCallback;
private final Module module;
private final String runConfigurationName;
private final Alarm myAlarm = new Alarm();
private static final int MINUTE = 60 * 1000;
public ActionScriptProfileControlPanel(String runConfigurationName, final Module module) {
this.runConfigurationName = runConfigurationName;
this.module = module;
treeModel = (new DefaultTreeModel(new DefaultMutableTreeNode(), true));
snapshotTree.setModel(treeModel);
snapshotTree.setRootVisible(false);
setupComponents();
}
public JPanel getMainPanel() {
return mainPanel;
}
enum State {
NONE, CPU_PROFILING, NORMAL
}
private volatile State currentState = State.NONE;
private void setCurrentState(final State state) {
currentState = state;
}
private void setStatus(final String status) {
ApplicationManager.getApplication().invokeLater(() -> myStatusLabel.setText(status));
}
private void setupComponents() {
EditSourceOnDoubleClickHandler.install(snapshotTree);
PopupHandler.installPopupHandler(snapshotTree, PROFILER_SNAPSHOT_GROUP_ID, ActionPlaces.UNKNOWN);
snapshotTree.setCellRenderer(new ColoredTreeCellRenderer() {
@Override
public void customizeCellRenderer(JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
setIcon(value instanceof CPUSnapshotNode ? FlexProfilerIcons.SnapshotCPU : FlexProfilerIcons.LiveObjects);
if (value.toString() != null) {
append(value.toString());
}
}
});
}
private void doCPUSnapshot() {
final CPUSnapshotNode newNode =
new CPUSnapshotNode(runConfigurationName, module, new Date(), profilerDataConsumer.getProfileData().getCallTree());
ApplicationManager.getApplication().invokeLater(() -> {
final MutableTreeNode root = (MutableTreeNode)treeModel.getRoot();
treeModel.insertNodeInto(newNode, root, root.getChildCount());
});
}
public void startProfiling() {
if (profilingManager != null) {
profilingManager.dispose();
}
profilingManager = new ProfilingManager(ActionScriptProfileSettings.getInstance().getPort());
final LiveModelController liveModelController = new LiveModelController();
final LiveObjectsNode liveObjectsNode = new LiveObjectsNode(runConfigurationName, module, profilingManager, liveModelController);
profilerDataConsumer = new ProfilerDataConsumer(liveModelController);
setStatus(ProfilerBundle.message("agent.connection.waiting"));
myAlarm.cancelAllRequests();
myAlarm.addRequest(() -> NOTIFICATION_GROUP.createNotification(ProfilerBundle.message("profiler.connection.timeout"), NotificationType.ERROR)
.notify(module.getProject()), MINUTE);
profilingManager.initializeProfiling(profilerDataConsumer, new ProfilingManager.Callback() {
public void finished(@Nullable String data, @Nullable IOException ex) {
if (data != null && connectionCallback != null) {
myAlarm.cancelAllRequests();
setStatus(ProfilerBundle.message("agent.connection.open"));
ApplicationManager.getApplication().invokeLater(connectionCallback);
setCurrentState(State.NORMAL);
}
else if (ex != null) {
setStatus(ProfilerBundle.message("agent.connection.close"));
setCurrentState(State.NONE);
ApplicationManager.getApplication().invokeLater(() -> treeModel.removeNodeFromParent(liveObjectsNode));
}
}
});
treeModel.insertNodeInto(liveObjectsNode, (MutableTreeNode)treeModel.getRoot(), 0);
}
public void setConnectionCallback(Runnable runnable) {
this.connectionCallback = runnable;
}
@Override
public void dispose() {
profilingManager.dispose();
Disposer.dispose(myAlarm);
}
public DefaultActionGroup createProfilerActionGroup() {
return new DefaultActionGroup(
new ToggleAction(ProfilerBundle.message("start.cpu.profiling"),
ProfilerBundle.message("start.cpu.profiling.description"),
FlexProfilerIcons.StartCPU) {
@Override
public boolean isSelected(AnActionEvent e) {
return currentState == State.CPU_PROFILING;
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
if (state) {
profilingManager.startCpuProfiling(new ProfilingManager.Callback() {
@Override
public void finished(@Nullable String data, @Nullable IOException ex) {
setCurrentState(State.CPU_PROFILING);
profilerDataConsumer.resetCpuUsageData();
}
});
e.getPresentation().setText(ProfilerBundle.message("stop.cpu.profiling"));
e.getPresentation().setDescription(ProfilerBundle.message("stop.cpu.profiling.description"));
e.getPresentation().setIcon(FlexProfilerIcons.StopCPU);
}
else {
profilingManager.stopCpuProfiling(new ProfilingManager.Callback() {
@Override
public void finished(@Nullable String data, @Nullable IOException ex) {
setCurrentState(State.NORMAL);
doCPUSnapshot();
}
});
e.getPresentation().setText(ProfilerBundle.message("start.cpu.profiling"));
e.getPresentation().setDescription(ProfilerBundle.message("start.cpu.profiling.description"));
e.getPresentation().setIcon(FlexProfilerIcons.StartCPU);
}
}
@Override
public void update(AnActionEvent e) {
super.update(e);
e.getPresentation().setEnabled(currentState != State.NONE);
}
},
new AnAction(ProfilerBundle.message("do.gc"),
ProfilerBundle.message("do.gc.description"),
FlexProfilerIcons.GC) {
@Override
public void actionPerformed(AnActionEvent e) {
profilingManager.doGc(new ProfilingManager.Callback() {
public void finished(@Nullable String data, @Nullable IOException ex) {
//TODO
}
});
}
@Override
public void update(AnActionEvent e) {
super.update(e);
e.getPresentation().setEnabled(currentState != State.NONE);
}
}
);
}
}