package com.github.masahirosuzuka.PhoneGapIntelliJPlugin.runner;
import com.github.masahirosuzuka.PhoneGapIntelliJPlugin.commandLine.PhoneGapAndroidTargets;
import com.github.masahirosuzuka.PhoneGapIntelliJPlugin.commandLine.PhoneGapCommandLine;
import com.github.masahirosuzuka.PhoneGapIntelliJPlugin.commandLine.PhoneGapIosTargets;
import com.github.masahirosuzuka.PhoneGapIntelliJPlugin.runner.ui.PhoneGapRunConfigurationEditor;
import com.intellij.diagnostic.logging.DefaultLogFilterModel;
import com.intellij.diagnostic.logging.LogConsole;
import com.intellij.diagnostic.logging.LogFilterModel;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.*;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.DefaultJDOMExternalizer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.EnvironmentUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
import static com.github.masahirosuzuka.PhoneGapIntelliJPlugin.commandLine.PhoneGapCommandLine.*;
import static com.github.masahirosuzuka.PhoneGapIntelliJPlugin.runner.ui.PhoneGapRunConfigurationEditor.*;
/**
* PhoneGapRunConfiguration.java
* <p/>
* Created by Masahiro Suzuka on 2014/04/05.
*/
public class PhoneGapRunConfiguration extends LocatableConfigurationBase {
private static final String CORDOVA_IOS_LOG_PATH = "/platforms/ios/cordova/console.log";
private static final Set<String> MAC_SPEC_PLATFORMS = ContainerUtil.immutableSet(PLATFORM_IOS,
PLATFORM_AMAZON_FIREOS,
PLATFORM_ANDROID,
PLATFORM_BLACKBERRY_10,
PLATFORM_BROWSER,
PLATFORM_FIREFOXOS);
private static final Set<String> WIN_SPEC_PLATFORMS = ContainerUtil.immutableSet(PLATFORM_AMAZON_FIREOS,
PLATFORM_ANDROID,
PLATFORM_BLACKBERRY_10,
PLATFORM_FIREFOXOS,
PLATFORM_WP_8,
PLATFORM_WINDOWS,
PLATFORM_BROWSER,
PLATFORM_WINDOWS_8);
private static final Set<String> LINUX_SPEC_PLATFORMS = ContainerUtil.immutableSet(PLATFORM_AMAZON_FIREOS,
PLATFORM_ANDROID,
PLATFORM_BROWSER,
PLATFORM_FIREFOXOS,
PLATFORM_UBUNTU);
private static final Set<String> REMOTE_BUILD_PLATFORMS = ContainerUtil.immutableSet(PLATFORM_IOS,
PLATFORM_ANDROID,
PLATFORM_WP_8);
public static final String ANDROID_HOME_VARIABLE = "ANDROID_HOME";
//public for serializer
@Nullable
public String myExecutable;
@Nullable
public String myWorkDir;
@Nullable
public String myCommand;
public boolean myPassParent = true;
@NotNull
public Map<String, String> myEnvs = new LinkedHashMap<>();
public boolean isPassParent() {
return myPassParent;
}
public void setPassParent(boolean passParent) {
myPassParent = passParent;
}
@NotNull
public Map<String, String> getEnvs() {
return myEnvs;
}
public void setEnvs(@NotNull Map<String, String> envs) {
myEnvs = envs;
}
@Nullable
public String getCommand() {
return myCommand;
}
@Nullable
public String myPlatform;
public String getExtraArgs() {
return myExtraArgs;
}
public void setExtraArgs(String extraArgs) {
myExtraArgs = extraArgs;
}
public String myExtraArgs;
public boolean hasTarget() {
return hasTarget;
}
public void setHasTarget(boolean hasTarget) {
this.hasTarget = hasTarget;
}
@Nullable
public String getTarget() {
return target;
}
public void setTarget(@Nullable String target) {
this.target = target;
}
public boolean hasTarget;
@Nullable
public String target;
private volatile PhoneGapCommandLine myCommandLine;
@Nullable
public String getWorkDir() {
return myWorkDir;
}
public void setWorkDir(@Nullable String workDir) {
this.myWorkDir = workDir;
}
@Nullable
public String getExecutable() {
return myExecutable;
}
public void setExecutable(@Nullable String executable) {
myExecutable = executable;
}
public void setCommand(@Nullable String myCommand) {
this.myCommand = myCommand;
}
@Nullable
public String getPlatform() {
return myPlatform;
}
public void setPlatform(@Nullable String myPlatform) {
this.myPlatform = myPlatform;
}
public PhoneGapRunConfiguration(Project project, ConfigurationFactory factory, String name) {
super(project, factory, name);
//defaults
}
@Override
public String suggestedName() {
return "Run PhoneGap";
}
@Override
public void readExternal(Element element) throws InvalidDataException {
super.readExternal(element);
//noinspection deprecation
DefaultJDOMExternalizer.readExternal(this, element);
}
@Override
public void writeExternal(Element element) throws WriteExternalException {
super.writeExternal(element);
//noinspection deprecation
DefaultJDOMExternalizer.writeExternal(this, element);
}
@NotNull
@Override
public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
return new PhoneGapRunConfigurationEditor(getProject());
}
@Override
public void checkConfiguration() throws RuntimeConfigurationException {
if (StringUtil.isEmpty(myCommand)) {
throw new RuntimeConfigurationError("Command is missing");
}
if (StringUtil.isEmpty(myPlatform)) {
throw new RuntimeConfigurationError("Platform is missing");
}
if (StringUtil.isEmpty(myExecutable)) {
throw new RuntimeConfigurationError("Executable is missing");
}
if (StringUtil.isEmpty(myWorkDir)) {
throw new RuntimeConfigurationError("Working directory is missing");
}
if (SystemInfo.isMac && !MAC_SPEC_PLATFORMS.contains(myPlatform)) {
throwOSWarning();
}
if (SystemInfo.isLinux && !LINUX_SPEC_PLATFORMS.contains(myPlatform)) {
throwOSWarning();
}
if (SystemInfo.isWindows && !WIN_SPEC_PLATFORMS.contains(myPlatform)) {
throwOSWarning();
}
if (myPlatform.equals(PLATFORM_FIREFOXOS) &&
(myCommand.equals(COMMAND_EMULATE) || myCommand.equals(COMMAND_RUN))) {
throwUnsupportedCommandWarning();
}
if (!REMOTE_BUILD_PLATFORMS.contains(myPlatform) &&
(myCommand.equals(COMMAND_REMOTE_BUILD) || myCommand.equals(COMMAND_REMOTE_RUN))) {
throwUnsupportedCommandWarning();
}
if (myPlatform.equals(PLATFORM_ANDROID) && StringUtil.isEmpty(EnvironmentUtil.getValue(ANDROID_HOME_VARIABLE))) {
checkExistsSdkWithWarning(PhoneGapAndroidTargets.getAndroidName(), "Cannot detect android SDK in path");
}
if (myPlatform.equals(PLATFORM_IOS)) {
checkExistsSdkWithWarning(ContainerUtil.newArrayList(PhoneGapIosTargets.getIosSimName(),
PhoneGapIosTargets.getIosDeployName()),
"Cannot detect ios-sim and ios-deploy in path");
}
}
public void throwOSWarning() throws RuntimeConfigurationWarning {
throw new RuntimeConfigurationWarning("Applications for platform " + myPlatform + " can not be built on this OS");
}
public void throwUnsupportedCommandWarning() throws RuntimeConfigurationWarning {
throw new RuntimeConfigurationWarning("Phonegap/Cordova doesn't support " + myCommand + " for " + myPlatform);
}
public PhoneGapCommandLine getCommandLine() {
PhoneGapCommandLine current = myCommandLine;
String executable = getExecutable();
String workDir = getWorkDir();
boolean passParentEnv = myPassParent;
Map<String, String> env = myEnvs;
if (current != null && StringUtil.equals(current.getPath(), executable) &&
StringUtil.equals(current.getWorkDir(), workDir) && passParentEnv == current.isPassParentEnv() &&
env.equals(current.getEnv())) {
return current;
}
assert executable != null;
assert workDir != null;
current = new PhoneGapCommandLine(executable, workDir, passParentEnv, env);
myCommandLine = current;
return current;
}
@SuppressWarnings("CloneDoesntCallSuperClone")
@Override
public PhoneGapRunConfiguration clone() {
final Element element = new Element("toClone");
try {
writeExternal(element);
PhoneGapRunConfiguration configuration =
(PhoneGapRunConfiguration)getFactory().createTemplateConfiguration(getProject());
configuration.setName(getName());
configuration.readExternal(element);
return configuration;
}
catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
@Nullable
@Override
public RunProfileState getState(@NotNull Executor executor,
@NotNull ExecutionEnvironment executionEnvironment) throws ExecutionException {
return new PhoneGapRunProfileState(getProject(), executionEnvironment, this);
}
private static void checkExistsSdkWithWarning(@Nullable String path, @NotNull String error) throws RuntimeConfigurationWarning {
if (path == null) return;
File file = PathEnvironmentVariableUtil.findInPath(path);
if (file != null && file.exists()) {
return;
}
throw new RuntimeConfigurationWarning(error);
}
private static void checkExistsSdkWithWarning(@Nullable List<String> paths, @NotNull String error) throws RuntimeConfigurationWarning {
if (paths == null) return;
for (String path : paths) {
File file = PathEnvironmentVariableUtil.findInPath(path);
if (file != null && file.exists()) {
return;
}
}
throw new RuntimeConfigurationWarning(error);
}
@Override
public final void customizeLogConsole(final LogConsole console) {
LogFilterModel model = console.getFilterModel();
if (model instanceof DefaultLogFilterModel) {
((DefaultLogFilterModel)model).setCheckStandartFilters(false);
}
}
@NotNull
@Override
public ArrayList<LogFileOptions> getAllLogFiles() {
if (!PLATFORM_IOS.equals(myPlatform) || StringUtil.isEmpty(myWorkDir)) super.getAllLogFiles();
return ContainerUtil.newArrayList(new LogFileOptions("console.log", getPathToLog(), true, false, true));
}
private String getPathToLog() {
assert myWorkDir != null;
return FileUtil
.toCanonicalPath(FileUtil.toSystemIndependentName(StringUtil.trimEnd(myWorkDir, File.separator)) + CORDOVA_IOS_LOG_PATH);
}
}