package com.github.guikeller.jettyrunner.ui;
import com.github.guikeller.jettyrunner.model.JettyRunnerConfiguration;
import com.intellij.compiler.impl.ModuleCompileScope;
import com.intellij.notification.*;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompileTask;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.PsiShortNamesCache;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* Controller - Jetty Runner Editor
* @see com.intellij.openapi.options.SettingsEditor
* @author Gui Keller
*/
public class JettyRunnerEditor extends SettingsEditor<JettyRunnerConfiguration> {
private JettyRunnerConfPanel configurationPanel;
private String mainOutputDirectory = "";
public JettyRunnerEditor(JettyRunnerConfiguration jettyRunnerConfiguration) {
this.configurationPanel = new JettyRunnerConfPanel();
super.resetFrom(jettyRunnerConfiguration);
}
/**
* This is invoked when the form is first loaded.
* The values may be stored in disk, if not, set some defaults
* @param jettyRunnerConfiguration jettyRunnerConfiguration
*/
@Override
protected void resetEditorFrom(JettyRunnerConfiguration jettyRunnerConfiguration) {
Project project = jettyRunnerConfiguration.getProject();
// WebApp Path
String webappPaths = jettyRunnerConfiguration.getWebappPaths();
if (webappPaths != null && !"".equals(webappPaths.trim())) {
this.configurationPanel.getPathField().setText(webappPaths);
} else {
String projectName = project.getName();
this.configurationPanel.getPathField().setText("/"+projectName);
}
// WebApp Folder (one level down to web.xml"
String webappFolders = jettyRunnerConfiguration.getWebappFolders();
if (webappFolders != null && !"".equals(webappFolders.trim())) {
this.configurationPanel.getWebappField().setText(webappFolders);
} else {
String webAppsFolder = getWebAppsFolder(project);
this.configurationPanel.getWebappField().setText(webAppsFolder);
}
// Classes directory
String classesDirectories = jettyRunnerConfiguration.getClassesDirectories();
if (classesDirectories != null && !"".equals(classesDirectories.trim())) {
this.configurationPanel.getClassesField().setText(classesDirectories);
} else {
String outputDirectory = getMainOutputDirectory(project);
this.configurationPanel.getClassesField().setText(outputDirectory);
}
// Runs on port
String runningOnPort = jettyRunnerConfiguration.getRunningOnPort();
if (runningOnPort != null && !"".equals(runningOnPort)) {
this.configurationPanel.getRunOnPortField().setText(runningOnPort);
} else {
this.configurationPanel.getRunOnPortField().setText("8080");
}
// Jetty XML (Optional)
this.configurationPanel.getXmlField().setText(jettyRunnerConfiguration.getJettyXml());
// Env Vars (Optional)
Map<String, String> environmentVariables = jettyRunnerConfiguration.getEnvironmentVariables();
if(environmentVariables != null && !environmentVariables.isEmpty()){
this.configurationPanel.getEnvironmentVariables().setEnvs(environmentVariables);
}
// Vm Args (Optional)
this.configurationPanel.getVmArgsField().setText(jettyRunnerConfiguration.getVmArgs());
}
/**
* This is invoked when the user fills the form and pushes apply/ok
* @param jettyRunnerConfiguration jettyRunnerConfiguration
* @throws ConfigurationException ex
*/
@Override
protected void applyEditorTo(JettyRunnerConfiguration jettyRunnerConfiguration) throws ConfigurationException {
jettyRunnerConfiguration.setWebappPaths(this.configurationPanel.getPathField().getText());
jettyRunnerConfiguration.setWebappFolders(this.configurationPanel.getWebappField().getText());
jettyRunnerConfiguration.setClassesDirectories(this.configurationPanel.getClassesField().getText());
jettyRunnerConfiguration.setRunningOnPort(this.configurationPanel.getRunOnPortField().getText());
jettyRunnerConfiguration.setJettyXml(this.configurationPanel.getXmlField().getText());
jettyRunnerConfiguration.setVmArgs(this.configurationPanel.getVmArgsField().getText());
jettyRunnerConfiguration.setPassParentEnvironmentVariables(this.configurationPanel.getEnvironmentVariables().isPassParentEnvs());
// Deals with adding / removing env vars before saving to the conf file
Map<String, String> envVars = this.configurationPanel.getEnvironmentVariables().getEnvs();
addOrRemoveEnvVar(jettyRunnerConfiguration.getEnvironmentVariables(), envVars);
jettyRunnerConfiguration.setEnvironmentVariables(envVars);
try {
// Not entirely sure if 'I have' to do this - the IntelliJ framework may do
jettyRunnerConfiguration.writeExternal(new Element(JettyRunnerConfiguration.PREFIX + UUID.randomUUID().toString()));
} catch (WriteExternalException e) {
throw new RuntimeException(e);
}
}
@NotNull
@Override
protected JComponent createEditor() {
return this.configurationPanel.getMainPanel();
}
// Helpers
/**
* Retrieves the output directory for the main module
* @param project Project
* @return String value
*/
private String getMainOutputDirectory(final Project project) {
// Preparing things up for a sneaky "CompileTask"
final CompilerManager compilerManager = CompilerManager.getInstance(project);
final Module[] modules = ModuleManager.getInstance(project).getModules();
final ModuleCompileScope compileScope = new ModuleCompileScope(project, modules, false);
final Module mainModule = modules[0];
// Though a "CompileTask" I can get hold of the "CompileContext"
CompileTask compileTask = new CompileTask() {
public boolean execute(CompileContext compileContext) {
// Through the "CompileContext" I can get the output directory of the main module
VirtualFile mainOutputDirectory = compileContext.getModuleOutputDirectory(mainModule);
if(mainOutputDirectory != null) {
String mainOutputDirectoryValue = mainOutputDirectory.getPresentableUrl();
JettyRunnerEditor.this.mainOutputDirectory = mainOutputDirectoryValue;
} else {
// Project hasn't been compiled yet, so there is no output directory
NotificationGroup notificationGroup = new NotificationGroup("IDEA Jetty Runner", NotificationDisplayType.BALLOON, true);
Notification notification = notificationGroup.createNotification("Jetty Runner - Couldn't determine the classes folder:<br>Please compile / make your project before creating the conf.", NotificationType.ERROR);
Notifications.Bus.notify(notification, project);
}
return true;
}
};
// Executes the task (synchronously), which invokes that internal 'execute' method
compilerManager.executeTask(compileTask, compileScope, "JettyRunner-By-GuiKeller", null);
return this.mainOutputDirectory;
}
/**
* Returns the most probable WebApps folder
* @param project Project
* @return String value
*/
private String getWebAppsFolder(Project project) {
// Using the api to look for the web.xml
PsiShortNamesCache namesCache = PsiShortNamesCache.getInstance(project);
PsiFile[] webXML = namesCache.getFilesByName("web.xml");
if (webXML == null || webXML.length < 1) return "";
// Grab the first one that the api found
PsiFile file = webXML[0];
// The parent folder is the "WEB-INF" folder
PsiDirectory webInfFolder = file.getParent();
if (webInfFolder == null) return "";
// The parent folder to "WEB-INF" is the WebApps folder
PsiDirectory webappFolder = webInfFolder.getParent();
if (webappFolder == null) return "";
// Folder found, returns it to the user
VirtualFile virtualFile = webappFolder.getVirtualFile();
return virtualFile.getPresentableUrl();
}
/**
* Adds / removes variables to the System Environment Variables
* @param currentVars Map<String,String>
* @param newVars Map<String,String>
*/
private void addOrRemoveEnvVar(Map<String, String> currentVars, Map<String, String> newVars){
// Removes the current env vars
if(currentVars !=null && !currentVars.isEmpty()) {
Set<String> keys = currentVars.keySet();
for (String key : keys) {
System.clearProperty(key);
}
}
// Adds the new env vars
if(newVars != null && !newVars.isEmpty()){
Set<String> keys = newVars.keySet();
for(String key : keys) {
String value = newVars.get(key);
System.setProperty(key, value);
}
}
}
public void setConfigurationPanel(JettyRunnerConfPanel configurationPanel) {
this.configurationPanel = configurationPanel;
}
}