/* * WPCleaner: A tool to help on Wikipedia maintenance tasks. * Copyright (C) 2013 Nicolas Vervelle * * See README.txt file for licensing information. */ package org.wikipediacleaner.gui.swing.bot; import java.awt.Component; import java.awt.Point; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.beans.EventHandler; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.swing.ImageIcon; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JTable; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import org.wikipediacleaner.api.check.algorithm.CheckErrorAlgorithm; import org.wikipediacleaner.api.check.algorithm.CheckErrorAlgorithmComparator; import org.wikipediacleaner.api.check.algorithm.CheckErrorAlgorithms; import org.wikipediacleaner.api.constants.EnumWikipedia; import org.wikipediacleaner.gui.swing.basic.Utilities; import org.wikipediacleaner.gui.swing.component.BooleanIconCellRenderer; import org.wikipediacleaner.gui.swing.component.IconCellRenderer; import org.wikipediacleaner.i18n.GT; import org.wikipediacleaner.images.EnumImageSize; /** * A table model for Check Wiki errors in the bot tools. */ public class BotCWTableModel extends AbstractTableModel { /** * Serialization. */ private static final long serialVersionUID = 1124626680835189828L; /** * Wiki. */ private final EnumWikipedia wiki; /** * List of available algorithms. */ private final List<CheckErrorAlgorithm> algorithms; /** * List of bot algorithms. */ private final List<CheckErrorAlgorithm> botAlgorithms; /** * List of algorithms selected for fixing errors. */ private final List<CheckErrorAlgorithm> fixAlgorithms; /** * List of algorithms selected for listing errors. */ private final List<CheckErrorAlgorithm> listAlgorithms; public final static int COLUMN_NUMBER = 0; public final static int COLUMN_FIX = COLUMN_NUMBER + 1; public final static int COLUMN_LIST = COLUMN_FIX + 1; public final static int COLUMN_DESCRIPTION = COLUMN_LIST + 1; public final static int COLUMN_BOT = COLUMN_DESCRIPTION + 1; public final static int NB_COLUMNS = COLUMN_BOT + 1; /** * @param wiki Wiki. */ public BotCWTableModel(EnumWikipedia wiki) { this.wiki = wiki; List<CheckErrorAlgorithm> tmpAlgorithms = CheckErrorAlgorithms.getAlgorithms(wiki); algorithms = new ArrayList<CheckErrorAlgorithm>(); for (CheckErrorAlgorithm algorithm : tmpAlgorithms) { if (algorithm.isAvailable() && CheckErrorAlgorithms.isAlgorithmActive(wiki, algorithm.getErrorNumber())) { algorithms.add(algorithm); } } botAlgorithms = new ArrayList<CheckErrorAlgorithm>(); fixAlgorithms = new ArrayList<CheckErrorAlgorithm>(); listAlgorithms = new ArrayList<CheckErrorAlgorithm>(); addAlgorithm(1 , true , false); // Template namespace in template usage addAlgorithm(2 , true , false); // Article with false <br/> addAlgorithm(4 , true , false); // <a> tags addAlgorithm(6 , true , false); // DEFAULTSORT with special letter addAlgorithm(7 , false, false); // Headlines all start with three "=" addAlgorithm(9 , true , false); // Categories more at one line addAlgorithm(11 , true , false); // HTML named entities addAlgorithm(16 , true , false); // Template with Unicode control characters addAlgorithm(17 , true , false); // Category duplication addAlgorithm(18 , true , false); // Category first letter small addAlgorithm(19 , false, false); // Headlines start with one "=" addAlgorithm(20 , true , false); // Symbol for dead addAlgorithm(22 , true , false); // Category with space addAlgorithm(25 , false, false); // Headline hierarchy addAlgorithm(27 , false, false); // Unicode syntax addAlgorithm(32 , false, false); // Double pipe in one link addAlgorithm(37 , true , false); // Title with special letters and no DEFAULTSORT addAlgorithm(44 , false, false); // Headlines with bold addAlgorithm(45 , true , false); // Interwiki double addAlgorithm(50 , true , false); // en dash or em dash addAlgorithm(54 , true , false); // Break in list addAlgorithm(57 , false, false); // Headlines end with colon addAlgorithm(64 , true , false); // Link equal to link text addAlgorithm(66 , false, false); // Image description with full <small> addAlgorithm(76 , false, false); // Link with no space addAlgorithm(85 , true , false); // Tag without content addAlgorithm(87 , false, false); // HTML named entities without semicolon addAlgorithm(88 , true , false); // DEFAULTSORT with blank at first position addAlgorithm(90 , true , false); // Internal link written as external link addAlgorithm(91 , true , false); // Interwiki link written as external link addAlgorithm(92 , true , false); // Headline double addAlgorithm(524 , true , false); // Duplicate template argument } /** * Add an algorithm to the list of algorithms that can be fixed by bot. * * @param errorNumber Error number. * @param fix True if algorithm should be used by default for fixing errors. * @param list True if algorithm should be used by default for listing errors. */ private void addAlgorithm(int errorNumber, boolean fix, boolean list) { CheckErrorAlgorithm algorithm = CheckErrorAlgorithms.getAlgorithm(wiki, errorNumber); if ((algorithm != null) && (algorithm.isAvailable()) && CheckErrorAlgorithms.isAlgorithmActive(wiki, errorNumber)) { boolean active = false; if (errorNumber <= CheckErrorAlgorithm.MAX_ERROR_NUMBER_WITH_LIST) { if (CheckErrorAlgorithms.isAlgorithmActive(wiki, errorNumber)) { active = true; } } else { if (algorithm.hasSpecialList()) { active = true; } } if (active) { botAlgorithms.add(algorithm); if (fix) { fixAlgorithms.add(algorithm); } if (list) { listAlgorithms.add(algorithm); } } } } /** * Configure a column model. * * @param table Table for which the column model should be configured. */ public void configureColumnModel(JTable table) { if (table == null) { return; } TableColumnModel model = table.getColumnModel(); TableColumn column = null; column = model.getColumn(COLUMN_BOT); column.setMinWidth(20); column.setPreferredWidth(20); column.setMaxWidth(20); column.setCellRenderer( new BooleanIconCellRenderer("commons-approve-icon.png", null)); column.setHeaderRenderer( new IconCellRenderer("commons-nuvola-apps-kcmsystem.png")); column = model.getColumn(COLUMN_DESCRIPTION); column.setMinWidth(100); column = model.getColumn(COLUMN_FIX); column.setMinWidth(20); column.setPreferredWidth(20); column.setMaxWidth(20); column.setHeaderRenderer( new IconCellRenderer("commons-nuvola-apps-kcmsystem.png")); column = model.getColumn(COLUMN_LIST); column.setMinWidth(20); column.setPreferredWidth(20); column.setMaxWidth(20); column.setHeaderRenderer( new IconCellRenderer("gnome-logviewer.png")); column = model.getColumn(COLUMN_NUMBER); column.setMinWidth(40); column.setPreferredWidth(40); column.setMaxWidth(40); table.addMouseListener(EventHandler.create( MouseListener.class, this, "mouseClicked", "", "mouseClicked")); } /** * Called when user clicks on the table. * * @param e Mouse event. */ public void mouseClicked(MouseEvent e) { if (e == null) { return; } // Extract information int count = e.getClickCount(); JTable target = (JTable) e.getSource(); int row = target.rowAtPoint(e.getPoint()); int column = target.columnAtPoint(e.getPoint()); int modelRow = target.convertRowIndexToModel(row); int modelColumn = target.convertColumnIndexToModel(column); // Act depending on the button clicked switch (e.getButton()) { case MouseEvent.BUTTON1: cellClickedLeft(count, modelRow, modelColumn); break; case MouseEvent.BUTTON3: cellClickedRight(target, e.getPoint()); break; } } /** * Called when user left clicks on the table. * * @param count Number of clicks. * @param modelRow Row number for the model. * @param modelColumn Column number for the model. */ private void cellClickedLeft( int count, int modelRow, int modelColumn) { if (count == 1) { CheckErrorAlgorithm algorithm = getAlgorithm(modelRow); List<CheckErrorAlgorithm> list = null; switch (modelColumn) { case COLUMN_FIX: list = fixAlgorithms; break; case COLUMN_LIST: list = listAlgorithms; break; } if (list != null) { if (list.contains(algorithm)) { list.remove(algorithm); } else { list.add(algorithm); } fireTableCellUpdated(modelRow, modelColumn); } } } /** * Called when user right clicks on the table. * * @param invoker Invoker. * @param point Position of the click. */ private void cellClickedRight(Component invoker, Point point) { JPopupMenu menu = new JPopupMenu(); JMenuItem item = null; ImageIcon icon = null; icon = Utilities.getImageIcon( "commons-nuvola-apps-kcmsystem.png", EnumImageSize.SMALL); item = new JMenuItem( GT._("Activate automatic fixing for all algorithms"), icon); item.addActionListener(EventHandler.create( ActionListener.class, this, "addAllFixAlgorithms")); menu.add(item); item = new JMenuItem( GT._("Deactivate automatic fixing for all algorithms"), icon); item.addActionListener(EventHandler.create( ActionListener.class, this, "removeAllFixAlgorithms")); menu.add(item); item = new JMenuItem( GT._("Reverse the selection"), icon); item.addActionListener(EventHandler.create( ActionListener.class, this, "reverseFixAlgorithms")); menu.add(item); menu.addSeparator(); icon = Utilities.getImageIcon( "gnome-logviewer.png", EnumImageSize.SMALL); item = new JMenuItem( GT._("Use the list of pages of each algorithm"), icon); item.addActionListener(EventHandler.create( ActionListener.class, this, "addAllListAlgorithms")); menu.add(item); item = new JMenuItem( GT._("Use no list of pages"), icon); item.addActionListener(EventHandler.create( ActionListener.class, this, "removeAllListAlgorithms")); menu.add(item); item = new JMenuItem( GT._("Reverse the selection"), icon); item.addActionListener(EventHandler.create( ActionListener.class, this, "reverseListAlgorithms")); menu.add(item); menu.show(invoker, point.x, point.y); } /** * Select all algorithms for fixing errors. */ public void addAllFixAlgorithms() { setFixAlgorithms(getAlgorithms()); } /** * Select no algorithms for fixing errors. */ public void removeAllFixAlgorithms() { setFixAlgorithms(null); } /** * Reverse the selection of algorithms for fixing errors. */ public void reverseFixAlgorithms() { List<CheckErrorAlgorithm> tmpAlgorithms = new ArrayList<CheckErrorAlgorithm>(); for (CheckErrorAlgorithm algorithm : getAlgorithms()) { if (!fixAlgorithms.contains(algorithm)) { tmpAlgorithms.add(algorithm); } } setFixAlgorithms(tmpAlgorithms); } /** * Select all algorithms for listing errors. */ public void addAllListAlgorithms() { setListAlgorithms(getAlgorithms()); } /** * Select no algorithms for listing errors. */ public void removeAllListAlgorithms() { setListAlgorithms(null); } /** * Reverse the selection of algorithms for listing errors. */ public void reverseListAlgorithms() { List<CheckErrorAlgorithm> tmpAlgorithms = new ArrayList<CheckErrorAlgorithm>(); for (CheckErrorAlgorithm algorithm : getAlgorithms()) { if (!listAlgorithms.contains(algorithm)) { tmpAlgorithms.add(algorithm); } } setListAlgorithms(tmpAlgorithms); } /** * @return Algorithms. */ public List<CheckErrorAlgorithm> getAlgorithms() { return algorithms; } /** * @return Algorithms selected for fixing errors. */ public List<CheckErrorAlgorithm> getFixAlgorithms() { if (fixAlgorithms != null) { Collections.sort(fixAlgorithms, new CheckErrorAlgorithmComparator()); } return fixAlgorithms; } /** * @param fixAlgorithms Algorithms selected for fixing errors. */ public void setFixAlgorithms(List<CheckErrorAlgorithm> fixAlgorithms) { this.fixAlgorithms.clear(); if (fixAlgorithms != null) { this.fixAlgorithms.addAll(fixAlgorithms); } fireTableDataChanged(); } /** * @return Algorithms selected for listing errors. */ public List<CheckErrorAlgorithm> getListAlgorithms() { if (listAlgorithms != null) { Collections.sort(listAlgorithms, new CheckErrorAlgorithmComparator()); } return listAlgorithms; } /** * @param listAlgorithms Algorithms selected for listing errors. */ public void setListAlgorithms(List<CheckErrorAlgorithm> listAlgorithms) { this.listAlgorithms.clear(); if (listAlgorithms != null) { this.listAlgorithms.addAll(listAlgorithms); } fireTableDataChanged(); } /** * @param rowIndex Row index. * @return Algorithm. */ public CheckErrorAlgorithm getAlgorithm(int rowIndex) { return algorithms.get(rowIndex); } /** * @param rowIndex Row index. * @return true if the algorithm is for bot. */ public boolean isBotAlgorithm(int rowIndex) { CheckErrorAlgorithm algorithm = getAlgorithm(rowIndex); return botAlgorithms.contains(algorithm); } /** * @return Number of rows. * @see javax.swing.table.TableModel#getRowCount() */ @Override public int getRowCount() { return (algorithms != null) ? algorithms.size() : 0; } /** * @return Number of columns. * @see javax.swing.table.TableModel#getColumnCount() */ @Override public int getColumnCount() { return NB_COLUMNS; } /** * @param rowIndex Row index. * @param columnIndex Column index. * @return Value at row and column. * @see javax.swing.table.TableModel#getValueAt(int, int) */ @Override public Object getValueAt(int rowIndex, int columnIndex) { if ((rowIndex >= 0) && (rowIndex < algorithms.size())) { CheckErrorAlgorithm algorithm = algorithms.get(rowIndex); switch (columnIndex) { case COLUMN_BOT: return Boolean.valueOf(botAlgorithms.contains(algorithm)); case COLUMN_DESCRIPTION: return algorithm.getShortDescriptionReplaced(); case COLUMN_FIX: return Boolean.valueOf(fixAlgorithms.contains(algorithm)); case COLUMN_LIST: return Boolean.valueOf(listAlgorithms.contains(algorithm)); case COLUMN_NUMBER: return Integer.valueOf(algorithm.getErrorNumber()); } } return null; } /** * @param column Column index. * @return Name of column. * @see javax.swing.table.AbstractTableModel#getColumnName(int) */ @Override public String getColumnName(int column) { switch (column) { case COLUMN_BOT: return "Bot"; case COLUMN_DESCRIPTION: return GT._("Description"); case COLUMN_FIX: return "Fix"; case COLUMN_LIST: return "List"; case COLUMN_NUMBER: return GT._("N°"); } return super.getColumnName(column); } /** * @param columnIndex Column index. * @return Class of that data in the column. * @see javax.swing.table.AbstractTableModel#getColumnClass(int) */ @Override public Class<?> getColumnClass(int columnIndex) { switch (columnIndex) { case COLUMN_BOT: return Boolean.class; case COLUMN_DESCRIPTION: return String.class; case COLUMN_FIX: return Boolean.class; case COLUMN_LIST: return Boolean.class; case COLUMN_NUMBER: return Integer.class; } return super.getColumnClass(columnIndex); } }