package com.jetbrains.actionscript.profiler;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.Executor;
import com.intellij.execution.RunManager;
import com.intellij.execution.configurations.*;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.flex.model.bc.TargetPlatform;
import com.intellij.icons.AllIcons;
import com.intellij.lang.javascript.flex.projectStructure.model.FlexBuildConfiguration;
import com.intellij.lang.javascript.flex.projectStructure.model.FlexBuildConfigurationManager;
import com.intellij.lang.javascript.flex.run.FlashPlayerTrustUtil;
import com.intellij.lang.javascript.flex.run.FlashRunConfiguration;
import com.intellij.lang.javascript.flex.run.FlexRunner;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.ui.SimpleToolWindowPanel;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.ui.content.*;
import com.intellij.ui.content.tabs.TabbedContentAction;
import com.jetbrains.actionscript.profiler.model.ActionScriptProfileSettings;
import com.jetbrains.actionscript.profiler.ui.ActionScriptProfileControlPanel;
import com.jetbrains.profiler.DefaultProfilerExecutor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
public class ActionScriptProfileRunner implements ProgramRunner<RunnerSettings> {
private static final Logger LOG = Logger.getInstance(ActionScriptProfileRunner.class.getName());
private static final String TOOLWINDOW_ID = ProfilerBundle.message("window.name");
private static final String PROFILE = "Profile";
private static final String PRELOAD_SWF_OPTION = "PreloadSwf";
private final FlexRunner myFlexRunner = new FlexRunner();
private static final char DELIMITER = '=';
@NotNull
public String getRunnerId() {
return PROFILE;
}
public boolean canRun(@NotNull String executorId, @NotNull RunProfile runProfile) {
return executorId.equals(DefaultProfilerExecutor.EXECUTOR_ID) && runProfile instanceof FlashRunConfiguration;
}
public RunnerSettings createConfigurationData(ConfigurationInfoProvider configurationInfoProvider) {
return null;
}
public void checkConfiguration(RunnerSettings runnerSettings, ConfigurationPerRunnerSettings configurationPerRunnerSettings)
throws RuntimeConfigurationException {
myFlexRunner.checkConfiguration(runnerSettings, configurationPerRunnerSettings);
}
public void onProcessStarted(RunnerSettings runnerSettings, ExecutionResult executionResult) {
myFlexRunner.onProcessStarted(runnerSettings, executionResult);
}
public SettingsEditor<RunnerSettings> getSettingsEditor(Executor executor, RunConfiguration runConfiguration) {
return null;
}
public void execute(@NotNull ExecutionEnvironment executionEnvironment) throws ExecutionException {
execute(executionEnvironment, null);
}
public void execute(@NotNull ExecutionEnvironment executionEnvironment,
@Nullable Callback callback) throws ExecutionException {
RunProfile runProfile = executionEnvironment.getRunProfile();
if (!startProfiling((FlashRunConfiguration)runProfile)) {
return;
}
RunManager.getInstance(executionEnvironment.getProject()).refreshUsagesList(executionEnvironment.getRunProfile());
myFlexRunner.execute(executionEnvironment, callback);
}
private static boolean startProfiling(RunProfileWithCompileBeforeLaunchOption state) {
Module[] modules = state.getModules();
if (modules.length > 0) {
startProfiling(state.getName(), modules[0]);
return true;
}
else {
// TODO error message
return false;
}
}
private static void startProfiling(final String runConfigurationName,
final Module module) {
if (!initProfilingAgent(module)) {
return;
}
final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(module.getProject());
if (toolWindowManager == null) {
return;
}
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
ToolWindow toolWindow = toolWindowManager.getToolWindow(TOOLWINDOW_ID);
if (toolWindow == null) {
toolWindow = toolWindowManager.registerToolWindow(TOOLWINDOW_ID, true, ToolWindowAnchor.BOTTOM, module.getProject());
final ContentManager contentManager = toolWindow.getContentManager();
contentManager.addContentManagerListener(new ContentManagerAdapter() {
@Override
public void contentRemoved(ContentManagerEvent event) {
super.contentRemoved(event);
if (contentManager.getContentCount() == 0) {
toolWindowManager.unregisterToolWindow(TOOLWINDOW_ID);
}
}
});
}
final ActionScriptProfileControlPanel profileControlPanel = new ActionScriptProfileControlPanel(runConfigurationName, module);
final SimpleToolWindowPanel toolWindowPanel = new SimpleToolWindowPanel(false, true);
toolWindowPanel.setContent(profileControlPanel.getMainPanel());
final Content content = ContentFactory.SERVICE.getInstance().createContent(toolWindowPanel, runConfigurationName, false);
toolWindow.getContentManager().addContent(content);
toolWindow.getContentManager().setSelectedContent(content);
content.setDisposer(profileControlPanel);
final DefaultActionGroup actionGroup = profileControlPanel.createProfilerActionGroup();
actionGroup.addSeparator();
final AnAction closeTabAction = new TabbedContentAction.CloseAction(content);
closeTabAction.getTemplatePresentation().setIcon(AllIcons.Actions.Cancel);
actionGroup.add(closeTabAction);
ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("FlexProfiler", actionGroup, false);
toolbar.setTargetComponent(toolWindowPanel);
toolWindowPanel.setToolbar(toolbar.getComponent());
final ToolWindow finalToolWindow = toolWindow;
profileControlPanel.setConnectionCallback(() -> {
finalToolWindow.show(null);
removePreloadingOfProfilerSwf();
});
toolWindow.hide(null);
profileControlPanel.startProfiling();
}
});
}
private static boolean initProfilingAgent(Module module) {
try {
String agentName = detectSuitableAgentNameForSdkUsedToLaunch(module);
URL resource = ActionScriptProfileRunner.class.getResource("/" + agentName);
File agentFile = null;
if (resource != null) {
agentFile = new File(transformEncodedSymbols(resource));
}
else {
resource = ActionScriptProfileRunner.class.getResource("ActionScriptProfileRunner.class");
if ("jar".equals(resource.getProtocol())) {
String filePath = resource.getFile();
filePath = filePath.substring(0, filePath.indexOf("!/"));
// skip file:
filePath = transformEncodedSymbols(new URL(filePath));
agentFile = new File(new File(filePath).getParentFile().getPath() + File.separator + agentName);
}
}
assert agentFile != null && agentFile.exists() : "Have not found " + agentName;
String pathToAgent = agentFile.getCanonicalPath();
final ActionScriptProfileSettings settings = ActionScriptProfileSettings.getInstance();
pathToAgent += "?port=" + settings.getPort() + "&host=" + settings.getHost();
FlashPlayerTrustUtil.updateTrustedStatus(module.getProject(), true, false, pathToAgent);
begFlashPlayerToPreloadProfilerSwf(pathToAgent);
}
catch (IOException e) {
LOG.warn(e);
return false;
}
return true;
}
private static String transformEncodedSymbols(URL url) {
String filePath;
try { // care of encoded spaces
filePath = url.toURI().getSchemeSpecificPart();
}
catch (URISyntaxException ex) {
filePath = url.getPath();
}
return filePath;
}
private static String detectSuitableAgentNameForSdkUsedToLaunch(Module module) {
FlexBuildConfiguration bc = FlexBuildConfigurationManager.getInstance(module).getActiveConfiguration();
boolean isPlayer9 = bc.getTargetPlatform() == TargetPlatform.Web && bc.getDependencies().getTargetPlayer().startsWith("9");
return "profiler_agent" + (isPlayer9 ? "_9" : "_10") + ".swf";
}
private static void begFlashPlayerToPreloadProfilerSwf(final String pathToAgent) throws IOException {
processMmCfg(new ProfilerPathMmCfgFixer() {
public String additionalOptions(String lineEnd) {
LOG.debug("Added profiler swf reference to mm.cfg");
return PRELOAD_SWF_OPTION + "=" + pathToAgent;
}
});
}
private static void removePreloadingOfProfilerSwf() {
try {
processMmCfg(new ProfilerPathMmCfgFixer() {
@Nullable
public String additionalOptions(String lineEnd) {
LOG.debug("Removed profiler swf reference from mm.cfg");
return null;
}
});
}
catch (IOException e) {
LOG.error(e);
}
}
interface MmCfgFixer {
String processOption(String option, String value, String line);
String additionalOptions(String lineEnd);
}
static abstract class ProfilerPathMmCfgFixer implements MmCfgFixer {
@Nullable
public String processOption(String option, String value, String line) {
if (!PRELOAD_SWF_OPTION.equals(option)) return line;
return null;
}
}
private static void processMmCfg(MmCfgFixer mmCfgFixer) throws IOException {
StringBuilder mmCfgContent = new StringBuilder();
final String mmCfgPath = ActionScriptProfileSettings.getMmCfgPath() ;
final File file = new File(mmCfgPath);
String lineEnd = System.getProperty("line.separator");
if (file.exists()) {
final LineNumberReader lineNumberReader =
new LineNumberReader(new InputStreamReader(new BufferedInputStream(new FileInputStream(file))));
while (true) {
final String s = lineNumberReader.readLine();
if (s == null) break;
final int i = s.indexOf(DELIMITER);
if (i != -1) {
String value = s.substring(i + 1);
String name = s.substring(0, i);
final String result = mmCfgFixer.processOption(name, value, s);
if (result != null) mmCfgContent.append(result).append(lineEnd);
}
else {
mmCfgContent.append(s).append(lineEnd); // TODO
}
}
}
final String options = mmCfgFixer.additionalOptions(lineEnd);
if (options != null) mmCfgContent.insert(0, options + lineEnd);
FileUtil.writeToFile(file, mmCfgContent.toString().getBytes());
}
}