/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* 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 Business Objects 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.
*/
/*
* JavaForeignImportModuleGenerator.java
* Creation date: Sep 27, 2005.
* By: Edward Lam
*/
package org.openquark.gems.client.generators;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.DefaultListCellRenderer;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JRootPane;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.LayoutFocusTraversalPolicy;
import javax.swing.ListSelectionModel;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileView;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.compiler.TypeChecker;
import org.openquark.cal.compiler.SourceModel.ModuleDefn;
import org.openquark.cal.services.CALFeatureName;
import org.openquark.cal.services.Perspective;
import org.openquark.cal.services.ResourceName;
import org.openquark.cal.services.Status;
import org.openquark.gems.client.GemCutter;
import org.openquark.gems.client.ValueRunner;
import org.openquark.gems.client.jfit.ForeignImportGenerator;
import org.openquark.gems.client.jfit.JFit;
import org.openquark.gems.client.valueentry.ValueEditorManager;
import org.openquark.util.FileSystemHelper;
import org.openquark.util.UnsafeCast;
import org.openquark.util.ui.DetailsDialog;
import org.openquark.util.ui.DialogBase;
import org.openquark.util.ui.ExtensionFileFilter;
import org.openquark.util.ui.SortedListModel;
/**
* This is the container class for the generator to create a cal module importing multiple Java functions and types.
* @author Edward Lam
*/
public final class JavaForeignImportModuleGenerator extends DialogBase {
private static final long serialVersionUID = -1549293981732061092L;
/** The value of the "java.home" system property. This is guaranteed to be defined. */
private static final String JAVA_HOME = System.getProperty("java.home");
/** The icon used by the generator. */
private static final Icon GENERATOR_ICON = new ImageIcon(GemGenerator.class.getResource("/Resources/supercombinator.gif"));
/** The icon to use for error messages. */
private static final Icon ERROR_ICON = new ImageIcon(GemCutter.class.getResource("/Resources/error.gif"));
/** The icon to use for warning messages. */
private static final Icon WARNING_ICON = new ImageIcon(GemCutter.class.getResource("/Resources/warning.gif"));
/** The icon to use if everything is ok. */
private static final Icon OK_ICON = new ImageIcon(GemCutter.class.getResource("/Resources/checkmark.gif"));
/** The icon to use to represent folders. */
private static final Icon FOLDER_ICON = new ImageIcon(GemCutter.class.getResource("/Resources/fldr_obj.gif"));
/** The icon to use to represent .jar files. */
private static final Icon JAR_ICON = new ImageIcon(GemCutter.class.getResource("/Resources/jar_obj.gif"));
/** The icon to use for an include pattern. */
private static final Icon INCLUDE_ICON = new ImageIcon(GemCutter.class.getResource("/Resources/add_exc.gif"));
/** The icon to use for an exclude pattern.*/
private static final Icon EXCLUDE_ICON = new ImageIcon(GemCutter.class.getResource("/Resources/remove_exc.gif"));
/** The perspective this UI is running in. */
private final Perspective perspective;
/** The JList displaying the roots from which to import the Java classes and members.
* The model consists of elements of type ImportSource. */
private ImportFromJList importFromJList;
/** The JList displaying the patterns used to filter the classes being imported. */
private PatternJList patternsJList;
/** The panel allowing the generation scope to be selected. */
private ScopeSelectorPanel scopeSelectorPanel;
/** The panel allowing methods to be excluded from generation. */
private MethodExcludeSelectorPanel methodExcludeSelectorPanel;
/** The OK button for the dialog. */
private JButton okButton = null;
/** The cancel button for the dialog. */
private JButton cancelButton = null;
/** The text field for entering the name of the new module. */
private final JTextField moduleNameField = new JTextField();
/** The label for displaying status messages. */
private final JLabel statusLabel = new JLabel();
/** Shared dialog instance for selecting imports. */
private SelectImportDialog selectImportDialog = null;
/**
* Test target.
* Comment out check for null Perspective to execute.
* @param args
*/
public static void main(String[] args) {
JavaForeignImportModuleGenerator generator = new JavaForeignImportModuleGenerator(null, null, null);
JFrame.setDefaultLookAndFeelDecorated(true);
generator.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
System.exit(0);
}
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
generator.setVisible(true);
}
/**
* Provider for a JavaForeignImportModuleGenerator.
* @author Edward Lam
*/
public static class Provider implements GemGenerator {
/**
* {@inheritDoc}
*/
public GemGenerator.GeneratedDefinitions launchGenerator(JFrame parent, Perspective perspective, ValueRunner valueRunner, ValueEditorManager valueEditorManager, TypeChecker typeChecker) {
if (parent == null || perspective == null) {
throw new NullPointerException();
}
String title = getGeneratorTitle();
JavaForeignImportModuleGenerator generatorUI = new JavaForeignImportModuleGenerator(parent, title, perspective);
// Loop around until either:
// 1) The user accepted the dialog and a module was successfully generated, or
// 2) The user canceled the dialog.
while (true) {
boolean accepted = generatorUI.doModal();
if (accepted) {
GeneratedDefinitions sourceDefinitions = generatorUI.getSourceDefinitions();
if (sourceDefinitions.getModuleDefn() == null) {
// Couldn't successfully generate a module. Try again.
continue;
}
return sourceDefinitions;
} else {
return null;
}
}
}
/**
* {@inheritDoc}
*/
public String getGeneratorMenuName() {
return GeneratorMessages.getString("JFIMF_JavaForeignImportMenuName");
}
/**
* {@inheritDoc}
*/
public String getGeneratorTitle() {
return GeneratorMessages.getString("JFIMF_JavaForeignImportTitle");
}
/**
* {@inheritDoc}
*/
public Icon getGeneratorIcon() {
return GENERATOR_ICON;
}
}
/**
* A log handler which will capture logged records to a Status object.
* @author Edward Lam
*/
private static class StatusHandler extends Handler {
/** The status object capturing the published records. */
private final Status capturingStatus = new Status("Status");
/**
* {@inheritDoc}
*/
@Override
public void flush() {
// Nothing to flush -- not buffered.
}
/**
* {@inheritDoc}
*/
@Override
public void close() throws SecurityException {
// Nothing to close, for now.
// Later, there may be some way of saying that the capturing status is no longer mutable..
}
/**
* {@inheritDoc}
*/
@Override
public void publish(LogRecord record) {
// Add the record to the capturing status.
String message = record.getMessage();
Level level = record.getLevel();
int levelValue = level.intValue();
// Only interested in warnings and errors.
Status.Severity severity = null;
if (levelValue >= Level.SEVERE.intValue()) {
severity = Status.Severity.ERROR;
} else if (levelValue >= Level.WARNING.intValue()) {
severity = Status.Severity.WARNING;
}
if (severity != null) {
capturingStatus.add(new Status(severity, message));
}
}
/**
* @return the status which captured the records published to this handler.
*/
public Status getStatus() {
return capturingStatus;
}
}
/**
* A simple class to encapsulate an import source.
* @author Edward Lam
*/
private static class ImportSource {
private final File importFile;
private final boolean isDir;
ImportSource(File importFile, boolean isDir) {
this.importFile = importFile;
this.isDir = isDir;
}
/**
* @return the file object associated with the import source.
*/
public File getImportFile() {
return this.importFile;
}
/**
* @return true if the source is a root folder. False if it is a .jar file.
*/
public boolean isDir() {
return this.isDir;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof ImportSource) {
ImportSource otherSource = (ImportSource)obj;
return isDir == otherSource.isDir && importFile.equals(otherSource.importFile);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return importFile.hashCode() + (isDir ? 17 : 37);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "(" + (isDir ? "directory: " : "file: ") + importFile.getPath() + ")";
}
}
/**
* Sorts import sources in the import source list.
* Folders come first, then files.
* @author Edward Lam
*/
static class ImportSourceComparator implements Comparator<ImportSource> {
/** Singleton instance. */
public static final ImportSourceComparator INSTANCE = new ImportSourceComparator();
/**
* {@inheritDoc}
*/
public int compare(ImportSource import1, ImportSource import2) {
// import 1 is a folder?
if (import1.isDir()) {
if (!import2.isDir()) {
return -1;
}
// import 2 is also a folder.
return import1.getImportFile().compareTo(import2.getImportFile());
} else {
// If here, import 1 is a file.
if (import2.isDir()) {
return 1;
}
// import 2 is also a file.
return import1.getImportFile().compareTo(import2.getImportFile());
}
}
}
/**
* The custom cell renderer for displaying the import sources in the importFromJList.
* @author Edward Lam
*/
private static class ImportFromJListCellRenderer extends DefaultListCellRenderer {
private static final long serialVersionUID = -8242855895470608433L;
/** Shared renderer instance. */
public static final ImportFromJListCellRenderer INSTANCE = new ImportFromJListCellRenderer();
/**
* {@inheritDoc}
*/
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
ImportSource importSource = (ImportSource)value;
File importFile = importSource.getImportFile();
boolean isDir = importSource.isDir();
String displayText = importFile.getPath();
if (importFile.getAbsolutePath().startsWith(JAVA_HOME) && importFile.getName().equals("rt.jar")) {
displayText += " " + GeneratorMessages.getString("JFIMF_RTJarDescription");
}
// Get the default renderer, displaying the import file.
JLabel defaultComponent = (JLabel)super.getListCellRendererComponent(list, displayText, index, isSelected, cellHasFocus);
defaultComponent.setIcon(isDir ? FOLDER_ICON : JAR_ICON);
return defaultComponent;
}
}
/**
* The JList for displaying the import sources.
*
* A special renderer to show whether the import source is a folder or a .jar file.
* The items are maintained in sorted order.
* Selection of items is automatically handled on add/edit/remove.
*
* To use, do not manipulate the list model. Instead, call the appropriate (add/edit/removeSelected)ImportSource() method.
*
* @author Edward Lam
*/
private static final class ImportFromJList extends JList {
private static final long serialVersionUID = 2899009970173033431L;
/**
* Constructor for a new list.
*/
private ImportFromJList() {
super(new SortedListModel<ImportSource>(ImportSourceComparator.INSTANCE));
setCellRenderer(ImportFromJListCellRenderer.INSTANCE);
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
/**
* @return the SortedListModel backing this JList.
*/
SortedListModel<ImportSource> getListModel() {
return UnsafeCast.unsafeCast(getModel());
}
/**
* @return the currently selected ImportSource, or null if the list has no selection
*/
public ImportSource getSelectedImport() {
return (ImportSource)getSelectedValue();
}
/**
* Add an import source to this JList.
* @param importSource the import source to add.
*/
void addImport(ImportSource importSource) {
getListModel().addElement(importSource);
// Select the import that was just added.
selectImport(importSource);
}
/**
* Select an import in this JList.
* @param importToSelect the import to select.
*/
private void selectImport(ImportSource importToSelect) {
int importIndex = 0;
for (Iterator<ImportSource> it = getListModel().iterator(); it.hasNext(); ) {
if (it.next().equals(importToSelect)) {
getSelectionModel().setSelectionInterval(importIndex, importIndex);
break;
}
importIndex++;
}
}
/**
* Edit an import in this JList.
* @param oldImport the old import.
* @param newImport the import with which oldImport will be replaced.
*/
void editImport(ImportSource oldImport, ImportSource newImport) {
// The easiest way to edit an import is to remove the old import, and add the new one.
getListModel().removeElement(oldImport);
getListModel().addElement(newImport);
// Select the import which was just edited.
selectImport(newImport);
}
/**
* Remove the import currently selected in this JList.
*/
void removeSelectedImport() {
ListSelectionModel selectionModel = getSelectionModel();
// Remove the selected element.
int selectionIndex = selectionModel.getMinSelectionIndex();
if (selectionIndex < 0) {
return;
}
getListModel().removeElement(getSelectedImport());
// Select another element if any.
int modelSize = getModel().getSize();
if (selectionIndex < modelSize) {
// Select the next element.
selectionModel.setSelectionInterval(selectionIndex, selectionIndex);
} else if (modelSize > 0) {
// Select the last element.
selectionModel.setSelectionInterval(modelSize - 1, modelSize - 1);
} else {
selectionModel.clearSelection();
}
}
}
/**
* Sorts patterns in the pattern list.
* Includes come first, then excludes.
* @author Edward Lam
*/
static class JFitPatternComparator implements Comparator<Object> {
/** Singleton instance. */
public static final JFitPatternComparator INSTANCE = new JFitPatternComparator();
/**
* {@inheritDoc}
*/
public int compare(Object o1, Object o2) {
if (o1 instanceof String) {
if (o2 instanceof String) {
return ((String)o1).compareTo((String)o2);
}
return -1;
}
if (o2 instanceof String) {
return 1;
}
JFit.Pattern pat1 = (JFit.Pattern)o1;
JFit.Pattern pat2 = (JFit.Pattern)o2;
// pattern 1 is an include pattern?
if (pat1.isInclude()) {
if (!pat2.isInclude()) {
return -1;
}
// pattern 2 is also an include pattern.
return pat1.getPattern().compareTo(pat2.getPattern());
} else {
// If here, pattern 1 is an exclude pattern.
if (pat2.isInclude()) {
return 1;
}
// pattern 2 is also an exclude pattern.
return pat1.getPattern().compareTo(pat2.getPattern());
}
}
}
/**
* The custom cell renderer for displaying include and exclude patterns in the patternJList.
* @author Edward Lam
*/
private static class PatternJListCellRenderer extends DefaultListCellRenderer {
private static final long serialVersionUID = 3352312909749939764L;
/** Shared renderer instance. */
public static final PatternJListCellRenderer INSTANCE = new PatternJListCellRenderer();
/**
* {@inheritDoc}
*/
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if (value instanceof String) {
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
// Get the default renderer, displaying the pattern string.
JFit.Pattern pattern = (JFit.Pattern)value;
JLabel defaultComponent = (JLabel)super.getListCellRendererComponent(list, pattern.getPattern(), index, isSelected, cellHasFocus);
defaultComponent.setIcon(pattern.isInclude() ? INCLUDE_ICON : EXCLUDE_ICON);
return defaultComponent;
}
}
/**
* The JList for displaying the include/exclude patterns.
*
* A special renderer to show whether a pattern is included or excluded.
* The items are maintained in sorted order.
* Selection of items is automatically handled on add/edit/remove.
*
* To use, do not manipulate the list model. Instead, call the appropriate (add/edit/removeSelected)Pattern() method.
*
* @author Edward Lam
*/
private static final class PatternJList extends JList {
private static final long serialVersionUID = 4724753052424581727L;
// The string to display when no patterns have been specified.
private static final String NO_PATTERNS_ELEMENT = GeneratorMessages.getString("JFIMF_NoPatternsElement");
/**
* Constructor for a new list.
*/
private PatternJList() {
super(new SortedListModel<Object>(JFitPatternComparator.INSTANCE));
setCellRenderer(PatternJListCellRenderer.INSTANCE);
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
getListModel().addElement(NO_PATTERNS_ELEMENT);
}
/**
* @return the SortedListModel backing this JList.
*/
SortedListModel<Object> getListModel() {
return UnsafeCast.unsafeCast(getModel());
}
/**
* @return the currently selected JFit.Pattern, or null if:
* 1) the list has no selection
* 2) the list selection is the no-patterns element.
*/
public JFit.Pattern getSelectedPattern() {
Object selectedValue = getSelectedValue();
if (selectedValue instanceof JFit.Pattern) {
return (JFit.Pattern)selectedValue;
}
return null;
}
/**
* Add a pattern to this JList.
* @param jfitPattern the pattern to add.
*/
void addPattern(JFit.Pattern jfitPattern) {
// If there were no patterns, we make sure the "No patterns" message is removed.
if (getListModel().getSize() < 2) {
// This does nothing if the single element is not the no patterns element.
getListModel().removeElement(NO_PATTERNS_ELEMENT);
// Clear selection so that the selection changed event will be fired when the first item is selected again.
getSelectionModel().clearSelection();
}
getListModel().addElement(jfitPattern);
// Select the pattern that was just added.
selectPattern(jfitPattern);
}
/**
* Select a pattern in this JList.
* @param patternToSelect the pattern to select.
*/
private void selectPattern(JFit.Pattern patternToSelect) {
int patternIndex = 0;
for (Iterator<Object> it = getListModel().iterator(); it.hasNext(); ) {
if (it.next().equals(patternToSelect)) {
getSelectionModel().setSelectionInterval(patternIndex, patternIndex);
break;
}
patternIndex++;
}
}
/**
* Edit a pattern in this JList.
* @param oldPattern the old pattern.
* @param newPattern the pattern with which oldPattern will be replaced.
*/
void editPattern(JFit.Pattern oldPattern, JFit.Pattern newPattern) {
// The easiest way to edit a pattern is to remove the old pattern, and add the new one.
getListModel().removeElement(oldPattern);
getListModel().addElement(newPattern);
// Select the pattern which was just edited.
selectPattern(newPattern);
}
/**
* Remove the pattern currently selected in this JList.
*/
void removeSelectedPattern() {
ListSelectionModel selectionModel = getSelectionModel();
// Remove the selected element.
int selectionIndex = selectionModel.getMinSelectionIndex();
if (selectionIndex < 0) {
return;
}
getListModel().removeElement(getSelectedPattern());
// Select another element.
int modelSize = getModel().getSize();
if (selectionIndex < modelSize) {
// Select the next element.
selectionModel.setSelectionInterval(selectionIndex, selectionIndex);
} else if (modelSize > 0) {
// Select the last element.
selectionModel.setSelectionInterval(modelSize - 1, modelSize - 1);
} else {
selectionModel.clearSelection();
}
// If there are no patterns, display the "No patterns" message.
if (modelSize == 0) {
getListModel().addElement(NO_PATTERNS_ELEMENT);
}
}
/**
* @return true if there are no patterns, false if there are.
*/
public boolean isEmpty() {
return (getModel().getSize() == 1) && getModel().getElementAt(0).equals(NO_PATTERNS_ELEMENT);
}
}
/**
* The dialog to select an import source.
* This is just a dialog with an embedded JFileChooser and a help panel at the top.
*
* To use: instantiate, then call showDialog().
*
* @author Edward Lam
*/
private static class SelectImportDialog extends DialogBase {
private static final long serialVersionUID = 8113203595172861783L;
/** Shared file chooser instance for selecting imports. */
private final JFileChooser fileChooser;
/** The help panel at the top of the dialog. */
private final TitlePanel helpPanel;
/** The current return value.
* This mirrors the same value in JFileChooser, which unfortunately has no accessors. */
private int returnValue = JFileChooser.ERROR_OPTION;
/** The size constrainer for this dialog. */
private final SizeConstrainer sizeConstrainer;
/**
* Constructor for a SelectImportDialog.
* @param parent the parent of this dialog.
*/
private SelectImportDialog(JDialog parent) {
super(parent, "");
sizeConstrainer = new SizeConstrainer();
addComponentListener(sizeConstrainer);
// main panel
JPanel topPanel = getTopPanel();
topPanel.setBorder(null); // cancel the border set by superclass..
setContentPane(topPanel);
// Keep track of the number of rows.
int numRows = 0;
// Help panel to the north.
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = numRows;
helpPanel = new TitlePanel();
topPanel.add(helpPanel, constraints);
numRows++;
}
// file chooser to the south.
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.BOTH;
constraints.gridx = 0;
constraints.gridy = numRows;
constraints.weightx = 1.0;
constraints.weighty = 1.0;
// Start out pointing at the current directory.
File currentDirectory = new File(".");
fileChooser = new JFileChooser(currentDirectory) {
private static final long serialVersionUID = 2625522895016359504L;
@Override
public void approveSelection() {
returnValue = JFileChooser.APPROVE_OPTION;
super.approveSelection();
closeDialog(false);
}
@Override
public void cancelSelection() {
returnValue = JFileChooser.CANCEL_OPTION;
super.cancelSelection();
closeDialog(true);
}
};
// Provide a custom icon for .jar files.
fileChooser.setFileView(new FileView() {
@Override
public Icon getIcon(File f) {
if (f.getName().endsWith(".jar")) {
return JAR_ICON;
}
return null;
}
});
topPanel.add(fileChooser, constraints);
numRows++;
}
// Handle window events.
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
returnValue = JFileChooser.CANCEL_OPTION;
closeDialog(true);
}
});
}
/**
* Show the dialog.
*
* @param currentFile
* If non-null the file chooser will be specified as "Change"ing
* the given import. Otherwise, it will be set up as adding a new
* import file.
* @param chooseDir
* if true, this the chooser is to select a directory. If false,
* it's so select a file.
* @return the return state of the file chooser on popdown:
* <ul>
* <li>JFileChooser.CANCEL_OPTION
* <li>JFileChooser.APPROVE_OPTION
* <li>JFileCHooser.ERROR_OPTION if an error occurs or the
* dialog is dismissed
* </ul>
*/
public int showDialog(File currentFile, boolean chooseDir) {
returnValue = JFileChooser.ERROR_OPTION;
// Customize the existing file chooser.
{
// Set the current directory if provided by currentFile.
if (currentFile != null) {
// Change the file chooser's current directory if a file was passed in.
File currentDirectory = currentFile.isDirectory() ? currentFile : currentFile.getParentFile();
fileChooser.setCurrentDirectory(currentDirectory);
}
if (chooseDir) {
// Set the file selection mode.
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
} else {
// Set the file selection mode.
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
// Set a custom file filter.
fileChooser.setFileFilter(new ExtensionFileFilter("jar", GeneratorMessages.getString("JFIMF_Chooser_ImportJarFilterDescription")));
}
// Customize the add button text.
String buttonText = GeneratorMessages.getString(
chooseDir ? (currentFile == null ? "JFIMF_Chooser_AddImportFolder" : "JFIMF_Chooser_ChangeImportFolder") :
(currentFile == null ? "JFIMF_Chooser_AddImportJar" : "JFIMF_Chooser_ChangeImportJar")
);
fileChooser.setApproveButtonText(buttonText);
fileChooser.setDialogType(JFileChooser.CUSTOM_DIALOG);
}
// Set the dialog description and title.
String title = fileChooser.getUI().getDialogTitle(fileChooser);
getAccessibleContext().setAccessibleDescription(title);
setTitle(title);
// Customize the help panel
{
String titleText;
String subtitleText;
if (chooseDir) {
titleText = GeneratorMessages.getString(currentFile == null ? "JFIMF_ChooserHelp_AddFolderTitle" : "JFIMF_ChooserHelp_ChangeFolderTitle");
subtitleText = GeneratorMessages.getString("JFIMF_ChooserHelp_ChooseFolderSubtitle");
} else {
titleText = GeneratorMessages.getString(currentFile == null ? "JFIMF_ChooserHelp_AddJarTitle" : "JFIMF_ChooserHelp_ChangeJarTitle");
subtitleText = GeneratorMessages.getString("JFIMF_ChooserHelp_ChooseJarSubtitle");
}
helpPanel.setTitleText(titleText);
helpPanel.setSubtitleText(subtitleText);
}
// Decorate like a file chooser dialog.
if (JDialog.isDefaultLookAndFeelDecorated() && UIManager.getLookAndFeel().getSupportsWindowDecorations()) {
getRootPane().setWindowDecorationStyle(JRootPane.FILE_CHOOSER_DIALOG);
}
pack();
sizeConstrainer.setMinimumSize(getSize());
doModal();
return returnValue;
}
/**
* @return the file selected in the embedded file chooser.
*/
public File getSelectedFile() {
return fileChooser.getSelectedFile();
}
}
/**
* The dialog to enter a pattern to add.
*
* @author Edward Lam
*/
private static class AddEditPatternDialog extends DialogBase {
private static final long serialVersionUID = 3566552733651814629L;
/** The radio button for selecting an include pattern. */
private final JRadioButton includeButton = new JRadioButton(GeneratorMessages.getString("JFIMF_Include"));
/** The radio button for selecting an exclude pattern. */
private final JRadioButton excludeButton = new JRadioButton(GeneratorMessages.getString("JFIMF_Exclude"));
/** The button group for the radio buttons. */
private final ButtonGroup buttonGroup = new ButtonGroup();
/** The text field for entering the pattern. */
private final JTextField patternField = new JTextField();
/** The OK button for the dialog. */
private JButton okButton = null;
/** The cancel button for the dialog. */
private JButton cancelButton = null;
/** The help panel at the top of the dialog. */
private final TitlePanel helpPanel;
/**
* Constructor for an AddEditPatternDialog.
* @param owner the owner dialog.
* @param selectedPattern the previously existing pattern, if any.
* If non-null, this is an edit dialog, and dialog will be initialized with this pattern.
* If null, this is an add dialog.
*/
private AddEditPatternDialog(Dialog owner, JFit.Pattern selectedPattern) {
super(owner, GeneratorMessages.getString(selectedPattern == null ? "JFIMF_AddPatternDialogTitle" : "JFIMF_EditPatternDialogTitle"));
// main panel
JPanel topPanel = getTopPanel();
topPanel.setBorder(null); // cancel the border set by superclass..
setContentPane(topPanel);
// Keep track of the number of rows.
int numRows = 0;
int iconTextGap = includeButton.getIconTextGap();
// Add the help panel.
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = numRows;
this.helpPanel = new TitlePanel();
helpPanel.setTitleText(getTitle());
helpPanel.setSubtitleText(GeneratorMessages.getString("JFIMF_AddPatternDialogSubtitle"));
topPanel.add(helpPanel, constraints);
numRows++;
}
// Add the radio panel
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = numRows;
// Set up the buttons in a button group.
buttonGroup.add(includeButton);
buttonGroup.add(excludeButton);
if (selectedPattern != null) {
if (selectedPattern.isInclude()) {
buttonGroup.setSelected(includeButton.getModel(), true);
} else {
buttonGroup.setSelected(excludeButton.getModel(), true);
}
} else {
buttonGroup.setSelected(includeButton.getModel(), true);
}
// Create the panel with the two radio buttons and some glue..
JPanel radioPanel = new JPanel();
radioPanel.setBorder(BorderFactory.createEmptyBorder(5, 5 - iconTextGap, 5, 5));
radioPanel.setLayout(new BoxLayout(radioPanel, BoxLayout.X_AXIS));
radioPanel.add(includeButton);
radioPanel.add(Box.createHorizontalStrut(5));
radioPanel.add(excludeButton);
radioPanel.add(Box.createHorizontalGlue());
topPanel.add(radioPanel, constraints);
numRows++;
}
// Add the Pattern entry area
{
GridBagConstraints constraints = new GridBagConstraints ();
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.insets = new Insets(0, 0, 0, 5);
constraints.gridx = 0;
constraints.gridy = numRows;
constraints.gridwidth = GridBagConstraints.REMAINDER;
JPanel patternLabelAndFieldPanel = new JPanel();
patternLabelAndFieldPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 0));
patternLabelAndFieldPanel.setLayout(new BoxLayout(patternLabelAndFieldPanel, BoxLayout.X_AXIS));
patternLabelAndFieldPanel.add(new JLabel(GeneratorMessages.getString("JFIMF_PatternHeader")));
patternLabelAndFieldPanel.add(Box.createHorizontalStrut(15));
patternField.setColumns(40);
if (selectedPattern != null) {
patternField.setText(selectedPattern.getPattern());
}
patternLabelAndFieldPanel.add(patternField);
patternLabelAndFieldPanel.add(Box.createHorizontalGlue());
topPanel.add(patternLabelAndFieldPanel, constraints);
numRows++;
}
// Add the label for the wildcard explanation.
{
GridBagConstraints constraints = new GridBagConstraints ();
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.insets = new Insets(0, 5, 10, 5);
constraints.gridx = 0;
constraints.gridy = numRows;
constraints.gridwidth = GridBagConstraints.REMAINDER;
JLabel wildcardExplanationLabel = new JLabel(GeneratorMessages.getString("JFIMF_WildcardExplanation"));
topPanel.add(wildcardExplanationLabel, constraints);
numRows++;
}
// Add the ok/cancel button area.
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.gridy = numRows;
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.anchor = GridBagConstraints.SOUTHEAST;
Box buttonBox = new Box (BoxLayout.X_AXIS);
buttonBox.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 5));
this.okButton = makeOKButton();
this.cancelButton = makeCancelButton();
getRootPane().setDefaultButton(okButton);
buttonBox.add (Box.createHorizontalGlue());
buttonBox.add (okButton);
buttonBox.add (Box.createHorizontalStrut(5));
buttonBox.add (cancelButton);
topPanel.add(buttonBox, constraints);
numRows++;
}
// Make the pattern field have default focus
setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() {
private static final long serialVersionUID = 2187420438565327738L;
@Override
public Component getDefaultComponent(Container c) {
return patternField;
}
});
// Esc cancels the dialog.
KeyListener dismissKeyListener = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
dispose();
e.consume();
}
}
};
// The ok button is only enabled if there is a pattern entered.
okButton.setEnabled(false);
patternField.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
okButton.setEnabled(patternField.getText().length() > 0);
}
});
// Cancel the dialog if the user presses ESC
addKeyListener(dismissKeyListener);
patternField.addKeyListener(dismissKeyListener);
okButton.addKeyListener(dismissKeyListener);
cancelButton.addKeyListener(dismissKeyListener);
this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
pack();
// Not resizeable.
setResizable(false);
// position relative to the owner frame.
setLocationRelativeTo(owner);
}
/**
* @return the JFit.Pattern entered by the user.
* Note: this will return a non-null result even if the dialog was cancelled.
*/
JFit.Pattern getFitPattern() {
return new JFit.Pattern(patternField.getText().trim(), includeButton.isSelected());
}
}
/**
* A panel displaying some radio buttons allowing the user to select a generation scope.
* @author Edward Lam
*/
private static class ScopeSelectorPanel extends JPanel {
private static final long serialVersionUID = -5400480868412313409L;
private final JRadioButton allPrivateButton = new JRadioButton(GeneratorMessages.getString("JFIMF_ScopeAllPrivate"));
private final JRadioButton partialPublicButton = new JRadioButton(GeneratorMessages.getString("JFIMF_ScopePartialPublic"));
private final JRadioButton allPublicButton = new JRadioButton(GeneratorMessages.getString("JFIMF_ScopeAllPublic"));
ScopeSelectorPanel() {
// Set up the buttons in a button group.
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(allPrivateButton);
buttonGroup.add(partialPublicButton);
buttonGroup.add(allPublicButton);
// Default to all private.
buttonGroup.setSelected(allPrivateButton.getModel(), true);
// Set the layout.
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
// Add the buttons.
this.add(allPrivateButton);
this.add(partialPublicButton);
this.add(allPublicButton);
}
/**
* @return the selected scope.
*/
ForeignImportGenerator.GenerationScope getSelectedScope() {
if (allPrivateButton.isSelected()) {
return ForeignImportGenerator.GenerationScope.ALL_PRIVATE;
}
if (partialPublicButton.isSelected()) {
return ForeignImportGenerator.GenerationScope.PARTIAL_PUBLIC;
}
if (allPublicButton.isSelected()) {
return ForeignImportGenerator.GenerationScope.ALL_PUBLIC;
}
return null;
}
}
/**
* A panel allowing the user to indicate methods to exclude.
* @author Edward Lam
*/
private static class MethodExcludeSelectorPanel extends JPanel {
private static final long serialVersionUID = 6319968816481922026L;
private final JCheckBox equalsHashCodeButton = new JCheckBox(GeneratorMessages.getString("JFIMF_OptionalEqualsHashCode"));
private final JCheckBox waitNotifyButton = new JCheckBox(GeneratorMessages.getString("JFIMF_OptionalWaitNotify"));
private final JCheckBox getClassButton = new JCheckBox(GeneratorMessages.getString("JFIMF_OptionalGetClass"));
MethodExcludeSelectorPanel() {
// Put the check boxes in a column in a panel
// Set the layout.
this.setLayout(new GridLayout(0, 1));
// Add the check boxes
add(equalsHashCodeButton);
add(waitNotifyButton);
add(getClassButton);
}
String[] getExcludeMethods() {
List<String> excludeMethodList = new ArrayList<String>();
if (!equalsHashCodeButton.isSelected()) {
excludeMethodList.add("java.lang.Object.equals");
excludeMethodList.add("java.lang.Object.hashCode");
}
if (!waitNotifyButton.isSelected()) {
excludeMethodList.add("java.lang.Object.wait");
excludeMethodList.add("java.lang.Object.notify");
excludeMethodList.add("java.lang.Object.notifyAll");
}
if (!getClassButton.isSelected()) {
excludeMethodList.add("java.lang.Object.getClass");
}
return excludeMethodList.toArray(new String[excludeMethodList.size()]);
}
}
/**
* Constructor for a new generator ui.
* Use a factory method to get an instance.
*
* @param owner the owner of the dialog
* @param dialogTitle if null, the default title will be used
* @param perspective the perspective the UI should use
*/
private JavaForeignImportModuleGenerator(Frame owner, String dialogTitle, Perspective perspective) {
super(owner, dialogTitle);
// (Java bug workaround) ensure JFileChooser will load..
ensureJFileChooserLoadable();
if (perspective == null) {
throw new NullPointerException();
}
this.perspective = perspective;
if (dialogTitle == null) {
setTitle(GeneratorMessages.getString("JFIMF_JavaForeignImportTitle"));
}
// main panel
JPanel topPanel = getTopPanel();
topPanel.setBorder(null); // cancel the border set by superclass..
setContentPane(topPanel);
// Keep track of the number of rows.
int numRows = 0;
// Add the title panel - the white panel at the top of the dialog.
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.NORTH;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = numRows;
TitlePanel titlePanel = new TitlePanel(true);
titlePanel.setTitleText(GeneratorMessages.getString("JFIMF_JavaForeignImportTitle"));
titlePanel.setSubtitleText(GeneratorMessages.getString("JFIMF_JavaForeignImportSubTitle"));
topPanel.add(titlePanel, constraints);
numRows++;
}
// Add the main panel
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.CENTER;
constraints.fill = GridBagConstraints.BOTH;
constraints.gridx = 0;
constraints.gridy = numRows;
constraints.weightx = 1;
constraints.weighty = 1;
topPanel.add(getMainPanel(), constraints);
numRows++;
}
// Add a separator.
{
addSeparator(topPanel, null, numRows, new Insets(10, 0, 5, 0));
numRows++;
}
// Add the button panel.
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.SOUTH;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = numRows;
this.okButton = makeOKButton();
this.cancelButton = makeCancelButton();
JPanel buttonPanel = new JPanel();
buttonPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
buttonPanel.add(Box.createHorizontalGlue());
buttonPanel.add(okButton);
buttonPanel.add(Box.createHorizontalStrut(5));
buttonPanel.add(cancelButton);
topPanel.add(buttonPanel, constraints);
numRows++;
}
getRootPane().setDefaultButton(okButton);
// Add default import source(s).
addDefaultImports();
// Update the state of the dialog.
updateState();
// Add listeners to update the error message if things change
moduleNameField.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
updateState();
}
});
importFromJList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
updateState();
}
});
patternsJList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
updateState();
}
});
// Esc cancels the dialog.
KeyListener dismissKeyListener = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
dispose();
e.consume();
}
}
};
// Cancel the dialog if the user presses ESC
addKeyListener(dismissKeyListener);
moduleNameField.addKeyListener(dismissKeyListener);
okButton.addKeyListener(dismissKeyListener);
cancelButton.addKeyListener(dismissKeyListener);
this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
pack();
setSize(600, getSize().height);
addComponentListener(new SizeConstrainer(getSize()));
// position relative to the owner frame.
setLocationRelativeTo(owner);
}
/**
* @return the generated module definition.
* Note: this method may display a modal dialog if problems were encountered.
*/
public GemGenerator.GeneratedDefinitions getSourceDefinitions() {
// Create a logger. Its handler will only handle warnings and errors.
Logger generatorLogger = Logger.getLogger(getClass().getPackage().getName());
generatorLogger.setLevel(Level.FINEST);
generatorLogger.setUseParentHandlers(false);
StatusHandler statusHandler = new StatusHandler();
// Only interested in warnings and errors.
statusHandler.setLevel(Level.WARNING);
generatorLogger.addHandler(statusHandler);
// Get the module name.
String moduleName = moduleNameField.getText();
// Get the patterns.
JFit.Pattern[] patterns = getPatterns();
// Get the selected generation scope
ForeignImportGenerator.GenerationScope generationScope = scopeSelectorPanel.getSelectedScope();
// Get the exclude methods.
String[] excludeMethods = methodExcludeSelectorPanel.getExcludeMethods();
//
// Get the input files and dirs.
//
List<File> inputFileList = new ArrayList<File>();
List<File> inputDirList = new ArrayList<File>();
for (Iterator<ImportSource> it = importFromJList.getListModel().iterator(); it.hasNext(); ) {
ImportSource importSource = it.next();
if (importSource.isDir()) {
inputDirList.add(importSource.getImportFile());
} else {
inputFileList.add(importSource.getImportFile());
}
}
File[] inputFiles = inputFileList.toArray(new File[inputFileList.size()]);
File[] inputDirs = inputDirList.toArray(new File[inputDirList.size()]);
// Instantiate the fit options.
JFit.Options options = JFit.Options.makeOptions(moduleName, inputFiles, inputDirs, null, patterns, generationScope, excludeMethods, generatorLogger);
// Create the fitter and fit.
JFit jfit = new JFit(options, perspective.getWorkspace(), generatorLogger);
final ModuleDefn defn = jfit.autoFit(); // null if an error occurred.
// Deal with warnings/errors.
Status capturedStatus = statusHandler.getStatus();
if (capturedStatus.getSeverity().compareTo(Status.Severity.WARNING) >= 0) {
String title = GeneratorMessages.getString("JFIMF_JavaForeignImportTitle");
String message = GeneratorMessages.getString("JFIMF_ProblemsGeneratingModule");
String details = capturedStatus.getDebugMessage();
DetailsDialog.MessageType messageType = capturedStatus.getSeverity() == Status.Severity.WARNING ?
DetailsDialog.MessageType.WARNING : DetailsDialog.MessageType.ERROR;
DetailsDialog dialog = new DetailsDialog(this, title, message, details, messageType);
dialog.doModal();
}
return new GemGenerator.GeneratedDefinitions() {
public ModuleDefn getModuleDefn() {
return defn;
}
public Map<String, String> getSourceElementMap() {
return null;
}
};
}
/**
* @return the patterns currently entered in the pattern list.
*/
private JFit.Pattern[] getPatterns() {
SortedListModel<Object> listModel = patternsJList.getListModel();
List<JFit.Pattern> patternList = new ArrayList<JFit.Pattern>(listModel.getSize());
for (Iterator<Object> it = listModel.iterator(); it.hasNext(); ) {
Object nextElem = it.next();
if (nextElem instanceof JFit.Pattern) {
patternList.add((JFit.Pattern)nextElem);
}
}
return patternList.toArray(new JFit.Pattern[patternList.size()]);
}
/**
* @return the main panel that shows the contents of the dialog
*/
private JPanel getMainPanel() {
JPanel mainPanel = new JPanel();
mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
mainPanel.setLayout(new GridBagLayout());
// Keep track of the number of rows.
int numRows = 0;
// Status label.
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.NORTHWEST;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = numRows;
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.insets = new Insets(5, 5, 10, 5);
mainPanel.add(statusLabel, constraints);
numRows++;
Font font = getFont();
if (font == null) {
font = UIManager.getFont("Label.font");
}
if (font != null) {
statusLabel.setFont(font.deriveFont(Font.BOLD));
}
}
// Module name entry
{
GridBagConstraints constraints = new GridBagConstraints ();
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.insets = new Insets(0, 0, 15, 5);
constraints.gridx = 0;
constraints.gridy = numRows;
constraints.gridwidth = GridBagConstraints.REMAINDER;
JPanel moduleLabelAndFieldPanel = new JPanel();
moduleLabelAndFieldPanel.setLayout(new BoxLayout(moduleLabelAndFieldPanel, BoxLayout.X_AXIS));
moduleLabelAndFieldPanel.add(new JLabel(GeneratorMessages.getString("JFIMF_ModuleNameHeader")));
moduleLabelAndFieldPanel.add(Box.createHorizontalStrut(15));
moduleNameField.setColumns(20);
moduleLabelAndFieldPanel.add(moduleNameField);
moduleLabelAndFieldPanel.add(Box.createHorizontalGlue());
mainPanel.add(moduleLabelAndFieldPanel, constraints);
numRows++;
// Make the module field have default focus
setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() {
private static final long serialVersionUID = -8536707255639997877L;
@Override
public Component getDefaultComponent(Container c) {
return moduleNameField;
}
});
}
// Import From Message.
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.NORTHWEST;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = numRows;
constraints.gridwidth = GridBagConstraints.REMAINDER;
JLabel messageLabel = new JLabel(GeneratorMessages.getString("JFIMF_ImportFromHeader"));
JPanel messagePanel = new JPanel();
messagePanel.setLayout(new BoxLayout(messagePanel, BoxLayout.X_AXIS));
messagePanel.add(messageLabel);
messagePanel.add(Box.createHorizontalStrut(200));
messagePanel.add(Box.createHorizontalGlue());
mainPanel.add(messagePanel, constraints);
numRows++;
}
{
// The buttons..
final JButton addJarButton;
final JButton addFolderButton;
final JButton editImportButton;
final JButton removeImportButton;
// Import From List
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.NORTHWEST;
constraints.fill = GridBagConstraints.BOTH;
constraints.gridx = 0;
constraints.gridy = numRows;
constraints.gridheight = 4; // the number of buttons.
constraints.weightx = 1;
constraints.weighty = 1;
constraints.gridwidth = GridBagConstraints.RELATIVE;
constraints.insets = new Insets(5, 15, 10, 5);
// A mutable list model. Only one item selectable.
importFromJList = new ImportFromJList();
JScrollPane importFromScrollPane = new JScrollPane(importFromJList);
importFromScrollPane.setMinimumSize(new Dimension(1000, 1));
mainPanel.add(importFromScrollPane, constraints);
// Do not increment numRows.
}
// Import button area
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.NORTH;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 2;
constraints.gridwidth = 1;
// It would have been nice to create a Box, but this doesn't make buttons the same size.
// A GridLayout makes all cells the same size, which doesn't allow for the extra space between the 2nd and 3rd buttons.
// So keep using the grid bag layout. This also allows us to easily make the buttons here the same
// size as those in the patterns aprea.
{
addJarButton = new JButton(getAddImportJarAction());
constraints.gridy = numRows;
constraints.insets = new Insets(5, 5, 5, 5);
mainPanel.add(addJarButton, constraints);
numRows++;
addFolderButton = new JButton(getAddImportFolderAction());
constraints.gridy = numRows;
constraints.insets = new Insets(0, 5, 10, 5);
mainPanel.add(addFolderButton, constraints);
numRows++;
editImportButton = new JButton(getChangeImportAction());
constraints.gridy = numRows;
constraints.insets = new Insets(0, 5, 5, 5);
mainPanel.add(editImportButton, constraints);
numRows++;
removeImportButton = new JButton(getRemoveImportAction());
constraints.gridy = numRows;
constraints.insets = new Insets(0, 5, 5, 5);
mainPanel.add(removeImportButton, constraints);
numRows++;
}
}
//
// Configure the buttons.
//
// Edit and remove start out disabled.
editImportButton.setEnabled(false);
removeImportButton.setEnabled(false);
// When the list selection changes, change enabling of the edit and remove buttons.
importFromJList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
boolean emptySelection = importFromJList.getSelectionModel().isSelectionEmpty();
editImportButton.setEnabled(!emptySelection);
removeImportButton.setEnabled(!emptySelection);
}
});
}
// Patterns Message.
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.NORTHWEST;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = numRows;
constraints.gridwidth = GridBagConstraints.REMAINDER;
JLabel messageLabel = new JLabel(GeneratorMessages.getString("JFIMF_PatternsHeader"));
JPanel messagePanel = new JPanel();
messagePanel.setLayout(new BoxLayout(messagePanel, BoxLayout.X_AXIS));
messagePanel.add(messageLabel);
messagePanel.add(Box.createHorizontalStrut(200));
messagePanel.add(Box.createHorizontalGlue());
mainPanel.add(messagePanel, constraints);
numRows++;
}
{
// The buttons.
final JButton addPatternButton;
final JButton editPatternButton;
final JButton removePatternButton;
// Patterns List.
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.NORTHWEST;
constraints.fill = GridBagConstraints.BOTH;
constraints.gridx = 0;
constraints.gridy = numRows;
constraints.gridheight = 3; // the number of buttons.
constraints.weightx = 1;
constraints.weighty = 1;
constraints.gridwidth = 1;
constraints.insets = new Insets(5, 15, 15, 5);
patternsJList = new PatternJList();
JScrollPane patternsScrollPane = new JScrollPane(patternsJList);
patternsScrollPane.setMinimumSize(new Dimension(1000, 1));
mainPanel.add(patternsScrollPane, constraints);
// Do not increment numRows.
}
// Patterns button area
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.NORTH;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 2;
constraints.gridwidth = 1;
// It would have been nice to create a Box, but this doesn't make buttons the same size.
// A GridLayout makes all cells the same size, which doesn't allow for the extra space between the 2nd and 3rd buttons.
// So keep using the grid bag layout. This also allows us to easily make the buttons here the same
// size as those in the patterns area.
{
addPatternButton = new JButton(getAddPatternAction());
constraints.gridy = numRows;
constraints.insets = new Insets(5, 5, 10, 5);
mainPanel.add(addPatternButton, constraints);
numRows++;
editPatternButton = new JButton(getEditPatternAction());
constraints.gridy = numRows;
constraints.insets = new Insets(0, 5, 5, 5);
mainPanel.add(editPatternButton, constraints);
numRows++;
removePatternButton = new JButton(getRemovePatternAction());
constraints.gridy = numRows;
constraints.insets = new Insets(0, 5, 5, 5);
mainPanel.add(removePatternButton, constraints);
numRows++;
}
}
//
// Configure the buttons.
//
// Edit and remove start out disabled.
editPatternButton.setEnabled(false);
removePatternButton.setEnabled(false);
// When the list selection changes, change enabling of the edit and remove buttons.
patternsJList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
boolean emptyPatternSelection = patternsJList.getSelectedPattern() == null;
editPatternButton.setEnabled(!emptyPatternSelection);
removePatternButton.setEnabled(!emptyPatternSelection);
}
});
}
// Scope Label.
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.NORTHWEST;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = numRows;
constraints.gridwidth = GridBagConstraints.REMAINDER;
JLabel messageLabel = new JLabel(GeneratorMessages.getString("JFIMF_ScopeHeader"));
JPanel messagePanel = new JPanel();
messagePanel.setLayout(new BoxLayout(messagePanel, BoxLayout.X_AXIS));
messagePanel.add(messageLabel);
messagePanel.add(Box.createHorizontalStrut(200));
messagePanel.add(Box.createHorizontalGlue());
mainPanel.add(messagePanel, constraints);
numRows++;
}
// Scope selection
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = numRows;
constraints.insets = new Insets(0, 15, 0, 0);
scopeSelectorPanel = new ScopeSelectorPanel();
mainPanel.add(scopeSelectorPanel, constraints);
numRows++;
}
// Object optional methods Label.
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.NORTHWEST;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = numRows;
constraints.gridwidth = GridBagConstraints.REMAINDER;
constraints.insets = new Insets(10, 0, 0, 0);
JLabel messageLabel = new JLabel(GeneratorMessages.getString("JFIMF_OptionalObjectMethodsHeader"));
JPanel messagePanel = new JPanel();
messagePanel.setLayout(new BoxLayout(messagePanel, BoxLayout.X_AXIS));
messagePanel.add(messageLabel);
messagePanel.add(Box.createHorizontalStrut(200));
messagePanel.add(Box.createHorizontalGlue());
mainPanel.add(messagePanel, constraints);
numRows++;
}
// Check boxes for optional Object methods.
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = numRows;
constraints.insets = new Insets(0, 15, 0, 0);
methodExcludeSelectorPanel = new MethodExcludeSelectorPanel();
mainPanel.add(methodExcludeSelectorPanel, constraints);
numRows++;
}
return mainPanel;
}
/**
* @return the action to return in response to pressing the "Add Pattern..." button.
*/
private Action getAddPatternAction() {
String buttonText = GeneratorMessages.getString("JFIMF_AddPattern");
return new AbstractAction(buttonText) {
private static final long serialVersionUID = 8776068822378523687L;
// Add an element.
public void actionPerformed(ActionEvent e) {
// Show the pattern dialog.
AddEditPatternDialog addPatternDialog = new AddEditPatternDialog(JavaForeignImportModuleGenerator.this, null);
boolean accepted = addPatternDialog.doModal();
// Add the pattern if accepted.
if (accepted) {
patternsJList.addPattern(addPatternDialog.getFitPattern());
}
}
};
}
/**
* Workaround for sun bug id 4711700
* A bug in the native image loading code sometimes causes the Windows file chooser to throw a
* NullPointerException when instantiated in jdk 1.4.2.
*/
private static void ensureJFileChooserLoadable() {
if (UIManager.getLookAndFeel().getName().startsWith("Windows")) {
JFileChooser fileChooser = null;
while (fileChooser == null) {
try {
fileChooser = new JFileChooser();
} catch (NullPointerException e) {
}
}
}
}
/**
* Display the File chooser for selecting an import source.
*
* @param currentFile
* If non-null the file chooser will be specified as "Change"ing
* the given import. Otherwise, it will be set up as adding a new
* import file.
* @param chooseDir
* if true, this the chooser is to select a directory. If false,
* it's so select a file.
* @return the File selected by the file chooser, or null if the chooser was
* not approved (eg. canceled).
*/
private File showSelectImportFileChooser(File currentFile, boolean chooseDir) {
if (selectImportDialog == null) {
selectImportDialog = new SelectImportDialog(JavaForeignImportModuleGenerator.this);
}
int result = selectImportDialog.showDialog(currentFile, chooseDir);
// Return the result..
if (result == JFileChooser.APPROVE_OPTION) {
return selectImportDialog.getSelectedFile();
}
return null;
}
/**
* @return the action to return in response to pressing the "Add Jar..." button.
*/
private Action getAddImportJarAction() {
String buttonText = GeneratorMessages.getString("JFIMF_AddImportJar");
return new AbstractAction(buttonText) {
private static final long serialVersionUID = -1327110975197857640L;
public void actionPerformed(ActionEvent e) {
File chosenFile = showSelectImportFileChooser(null, false);
if (chosenFile != null) {
ImportSource importSource = new ImportSource(chosenFile, false);
importFromJList.addImport(importSource);
}
}
};
}
/**
* @return the action to return in response to pressing the "Add Folder..." button.
*/
private Action getAddImportFolderAction() {
String buttonText = GeneratorMessages.getString("JFIMF_AddImportFolder");
return new AbstractAction(buttonText) {
private static final long serialVersionUID = -7399609649158755721L;
public void actionPerformed(ActionEvent e) {
File chosenFile = showSelectImportFileChooser(null, true);
if (chosenFile != null) {
ImportSource importSource = new ImportSource(chosenFile, true);
importFromJList.addImport(importSource);
}
}
};
}
/**
* @return the action to return in response to pressing the "Change..." button for imports.
*/
private Action getChangeImportAction() {
String buttonText = GeneratorMessages.getString("JFIMF_ChangeImport");
return new AbstractAction(buttonText) {
private static final long serialVersionUID = -2922337374842017884L;
public void actionPerformed(ActionEvent e) {
ImportSource selectedImportSource = (ImportSource)importFromJList.getSelectedValue();
if (selectedImportSource != null) {
File chosenFile = showSelectImportFileChooser(selectedImportSource.getImportFile(), selectedImportSource.isDir());
if (chosenFile != null) {
ImportSource newImportSource = new ImportSource(chosenFile, selectedImportSource.isDir());
importFromJList.editImport(selectedImportSource, newImportSource);
}
}
}
};
}
/**
* @return the action to return in response to pressing the "Remove" button for imports.
*/
private Action getRemoveImportAction() {
String buttonText = GeneratorMessages.getString("JFIMF_RemoveImport");
return new AbstractAction(buttonText) {
private static final long serialVersionUID = -6469392988956528085L;
public void actionPerformed(ActionEvent e) {
importFromJList.removeSelectedImport();
}
};
}
/**
* @return the action to return in response to pressing the "Edit..." button for patterns.
*/
private Action getEditPatternAction() {
String buttonText = GeneratorMessages.getString("JFIMF_EditPattern");
return new AbstractAction(buttonText) {
private static final long serialVersionUID = -527928622059171444L;
// Edit selected element.
public void actionPerformed(ActionEvent e) {
JFit.Pattern selectedPattern = patternsJList.getSelectedPattern();
// Show the pattern dialog.
AddEditPatternDialog editPatternDialog = new AddEditPatternDialog(JavaForeignImportModuleGenerator.this, selectedPattern);
boolean accepted = editPatternDialog.doModal();
// Edit the pattern if accepted.
if (accepted) {
patternsJList.editPattern(selectedPattern, editPatternDialog.getFitPattern());
}
}
};
}
/**
* @return the action to return in response to pressing the "Remove" button for patterns.
*/
private Action getRemovePatternAction() {
String buttonText = GeneratorMessages.getString("JFIMF_RemovePattern");
return new AbstractAction(buttonText) {
private static final long serialVersionUID = -1965662784577019107L;
public void actionPerformed(ActionEvent e) {
// Remove the selected pattern.
patternsJList.removeSelectedPattern();
}
};
}
/**
* Add default imports to the Import From area.
*/
private void addDefaultImports() {
// Add the runtime jar if it exists.
// (java.home)/lib/rt.jar
File javaRuntimeJar = new File(JAVA_HOME, "lib" + System.getProperty("file.separator") + "rt.jar");
if (FileSystemHelper.fileExists(javaRuntimeJar)) {
ImportSource importSource = new ImportSource(javaRuntimeJar, false);
importFromJList.addImport(importSource);
}
}
/**
* Updates the state of the Ok button to only be enabled if the user has entered
* all required information. Also updates the information message displayed.
*/
private void updateState() {
// Make sure that you check for errors first, then for warnings.
// Be careful about the order of checks, more important checks come first.
/*
* Errors.
*/
// Check for a valid module name.
String moduleNameString = moduleNameField.getText();
if (moduleNameString.length() == 0) {
statusLabel.setText(GeneratorMessages.getString("JFIMF_NoModuleName"));
statusLabel.setIcon(ERROR_ICON);
okButton.setEnabled(false);
return;
}
ModuleName moduleName = ModuleName.maybeMake(moduleNameString);
if (moduleName == null) {
statusLabel.setText(GeneratorMessages.getString("JFIMF_InvalidModuleName"));
statusLabel.setIcon(ERROR_ICON);
okButton.setEnabled(false);
return;
}
// Check for a location to import from.
if (importFromJList.getModel().getSize() < 1) {
String message = GeneratorMessages.getString("JFIMF_NoImportFrom");
statusLabel.setText(message);
statusLabel.setToolTipText(message);
statusLabel.setIcon(ERROR_ICON);
okButton.setEnabled(false);
return;
}
// Check that the patterns pane is non-empty.
if (patternsJList.isEmpty()) {
String message = GeneratorMessages.getString("JFIMF_NoPatternsMessage");
statusLabel.setText(message);
statusLabel.setToolTipText(message);
statusLabel.setIcon(ERROR_ICON);
okButton.setEnabled(false);
return;
}
// If here, no errors. Warnings at worst.
okButton.setEnabled(true);
/*
* Warnings.
*/
// Check whether the module name exists.
// This goes to the workspace source manager -- so it's a bit hacky.
// We can't just ask the workspace if it has the module though, since it might be the case where the workspace isn't
// using a module in the nullary case.
if (perspective.getWorkspace().getSourceManager(moduleName).getResourceStore().hasFeature(
new ResourceName(CALFeatureName.getModuleFeatureName(moduleName)))) {
String message = GeneratorMessages.getString("JFIMF_ModuleExists");
statusLabel.setText(message);
statusLabel.setToolTipText(message);
statusLabel.setIcon(WARNING_ICON);
return;
}
// everything is fine.
String message = GeneratorMessages.getString("JFIMF_OkGenerateModule");
statusLabel.setText(message);
statusLabel.setToolTipText(message);
statusLabel.setIcon(OK_ICON);
}
}