blob: c4802c11c906488a2bf6caf41ed8e81b3d9a7ee7 [file] [log] [blame]
/*
* Copyright 2016 The Bazel Authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.idea.blaze.base.run.exporter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.io.FileAttributeProvider;
import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
import com.google.idea.blaze.base.projectview.ProjectViewManager;
import com.google.idea.blaze.base.projectview.ProjectViewSet;
import com.google.idea.blaze.base.projectview.ProjectViewSet.ProjectViewFile;
import com.google.idea.blaze.base.run.BlazeRunConfiguration;
import com.google.idea.blaze.base.settings.Blaze;
import com.intellij.execution.RunManager;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.fileChooser.FileChooserDialog;
import com.intellij.openapi.fileChooser.FileChooserFactory;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.ValidationInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.AnActionButton;
import com.intellij.ui.BooleanTableCellEditor;
import com.intellij.ui.BooleanTableCellRenderer;
import com.intellij.ui.ColoredTableCellRenderer;
import com.intellij.ui.FieldPanel;
import com.intellij.ui.GuiUtils;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.table.JBTable;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import javax.swing.DefaultCellEditor;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.table.TableColumn;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
/** UI for exporting blaze run configurations. */
public class ExportRunConfigurationDialog extends DialogWrapper {
private final ImmutableList<RunConfiguration> blazeConfigurations;
private final ExportRunConfigurationTableModel tableModel;
private final JBTable table;
private final FieldPanel outputDirectoryPanel;
ExportRunConfigurationDialog(Project project) {
super(project, true);
blazeConfigurations =
ImmutableList.copyOf(
RunManager.getInstance(project)
.getAllConfigurationsList()
.stream()
.filter((config) -> config instanceof BlazeRunConfiguration)
.collect(Collectors.toList()));
tableModel = new ExportRunConfigurationTableModel(blazeConfigurations);
table = new JBTable(tableModel);
TableColumn booleanColumn = table.getColumnModel().getColumn(0);
booleanColumn.setCellRenderer(new BooleanTableCellRenderer());
booleanColumn.setCellEditor(new BooleanTableCellEditor());
int width = table.getFontMetrics(table.getFont()).stringWidth(table.getColumnName(0)) + 10;
booleanColumn.setPreferredWidth(width);
booleanColumn.setMinWidth(width);
booleanColumn.setMaxWidth(width);
table
.getColumnModel()
.getColumn(2)
.setCellEditor(new DefaultCellEditor(GuiUtils.createUndoableTextField()));
TableColumn nameColumn = table.getColumnModel().getColumn(1);
nameColumn.setCellRenderer(
new ColoredTableCellRenderer() {
@Override
protected void customizeCellRenderer(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
RunConfiguration config = blazeConfigurations.get(row);
setIcon(config.getType().getIcon());
append(config.getName());
}
});
table.setPreferredSize(new Dimension(700, 700));
table.setShowColumns(true);
final ActionListener browseAction = e -> chooseDirectory();
outputDirectoryPanel =
new FieldPanel("Export configurations to directory:", null, browseAction, null);
File defaultExportDirectory = defaultExportDirectory(project);
if (defaultExportDirectory != null) {
outputDirectoryPanel.setText(defaultExportDirectory.getPath());
}
String buildSystem = Blaze.buildSystemName(project);
setTitle(String.format("Export %s Run Configurations", buildSystem));
init();
}
/** Try to find a checked-in project view file. Otherwise, fall back to the workspace root. */
@Nullable
private static File defaultExportDirectory(Project project) {
WorkspaceRoot workspaceRoot = WorkspaceRoot.fromProjectSafe(project);
if (workspaceRoot == null) {
return null;
}
ProjectViewSet projectViewSet = ProjectViewManager.getInstance(project).getProjectViewSet();
if (projectViewSet != null) {
for (ProjectViewFile projectViewFile : projectViewSet.getProjectViewFiles()) {
File file = projectViewFile.projectViewFile;
if (file != null && FileUtil.isAncestor(workspaceRoot.directory(), file, false)) {
return file.getParentFile();
}
}
}
return workspaceRoot.directory();
}
private String getOutputDirectoryPath() {
return Strings.nullToEmpty(outputDirectoryPanel.getText()).trim();
}
private void chooseDirectory() {
FileChooserDescriptor descriptor =
FileChooserDescriptorFactory.createSingleFolderDescriptor()
.withTitle("Export Directory Location")
.withDescription("Choose directory to export run configurations to")
.withHideIgnored(false);
FileChooserDialog chooser =
FileChooserFactory.getInstance().createFileChooser(descriptor, null, null);
final VirtualFile[] files;
File existingLocation = new File(getOutputDirectoryPath());
if (existingLocation.exists()) {
VirtualFile toSelect =
LocalFileSystem.getInstance().refreshAndFindFileByPath(existingLocation.getPath());
files = chooser.choose(null, toSelect);
} else {
files = chooser.choose(null);
}
if (files.length == 0) {
return;
}
VirtualFile file = files[0];
outputDirectoryPanel.setText(file.getPath());
}
@Nullable
@Override
protected ValidationInfo doValidate() {
String outputDir = getOutputDirectoryPath();
if (outputDir.isEmpty()) {
return new ValidationInfo("Choose an output directory");
}
if (!FileAttributeProvider.getInstance().exists(new File(outputDir))) {
return new ValidationInfo("Invalid output directory");
}
Set<String> names = new HashSet<>();
for (int i = 0; i < blazeConfigurations.size(); i++) {
if (!tableModel.enabled[i]) {
continue;
}
if (!names.add(tableModel.paths[i])) {
return new ValidationInfo("Duplicate output file name '" + tableModel.paths[i] + "'");
}
}
return null;
}
@Override
protected void doOKAction() {
File outputDir = new File(getOutputDirectoryPath());
List<File> outputFiles = new ArrayList<>();
for (int i = 0; i < blazeConfigurations.size(); i++) {
if (!tableModel.enabled[i]) {
continue;
}
File outputFile = new File(outputDir, tableModel.paths[i]);
writeConfiguration(blazeConfigurations.get(i), outputFile);
outputFiles.add(outputFile);
}
LocalFileSystem.getInstance().refreshIoFiles(outputFiles);
super.doOKAction();
}
private static void writeConfiguration(RunConfiguration configuration, File outputFile) {
try (FileOutputStream writer = new FileOutputStream(outputFile, false)) {
XMLOutputter xmlOutputter = new XMLOutputter(Format.getCompactFormat());
xmlOutputter.output(RunConfigurationSerializer.writeToXml(configuration), writer);
} catch (IOException e) {
throw new RuntimeException("Error exporting run configuration to file: " + outputFile);
}
}
@Override
protected JComponent createNorthPanel() {
return outputDirectoryPanel;
}
@Override
protected JComponent createCenterPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(IdeBorderFactory.createTitledBorder("Run Configurations", false));
panel.add(
ToolbarDecorator.createDecorator(table).addExtraAction(new SelectAllButton()).createPanel(),
BorderLayout.CENTER);
return panel;
}
private class SelectAllButton extends AnActionButton {
boolean allSelected = false;
private SelectAllButton() {
super("Select All", AllIcons.Actions.Selectall);
}
@Override
public synchronized void actionPerformed(AnActionEvent anActionEvent) {
boolean newState = !allSelected;
for (int i = 0; i < tableModel.enabled.length; i++) {
table.setValueAt(newState, i, 0);
}
allSelected = newState;
Presentation presentation = anActionEvent.getPresentation();
if (allSelected) {
presentation.setText("Deselect All");
presentation.setIcon(AllIcons.Actions.Unselectall);
} else {
presentation.setText("Select All");
presentation.setIcon(AllIcons.Actions.Selectall);
}
tableModel.fireTableDataChanged();
table.revalidate();
table.repaint();
}
}
}