package com.intellij.lang.javascript.flex.run;
import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.executors.DefaultRunExecutor;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.RunContentBuilder;
import com.intellij.execution.ui.ExecutionConsole;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.ide.actions.ShowFilePathAction;
import com.intellij.lang.javascript.flex.FlexBundle;
import com.intellij.lang.javascript.flex.actions.airpackage.AirPackageUtil;
import com.intellij.lang.javascript.flex.flexunit.FlexUnitConnection;
import com.intellij.lang.javascript.flex.flexunit.FlexUnitRunConfiguration;
import com.intellij.lang.javascript.flex.flexunit.FlexUnitRunnerParameters;
import com.intellij.lang.javascript.flex.flexunit.SwfPolicyFileConnection;
import com.intellij.lang.javascript.flex.projectStructure.model.FlexBuildConfiguration;
import com.intellij.lang.javascript.flex.projectStructure.options.BCUtils;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.ui.HyperlinkAdapter;
import com.intellij.util.PathUtil;
import com.intellij.xdebugger.DefaultDebugProcessHandler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
public class FlexRunner extends FlexBaseRunner {
protected RunContentDescriptor launchFlexConfig(final Module module,
final FlexBuildConfiguration bc,
final FlashRunnerParameters runnerParameters,
final RunProfileState state,
final RunContentDescriptor contentToReuse,
final ExecutionEnvironment environment) throws ExecutionException {
switch (bc.getTargetPlatform()) {
case Web:
final String urlOrPath = runnerParameters.isLaunchUrl()
? runnerParameters.getUrl()
: bc.isUseHtmlWrapper()
? PathUtil.getParentPath(bc.getActualOutputFilePath()) + "/" + BCUtils.getWrapperFileName(bc)
: bc.getActualOutputFilePath();
launchWithSelectedApplication(urlOrPath, runnerParameters.getLauncherParameters());
break;
case Desktop:
return standardLaunch(module.getProject(), state, contentToReuse, environment);
case Mobile:
switch (runnerParameters.getMobileRunTarget()) {
case Emulator:
return standardLaunch(module.getProject(), state, contentToReuse, environment);
case AndroidDevice:
final String androidDescriptorPath = getAirDescriptorPath(bc, bc.getAndroidPackagingOptions());
final String androidAppId = getApplicationId(androidDescriptorPath);
if (androidAppId == null) {
Messages.showErrorDialog(module.getProject(),
FlexBundle.message("failed.to.read.app.id", FileUtil.toSystemDependentName(androidDescriptorPath)),
FlexBundle.message("error.title"));
return null;
}
if (packAndInstallToAndroidDevice(module, bc, runnerParameters, androidAppId, false)) {
launchOnAndroidDevice(module.getProject(), bc.getSdk(), runnerParameters.getDeviceInfo(), androidAppId, false);
}
return null;
case iOSSimulator:
final String adtVersionSimulator = AirPackageUtil.getAdtVersion(module.getProject(), bc.getSdk());
final String iosSimulatorDescriptorPath = getAirDescriptorPath(bc, bc.getIosPackagingOptions());
final String iosSimulatorAppId = getApplicationId(iosSimulatorDescriptorPath);
if (iosSimulatorAppId == null) {
Messages.showErrorDialog(module.getProject(), FlexBundle.message("failed.to.read.app.id",
FileUtil.toSystemDependentName(iosSimulatorDescriptorPath)),
FlexBundle.message("error.title"));
return null;
}
if (packAndInstallToIOSSimulator(module, bc, runnerParameters, adtVersionSimulator, iosSimulatorAppId, false)) {
launchOnIosSimulator(module.getProject(), bc.getSdk(), iosSimulatorAppId, runnerParameters.getIOSSimulatorSdkPath(), false);
}
return null;
case iOSDevice:
final String adtVersion = AirPackageUtil.getAdtVersion(module.getProject(), bc.getSdk());
if (StringUtil.compareVersionNumbers(adtVersion, "3.4") >= 0) {
if (packAndInstallToIOSDevice(module, bc, runnerParameters, adtVersion, false)) {
final String appName = getApplicationName(getAirDescriptorPath(bc, bc.getIosPackagingOptions()));
ToolWindowManager.getInstance(module.getProject())
.notifyByBalloon(ToolWindowId.RUN, MessageType.INFO, FlexBundle.message("ios.application.installed.to.run", appName));
}
}
else {
if (AirPackageUtil.packageIpaForDevice(module, bc, runnerParameters, adtVersion, false)) {
final String outputFolder = PathUtil.getParentPath(bc.getActualOutputFilePath());
final String ipaName = bc.getIosPackagingOptions().getPackageFileName() + ".ipa";
final String message = FlexBundle.message("ios.application.packaged.to.run", ipaName);
ToolWindowManager.getInstance(module.getProject())
.notifyByBalloon(ToolWindowId.RUN, MessageType.INFO, message, null, new HyperlinkAdapter() {
protected void hyperlinkActivated(final HyperlinkEvent e) {
ShowFilePathAction.openFile(new File(outputFolder + "/" + ipaName));
}
});
}
}
break;
}
break;
}
return null;
}
@Nullable
private RunContentDescriptor standardLaunch(final Project project,
final RunProfileState state,
final RunContentDescriptor contentToReuse, final ExecutionEnvironment environment)
throws ExecutionException {
final ExecutionResult executionResult = state.execute(environment.getExecutor(), this);
if (executionResult == null) return null;
final RunContentBuilder contentBuilder = new RunContentBuilder(executionResult, environment);
return contentBuilder.showRunContent(contentToReuse);
}
protected RunContentDescriptor launchWebFlexUnit(final Project project,
final RunContentDescriptor contentToReuse,
final ExecutionEnvironment env,
final FlexUnitRunnerParameters params,
final String swfFilePath) throws ExecutionException {
final SwfPolicyFileConnection policyFileConnection = new SwfPolicyFileConnection();
policyFileConnection.open(params.getSocketPolicyPort());
final FlexUnitConnection flexUnitConnection = new FlexUnitConnection();
flexUnitConnection.open(params.getPort());
final ProcessHandler processHandler = new DefaultDebugProcessHandler() {
@Override
protected void destroyProcessImpl() {
flexUnitConnection.write("Finish");
flexUnitConnection.close();
policyFileConnection.close();
super.destroyProcessImpl();
}
@Override
public boolean detachIsDefault() {
return false;
}
};
final ExecutionConsole console = createFlexUnitRunnerConsole(project, env, processHandler);
flexUnitConnection.addListener(new FlexUnitListener(processHandler));
launchWithSelectedApplication(swfFilePath, params.getLauncherParameters());
final RunContentBuilder contentBuilder =
new RunContentBuilder(new DefaultExecutionResult(console, processHandler), env);
Disposer.register(project, contentBuilder);
return contentBuilder.showRunContent(contentToReuse);
}
@Nullable
protected RunContentDescriptor launchAirFlexUnit(final Project project,
final RunProfileState state,
final RunContentDescriptor contentToReuse,
final ExecutionEnvironment env,
final FlexUnitRunnerParameters params) throws ExecutionException {
final ExecutionResult executionResult;
final SwfPolicyFileConnection policyFileConnection = new SwfPolicyFileConnection();
policyFileConnection.open(params.getSocketPolicyPort());
final FlexUnitConnection flexUnitConnection = new FlexUnitConnection();
flexUnitConnection.open(params.getPort());
executionResult = state.execute(env.getExecutor(), this);
if (executionResult == null) {
flexUnitConnection.close();
policyFileConnection.close();
return null;
}
flexUnitConnection.addListener(new FlexUnitListener(executionResult.getProcessHandler()));
executionResult.getProcessHandler().addProcessListener(new ProcessAdapter() {
@Override
public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) {
flexUnitConnection.write("Finish");
}
@Override
public void processTerminated(ProcessEvent event) {
flexUnitConnection.close();
policyFileConnection.close();
}
});
final RunContentBuilder contentBuilder = new RunContentBuilder(executionResult, env);
return contentBuilder.showRunContent(contentToReuse);
}
public boolean canRun(@NotNull final String executorId, @NotNull final RunProfile profile) {
return DefaultRunExecutor.EXECUTOR_ID.equals(executorId) &&
(profile instanceof FlashRunConfiguration || profile instanceof FlexUnitRunConfiguration);
}
@NotNull
public String getRunnerId() {
return "FlexRunner";
}
private static class FlexUnitListener implements FlexUnitConnection.Listener {
private final ProcessHandler myProcessHandler;
public FlexUnitListener(ProcessHandler processHandler) {
myProcessHandler = processHandler;
}
public void statusChanged(FlexUnitConnection.ConnectionStatus status) {
if (status == FlexUnitConnection.ConnectionStatus.CONNECTION_FAILED || status == FlexUnitConnection.ConnectionStatus.DISCONNECTED) {
myProcessHandler.destroyProcess();
}
}
public void onData(final String line) {
Runnable runnable = () -> myProcessHandler.notifyTextAvailable(line + "\n", ProcessOutputTypes.STDOUT);
if (ApplicationManager.getApplication().isUnitTestMode()) {
try {
SwingUtilities.invokeAndWait(runnable);
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
else {
runnable.run();
}
}
public void onFinish() {
// ignore
}
}
}