Java tutorial
package org.cytoscape.tableimport.internal.ui; /* * #%L * Cytoscape Table Import Impl (table-import-impl) * $Id:$ * $HeadURL:$ * %% * Copyright (C) 2006 - 2016 The Cytoscape Consortium * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 2.1 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 General Lesser Public License for more details. * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * <http://www.gnu.org/licenses/lgpl-2.1.html>. * #L% */ import static javax.swing.GroupLayout.DEFAULT_SIZE; import static javax.swing.GroupLayout.PREFERRED_SIZE; import static javax.swing.GroupLayout.Alignment.CENTER; import static javax.swing.GroupLayout.Alignment.LEADING; import static javax.swing.LayoutStyle.ComponentPlacement.RELATED; import static javax.swing.LayoutStyle.ComponentPlacement.UNRELATED; import static org.cytoscape.tableimport.internal.util.AttributeDataType.TYPE_BOOLEAN; import static org.cytoscape.tableimport.internal.util.AttributeDataType.TYPE_FLOATING; import static org.cytoscape.tableimport.internal.util.AttributeDataType.TYPE_INTEGER; import static org.cytoscape.tableimport.internal.util.AttributeDataType.TYPE_LONG; import static org.cytoscape.tableimport.internal.util.AttributeDataType.TYPE_STRING; import static org.cytoscape.tableimport.internal.util.ImportType.NETWORK_IMPORT; import static org.cytoscape.tableimport.internal.util.ImportType.TABLE_IMPORT; import static org.cytoscape.tableimport.internal.util.SourceColumnSemantic.ALIAS; import static org.cytoscape.tableimport.internal.util.SourceColumnSemantic.ATTR; import static org.cytoscape.tableimport.internal.util.SourceColumnSemantic.KEY; import static org.cytoscape.tableimport.internal.util.SourceColumnSemantic.NONE; import static org.cytoscape.util.swing.LookAndFeelUtil.isAquaLAF; import java.awt.Color; import java.awt.Component; import java.awt.Dialog.ModalityType; import java.awt.Font; import java.awt.Point; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowFocusListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.nio.charset.Charset; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.Vector; import javax.accessibility.AccessibleComponent; import javax.swing.AbstractAction; import javax.swing.ActionMap; import javax.swing.BorderFactory; import javax.swing.DefaultListCellRenderer; import javax.swing.GroupLayout; import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.KeyStroke; import javax.swing.LayoutStyle.ComponentPlacement; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ListSelectionEvent; import javax.swing.event.TableColumnModelEvent; import javax.swing.event.TableColumnModelListener; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.DataFormatter; import org.apache.poi.ss.usermodel.FormulaEvaluator; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.cytoscape.tableimport.internal.reader.SupportedFileType; import org.cytoscape.tableimport.internal.reader.TextDelimiter; import org.cytoscape.tableimport.internal.util.AttributeDataType; import org.cytoscape.tableimport.internal.util.FileType; import org.cytoscape.tableimport.internal.util.ImportType; import org.cytoscape.tableimport.internal.util.SourceColumnSemantic; import org.cytoscape.tableimport.internal.util.TypeUtil; import org.cytoscape.tableimport.internal.util.URLUtil; import org.cytoscape.util.swing.ColumnResizer; import org.cytoscape.util.swing.IconManager; import org.cytoscape.util.swing.LookAndFeelUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import au.com.bytecode.opencsv.CSVReader; /** * General purpose preview table panel. */ @SuppressWarnings("serial") public class PreviewTablePanel extends JPanel { private static final float ICON_FONT_SIZE = 14.0f; // Lines start with this char will be ignored. private String commentChar; private int startLine; // Tracking attribute data type. private SourceColumnSemantic[] types; private AttributeDataType[] dataTypes; private String[] listDelimiters; private Set<?> keySet; /* * GUI Components */ private JLabel sheetLabel; private JComboBox<Sheet> sheetComboBox; private JTable previewTable; private JButton selectAllButton; private JButton selectNoneButton; private JScrollPane tableScrollPane; private PropertyChangeSupport changes = new PropertyChangeSupport(this); private ImportType importType; private final IconManager iconManager; private EditDialog editDialog; private int lastDialogIndex = -1; private long lastDialogTime; private boolean updating; private final Object lock = new Object(); private static final Logger logger = LoggerFactory.getLogger(PreviewTablePanel.class); /** * Creates a new PreviewTablePanel object. */ public PreviewTablePanel(final IconManager iconManager) { this(TABLE_IMPORT, iconManager); } /** * Creates a new PreviewTablePanel object. */ public PreviewTablePanel(final ImportType importType, final IconManager iconManager) { this.importType = importType; this.iconManager = iconManager; initComponents(); } public void setKeyAttributeList(Set<?> keySet) { this.keySet = keySet; } @Override public void addPropertyChangeListener(PropertyChangeListener l) { if (changes == null) return; changes.addPropertyChangeListener(l); } @Override public void removePropertyChangeListener(PropertyChangeListener l) { changes.removePropertyChangeListener(l); } private void initComponents() { setBorder(LookAndFeelUtil.createTitledBorder("Preview")); sheetLabel = new JLabel("Sheet:"); sheetLabel.setVisible(false); final JLabel instructionLabel = new JLabel("Click on a column to edit it."); instructionLabel.setFont(instructionLabel.getFont().deriveFont(LookAndFeelUtil.getSmallFontSize())); LookAndFeelUtil.equalizeSize(getSelectAllButton(), getSelectNoneButton()); final GroupLayout layout = new GroupLayout(this); this.setLayout(layout); layout.setAutoCreateContainerGaps(true); layout.setAutoCreateGaps(false); layout.setHorizontalGroup(layout.createParallelGroup(LEADING, true).addGroup(layout.createSequentialGroup() .addComponent(sheetLabel, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE).addPreferredGap(RELATED) .addComponent(getSheetComboBox(), PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE) .addPreferredGap(UNRELATED).addComponent(instructionLabel).addGap(20, 20, Short.MAX_VALUE) .addComponent(getSelectAllButton(), PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE) .addPreferredGap(RELATED) .addComponent(getSelectNoneButton(), PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)) .addComponent(getTableScrollPane(), DEFAULT_SIZE, 320, Short.MAX_VALUE)); layout.setVerticalGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(CENTER, false).addComponent(sheetLabel) .addComponent(getSheetComboBox(), PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE) .addComponent(instructionLabel).addComponent(getSelectAllButton()) .addComponent(getSelectNoneButton())) .addPreferredGap(RELATED).addComponent(getTableScrollPane(), 120, 160, Short.MAX_VALUE) .addPreferredGap(RELATED)); ColumnResizer.adjustColumnPreferredWidths(getPreviewTable()); updatePreviewTable(); } public JTable getPreviewTable() { if (previewTable == null) { previewTable = new JTable( new PreviewTableModel(new Vector<Vector<String>>(), new Vector<String>(), false)); previewTable.setShowGrid(false); previewTable.setCellSelectionEnabled(false); previewTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); previewTable.setDefaultEditor(Object.class, null); if (importType == NETWORK_IMPORT) { final TableCellRenderer netRenderer = new PreviewTableCellRenderer(); previewTable.setDefaultRenderer(Object.class, netRenderer); } else { previewTable.setDefaultRenderer(Object.class, new PreviewTableCellRenderer()); } final JTableHeader hd = previewTable.getTableHeader(); hd.setReorderingAllowed(false); hd.setDefaultRenderer(new PreviewTableHeaderRenderer()); final TableColumnModelListener colModelListener = new TableColumnModelListener() { @Override public void columnMoved(TableColumnModelEvent e) { disposeEditDialog(); } @Override public void columnMarginChanged(ChangeEvent e) { disposeEditDialog(); } @Override public void columnSelectionChanged(ListSelectionEvent e) { } @Override public void columnRemoved(TableColumnModelEvent e) { } @Override public void columnAdded(TableColumnModelEvent e) { } }; hd.addMouseListener(new MouseAdapter() { @Override public void mousePressed(final MouseEvent e) { final TableColumnModel columnModel = previewTable.getColumnModel(); final int newColIdx = columnModel.getColumnIndexAtX(e.getX()); final int idx = editDialog != null ? editDialog.index : -1; disposeEditDialog(); if (idx != newColIdx) showEditDialog(newColIdx); // Do not show editor dialog when the user is resizing the columns previewTable.getColumnModel().removeColumnModelListener(colModelListener); previewTable.getColumnModel().addColumnModelListener(colModelListener); } @Override public void mouseReleased(MouseEvent e) { previewTable.getColumnModel().removeColumnModelListener(colModelListener); } }); // Also close the editor dialog when the table changes previewTable.getModel().addTableModelListener(new TableModelListener() { @Override public void tableChanged(TableModelEvent e) { disposeEditDialog(); } }); } return previewTable; } public String[] getAttributeNames() { String[] names = null; final PreviewTableModel model = (PreviewTableModel) getPreviewTable().getModel(); final int columnCount = model.getColumnCount(); names = new String[columnCount]; for (int i = 0; i < columnCount; i++) names[i] = model.getColumnName(i); return names; } public SourceColumnSemantic[] getTypes() { return types; } public void setType(final int index, final SourceColumnSemantic newType) { if (index < 0) return; if (types != null && types.length > index) { // First replace the index that currently has this unique type by the default type if (newType.isUnique()) replaceType(newType, TypeUtil.getDefaultType(importType)); final SourceColumnSemantic oldType = types[index]; types[index] = newType; if (newType != oldType) changes.fireIndexedPropertyChange(DataEvents.ATTR_TYPE_CHANGED, index, oldType, newType); } } protected void fillTypes(final SourceColumnSemantic newValue) { if (types != null) Arrays.fill(types, newValue); } protected void replaceType(final SourceColumnSemantic type1, final SourceColumnSemantic type2) { if (types != null) { for (int i = 0; i < types.length; i++) { if (types[i] == type1) setType(i, type2); } } } public AttributeDataType[] getDataTypes() { return dataTypes; } public AttributeDataType getDataType(final int index) { if (dataTypes != null && dataTypes.length > index) return dataTypes[index]; return null; } public void setDataType(final int index, final AttributeDataType newValue) { if (index < 0) return; if (dataTypes != null && dataTypes.length > index) { final AttributeDataType oldValue = dataTypes[index]; dataTypes[index] = newValue; if (newValue != oldValue) changes.fireIndexedPropertyChange(DataEvents.ATTR_DATA_TYPE_CHANGED, index, oldValue, newValue); } } public String[] getListDelimiters() { return listDelimiters; } public void setListDelimiter(final int index, final String newValue) { if (index < 0) return; if (listDelimiters != null && listDelimiters.length > index) listDelimiters[index] = newValue; } public FileType getFileType() { final String name = getSourceName(); if (name != null && name.startsWith("gene_association")) return FileType.GENE_ASSOCIATION_FILE; return FileType.ATTRIBUTE_FILE; } public String getSourceName() { return getPreviewTable().getName(); } /** * Load file and show preview. */ public void updatePreviewTable(final Workbook workbook, final String fileType, final String fileFullName, final InputStream tempIs, final List<String> delimiters, final String commentLineChar, final int startLine) throws IOException { if (tempIs == null) return; if ((commentLineChar != null) && (commentLineChar.trim().length() != 0)) this.commentChar = commentLineChar; else this.commentChar = null; this.startLine = startLine; updating = true; try { getSheetComboBox().removeAllItems(); getSheetComboBox().setVisible(false); sheetLabel.setVisible(false); PreviewTableModel newModel = null; if (SupportedFileType.EXCEL.getExtension().equalsIgnoreCase(fileType) || SupportedFileType.OOXML.getExtension().equalsIgnoreCase(fileType)) { final int numberOfSheets = workbook.getNumberOfSheets(); if (numberOfSheets == 0) throw new IllegalStateException("No sheet found in the workbook."); for (int i = 0; i < numberOfSheets; i++) { final Sheet sheet = workbook.getSheetAt(i); if (sheet.getPhysicalNumberOfRows() > 0) getSheetComboBox().addItem(sheet); } if (getSheetComboBox().getItemCount() > 0) getSheetComboBox().setSelectedIndex(0); if (getSheetComboBox().getItemCount() > 1) { sheetLabel.setVisible(true); getSheetComboBox().setVisible(true); } /* * Load each sheet in the workbook. */ if (getSheetComboBox().getItemCount() > 0) { final Sheet sheet = workbook.getSheetAt(0); updatePreviewTable(sheet); } else { throw new RuntimeException("No data found in the Excel sheets."); } } else { newModel = parseText(tempIs, delimiters, startLine); String sourceName; String[] urlParts = fileFullName.split("/"); if (urlParts.length > 0 && !fileFullName.isEmpty()) sourceName = urlParts[urlParts.length - 1]; else sourceName = "Source Table"; dataTypes = TypeUtil.guessDataTypes(newModel); types = TypeUtil.guessTypes(importType, newModel, dataTypes, null); listDelimiters = new String[newModel.getColumnCount()]; updatePreviewTable(newModel, sourceName); } } finally { updating = false; } } public void setFirstRowAsColumnNames() { final PreviewTableModel model = (PreviewTableModel) getPreviewTable().getModel(); model.setFirstRowNames(true); types = TypeUtil.guessTypes(importType, model, dataTypes, null); updatePreviewTable(); ColumnResizer.adjustColumnPreferredWidths(getPreviewTable()); } protected boolean isCytoscapeAttributeFile(final URL sourceURL) throws IOException { InputStream is = null; boolean testResult = true; try { BufferedReader bufRd = null; is = URLUtil.getInputStream(sourceURL); try { String line = null; int i = 0; bufRd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8").newDecoder())); // Test first two lines to check the file type. while ((line = bufRd.readLine()) != null) { if (i == 0) { String[] elements = line.split(" +"); if (elements.length == 1) { // True so far. } else { elements = line.split("[(]"); if ((elements.length == 2) && elements[1].startsWith("class=")) { // true so far. } else { testResult = false; break; } } } else if (i == 1) { String[] elements = line.split(" += +"); if (elements.length != 2) testResult = false; } else if (i >= 2) { break; } i++; } } finally { if (bufRd != null) bufRd.close(); } } finally { if (is != null) is.close(); } return testResult; } public int checkKeyMatch(final int targetColumn) { int matched = 0; if (keySet != null && !keySet.isEmpty()) { final TableModel curModel = getPreviewTable().getModel(); try { curModel.getValueAt(0, targetColumn); } catch (ArrayIndexOutOfBoundsException e) { return 0; } final int rowCount = curModel.getRowCount(); for (int i = 0; i < rowCount; i++) { final Object val = curModel.getValueAt(i, targetColumn); if (val != null && keySet.contains(val)) matched++; } } return matched; } public int getPreviewSize() { return 100; } /** * Returns the first index of the table column that has the passed type. */ protected int getColumnIndex(final SourceColumnSemantic type) { if (types != null) return Arrays.asList(types).indexOf(type); return -1; } protected void setAliasColumn(final int index, final boolean flag) { if (types != null && types.length > index) { types[index] = flag ? ALIAS : ATTR; updatePreviewTable(); } } protected boolean isImported(final int index) { if (types != null && types.length > index) return types[index] != NONE; return false; } private PreviewTableModel parseExcel(final Sheet sheet, int startLine) throws IOException { int size = getPreviewSize(); if (size == -1) size = Integer.MAX_VALUE; int maxCol = 0; final Vector<Vector<String>> data = new Vector<>(); int rowCount = 0; int validRowCount = 0; FormulaEvaluator evaluator = sheet.getWorkbook().getCreationHelper().createFormulaEvaluator(); DataFormatter formatter = new DataFormatter(); Row row; while (((row = sheet.getRow(rowCount)) != null) && (validRowCount < size)) { if (rowCount >= startLine) { final Vector<String> rowVector = new Vector<>(); if (maxCol < row.getLastCellNum()) maxCol = row.getLastCellNum(); for (short j = 0; j < maxCol; j++) { Cell cell = row.getCell(j); if (cell == null || cell.getCellType() == Cell.CELL_TYPE_ERROR || (cell.getCellType() == Cell.CELL_TYPE_FORMULA && cell.getCachedFormulaResultType() == Cell.CELL_TYPE_ERROR)) { rowVector.add(null); } else { rowVector.add(formatter.formatCellValue(cell, evaluator)); } } data.add(rowVector); validRowCount++; } rowCount++; } final boolean firstRowNames = importType == NETWORK_IMPORT || importType == TABLE_IMPORT; return new PreviewTableModel(data, new Vector<String>(), firstRowNames); } private PreviewTableModel parseText(InputStream tempIs, List<String> delimiters, int startLine) throws IOException { String line; String attrName = "Attr1"; Vector<Vector<String>> data = null; int maxColumn; BufferedReader bufRd = new BufferedReader( new InputStreamReader(tempIs, Charset.forName("UTF-8").newDecoder())); /* * Generate reg. exp. for delimiter. */ final String delimiterRegEx; if (delimiters != null) { StringBuffer delimiterBuffer = new StringBuffer(); if (delimiters.size() != 0) { delimiterBuffer.append("["); for (String delimiter : delimiters) delimiterBuffer.append(delimiter); delimiterBuffer.append("]"); } delimiterRegEx = delimiterBuffer.toString(); } else { // treat as cytoscape attribute files. delimiterRegEx = " += +"; // Extract first column for attr name. line = bufRd.readLine(); String[] line1 = line.split(" +"); attrName = line1[0]; } /* * Read & extract one line at a time. The line can be Tab delimited, */ final int size = getPreviewSize(); boolean importAll = false; if (size == -1) importAll = true; // Distinguish between CSV files and everything else. // TODO: Since the CSV parser allows for other delimiters, consider exploring using it for everything. // The variables are modified by both the new method and the old method. int rowCount = 0; int validRowCount = 0; maxColumn = 0; data = new Vector<>(); if (delimiters != null && delimiters.contains(TextDelimiter.COMMA.getDelimiter()) && delimiters.size() == 1) { // Only if there is exactly one delimiter and that delimiter is a // comma should you read the file using OpenCSV // New method... Using OpenCSV final CSVReader reader = new CSVReader(bufRd); String[] rowData; // Note that rowData is roughly equivalent to "parts" in the old code. while ((rowData = reader.readNext()) != null) { final List<String> list = Arrays.asList(rowData); line = list.isEmpty() ? "" : String.join(TextDelimiter.COMMA.getDelimiter(), list); if (!ignoreLine(line, rowCount)) { final Vector<String> row = new Vector<>(); for (String field : rowData) row.add(field); if (rowData.length > maxColumn) maxColumn = rowData.length; data.add(row); validRowCount++; } rowCount++; if (importAll == false && validRowCount >= size) break; } try { reader.close(); } catch (Exception e) { } } else { // Old method... Using naive splitting. String[] parts; while ((line = bufRd.readLine()) != null) { if (!ignoreLine(line, rowCount)) { final Vector<String> row = new Vector<>(); if (delimiterRegEx.length() == 0) { parts = new String[1]; parts[0] = line; } else { parts = line.split(delimiterRegEx); } for (String entry : parts) { row.add(entry); } if (parts.length > maxColumn) maxColumn = parts.length; data.add(row); validRowCount++; } rowCount++; if (importAll == false && validRowCount >= size) break; } } // If the inputStream is passed in from parameter, do not close it if (tempIs != null) tempIs.close(); final boolean firstRowNames = importType == NETWORK_IMPORT || importType == TABLE_IMPORT; if (delimiters == null) { // Cytoscape attr file. final Vector<String> columnNames = new Vector<>(); columnNames.add("Key"); columnNames.add(attrName); return new PreviewTableModel(data, columnNames, firstRowNames); } else { return new PreviewTableModel(data, new Vector<String>(), firstRowNames); } } private boolean ignoreLine(final String line, int index) { return ((commentChar != null) && line.startsWith(commentChar)) || (line.trim().length() == 0) || (index < startLine); } private void showEditDialog(final int colIdx) { if (colIdx == lastDialogIndex && System.currentTimeMillis() - lastDialogTime < 100) return; lastDialogIndex = -1; lastDialogTime = 0; final Window parent = SwingUtilities.getWindowAncestor(PreviewTablePanel.this); final PreviewTableModel model = (PreviewTableModel) getPreviewTable().getModel(); final String attrName = model.getColumnName(colIdx); final List<SourceColumnSemantic> availableTypes = TypeUtil.getAvailableTypes(importType); final AttributeEditorPanel attrEditorPanel = new AttributeEditorPanel(parent, attrName, availableTypes, types[colIdx], dataTypes[colIdx], listDelimiters[colIdx], iconManager); if (LookAndFeelUtil.isWinLAF()) { attrEditorPanel.setBorder( BorderFactory.createMatteBorder(1, 1, 1, 1, UIManager.getColor("activeCaptionBorder"))); attrEditorPanel.setBackground(UIManager.getColor("TableHeader.background")); } editDialog = new EditDialog(parent, ModalityType.MODELESS, colIdx, attrEditorPanel); editDialog.setUndecorated(true); editDialog.add(attrEditorPanel); final ActionMap actionMap = attrEditorPanel.getActionMap(); final InputMap inputMap = attrEditorPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "VK_ESCAPE"); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "VK_ENTER"); actionMap.put("VK_ESCAPE", new AbstractAction("VK_ESCAPE") { @Override public void actionPerformed(ActionEvent e) { disposeEditDialog(); } }); actionMap.put("VK_ENTER", new AbstractAction("VK_ENTER") { @Override public void actionPerformed(ActionEvent e) { disposeEditDialog(); } }); attrEditorPanel.addPropertyChangeListener("attributeName", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { final String attrName = (String) evt.getNewValue(); if (attrName != null && !attrName.trim().isEmpty()) { ((PreviewTableModel) getPreviewTable().getModel()).setColumnName(colIdx, attrName); getPreviewTable().getColumnModel().getColumn(colIdx).setHeaderValue(attrName); updatePreviewTable(); } } }); attrEditorPanel.addPropertyChangeListener("attributeType", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { setType(colIdx, (SourceColumnSemantic) evt.getNewValue()); updatePreviewTable(); } }); attrEditorPanel.addPropertyChangeListener("attributeDataType", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { final AttributeDataType newDataType = (AttributeDataType) evt.getNewValue(); if (newDataType.isList()) setListDelimiter(colIdx, attrEditorPanel.getListDelimiter()); setDataType(colIdx, newDataType); updatePreviewTable(); } }); attrEditorPanel.addPropertyChangeListener("listDelimiter", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { setListDelimiter(colIdx, (String) evt.getNewValue()); updatePreviewTable(); } }); positionEditDialog(); editDialog.addWindowListener(new WindowAdapter() { @Override public void windowOpened(WindowEvent e) { getPreviewTable().getTableHeader().repaint(); attrEditorPanel.getAttributeNameTextField().requestFocusInWindow(); } @Override public void windowClosed(WindowEvent e) { getPreviewTable().getTableHeader().repaint(); } }); editDialog.addWindowFocusListener(new WindowFocusListener() { @Override public void windowLostFocus(WindowEvent e) { if (editDialog != null) { lastDialogIndex = editDialog.index; lastDialogTime = System.currentTimeMillis(); } disposeEditDialog(); } @Override public void windowGainedFocus(WindowEvent e) { } }); editDialog.pack(); editDialog.setVisible(true); } private void positionEditDialog() { if (editDialog != null) { final JTableHeader hd = getPreviewTable().getTableHeader(); // Get the column header location // (see: https://bugs.openjdk.java.net/browse/JDK-4408424) final AccessibleComponent ac = hd.getAccessibleContext().getAccessibleChild(editDialog.index) .getAccessibleContext().getAccessibleComponent(); final Point screenPt = ac.getLocationOnScreen(); final Point compPt = ac.getLocation(); int xOffset = screenPt.x - compPt.x; int yOffset = screenPt.y - compPt.y + hd.getBounds().height; final Point pt = ac.getBounds().getLocation(); pt.translate(xOffset, yOffset); // This prevent the dialog from being positioned completely outside the parent panel pt.x = Math.max(pt.x, getTableScrollPane().getLocationOnScreen().x - editDialog.getBounds().width); pt.x = Math.min(pt.x, getTableScrollPane().getLocationOnScreen().x + getTableScrollPane().getBounds().width); // Show the dialog right below the column header editDialog.setLocation(pt); } } protected void disposeEditDialog() { synchronized (lock) { if (editDialog != null) { editDialog.dispose(); editDialog = null; } } } private void updatePreviewTable(final Sheet sheet) throws IOException { final PreviewTableModel newModel = parseExcel(sheet, startLine); if (newModel.getRowCount() > 0) { final String sheetName = sheet.getSheetName(); dataTypes = TypeUtil.guessDataTypes(newModel); types = TypeUtil.guessTypes(importType, newModel, dataTypes, null); listDelimiters = new String[newModel.getColumnCount()]; updatePreviewTable(newModel, sheetName); } if (getPreviewTable() == null) throw new IllegalStateException("No data found in the Excel sheets."); } private void updatePreviewTable(final PreviewTableModel newModel, final String name) { getPreviewTable().setName(name); getPreviewTable().setModel(newModel); ColumnResizer.adjustColumnPreferredWidths(getPreviewTable()); updatePreviewTable(); } protected void updatePreviewTable() { getPreviewTable().revalidate(); getPreviewTable().repaint(); getPreviewTable().getTableHeader().resizeAndRepaint(); getSelectAllButton().setEnabled(false); getSelectNoneButton().setEnabled(false); if (types != null) { getSelectAllButton().setEnabled(Arrays.asList(types).contains(NONE)); for (SourceColumnSemantic t : types) { if (t != NONE) { getSelectNoneButton().setEnabled(true); break; } } } } private JComboBox<Sheet> getSheetComboBox() { if (sheetComboBox == null) { sheetComboBox = new JComboBox<>(); sheetComboBox.setVisible(false); sheetComboBox.setRenderer(new DefaultListCellRenderer() { @Override public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); setText(((Sheet) value).getSheetName()); return this; } }); sheetComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { if (!updating) { disposeEditDialog(); final Sheet sheet = (Sheet) sheetComboBox.getSelectedItem(); try { if (sheet != null) updatePreviewTable(sheet); } catch (IOException e) { logger.error("Cannot preview Excel sheet '" + sheet.getSheetName() + "'.", e); } } } }); } return sheetComboBox; } private JButton getSelectAllButton() { if (selectAllButton == null) { selectAllButton = new JButton("Select All"); selectAllButton.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { disposeEditDialog(); // Replace types "NONE" with new guessed types. // NOTE: This must not change the current data types! final Set<SourceColumnSemantic> ignoredTypes = new HashSet<>(Arrays.asList(types)); final SourceColumnSemantic[] newTypes = TypeUtil.guessTypes(importType, getPreviewTable().getModel(), dataTypes, ignoredTypes); for (int i = 0; i < newTypes.length; i++) { if (types.length > i && types[i] == NONE) setType(i, newTypes[i]); } updatePreviewTable(); } }); if (isAquaLAF()) { selectAllButton.putClientProperty("JButton.buttonType", "gradient"); selectAllButton.putClientProperty("JComponent.sizeVariant", "small"); } selectAllButton.setEnabled(false); } return selectAllButton; } private JButton getSelectNoneButton() { if (selectNoneButton == null) { selectNoneButton = new JButton("Select None"); selectNoneButton.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { disposeEditDialog(); fillTypes(NONE); updatePreviewTable(); } }); if (isAquaLAF()) { selectNoneButton.putClientProperty("JButton.buttonType", "gradient"); selectNoneButton.putClientProperty("JComponent.sizeVariant", "small"); } selectNoneButton.setEnabled(false); } return selectNoneButton; } private JScrollPane getTableScrollPane() { if (tableScrollPane == null) { tableScrollPane = new JScrollPane(getPreviewTable()); tableScrollPane.getHorizontalScrollBar().addAdjustmentListener(new AdjustmentListener() { @Override public void adjustmentValueChanged(AdjustmentEvent e) { // Realign the Attribute Editor Dialog if it is open if (!e.getValueIsAdjusting()) positionEditDialog(); } }); } return tableScrollPane; } class PreviewTableModel extends DefaultTableModel { private boolean firstRowNames; public PreviewTableModel(final Vector<Vector<String>> data, final Vector<String> columnNames, final boolean firstRowNames) { super(data, columnNames); this.firstRowNames = firstRowNames; } @SuppressWarnings("unchecked") public void setColumnName(final int column, final String name) { if (columnIdentifiers.isEmpty()) columnIdentifiers.setSize(getColumnCount()); if (columnIdentifiers.size() > column) { columnIdentifiers.set(column, name); this.fireTableChanged(new TableModelEvent(this)); } } public void setFirstRowNames(final boolean firstRowNames) { this.firstRowNames = firstRowNames; this.fireTableStructureChanged(); } public boolean isFirstRowNames() { return firstRowNames; } @Override public int getRowCount() { return firstRowNames ? dataVector.size() - 1 : dataVector.size(); } @Override @SuppressWarnings("unchecked") public int getColumnCount() { return dataVector.size() > 0 ? ((Vector<String>) dataVector.get(0)).size() : 0; } @Override @SuppressWarnings("unchecked") public String getColumnName(final int column) { String colName = null; // First check is the name has been overwritten by the user if (columnIdentifiers.size() > column) colName = (String) columnIdentifiers.get(column); if (colName == null) { if (firstRowNames && dataVector.size() > 0) { // No overwritten name and should use the first data row as column names final Vector<String> firstRow = (Vector<String>) dataVector.get(0); if (firstRow != null && firstRow.size() > column) { colName = firstRow.get(column); } } } if (colName == null) { // Just return a default name colName = "Column " + (column + 1); } return colName; } @Override public Class<?> getColumnClass(final int column) { return String.class; } @Override public boolean isCellEditable(final int row, final int column) { return false; } @Override public Object getValueAt(int row, final int column) { if (firstRowNames) row++; return super.getValueAt(row, column); } @Override public void setValueAt(final Object aValue, int row, final int column) { if (firstRowNames) row++; super.setValueAt(aValue, row, column); } } private class PreviewTableHeaderRenderer extends JPanel implements TableCellRenderer { private final JLabel typeLabel; private final JLabel nameLabel; private final JLabel editLabel; PreviewTableHeaderRenderer() { setBorder(UIManager.getBorder("TableHeader.cellBorder")); setBackground(UIManager.getColor("TableHeader.background")); nameLabel = new JLabel(); nameLabel.setFont(UIManager.getFont("TableHeader.font")); typeLabel = new JLabel(); typeLabel.setFont(iconManager.getIconFont(ICON_FONT_SIZE)); editLabel = new JLabel(IconManager.ICON_CARET_LEFT); editLabel.setFont(iconManager.getIconFont(12.0f)); editLabel.setHorizontalAlignment(JLabel.CENTER); // Forces the edit label to always have the same size, no matter its state final JLabel tempLabel = new JLabel(IconManager.ICON_CARET_DOWN); tempLabel.setFont(iconManager.getIconFont(12.0f)); LookAndFeelUtil.equalizeSize(editLabel, tempLabel); final GroupLayout layout = new GroupLayout(this); this.setLayout(layout); layout.setAutoCreateContainerGaps(false); layout.setAutoCreateGaps(false); layout.setHorizontalGroup(layout.createSequentialGroup().addGap(6).addComponent(typeLabel) .addPreferredGap(ComponentPlacement.RELATED).addComponent(nameLabel) .addGap(5, 5, Short.MAX_VALUE).addComponent(editLabel).addGap(6)); layout.setVerticalGroup( layout.createSequentialGroup().addGap(4).addGroup(layout.createParallelGroup(CENTER, false) .addComponent(typeLabel).addComponent(nameLabel).addComponent(editLabel)).addGap(4)); } @Override public Component getTableCellRendererComponent(JTable tbl, Object val, boolean isS, boolean hasF, int row, int col) { nameLabel.setText(val != null ? val.toString() : ""); Color fgColor = UIManager.getColor("TableHeader.foreground"); // Set type icon if (types != null && types.length > col) { SourceColumnSemantic type = types[col]; if (type == null) type = NONE; final AttributeDataType dataType = dataTypes != null && dataTypes.length > col ? dataTypes[col] : TYPE_STRING; typeLabel.setForeground(type.getForeground()); typeLabel.setText(type.getText()); setToolTipText( "<html>" + type.getDescription() + " - <i>" + dataType.getDescription() + "</i></html>"); if (type == NONE) fgColor = UIManager.getColor("TextField.inactiveForeground"); } else { fgColor = UIManager.getColor("TextField.inactiveForeground"); } if (editDialog != null && editDialog.index == col && editDialog.isVisible()) editLabel.setText(IconManager.ICON_CARET_DOWN); else editLabel.setText(IconManager.ICON_CARET_LEFT); nameLabel.setForeground(fgColor); this.invalidate(); return this; } } private class PreviewTableCellRenderer extends DefaultTableCellRenderer { public PreviewTableCellRenderer() { setOpaque(true); setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { setHorizontalAlignment(DefaultTableCellRenderer.CENTER); setFont(getFont().deriveFont(LookAndFeelUtil.getSmallFontSize())); setFont(getFont().deriveFont(getColumnIndex(KEY) == column ? Font.BOLD : Font.PLAIN)); setText(value == null ? "" : value.toString()); if (isImported(column)) setForeground(table.getForeground()); else setForeground(UIManager.getColor("TextField.inactiveForeground")); final AttributeDataType dataType = getDataType(column); if (dataType == TYPE_INTEGER || dataType == TYPE_LONG || dataType == TYPE_FLOATING) setHorizontalAlignment(JLabel.RIGHT); else if (dataType == TYPE_BOOLEAN) setHorizontalAlignment(JLabel.CENTER); else setHorizontalAlignment(JLabel.LEFT); return this; } } private class EditDialog extends JDialog { final int index; final AttributeEditorPanel editPanel; EditDialog(final Window parent, final ModalityType modType, int index, AttributeEditorPanel editPanel) { super(parent, modType); this.index = index; this.editPanel = editPanel; } } }