/** * 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; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.io.File; import java.nio.file.Path; import java.util.LinkedList; import java.util.List; import javax.swing.Action; import javax.swing.ComboBoxModel; import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultListCellRenderer; import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileSystemView; import com.lowagie.text.Font; import com.rapidminer.core.io.data.source.DataSourceFactory; import com.rapidminer.core.io.data.source.DataSourceFactoryRegistry; import com.rapidminer.core.io.data.source.FileDataSource; import com.rapidminer.core.io.data.source.FileDataSourceFactory; import com.rapidminer.gui.tools.ExtendedJFileChooser; import com.rapidminer.gui.tools.ResourceAction; import com.rapidminer.gui.tools.ResourceLabel; import com.rapidminer.gui.tools.SwingTools; import com.rapidminer.gui.tools.components.LinkLocalButton; import com.rapidminer.studio.io.gui.internal.DataImportWizardUtils; import com.rapidminer.studio.io.gui.internal.DataWizardEventType; /** * A view that shows a {@link JFileChooser} to allow file selection for a {@link FileDataSource}s. * * @author Nils Woehler * @since 7.0.0 * */ final class LocalFileLocationChooserView extends JPanel { private static final long serialVersionUID = 1L; private final ExtendedJFileChooser fileChooser; private final JPanel selectedTypePanel; private final JComboBox<FileDataSourceFactory<?>> factoryDropDownComboBox; private final List<ChangeListener> changeListeners = new LinkedList<>(); private FileDataSourceFactory<?> fileDataSourceFactory; private boolean showFileDataSourceComboBox = false; private final Action changeTypeAction = new ResourceAction(false, "io.dataimport.step.file_selection.change_type") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { showFileDataSourceComboBox(); } }; private final Action selectTypeAction = new ResourceAction(false, "io.dataimport.step.file_selection.select_type") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { chooseDataSourceFileTypeFromComboBox(); } }; private final ChangeListener fileChangeListener = new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { // Update detected file type each time the users changes the selected file final Path selectedLocation = getSelectedLocation(); if (selectedLocation != null) { fileDataSourceFactory = LocalFileDataSourceFactory.lookupFactory(selectedLocation); } updateFileTypePanel(); } }; /** * The view constructor. * * @param fileFilters * the file filters for this file chooser */ LocalFileLocationChooserView(List<FileFilter> fileFilters) { this.fileChooser = new ExtendedJFileChooser("", FileSystemView.getFileSystemView().getDefaultDirectory()); this.fileChooser.setControlButtonsAreShown(false); this.fileChooser.setAcceptAllFileFilterUsed(true); if (fileFilters != null) { for (FileFilter filter : fileFilters) { this.fileChooser.addChoosableFileFilter(filter); } } // create combo box for all available factories this.factoryDropDownComboBox = new JComboBox<>(); FileDataSourceFactory<?>[] comboBoxItems = DataSourceFactoryRegistry.INSTANCE.getFileFactories() .toArray(new FileDataSourceFactory[0]); ComboBoxModel<FileDataSourceFactory<?>> comboBoxModel = new DefaultComboBoxModel<>(comboBoxItems); factoryDropDownComboBox.setModel(comboBoxModel); factoryDropDownComboBox.setRenderer(new DefaultListCellRenderer() { private static final long serialVersionUID = 1L; @Override public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) { Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); setText(DataImportWizardUtils.getFactoryLabel((DataSourceFactory<?>) value)); return comp; } }); setLayout(new BorderLayout()); // add file chooser to panel add(fileChooser, BorderLayout.CENTER); this.selectedTypePanel = new JPanel(new GridBagLayout()); add(selectedTypePanel, BorderLayout.SOUTH); registerChangeListener(fileChangeListener); fileChangeListener.stateChanged(null); } /** * Applies the file type selection made by the user in the file type combobox. */ protected void chooseDataSourceFileTypeFromComboBox() { // toggle marker variable this.showFileDataSourceComboBox = false; String oldFileType = fileDataSourceFactory != null ? fileDataSourceFactory.getI18NKey() : "unknown"; // update selected factory this.fileDataSourceFactory = (FileDataSourceFactory<?>) factoryDropDownComboBox.getSelectedItem(); // log file type change String newFileType = fileDataSourceFactory.getI18NKey(); DataImportWizardUtils.logStats(DataWizardEventType.FILE_TYPE_CHANGED, oldFileType + "->" + newFileType); fireChangeEvent(); updateFileTypePanel(); } private void showFileDataSourceComboBox() { // toggle marker variable this.showFileDataSourceComboBox = true; updateFileTypePanel(); } /** * @return the selected file location. Might be {@code null} in case no file has been selected. */ Path getSelectedLocation() { File selectedFile = fileChooser.getSelectedFile(); return selectedFile != null ? selectedFile.toPath() : null; } /** * @return the data source factory for the selected file. Might be {@code null} in case no file * has been selected. */ FileDataSourceFactory<?> getFileDataSourceFactory() { return fileDataSourceFactory; } /** * Registers a new {@link ChangeListener} for the file chooser. * * @param listener * the {@link ChangeListener} to register */ void registerChangeListener(ChangeListener listener) { this.fileChooser.addChangeListener(listener); this.changeListeners.add(listener); } /** * Fires a state changed event for registered {@link ChangeListener}. Skips the * {@link #fileChangeListener} to avoid new file type lookup when choosing a different file * import type. */ private void fireChangeEvent() { for (ChangeListener listener : changeListeners) { if (listener == fileChangeListener) { // skip local file change listener to avoid new file type lookup continue; } try { listener.stateChanged(new ChangeEvent(fileChooser)); } catch (RuntimeException e) { // ignore } } } /** * Sets the selected file and updates the {@link JFileChooser} accordingly. * * @param selectedFile * the selected file. Must not be <code>null</code>. */ void setSelectedFile(Path selectedFile) { this.fileChooser.setSelectedFile(selectedFile.toFile()); } /** * @param fileDataSource * updates the file data source factory for this view */ public void setFileDataSourceFactory(FileDataSourceFactory<?> fileDataSourceFactory) { this.fileDataSourceFactory = fileDataSourceFactory; } private void updateFileTypePanel() { SwingTools.invokeLater(new Runnable() { @Override public void run() { selectedTypePanel.removeAll(); GridBagConstraints constraint = new GridBagConstraints(); constraint.fill = GridBagConstraints.NONE; constraint.anchor = GridBagConstraints.WEST; constraint.insets = new Insets(10, 3, 10, 0); constraint.fill = GridBagConstraints.BOTH; constraint.weightx = 1.0; JPanel fillerPanel = new JPanel(); selectedTypePanel.add(fillerPanel, constraint); // set constraints for other component constraint.fill = GridBagConstraints.NONE; constraint.weightx = 0.0; if (getSelectedLocation() != null) { boolean showComboBox = showFileDataSourceComboBox; // add first label if (fileDataSourceFactory != null) { if (showComboBox) { JLabel typeLabel = new ResourceLabel("io.dataimport.step.file_selection.select_type"); selectedTypePanel.add(typeLabel, constraint); } else { JLabel typeLabel = new ResourceLabel("io.dataimport.step.file_selection.type_detected"); selectedTypePanel.add(typeLabel, constraint); String selectedTypeName = DataImportWizardUtils.getFactoryLabel(fileDataSourceFactory); JLabel selectedTypeLabel = new JLabel(selectedTypeName); selectedTypeLabel.setFont(selectedTypeLabel.getFont().deriveFont(Font.BOLD)); selectedTypePanel.add(selectedTypeLabel, constraint); } } else { JLabel unknownTypeLabel = new ResourceLabel("io.dataimport.step.file_selection.unknown_type"); selectedTypePanel.add(unknownTypeLabel, constraint); showComboBox = true; } if (showComboBox) { selectedTypePanel.add(factoryDropDownComboBox, constraint); LinkLocalButton changeTypeButton = new LinkLocalButton(selectTypeAction); changeTypeButton.setAlignmentX(SwingConstants.CENTER); selectedTypePanel.add(changeTypeButton, constraint); } else { LinkLocalButton changeTypeButton = new LinkLocalButton(changeTypeAction); changeTypeButton.setAlignmentX(SwingConstants.CENTER); selectedTypePanel.add(changeTypeButton, constraint); } } else { // add a no file selected label selectedTypePanel.add(new ResourceLabel("io.dataimport.step.file_selection.no_file_selected"), constraint); } // ensure same height for every panel content to avoid flickering selectedTypePanel.setPreferredSize(new Dimension(700, 41)); selectedTypePanel.setMinimumSize(new Dimension(700, 41)); selectedTypePanel.revalidate(); selectedTypePanel.repaint(); } }); } }