/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.studio.io.data.internal.file.csv;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.OverlayLayout;
import javax.swing.ScrollPaneConstants;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.TableModel;
import com.rapidminer.core.io.gui.InvalidConfigurationException;
import com.rapidminer.gui.look.Colors;
import com.rapidminer.gui.tools.CharTextField;
import com.rapidminer.gui.tools.ColoredTableCellRenderer;
import com.rapidminer.gui.tools.ExtendedJTable;
import com.rapidminer.gui.tools.ProgressThread;
import com.rapidminer.gui.tools.ResourceAction;
import com.rapidminer.gui.tools.RowNumberTable;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.gui.tools.UpdateQueue;
import com.rapidminer.gui.tools.bubble.BubbleWindow;
import com.rapidminer.gui.tools.bubble.BubbleWindow.AlignedSide;
import com.rapidminer.gui.tools.bubble.BubbleWindow.BubbleStyle;
import com.rapidminer.gui.tools.bubble.ComponentBubbleWindow;
import com.rapidminer.gui.tools.dialogs.ButtonDialog;
import com.rapidminer.operator.nio.ErrorTableModel;
import com.rapidminer.operator.nio.ImportWizardUtils;
import com.rapidminer.operator.nio.LoadingContentPane;
import com.rapidminer.operator.nio.model.CSVResultSet.ColumnSplitter;
import com.rapidminer.operator.nio.model.CSVResultSetConfiguration;
import com.rapidminer.studio.io.gui.internal.DataImportWizardUtils;
import com.rapidminer.studio.io.gui.internal.DataWizardEventType;
import com.rapidminer.studio.io.gui.internal.steps.configuration.CollapsibleErrorTable;
import com.rapidminer.tools.I18N;
import com.rapidminer.tools.LineParser;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.io.Encoding;
/**
* Panel used to specify the {@link CSVResultSetConfiguration} in the
* {@link CSVFormatSpecificationWizardStep}.
*
* @author Sebastian Loh, Simon Fischer, Gisa Schaefer
* @since 7.0.0
*/
public class CSVFormatSpecificationPanel extends JPanel {
private static final long serialVersionUID = -6249118015226310854L;
/** the icon for the error message */
private static final ImageIcon ERROR_MESSAGE_ICON = SwingTools
.createIcon(I18N.getGUILabel("csv_format_specification.empty_table.icon"));
/** the text for the error message when the table is empty */
private static final String ERROR_MESSAGE_TEXT = I18N.getGUILabel("csv_format_specification.empty_table.label");
/** normal font for the error message */
private static final Font ERROR_MESSAGE_FONT = new JLabel().getFont();
/** bigger font for the preview lettering */
private static final Font PREVIW_LETTERING_FONT = ERROR_MESSAGE_FONT.deriveFont(Font.BOLD, 180);
/** the preview lettering text */
private static final String PREVIW_LETTERING = I18N.getGUILabel("csv_format_specification.preview_background");
/** default decimal character for numbers */
private static final char DEFAULT_DECIMAL_CHARACTER = '.';
/**
* Enum for the entries of the separationComboBox which can be used to set the separator of the
* csv file.
*/
private enum ColumnSeparator {
COMMA("csv_format_specification.character_comma", LineParser.SPLIT_BY_COMMA_EXPRESSION),
SEMICOLON("csv_format_specification.character_semicolon", LineParser.SPLIT_BY_SEMICOLON_EXPRESSION),
SPACE("csv_format_specification.character_space", LineParser.SPLIT_BY_SPACE_EXPRESSION),
TAB("csv_format_specification.character_tab", LineParser.SPLIT_BY_TAB_EXPRESSION),
REGULAR_EXPRESSION("csv_format_specification.regular_expression", LineParser.DEFAULT_SPLIT_EXPRESSION);
private String text;
private String separator;
private ColumnSeparator(String i18nForText, String separator) {
text = I18N.getGUILabel(i18nForText);
this.separator = separator;
}
public String getSeparator() {
return separator;
}
@Override
public String toString() {
return text;
}
}
private List<ChangeListener> changeListeners = new LinkedList<>();
private final JCheckBox trimLinesBox = new JCheckBox(I18N.getGUILabel("csv_format_specification.trim_lines"), true);
private final JComboBox<String> encodingComboBox = new JComboBox<String>(Encoding.CHARSETS);
private final JCheckBox skipCommentsBox = new JCheckBox(I18N.getGUILabel("csv_format_specification.scip_comments"),
true);
private final JCheckBox headerRow = new JCheckBox(I18N.getGUILabel("csv_format_specification.header_row"), true);
private final JComboBox<ColumnSeparator> separationComboBox = new JComboBox<ColumnSeparator>(ColumnSeparator.values());
private final JCheckBox useQuotesBox = new JCheckBox(I18N.getGUILabel("csv_format_specification.use_quotes"), true);
private final JTextField commentCharacterTextField = new JTextField(LineParser.DEFAULT_COMMENT_CHARACTER_STRING);
private final CharTextField quoteCharacterTextField = new CharTextField(LineParser.DEFAULT_QUOTE_CHARACTER);
private final CharTextField escapeCharacterTextField = new CharTextField(LineParser.DEFAULT_QUOTE_ESCAPE_CHARACTER);
private final JTextField regexTextField = new JTextField(LineParser.DEFAULT_SPLIT_EXPRESSION);
private final JButton regexEvalButton = new JButton();
private final JSpinner startRowSpinner = new JSpinner(new SpinnerNumberModel(1, 1, null, 1));
private final JSpinner headerRowSpinner = new JSpinner(new SpinnerNumberModel(1, 1, null, 1));
private final CharTextField decimalCharacterTextField = new CharTextField();
private LoadingContentPane loadingContentPane;
private JLabel overlayLabel;
private final CSVResultSetConfiguration configuration;
private ExtendedJTable previewTable;
private JScrollPane tablePane;
private final ErrorTableModel errorTableModel = new ErrorTableModel();
private final CollapsibleErrorTable collapsibleErrorTable = new CollapsibleErrorTable(errorTableModel);
private BubbleWindow currentErrorWindow;
private boolean keepBubble = false;
public CSVFormatSpecificationPanel(CSVResultSetConfiguration csvConfiguration) {
this.configuration = csvConfiguration;
// configuration -> UI components
skipCommentsBox.setSelected(configuration.isSkipComments());
useQuotesBox.setSelected(configuration.isUseQuotes());
headerRow.setSelected(configuration.hasHeaderRow());
trimLinesBox.setSelected(configuration.isTrimLines());
commentCharacterTextField.setText(configuration.getCommentCharacters());
escapeCharacterTextField.setText(String.valueOf(configuration.getEscapeCharacter()));
quoteCharacterTextField.setText(String.valueOf(configuration.getQuoteCharacter()));
encodingComboBox.setSelectedItem(configuration.getEncoding().name());
// do not fire action event when using keyboard to move up and down
encodingComboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
startRowSpinner.setValue(configuration.getStartingRow() + 1);
headerRowSpinner.setValue(configuration.getHeaderRow() + 1);
decimalCharacterTextField.setText(String.valueOf(configuration.getDecimalCharacter()));
String sep = configuration.getColumnSeparators();
separationComboBox.setSelectedItem(ColumnSeparator.REGULAR_EXPRESSION);
if (sep.equals(LineParser.SPLIT_BY_COMMA_EXPRESSION)) {
separationComboBox.setSelectedItem(ColumnSeparator.COMMA);
} else if (sep.equals(LineParser.SPLIT_BY_SEMICOLON_EXPRESSION)) {
separationComboBox.setSelectedItem(ColumnSeparator.SEMICOLON);
} else if (sep.equals(LineParser.SPLIT_BY_TAB_EXPRESSION)) {
separationComboBox.setSelectedItem(ColumnSeparator.TAB);
} else if (sep.equals(LineParser.SPLIT_BY_SPACE_EXPRESSION)) {
separationComboBox.setSelectedItem(ColumnSeparator.SPACE);
}
// do not fire action event when using keyboard to move up and down
separationComboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
registerListeners();
makePanel();
}
private static UpdateQueue updateQueue;
void startDataFetching() {
updateQueue = new UpdateQueue("CSV-Preview-Fetcher");
updateQueue.start();
settingsChanged();
}
void stopDataFetching() {
if (updateQueue != null) {
updateQueue.shutdown();
updateQueue = null;
}
}
private void registerListeners() {
headerRow.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
DataImportWizardUtils.logStats(DataWizardEventType.CSV_HEADER_ROW_STATE,
Boolean.toString(headerRow.isSelected()));
configuration.setHasHeaderRow(headerRow.isSelected());
headerRowSpinner.setEnabled(headerRow.isSelected());
previewTable.repaint();
fireStateChanged();
}
});
encodingComboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
configuration.setEncoding(Encoding.getEncoding(encodingComboBox.getSelectedItem().toString()));
settingsChanged();
}
}
});
separationComboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
DataImportWizardUtils.logStats(DataWizardEventType.CSV_SEPARATOR_CHANGED,
configuration.getColumnSeparators() + "->" + getSplitExpression());
enableFields();
configuration.setColumnSeparators(getSplitExpression());
settingsChanged();
}
}
});
trimLinesBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
configuration.setTrimLines(trimLinesBox.isSelected());
settingsChanged();
}
});
skipCommentsBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
commentCharacterTextField.setEnabled(skipCommentsBox.isSelected());
configuration.setSkipComments(skipCommentsBox.isSelected());
settingsChanged();
}
});
useQuotesBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
quoteCharacterTextField.setEnabled(useQuotesBox.isSelected());
escapeCharacterTextField.setEnabled(useQuotesBox.isSelected());
configuration.setUseQuotes(useQuotesBox.isSelected());
settingsChanged();
}
});
quoteCharacterTextField.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if (!quoteCharacterTextField.getText().isEmpty()) {
configuration.setQuoteCharacter(quoteCharacterTextField.getText().charAt(0));
settingsChanged();
}
}
});
quoteCharacterTextField.addFocusListener(new FocusListener() {
@Override
public void focusLost(FocusEvent e) {
if (quoteCharacterTextField.getText().isEmpty()) {
quoteCharacterTextField.setCharacter(LineParser.DEFAULT_QUOTE_CHARACTER);
configuration.setQuoteCharacter(LineParser.DEFAULT_QUOTE_CHARACTER);
settingsChanged();
}
}
@Override
public void focusGained(FocusEvent e) {
// not needed
}
});
escapeCharacterTextField.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if (!escapeCharacterTextField.getText().isEmpty()) {
configuration.setEscapeCharacter(escapeCharacterTextField.getText().charAt(0));
settingsChanged();
}
}
});
escapeCharacterTextField.addFocusListener(new FocusListener() {
@Override
public void focusLost(FocusEvent e) {
if (escapeCharacterTextField.getText().isEmpty()) {
escapeCharacterTextField.setCharacter(LineParser.DEFAULT_QUOTE_ESCAPE_CHARACTER);
configuration.setEscapeCharacter(LineParser.DEFAULT_QUOTE_ESCAPE_CHARACTER);
settingsChanged();
}
}
@Override
public void focusGained(FocusEvent e) {
// not needed
}
});
commentCharacterTextField.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
configuration.setCommentCharacters(commentCharacterTextField.getText());
settingsChanged();
}
});
commentCharacterTextField.addFocusListener(new FocusListener() {
@Override
public void focusLost(FocusEvent e) {
if (commentCharacterTextField.getText().isEmpty()) {
commentCharacterTextField.setText(LineParser.DEFAULT_COMMENT_CHARACTER_STRING);
configuration.setCommentCharacters(LineParser.DEFAULT_COMMENT_CHARACTER_STRING);
settingsChanged();
}
}
@Override
public void focusGained(FocusEvent e) {
// not needed
}
});
decimalCharacterTextField.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if (!decimalCharacterTextField.getText().isEmpty()) {
configuration.setDecimalCharacter(decimalCharacterTextField.getText().charAt(0));
}
}
});
decimalCharacterTextField.addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
// not needed
}
@Override
public void focusLost(FocusEvent e) {
if (decimalCharacterTextField.getText().isEmpty()) {
decimalCharacterTextField.setCharacter(DEFAULT_DECIMAL_CHARACTER);
configuration.setDecimalCharacter(DEFAULT_DECIMAL_CHARACTER);
}
}
});
startRowSpinner.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
updateStartingRow();
fireStateChanged();
}
});
headerRowSpinner.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
updateHeaderRow();
fireStateChanged();
}
});
}
/**
* Creates a panel containing a settings panel at the top, a preview table in the middle and an
* error panel at the bottom.
*/
private void makePanel() {
this.setLayout(new BorderLayout(0, ButtonDialog.GAP));
this.add(makeSettingsPanel(), BorderLayout.NORTH);
this.add(makePreviewTable(), BorderLayout.CENTER);
this.add(collapsibleErrorTable, BorderLayout.SOUTH);
}
/**
* @return the panel containing all the file encoding options
*/
private JPanel makeSettingsPanel() {
// the left settings panel
JPanel leftPanel = new JPanel(ButtonDialog.createGridLayout(4, 2));
leftPanel.add(headerRow);
leftPanel.add(headerRowSpinner);
leftPanel.add(new JLabel(I18N.getGUILabel("csv_format_specification.start_row")));
leftPanel.add(startRowSpinner);
leftPanel.add(new JLabel(I18N.getGUILabel("csv_format_specification.column_separation")));
leftPanel.add(separationComboBox);
leftPanel.add(new JLabel());
leftPanel.add(makeRegexPanel());
leftPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
// the middle settings panel
JPanel middlePanel = new JPanel(ButtonDialog.createGridLayout(4, 2));
middlePanel.add(new JLabel(I18N.getGUILabel("csv_format_specification.file_encoding")));
middlePanel.add(encodingComboBox);
middlePanel.add(new JLabel(I18N.getGUILabel("csv_format_specification.escape_character")));
middlePanel.add(escapeCharacterTextField);
middlePanel.add(new JLabel(I18N.getGUILabel("csv_format_specification.decimal_character")));
middlePanel.add(decimalCharacterTextField);
middlePanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createMatteBorder(0, 1, 0, 1, SwingTools.brightenColor(Color.GRAY)),
BorderFactory.createEmptyBorder(0, 15, 0, 15)));
// the right settings panel
JPanel rightPanel = new JPanel(ButtonDialog.createGridLayout(4, 2));
rightPanel.add(useQuotesBox);
rightPanel.add(quoteCharacterTextField);
rightPanel.add(trimLinesBox);
rightPanel.add(new JLabel());
rightPanel.add(skipCommentsBox);
rightPanel.add(commentCharacterTextField);
rightPanel.setBorder(BorderFactory.createEmptyBorder(0, 7, 0, 0));
// add the three settings panel together
JPanel settingsPanel = new JPanel(ButtonDialog.createGridLayout(1, 3));
settingsPanel.add(leftPanel);
settingsPanel.add(middlePanel);
settingsPanel.add(rightPanel);
return settingsPanel;
}
/**
* Fills the tablePane with content.
*/
private JComponent makePreviewTable() {
previewTable = new ExtendedJTable(false, false, false);
// ensure same background as JPanels in case of only few rows
previewTable.setBackground(Colors.PANEL_BACKGROUND);
previewTable.setColoredTableCellRenderer(new ColoredTableCellRenderer() {
private final Font boldFont = getFont().deriveFont(Font.BOLD);
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
adjustCell(row, label, boldFont);
return label;
}
});
loadingContentPane = new LoadingContentPane("loading_data", previewTable);
tablePane = new JScrollPane(loadingContentPane);
tablePane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
tablePane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
tablePane.setBorder(null);
// add PREVIEW label in front of scrollpane
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.setLayout(new OverlayLayout(layeredPane));
layeredPane.add(tablePane, JLayeredPane.DEFAULT_LAYER);
JPanel overlayPanel = new JPanel(new BorderLayout());
overlayPanel.setOpaque(false);
overlayLabel = new JLabel("", SwingConstants.CENTER);
showPreviewLettering();
overlayPanel.add(overlayLabel, BorderLayout.CENTER);
layeredPane.add(overlayPanel, JLayeredPane.PALETTE_LAYER);
return layeredPane;
}
/**
* Shows the preview lettering.
*/
private void showPreviewLettering() {
overlayLabel.setText(PREVIW_LETTERING);
overlayLabel.setFont(PREVIW_LETTERING_FONT);
overlayLabel.setForeground(DataImportWizardUtils.getPreviewFontColor());
overlayLabel.setIcon(null);
}
/**
* Shows an error message about empty data.
*/
private void showEmptyMessage() {
overlayLabel.setText(ERROR_MESSAGE_TEXT);
overlayLabel.setFont(ERROR_MESSAGE_FONT);
overlayLabel.setForeground(Color.BLACK);
overlayLabel.setIcon(ERROR_MESSAGE_ICON);
}
/**
* @return a panel containing the regexTextField and the regexEvalButton
*/
private JPanel makeRegexPanel() {
JPanel regexPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.gridx = 0;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.gridx = GridBagConstraints.RELATIVE;
regexPanel.add(regexTextField, gbc);
gbc.fill = GridBagConstraints.NONE;
gbc.weightx = 0;
gbc.gridx = 1;
Action evalRegexAction = new ResourceAction(true, "import_regex_refresh") {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
configuration.setColumnSeparators(getSplitExpression());
settingsChanged();
}
};
regexEvalButton.setAction(evalRegexAction);
regexEvalButton.setVisible(false);
regexTextField.setVisible(false);
regexTextField.addFocusListener(new FocusListener() {
@Override
public void focusLost(FocusEvent e) {
if (!e.isTemporary()) {
configuration.setColumnSeparators(getSplitExpression());
settingsChanged();
}
}
@Override
public void focusGained(FocusEvent e) {
// nothing to do
}
});
regexPanel.add(regexEvalButton, gbc);
return regexPanel;
}
/**
* @return the split expression determined by the separationComboBox
*/
private String getSplitExpression() {
String splitExpression = null;
if (separationComboBox.getSelectedItem() != ColumnSeparator.REGULAR_EXPRESSION) {
splitExpression = ((ColumnSeparator) separationComboBox.getSelectedItem()).getSeparator();
} else {
splitExpression = regexTextField.getText();
if ("".equals(splitExpression)) {
splitExpression = null;
} else {
try {
Pattern.compile(splitExpression);
} catch (PatternSyntaxException pse) {
splitExpression = null;
}
}
}
return splitExpression;
}
/**
* Reloads the data with the new settings and adjusts the display accordingly.
*/
private void settingsChanged() {
ProgressThread loadingDataThread = new ProgressThread("loading_data") {
@Override
public void run() {
try {
final TableModel model = configuration.makePreviewTableModel(getProgressListener());
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
previewTable.setModel(model);
tablePane.setRowHeaderView(new RowNumberTable(previewTable));
if (model.getRowCount() > 0) {
showPreviewLettering();
} else {
showEmptyMessage();
}
updateErrorTable();
fireStateChanged();
}
});
} catch (Exception e) {
updateErrorTable();
ImportWizardUtils.showErrorMessage(configuration.getResourceName(), e.toString(), e);
}
}
};
loadingContentPane.init(loadingDataThread);
updateQueue.executeBackgroundJob(loadingDataThread);
}
/**
* Updates the content of the errorTable and the label that is used to show it.
*/
private void updateErrorTable() {
errorTableModel.setErrors(configuration.getErrors());
// make the first row smaller
collapsibleErrorTable.getTable().getColumnModel().getColumn(0).setMaxWidth(150);
collapsibleErrorTable.getTable().getColumnModel().getColumn(0).setPreferredWidth(100);
// make the last row wider
collapsibleErrorTable.getTable().getColumnModel().getColumn(3).setMaxWidth(800);
collapsibleErrorTable.getTable().getColumnModel().getColumn(3).setPreferredWidth(400);
SwingTools.invokeLater(new Runnable() {
@Override
public void run() {
collapsibleErrorTable.update();
}
});
}
/**
* Enables the text field and button associated to the Column Separator combobox item
* "Regular Expression".
*/
private void enableFields() {
regexTextField.setVisible(separationComboBox.getSelectedItem() == ColumnSeparator.REGULAR_EXPRESSION);
regexEvalButton.setVisible(separationComboBox.getSelectedItem() == ColumnSeparator.REGULAR_EXPRESSION);
}
/**
* Adjust how the cell is displayed. If it is a header cell it should be opaque with a dark
* background, otherwise it should be transparent to show the lettering in the background. If
* the row is the header the font is bold, if the row is smaller than the start row it is gray,
* otherwise black.
*/
private void adjustCell(int row, JLabel cell, Font boldFont) {
if (configuration.hasHeaderRow() && row == configuration.getHeaderRow()) {
cell.setBackground(Color.LIGHT_GRAY);
cell.setFont(boldFont);
cell.setForeground(Color.BLACK);
} else if (row < configuration.getStartingRow()) {
cell.setForeground(Color.LIGHT_GRAY);
} else {
cell.setForeground(Color.BLACK);
}
}
/**
* Sets the starting row as defined in the startingRowTextField and repaints the table.
*/
private void updateStartingRow() {
int startingRow = (int) startRowSpinner.getValue();
configuration.setStartingRow(startingRow - 1);
previewTable.repaint();
}
/**
* Sets the header row as defined in the headerRowSpinner and repaints the table.
*/
private void updateHeaderRow() {
int headerRow = (int) headerRowSpinner.getValue();
configuration.setHeaderRow(headerRow - 1);
previewTable.repaint();
}
/**
* Kills the current error bubble.
*/
void killCurrentErrorBubbleWindow() {
if (currentErrorWindow != null) {
currentErrorWindow.killBubble(true);
currentErrorWindow = null;
}
}
/**
* Creates an error bubble for the component and kills other error bubbles.
*
* @param component
* the component for which to show the bubble
* @param i18n
* the i18n key
* @param arguments
* arguments for the i18n
*/
private void createErrorBubbleWindow(JComponent component, String i18n, Object... arguments) {
killCurrentErrorBubbleWindow();
JButton okayButton = new JButton(I18N.getGUILabel("io.dataimport.step.excel.sheet_selection.got_it"));
final ComponentBubbleWindow errorWindow = new ComponentBubbleWindow(component, BubbleStyle.ERROR,
SwingUtilities.getWindowAncestor(this), AlignedSide.BOTTOM, i18n, null, null, false, true,
new JButton[] { okayButton }, arguments);
okayButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
errorWindow.killBubble(false);
}
});
// show and remember error window
errorWindow.setVisible(true);
currentErrorWindow = errorWindow;
};
/**
* Shows a "header row not found"-bubble
*/
void notifyHeaderRowNotFound() {
keepBubble = true;
createErrorBubbleWindow(headerRowSpinner, "io.dataimport.step.csv.format_specification.header_row_not_found");
}
/**
* Shows a "start row not found"-bubble
*/
void notifyStartRowNotFound() {
keepBubble = true;
createErrorBubbleWindow(startRowSpinner, "io.dataimport.step.csv.format_specification.start_row_not_found");
}
/**
* Shows a "header row behind start row"-bubble
*/
void notifyHeaderRowBehindStartRow() {
keepBubble = false;
createErrorBubbleWindow(headerRowSpinner, "io.dataimport.step.csv.format_specification.invalid_header_row",
configuration.getHeaderRow() + 1, configuration.getStartingRow() + 1);
}
/**
* Sets the column separator associated to the splitter.
*
* @param splitter
* a {@link ColumnSplitter}
*/
void setColumnSeparator(ColumnSplitter splitter) {
switch (splitter) {
case COMMA:
separationComboBox.setSelectedItem(ColumnSeparator.COMMA);
break;
case TAB:
separationComboBox.setSelectedItem(ColumnSeparator.TAB);
break;
case PIPE:
regexTextField.setText(splitter.getPattern().pattern());
separationComboBox.setSelectedItem(ColumnSeparator.REGULAR_EXPRESSION);
break;
case TILDE:
regexTextField.setText(splitter.getPattern().pattern());
separationComboBox.setSelectedItem(ColumnSeparator.REGULAR_EXPRESSION);
break;
default:
case SEMI_COLON:
separationComboBox.setSelectedItem(ColumnSeparator.SEMICOLON);
break;
}
}
/**
* Checks that the table has content and the header row is not behind the start row.
*
* @throws InvalidConfigurationException
* if the conditions are not fulfilled
*/
void validateConfiguration() throws InvalidConfigurationException {
if (previewTable.getModel().getRowCount() == 0) {
throw new InvalidConfigurationException();
}
if (configuration.hasHeaderRow() && configuration.getHeaderRow() > configuration.getStartingRow()) {
notifyHeaderRowBehindStartRow();
throw new InvalidConfigurationException();
} else if (keepBubble) {
// ensure that row-not-found-bubbles are not killed until something changed
throw new InvalidConfigurationException();
} else {
killCurrentErrorBubbleWindow();
}
}
/**
* Registers a new change listener.
*
* @param changeListener
* the listener to register
*/
void addChangeListener(ChangeListener changeListener) {
this.changeListeners.add(changeListener);
}
/**
* Fires a {@link ChangeEvent} that informs the listeners of a changed state.
*/
private void fireStateChanged() {
keepBubble = false;
ChangeEvent event = new ChangeEvent(this);
for (ChangeListener listener : changeListeners) {
try {
listener.stateChanged(event);
} catch (RuntimeException rte) {
LogService.getRoot().log(Level.WARNING,
"com.rapidminer.gui.io.dataimport.AbstractWizardStep.changelistener_failed", rte);
}
}
}
}