/*
* Copyright (c) 2007-2009, Osmorc Development Team
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
* * Neither the name of 'Osmorc Development Team' nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.osmorc.facet.ui;
import com.intellij.CommonBundle;
import com.intellij.facet.ui.*;
import com.intellij.openapi.compiler.CompilerPaths;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.fileTypes.FileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.ui.*;
import com.intellij.ui.table.JBTable;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.osgi.jps.model.OutputPathType;
import org.osmorc.facet.OsmorcFacetConfiguration;
import org.osmorc.i18n.OsmorcBundle;
import javax.swing.*;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import static org.jetbrains.osgi.jps.model.OutputPathType.*;
/**
* The facet editor tab which is used to set up Osmorc facet settings concerning the bundle JAR created by Osmorc.
*
* @author <a href="mailto:janthomae@janthomae.de">Jan Thomä</a>
* @author Robert F. Beeger (robert@beeger.net)
*/
public class OsmorcFacetJAREditorTab extends FacetEditorTab {
private JPanel myRoot;
private JTable myAdditionalJARContentsTable;
private EditorTextField myIgnoreFilePatternTextField;
private JCheckBox myAlwaysRebuildBundleJARCheckBox;
private JLabel myFileIgnorePatternLabel;
private JPanel myIgnoreFilePatternPanel;
private JTextField myJarFileTextField;
private JRadioButton myPlaceInCompilerOutputPathRadioButton;
private JRadioButton myPlaceInProjectWideRadioButton;
private JRadioButton myPlaceInThisPathRadioButton;
private TextFieldWithBrowseButton myJarOutputPathChooser;
private JPanel myAdditionalJarContentsPanel;
private final FacetEditorContext myEditorContext;
private final FacetValidatorsManager myValidatorsManager;
private final AdditionalJARContentsTableModel myAdditionalJARContentsTableModel;
private boolean myModified;
public OsmorcFacetJAREditorTab(FacetEditorContext editorContext, FacetValidatorsManager validatorsManager) {
myEditorContext = editorContext;
myValidatorsManager = validatorsManager;
Project project = editorContext.getProject();
myIgnoreFilePatternTextField = new EditorTextField("", project, FileTypes.PLAIN_TEXT);
FileType type = FileTypeManager.getInstance().getFileTypeByFileName("*.regexp");
if (type == FileTypes.UNKNOWN) {
type = FileTypeManager.getInstance().getFileTypeByFileName("*.txt"); // RegExp plugin is not installed
}
PsiFile file = PsiFileFactory.getInstance(project).createFileFromText("*.regexp", type, myIgnoreFilePatternTextField.getText(), -1, true);
myIgnoreFilePatternTextField.setNewDocumentAndFileType(type, PsiDocumentManager.getInstance(project).getDocument(file));
myIgnoreFilePatternPanel.add(myIgnoreFilePatternTextField, BorderLayout.CENTER);
UserActivityWatcher watcher = new UserActivityWatcher();
watcher.addUserActivityListener(new UserActivityListener() {
public void stateChanged() {
myModified = true;
updateGui();
}
});
watcher.register(myRoot);
myJarOutputPathChooser.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
onOutputPathSelect();
}
});
ChangeListener listener = new ChangeListener() {
public void stateChanged(ChangeEvent e) {
updateGui();
}
};
myPlaceInProjectWideRadioButton.addChangeListener(listener);
myPlaceInThisPathRadioButton.addChangeListener(listener);
myPlaceInCompilerOutputPathRadioButton.addChangeListener(listener);
myAdditionalJARContentsTableModel = new AdditionalJARContentsTableModel();
myAdditionalJARContentsTable = new JBTable(myAdditionalJARContentsTableModel);
TableColumn col = myAdditionalJARContentsTable.getColumnModel().getColumn(0);
FileSelectorTableCellEditor selectorTableCellEditor = new FileSelectorTableCellEditor(project, myEditorContext.getModule());
col.setCellEditor(selectorTableCellEditor);
selectorTableCellEditor.addCellEditorListener(new CellEditorListener() {
@Override
public void editingCanceled(ChangeEvent e) { }
@Override
public void editingStopped(ChangeEvent e) {
// ok we finished editing the left, now get the stuff from there, calculate a destination name and edit the other cell
int row = myAdditionalJARContentsTable.getSelectedRow();
if (row > -1) {
Pair<String, String> additionalJARContent = myAdditionalJARContentsTableModel.getAdditionalJARContent(row);
VirtualFile preselectedPath = LocalFileSystem.getInstance().findFileByPath(additionalJARContent.getFirst());
String destinationName = preselectedPath != null ? determineMostLikelyLocationInJar(preselectedPath) : "";
myAdditionalJARContentsTableModel.changeAdditionalJARConent(row, additionalJARContent.first, destinationName);
myAdditionalJARContentsTable.editCellAt(row, 1);
myAdditionalJARContentsTable.getEditorComponent().requestFocus();
}
}
});
myAdditionalJarContentsPanel.add(
ToolbarDecorator.createDecorator(myAdditionalJARContentsTable)
.setAddAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
onAddAdditionalJarContent();
}
})
.setRemoveAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
onRemoveAdditionalJarContent();
}
})
.setEditAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
onEditAdditionalJARContent();
}
})
.disableUpDownActions().createPanel(), BorderLayout.CENTER);
myValidatorsManager.registerValidator(new FacetEditorValidator() {
@NotNull
@Override
public ValidationResult check() {
if (StringUtil.isEmptyOrSpaces(myJarFileTextField.getText())) {
return new ValidationResult(OsmorcBundle.message("facet.editor.jar.empty.jar.name"));
}
if (getSelectedOutputPathType() == SpecificOutputPath && StringUtil.isEmptyOrSpaces(myJarOutputPathChooser.getText())) {
return new ValidationResult(OsmorcBundle.message("facet.editor.jar.empty.output.path"));
}
return ValidationResult.OK;
}
});
}
private void onEditAdditionalJARContent() {
int row = myAdditionalJARContentsTable.getSelectedRow();
if (row > -1) {
Pair<String, String> additionalJARContent = myAdditionalJARContentsTableModel.getAdditionalJARContent(row);
Project project = myEditorContext.getProject();
FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleLocalFileDescriptor().withTitle(OsmorcBundle.message("facet.editor.select.source.title"));
VirtualFile preselectedPath = LocalFileSystem.getInstance().findFileByPath(additionalJARContent.getFirst());
if (preselectedPath == null) {
Module module = myEditorContext.getModule();
VirtualFile[] contentRoots = ModuleRootManager.getInstance(module).getContentRoots();
if (contentRoots.length > 0) {
for (VirtualFile contentRoot : contentRoots) {
VirtualFile path = VfsUtilCore.findRelativeFile(additionalJARContent.getFirst(), contentRoot);
if (path != null) {
preselectedPath = path;
break;
}
}
if (preselectedPath == null) {
preselectedPath = contentRoots[0];
}
}
else if (project.getBaseDir() != null) {
preselectedPath = project.getBaseDir();
}
}
VirtualFile[] files = FileChooser.chooseFiles(descriptor, project, preselectedPath);
if (files.length > 0) {
String sourcePath = files[0].getPath();
String destPath = determineMostLikelyLocationInJar(files[0]);
myAdditionalJARContentsTableModel.changeAdditionalJARConent(row, sourcePath, destPath);
myAdditionalJARContentsTable.editCellAt(row, 1);
myAdditionalJARContentsTable.getEditorComponent().requestFocus();
}
}
}
private void onRemoveAdditionalJarContent() {
final int row = myAdditionalJARContentsTable.getSelectedRow();
final int editingCol = myAdditionalJARContentsTable.getEditingColumn();
final int editingRow = myAdditionalJARContentsTable.getEditingRow();
if (editingCol != -1 && editingRow != -1) {
TableCellEditor editor = myAdditionalJARContentsTable.getCellEditor(editingRow, editingCol);
editor.cancelCellEditing();
}
myAdditionalJARContentsTableModel.deleteAdditionalJARContent(row);
}
private void onAddAdditionalJarContent() {
Project project = myEditorContext.getProject();
FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createAllButJarContentsDescriptor().withTitle(OsmorcBundle.message("facet.editor.select.source.title"));
VirtualFile rootFolder = null;
VirtualFile[] contentRoots = ModuleRootManager.getInstance(myEditorContext.getModule()).getContentRoots();
if (contentRoots.length > 0) {
rootFolder = contentRoots[0];
}
else if (project.getBaseDir() != null) {
rootFolder = project.getBaseDir();
}
VirtualFile[] files = FileChooser.chooseFiles(descriptor, project, rootFolder);
for (VirtualFile file : files) {
String destFile = determineMostLikelyLocationInJar(file);
int row = myAdditionalJARContentsTableModel.addAdditionalJARContent(file.getPath(), destFile);
myAdditionalJARContentsTable.editCellAt(row, 1);
myAdditionalJARContentsTable.getEditorComponent().requestFocus();
}
}
private String determineMostLikelyLocationInJar(@NotNull VirtualFile file) {
VirtualFile[] contentRoots = ModuleRootManager.getInstance(myEditorContext.getModule()).getContentRoots();
for (VirtualFile contentRoot : contentRoots) {
if (VfsUtilCore.isAncestor(contentRoot, file, false)) {
return VfsUtilCore.getRelativePath(file, contentRoot);
}
}
VirtualFile projectBaseFolder = myEditorContext.getProject().getBaseDir();
if (projectBaseFolder != null && VfsUtilCore.isAncestor(projectBaseFolder, file, false)) {
return VfsUtilCore.getRelativePath(file, projectBaseFolder);
}
return file.getName();
}
private void updateGui() {
Boolean bnd = myEditorContext.getUserData(OsmorcFacetGeneralEditorTab.BND_CREATION_KEY);
Boolean bundlor = myEditorContext.getUserData(OsmorcFacetGeneralEditorTab.BUNDLOR_CREATION_KEY);
boolean useExternalTool = Boolean.TRUE.equals(bnd) || Boolean.TRUE.equals(bundlor);
myJarOutputPathChooser.setEnabled(myPlaceInThisPathRadioButton.isSelected());
myAdditionalJARContentsTable.setEnabled(!useExternalTool);
myIgnoreFilePatternTextField.setEnabled(!useExternalTool);
myAdditionalJARContentsTable.setEnabled(!useExternalTool);
myAlwaysRebuildBundleJARCheckBox.setEnabled(!useExternalTool);
myAdditionalJarContentsPanel.setEnabled(!useExternalTool);
myFileIgnorePatternLabel.setEnabled(!useExternalTool);
myValidatorsManager.validate();
}
private void onOutputPathSelect() {
String current = myJarOutputPathChooser.getText();
final VirtualFile moduleOutputDir = CompilerPaths.getModuleOutputDirectory(myEditorContext.getModule(), false);
VirtualFile toSelect = StringUtil.isNotEmpty(current) ? LocalFileSystem.getInstance().findFileByPath(current) : moduleOutputDir;
FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor().withTitle(OsmorcBundle.message("facet.editor.select.bundle.dir.title"));
FileChooser.chooseFile(descriptor, myEditorContext.getProject(), toSelect, new Consumer<VirtualFile>() {
@Override
public void consume(VirtualFile file) {
if (moduleOutputDir != null && VfsUtilCore.isAncestor(moduleOutputDir, file, false)) {
Messages.showErrorDialog(myRoot, OsmorcBundle.message("facet.editor.jar.cannot.be.in.output.path"), CommonBundle.message("title.error"));
myJarOutputPathChooser.setText("");
}
else {
myJarOutputPathChooser.setText(file.getPath());
}
}
});
}
@Nls
@Override
public String getDisplayName() {
return "Bundle .jar";
}
@NotNull
@Override
public JComponent createComponent() {
return myRoot;
}
@Override
public boolean isModified() {
return myModified;
}
@Override
public void apply() throws ConfigurationException {
String jarFileName = myJarFileTextField.getText();
if (StringUtil.isEmptyOrSpaces(jarFileName)) {
throw new ConfigurationException(OsmorcBundle.message("facet.editor.jar.empty.jar.name"));
}
OsmorcFacetConfiguration configuration = (OsmorcFacetConfiguration)myEditorContext.getFacet().getConfiguration();
OutputPathType pathType = getSelectedOutputPathType();
if (pathType == SpecificOutputPath) {
String location = myJarOutputPathChooser.getText();
if (StringUtil.isEmptyOrSpaces(location)) {
throw new ConfigurationException(OsmorcBundle.message("facet.editor.jar.empty.output.path"));
}
String completeOutputPath = new File(location, jarFileName).getPath();
configuration.setJarFileLocation(completeOutputPath, pathType);
}
else {
configuration.setJarFileLocation(jarFileName, pathType);
}
configuration.setIgnoreFilePattern(myIgnoreFilePatternTextField.getText());
configuration.setAlwaysRebuildBundleJAR(myAlwaysRebuildBundleJARCheckBox.isSelected());
configuration.setAdditionalJARContents(myAdditionalJARContentsTableModel.getAdditionalContents());
myModified = false;
}
@Override
public void reset() {
OsmorcFacetConfiguration configuration = (OsmorcFacetConfiguration)myEditorContext.getFacet().getConfiguration();
OutputPathType outputPathType = configuration.getOutputPathType();
myPlaceInCompilerOutputPathRadioButton.setSelected(outputPathType == CompilerOutputPath);
myPlaceInProjectWideRadioButton.setSelected(outputPathType == OsgiOutputPath);
myPlaceInThisPathRadioButton.setSelected(outputPathType == SpecificOutputPath);
myJarFileTextField.setText(configuration.getJarFileName());
if (outputPathType == SpecificOutputPath) {
myJarOutputPathChooser.setText(configuration.getJarFilePath());
}
else {
myJarOutputPathChooser.setText("");
}
myAdditionalJARContentsTableModel.replaceContent(configuration.getAdditionalJARContents());
myIgnoreFilePatternTextField.setText(configuration.getIgnoreFilePattern());
myAlwaysRebuildBundleJARCheckBox.setSelected(configuration.isAlwaysRebuildBundleJAR());
updateGui();
myModified = false;
}
@Override
public void onTabEntering() {
updateGui();
}
@Override
public void disposeUIResources() { }
@Override
public String getHelpTopic() {
return "reference.settings.module.facet.osgi";
}
private OutputPathType getSelectedOutputPathType() {
return myPlaceInProjectWideRadioButton.isSelected() ? OsgiOutputPath :
myPlaceInCompilerOutputPathRadioButton.isSelected() ? CompilerOutputPath :
SpecificOutputPath;
}
}