/* * RapidMiner * * Copyright (C) 2001-2011 by Rapid-I and the contributors * * Complete list of developers available at our web site: * * http://rapid-i.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.gui.flow; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.LinkedList; import java.util.List; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JToggleButton; import javax.swing.ListSelectionModel; import javax.swing.SwingConstants; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellRenderer; import com.rapidminer.Process; import com.rapidminer.gui.MainFrame; import com.rapidminer.gui.processeditor.ProcessEditor; import com.rapidminer.gui.tools.ExtendedJScrollPane; import com.rapidminer.gui.tools.ExtendedJTable; import com.rapidminer.gui.tools.ResourceAction; import com.rapidminer.gui.tools.ResourceDockKey; import com.rapidminer.gui.tools.ResourceMenu; import com.rapidminer.gui.tools.SwingTools; import com.rapidminer.gui.tools.ViewToolBar; import com.rapidminer.operator.Operator; import com.rapidminer.operator.ProcessSetupError; import com.rapidminer.operator.ports.Port; import com.rapidminer.operator.ports.metadata.MetaDataError; import com.rapidminer.operator.ports.quickfix.QuickFix; import com.rapidminer.tools.I18N; import com.vlsolutions.swing.docking.DockKey; import com.vlsolutions.swing.docking.Dockable; /** * * @author Simon Fischer */ public class ErrorTable extends JPanel implements Dockable, ProcessEditor { private static final long serialVersionUID = -954934789614113138L; private static final ImageIcon IMAGE_WARNING = SwingTools.createIcon("16/sign_warning.png"); private static final ImageIcon IMAGE_ERROR = SwingTools.createIcon("16/error.png"); private static final ImageIcon IMAGE_NO_QUICKFIX = SwingTools.createIcon("16/" + I18N.getMessage(I18N.getGUIBundle(), "gui.errortable.no_quickfix_available.icon")); private static final ImageIcon IMAGE_QUICKFIX = SwingTools.createIcon("16/" + I18N.getMessage(I18N.getGUIBundle(), "gui.errortable.choose_quickfix.icon")); private static final String[] COLUMN_NAMES = { I18N.getMessage(I18N.getGUIBundle(), "gui.errortable.header.message.label"), I18N.getMessage(I18N.getGUIBundle(), "gui.errortable.header.fixes.label"), I18N.getMessage(I18N.getGUIBundle(), "gui.errortable.header.location.label") }; private static final String[] COLUMN_TOOLTIPS = { I18N.getMessage(I18N.getGUIBundle(), "gui.errortable.header.message.tip"), I18N.getMessage(I18N.getGUIBundle(), "gui.errortable.header.fixes.tip"), I18N.getMessage(I18N.getGUIBundle(), "gui.errortable.header.location.tip") }; private final MainFrame mainFrame; private final TableCellRenderer iconRenderer = new DefaultTableCellRenderer() { private static final long serialVersionUID = 1L; @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (value instanceof ProcessSetupError) { JLabel label = (JLabel)super.getTableCellRendererComponent(table, ((ProcessSetupError)value).getMessage(), isSelected, hasFocus, row, column); switch (((ProcessSetupError) value).getSeverity()) { case WARNING: label.setIcon(IMAGE_WARNING); break; case ERROR: label.setIcon(IMAGE_ERROR); break; default: label.setIcon(null); // cannot happen } return label; } else if (value instanceof Port) { JLabel label = (JLabel)super.getTableCellRendererComponent(table, ((Port)value).getSpec(), isSelected, hasFocus, row, column); label.setIcon(((Port) value).getPorts().getOwner().getOperator().getOperatorDescription().getSmallIcon()); return label; } else if (value instanceof Operator) { JLabel label = (JLabel)super.getTableCellRendererComponent(table, ((Operator)value).getName(), isSelected, hasFocus, row, column); label.setIcon(((Operator)value).getOperatorDescription().getSmallIcon()); return label; } else { if (column == 1) { JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (value == null) { label.setIcon(IMAGE_NO_QUICKFIX); label.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.errortable.no_quickfix_available.label")); } if (value instanceof List) { label.setIcon(IMAGE_QUICKFIX); label.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.errortable.choose_quickfix.label", ((List) value).size())); } if (value instanceof QuickFix) { QuickFix quickFix = (QuickFix) value; label.setIcon((Icon) quickFix.getAction().getValue(Action.SMALL_ICON)); label.setText(quickFix.toString()); } return label; } else { JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); label.setIcon(null); return label; } } } }; private final ExtendedJTable table = new ExtendedJTable() { private static final long serialVersionUID = 3731781319040565353L; @Override public TableCellRenderer getCellRenderer(int row, int column) { return iconRenderer; } @Override public void populatePopupMenu(JPopupMenu menu) { List<? extends QuickFix> fixes = errors.get(getSelectedRow()).getQuickFixes(); if (!fixes.isEmpty()) { JMenu fixMenu = new ResourceMenu("quick_fixes"); for (QuickFix fix : fixes) { fixMenu.add(fix.getAction()); } menu.add(fixMenu); menu.addSeparator(); } super.populatePopupMenu(menu); } @Override protected JTableHeader createDefaultTableHeader() { return new JTableHeader(columnModel) { private static final long serialVersionUID = -2000774622129683602L; @Override public String getToolTipText(MouseEvent e) { java.awt.Point p = e.getPoint(); int index = columnModel.getColumnIndexAtX(p.x); int realIndex = columnModel.getColumn(index).getModelIndex(); return COLUMN_TOOLTIPS[realIndex]; }; }; }; @Override public String getToolTipText(MouseEvent e) { Point p = e.getPoint(); int realColumnIndex = convertColumnIndexToModel(columnAtPoint(p)); int rowIndex = rowAtPoint(p); if (rowIndex >= 0 && rowIndex < getRowCount() && realColumnIndex == 1) { Object value = getModel().getValueAt(rowIndex, realColumnIndex); if (value == null) { return I18N.getMessage(I18N.getGUIBundle(), "gui.errortable.no_quickfix_available.tip"); } if (value instanceof List) { return I18N.getMessage(I18N.getGUIBundle(), "gui.errortable.choose_quickfix.tip", ((List) value).size()); } if (value instanceof QuickFix) { return ((QuickFix) value).toString(); } } return super.getToolTipText(e); } }; private final JLabel headerLabel = new JLabel(); private final JToggleButton onlyCurrent = new JToggleButton(new ResourceAction(true, "error_table_only_current") { private static final long serialVersionUID = -1454330266199555397L; @Override public void actionPerformed(ActionEvent e) { updateErrors(); } }); private List<ProcessSetupError> errors = new LinkedList<ProcessSetupError>(); private final AbstractTableModel model = new AbstractTableModel() { private static final long serialVersionUID = 1L; @Override public String getColumnName(int col) { return COLUMN_NAMES[col]; } @Override public boolean isCellEditable(int row, int col) { return false; } @Override public int getColumnCount() { return 3; } @Override public int getRowCount() { return errors.size(); } @Override public Object getValueAt(int rowIndex, int columnIndex) { ProcessSetupError error = errors.get(rowIndex); switch (columnIndex) { case 0: return error; case 1: List<? extends QuickFix> fixes = error.getQuickFixes(); if (fixes.size() > 1) { return fixes; } if (fixes.size() == 1) { return fixes.get(0); } return null; case 2: if (error instanceof MetaDataError) { return ((MetaDataError)error).getPort(); } else { return error.getOwner().getOperator(); } default: return null; } } }; private Process currentProcess; public ErrorTable(final MainFrame mainFrame) { super(new BorderLayout()); this.mainFrame = mainFrame; onlyCurrent.setSelected(false); table.setShowVerticalLines(false); table.setModel(model); table.installToolTip(); table.getColumnModel().getColumn(0).setPreferredWidth(400); table.getColumnModel().getColumn(1).setPreferredWidth(200); table.getColumnModel().getColumn(2).setPreferredWidth(150); headerLabel.setHorizontalAlignment(SwingConstants.CENTER); headerLabel.setBorder(BorderFactory.createEmptyBorder(6,6,6,6)); table.setBorder(null); JScrollPane scrollPane = new ExtendedJScrollPane(table); scrollPane.setBorder(null); add(scrollPane, BorderLayout.CENTER); ViewToolBar toolBar = new ViewToolBar(); toolBar.add(onlyCurrent); onlyCurrent.setText(null); toolBar.add(headerLabel); add(toolBar, BorderLayout.NORTH); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); table.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { switch (table.getSelectedColumn()) { // quick fixes case 1: List<? extends QuickFix> quickFixes = errors.get(table.getSelectedRow()).getQuickFixes(); if (quickFixes.size() == 1) { quickFixes.get(0).apply(); } if (quickFixes.size() > 1) { new QuickFixDialog(quickFixes).setVisible(true); } break; default: ProcessSetupError error = errors.get(table.getSelectedRow()); Operator op = error.getOwner().getOperator(); ErrorTable.this.mainFrame.selectOperator(op); // other } } } }); } private void updateErrors() { if ((currentOperator != null) && onlyCurrent.isSelected()) { fill(currentOperator); } else { if (currentProcess != null) { fill(currentProcess.getRootOperator()); } } } private void fill(Operator root) { int numTotal = root.getProcess().getRootOperator().getErrorList().size(); errors = root.getErrorList(); String errorString; switch (errors.size()) { case 0: errorString = "No problems found"; break; case 1: errorString = "One potential problem"; break; default: errorString = errors.size() + " potential problems"; break; } if (errors.size() != numTotal) { errorString = errorString + " (" + (numTotal-errors.size()) + " Filtered)"; } headerLabel.setText(errorString); model.fireTableDataChanged(); } @Override public void processChanged(Process process) { currentProcess = process; updateErrors(); } @Override public void processUpdated(Process process) { currentProcess = process; updateErrors(); } @Override public void setSelection(List<Operator> selection) { this.currentOperator = selection.isEmpty() ? null : selection.get(0); updateErrors(); } public static final String ERROR_TABLE_DOCK_KEY = "error_table"; private final DockKey DOCK_KEY = new ResourceDockKey(ERROR_TABLE_DOCK_KEY); { DOCK_KEY.setDockGroup(MainFrame.DOCK_GROUP_ROOT); } private Operator currentOperator; @Override public Component getComponent() { return this; } @Override public DockKey getDockKey() { return DOCK_KEY; } }