/* * 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; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.Insets; import java.awt.event.ActionListener; import java.beans.EventHandler; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JTextPane; import javax.swing.JToggleButton; import javax.swing.JToolBar; import javax.swing.ScrollPaneConstants; import javax.swing.SwingConstants; import javax.swing.WindowConstants; import javax.swing.table.TableColumnModel; import org.wikipediacleaner.api.constants.EnumWikipedia; import org.wikipediacleaner.api.data.AutomaticFixing; import org.wikipediacleaner.api.data.Page; import org.wikipediacleaner.api.data.PageComparator; import org.wikipediacleaner.gui.swing.action.ActionDisambiguationAnalysis; import org.wikipediacleaner.gui.swing.action.ActionExternalViewer; import org.wikipediacleaner.gui.swing.action.ActionFullAnalysis; import org.wikipediacleaner.gui.swing.basic.BasicWindow; import org.wikipediacleaner.gui.swing.basic.BasicWorker; import org.wikipediacleaner.gui.swing.basic.DefaultBasicWindowListener; import org.wikipediacleaner.gui.swing.basic.DefaultBasicWorkerListener; import org.wikipediacleaner.gui.swing.basic.Utilities; import org.wikipediacleaner.gui.swing.component.PageListCellRenderer; import org.wikipediacleaner.gui.swing.component.PageListModel; import org.wikipediacleaner.gui.swing.worker.AutomaticFixingWorker; import org.wikipediacleaner.i18n.GT; import org.wikipediacleaner.images.EnumImageSize; import org.wikipediacleaner.utils.Configuration; /** * Automatic fixing window. */ public class AutomaticFixingWindow extends OnePageWindow { //public final static Integer WINDOW_VERSION = Integer.valueOf(2); private JTable tableAutomaticFixing; private AutomaticFixingTableModel modelAutomaticFixing; private JButton buttonAdd; private JButton buttonModify; private JButton buttonRemove; private JButton buttonClear; private JButton buttonRun; private JButton buttonSave; private JButton buttonTest; private JToggleButton buttonAutomaticCW; private JCheckBox chkForceCW; private JTextPane paneOriginal; private JTextPane paneResult; JList<Page> listPages; PageListModel modelPages; private PageListCellRenderer listCellRenderer; Collection<Page> pages; /** * Create and display an AutomaticFixingWindow. * * @param pages List of pages for automatic fixing. * @param referencePage Reference page. * @param wikipedia Wikipedia. */ public static void createAutomaticFixingWindow( final Collection<Page> pages, final Page referencePage, final EnumWikipedia wikipedia) { createWindow( "AutomaticFixingWindow", wikipedia, WindowConstants.DISPOSE_ON_CLOSE, AutomaticFixingWindow.class, new DefaultBasicWindowListener() { @Override public void initializeWindow(BasicWindow window) { if (window instanceof AutomaticFixingWindow) { AutomaticFixingWindow fixing = (AutomaticFixingWindow) window; fixing.setPage(referencePage); fixing.pages = pages; fixing.modelPages = new PageListModel(); fixing.modelPages.setComparator(PageComparator.getNamespaceFirstComparator()); fixing.modelPages.setShowDisambiguation(true); fixing.modelPages.setShowOther(true); fixing.modelPages.setShowRedirect(true); } } @Override public void displayWindow(BasicWindow window) { if (window instanceof AutomaticFixingWindow) { AutomaticFixingWindow fixing = (AutomaticFixingWindow) window; fixing.actionReload(); } } }); } /** * @return Window title. * @see org.wikipediacleaner.gui.swing.basic.BasicWindow#getTitle() */ @Override public String getTitle() { if (getPageName() != null) { return GT._("Automatic fixing") + " - " + getPageName(); } return GT._("Automatic fixing"); } /** * @return Window components. */ @Override protected Component createComponents() { JPanel panel = new JPanel(new GridBagLayout()); // Initialize constraints GridBagConstraints constraints = new GridBagConstraints(); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridheight = 1; constraints.gridwidth = 1; constraints.gridx = 0; constraints.gridy = 0; constraints.insets = new Insets(2, 2, 2, 2); constraints.ipadx = 0; constraints.ipady = 0; constraints.weightx = 0; constraints.weighty = 0; // Contents constraints.fill = GridBagConstraints.BOTH; constraints.gridwidth = 1; constraints.gridx = 0; constraints.weightx = 1; constraints.weighty = 1; JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); split.setLeftComponent(createLinksComponents()); split.setRightComponent(createAutomaticFixingComponents()); split.setPreferredSize(new Dimension(1000, 700)); split.setMinimumSize(new Dimension(200, 200)); split.setResizeWeight(0.0); split.setDividerLocation(200 + split.getInsets().left); panel.add(split, constraints); constraints.gridy++; updateComponentState(); return panel; } /** * @return Automatic fixing components. */ private Component createAutomaticFixingComponents() { JPanel panel = new JPanel(new GridBagLayout()); // Initialize constraints GridBagConstraints constraints = new GridBagConstraints(); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridheight = 1; constraints.gridwidth = 1; constraints.gridx = 0; constraints.gridy = 0; constraints.insets = new Insets(1, 1, 1, 1); constraints.ipadx = 0; constraints.ipady = 0; constraints.weightx = 1; constraints.weighty = 0; // Warning String txtWarning = GT._("This feature may modify a lot of pages in a short period of time.") + "\n" + GT._("On some Wikipedia projects, you may need the bot status for doing this.") + "\n" + GT._("Please, check if you need the bot status by reading the rules of Wikipedia."); JTextArea lblWarning = new JTextArea(txtWarning); lblWarning.setEditable(false); lblWarning.setBackground(getParentComponent().getBackground()); lblWarning.setForeground(Color.RED); constraints.fill = GridBagConstraints.BOTH; constraints.gridwidth = 2; constraints.weighty = 0; panel.add(lblWarning, constraints); constraints.gridy++; // Commands JToolBar toolBarButtons = new JToolBar(SwingConstants.HORIZONTAL); toolBarButtons.setFloatable(false); buttonAdd = Utilities.createJButton( "gnome-list-add.png", EnumImageSize.NORMAL, GT._("Add"), false, null); buttonAdd.addActionListener(EventHandler.create( ActionListener.class, this, "actionAddAutomaticFixing")); toolBarButtons.add(buttonAdd); buttonRemove = Utilities.createJButton( "gnome-list-remove.png", EnumImageSize.NORMAL, GT._("Remove"), false, null); buttonRemove.addActionListener(EventHandler.create( ActionListener.class, this, "ActionRmvAutomaticFixing")); toolBarButtons.add(buttonRemove); buttonModify = Utilities.createJButton( "gnome-accessories-text-editor.png", EnumImageSize.NORMAL, GT._("Modify"), false, null); buttonModify.addActionListener(EventHandler.create( ActionListener.class, this, "actionMdfAutomaticFixing")); toolBarButtons.add(buttonModify); buttonClear = Utilities.createJButton( "gnome-edit-clear.png", EnumImageSize.NORMAL, GT._("Clear"), false, null); buttonClear.addActionListener(EventHandler.create( ActionListener.class, this, "actionClrAutomaticFixing")); toolBarButtons.add(buttonClear); buttonSave = Utilities.createJButton( "gnome-media-floppy.png", EnumImageSize.NORMAL, GT._("Save"), false, null); buttonSave.addActionListener(EventHandler.create( ActionListener.class, this, "actionSaveAutomaticFixing")); toolBarButtons.add(buttonSave); toolBarButtons.addSeparator(); buttonAutomaticCW = Utilities.createJToggleButton( "commons-nuvola-web-broom.png", EnumImageSize.NORMAL, GT._("Automatic fixing for Check Wiki"), false); buttonAutomaticCW.setSelected(true); toolBarButtons.add(buttonAutomaticCW); toolBarButtons.addSeparator(); buttonRun = Utilities.createJButton( "gnome-system-run.png", EnumImageSize.NORMAL, GT._("Fix selected pages"), false, null); buttonRun.addActionListener(EventHandler.create( ActionListener.class, this, "actionRunAutomaticFixing")); toolBarButtons.add(buttonRun); buttonTest = Utilities.createJButton( "gnome-edit-find.png", EnumImageSize.NORMAL, GT._("Test automatic replacements"), false, null); buttonTest.addActionListener(EventHandler.create( ActionListener.class, this, "actionTestAutomaticFixing")); toolBarButtons.add(buttonTest); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridwidth = 2; constraints.weighty = 0; panel.add(toolBarButtons, constraints); constraints.gridy++; chkForceCW = Utilities.createJCheckBox( GT._("Always apply automatic fixing for Check Wiki"), false); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridwidth = 2; constraints.weighty = 0; panel.add(chkForceCW, constraints); constraints.gridy++; // Comment constraints.gridwidth = 1; addComment(panel, constraints); constraints.gridy++; constraints.gridx = 0; // Automatic fixing list modelAutomaticFixing = new AutomaticFixingTableModel(null); tableAutomaticFixing = new JTable(modelAutomaticFixing); TableColumnModel columnModel = tableAutomaticFixing.getColumnModel(); columnModel.getColumn(AutomaticFixingTableModel.COLUMN_FROM).setMinWidth(200); columnModel.getColumn(AutomaticFixingTableModel.COLUMN_TO).setMinWidth(200); columnModel.getColumn(AutomaticFixingTableModel.COLUMN_REGEX).setMinWidth(40); columnModel.getColumn(AutomaticFixingTableModel.COLUMN_REGEX).setPreferredWidth(40); columnModel.getColumn(AutomaticFixingTableModel.COLUMN_REGEX).setMaxWidth(40); JScrollPane scrollAutomaticFixing = new JScrollPane(tableAutomaticFixing); scrollAutomaticFixing.setMinimumSize(new Dimension(100, 100)); scrollAutomaticFixing.setPreferredSize(new Dimension(400, 150)); scrollAutomaticFixing.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); constraints.fill = GridBagConstraints.BOTH; constraints.gridwidth = 2; constraints.weighty = 1; panel.add(scrollAutomaticFixing, constraints); constraints.gridy++; // Test panel JPanel testPanel = new JPanel(new GridLayout(1, 0)); testPanel.setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), GT._("Test automatic replacements"))); paneOriginal = new JTextPane(); JScrollPane scrollOriginal = new JScrollPane(paneOriginal); scrollOriginal.setMinimumSize(new Dimension(50, 50)); scrollOriginal.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); testPanel.add(scrollOriginal); paneResult = new JTextPane(); paneResult.setEditable(false); JScrollPane scrollResult = new JScrollPane(paneResult); scrollResult.setMinimumSize(new Dimension(50, 50)); scrollResult.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); testPanel.add(scrollResult); constraints.weighty = 0.5; panel.add(testPanel, constraints); constraints.gridy++; return panel; } /** * @return Links components. */ private Component createLinksComponents() { JPanel panel = new JPanel(new GridBagLayout()); listPages = new JList<Page>(modelPages); // Initialize constraints GridBagConstraints constraints = new GridBagConstraints(); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridheight = 1; constraints.gridwidth = 1; constraints.gridx = 0; constraints.gridy = 0; constraints.insets = new Insets(0, 0, 0, 0); constraints.ipadx = 0; constraints.ipady = 0; constraints.weightx = 1; constraints.weighty = 0; // Button toolbar JToolBar toolbar = new JToolBar(SwingConstants.HORIZONTAL); toolbar.setFloatable(false); ActionFullAnalysis.addButton( getParentComponent(), toolbar, getWikipedia(), listPages, null, true, true); ActionDisambiguationAnalysis.addButton( getParentComponent(), toolbar, getWikipedia(), listPages, true, true); ActionExternalViewer.addButton( toolbar, getWikipedia(), listPages, false, true, true); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.weighty = 0; panel.add(toolbar, constraints); constraints.gridy++; // Pages listCellRenderer = new PageListCellRenderer(); listCellRenderer.showRedirect(true); listPages.setCellRenderer(listCellRenderer); JScrollPane scrollPages = new JScrollPane(listPages); scrollPages.setMinimumSize(new Dimension(100, 100)); scrollPages.setPreferredSize(new Dimension(200, 500)); scrollPages.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); constraints.fill = GridBagConstraints.BOTH; constraints.weighty = 1; panel.add(scrollPages, constraints); constraints.gridy++; return panel; } /** * Update status of components * @see org.wikipediacleaner.gui.swing.OnePageWindow#updateComponentState() */ @Override protected void updateComponentState() { super.updateComponentState(); boolean hasReference = (getPage() != null); buttonAdd.setEnabled(true); buttonClear.setEnabled(true); buttonModify.setEnabled(true); buttonRemove.setEnabled(true); buttonRun.setEnabled(true); buttonSave.setEnabled(hasReference); buttonTest.setEnabled(true); } /** * Clean page. */ @Override protected void clean() { modelPages.clear(); updateComponentState(); } /** * @return Currently selected automatic fixing expression. */ protected AutomaticFixing getSelectedAutomaticFixing() { int row = tableAutomaticFixing.getSelectedRow(); if (row < 0) { return null; } return modelAutomaticFixing.getAutomaticFixing( Utilities.convertRowIndexToModel(tableAutomaticFixing, row)); } /** * Action called when Reload button is pressed. */ @Override protected void actionReload() { clean(); // Fill list of pages if (pages != null) { modelPages.setElements(pages); } listPages.clearSelection(); listPages.setSelectionInterval(0, modelPages.getSize() - 1); // Fill list of automatic fixing Configuration config = Configuration.getConfiguration(); Page page = getPage(); if (page != null) { Object[] automaticFixing = config.getPojoArray( page.getWikipedia(), Configuration.POJO_AUTOMATIC_FIXING, page.getTitle(), AutomaticFixing.class); if (automaticFixing != null) { List<AutomaticFixing> data = new ArrayList<AutomaticFixing>(automaticFixing.length); for (int i = 0; i < automaticFixing.length; i++) { if (automaticFixing[i] instanceof AutomaticFixing) { data.add((AutomaticFixing) automaticFixing[i]); } } modelAutomaticFixing.setData(data); } else { modelAutomaticFixing.setData(null); } } // Update components updateComponentState(); } /** * Action called when Add Automatic Fixing button is pressed. */ public void actionAddAutomaticFixing() { AutomaticFixing fixing = new AutomaticFixing(); if (inputAutomaticFixing(fixing)) { modelAutomaticFixing.addAutomaticFixing(fixing); } } /** * Action called when Modify Automatic Fixing button is pressed. */ public void actionMdfAutomaticFixing() { AutomaticFixing fixing = getSelectedAutomaticFixing(); if (fixing == null) { return; } if (inputAutomaticFixing(fixing)) { tableAutomaticFixing.repaint(); } } /** * Display a dialog box to edit an automatic fixing expression. * * @param fixing Automatic fixing expression. * @return True if the edition has been done. */ private boolean inputAutomaticFixing(AutomaticFixing fixing) { // Create a panel for information about an automatic fixing expression. JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints constraints = new GridBagConstraints(); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridheight = 1; constraints.gridwidth = 1; constraints.gridx = 0; constraints.gridy = 0; constraints.insets = new Insets(0, 0, 0, 0); constraints.ipadx = 0; constraints.ipady = 0; constraints.weightx = 1; constraints.weighty = 0; // Initial text JLabel labelOriginal = Utilities.createJLabel(GT._("Text that should be replaced")); constraints.weightx = 0; panel.add(labelOriginal, constraints); constraints.gridx++; JTextField textOriginal = Utilities.createJTextField(fixing.getOriginalText(), 50); constraints.weightx = 1; panel.add(textOriginal, constraints); constraints.gridy++; constraints.gridx = 0; // Replacement text JLabel labelReplacement = Utilities.createJLabel(GT._("Text to be used as a replacement")); constraints.weightx = 0; panel.add(labelReplacement, constraints); constraints.gridx++; JTextField textReplacement = Utilities.createJTextField(fixing.getReplacementText(), 50); constraints.weightx = 1; panel.add(textReplacement, constraints); constraints.gridy++; constraints.gridx = 0; // Regular expression JCheckBox chkRegex = Utilities.createJCheckBox(GT._("Use regular expressions"), fixing.getRegex()); constraints.gridwidth = 2; constraints.weightx = 1; panel.add(chkRegex, constraints); constraints.gridwidth = 1; constraints.gridy++; int result = JOptionPane.showConfirmDialog( getParentComponent(), panel, GT._("Automatic fixing"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); if (result != JOptionPane.OK_OPTION) { return false; } String originalText = textOriginal.getText(); if ((originalText == null) || (originalText.length() == 0)) { return false; } String replacementText = textReplacement.getText(); if (replacementText == null) { return false; } fixing.setOriginalText(originalText); fixing.setReplacementText(replacementText); fixing.setRegex(chkRegex.isSelected()); return true; } /** * Action called when Remove Automatic Fixing button is pressed. */ public void ActionRmvAutomaticFixing() { AutomaticFixing fixing = getSelectedAutomaticFixing(); if (fixing != null) { modelAutomaticFixing.removeAutomaticFixing(fixing); } } /** * Action called when Clear Automatic Fixing button is pressed. */ public void actionClrAutomaticFixing() { modelAutomaticFixing.setData(null); } /** * Action called when Run Automatic Fixing button is pressed. */ public void actionRunAutomaticFixing() { runAutomaticFixing(true); } /** * Action called when Run Automatic Fixing button is pressed. * * @param save True if modifications should be saved. */ private void runAutomaticFixing(boolean save) { // Check that information is set List<Page> values = listPages.getSelectedValuesList(); if ((values == null) || (values.size() == 0)) { Utilities.displayWarning( getParentComponent(), GT._("You must select the pages on which running automatic fixing.")); return; } String comment = getComment(); if ((comment != null) && (comment.trim().length() == 0)) { if (save) { Utilities.displayWarning(getParentComponent(), GT._( "A comment is required for automatic fixing.")); return; } comment = "Test"; } List<AutomaticFixing> fixing = modelAutomaticFixing.getData(); if ((fixing == null) || (fixing.isEmpty())) { if (!buttonAutomaticCW.isSelected() || !chkForceCW.isSelected()) { Utilities.displayWarning( getParentComponent(), GT._("You must input the initial and destination texts.")); return; } } // Warn the user about what this function does if (save) { int answer = Utilities.displayYesNoWarning( getParentComponent(), GT._("!!! WARNING !!!") + "\n" + GT._("This function will carry out all replacements on all selected pages.") + "\n" + GT._("It may modify a lot of pages in a short period of time.") + "\n" + GT._("On some Wikipedia projects, you may need the bot status for doing this.") + "\n" + GT._("Please, check if you need the bot status by reading the rules of Wikipedia.") + "\n" + GT._("Also, verify again the texts you have inputted before running this function.") + "\n" + GT._("You can also test the modifications before actually doing them.") + "\n" + GT._("Do you want to proceed with the modifications ?")); if (answer != JOptionPane.YES_OPTION) { return; } } // Prepare the replacements Page[] tmpPages = new Page[values.size()]; for (int i = 0; i < values.size(); i++) { tmpPages[i] = values.get(i); } Map<String, List<AutomaticFixing>> replacements = new HashMap<String, List<AutomaticFixing>>(); if (getPage() != null) { replacements.put("[[" + getPage().getTitle() + "]]", fixing); } else { replacements.put(null, fixing); } // Do the replacements AutomaticFixingWorker dabWorker = new AutomaticFixingWorker( getWikipedia(), this, tmpPages, replacements, comment, true, buttonAutomaticCW.isSelected(), chkForceCW.isSelected(), save); dabWorker.setListener(new DefaultBasicWorkerListener() { @Override public void afterFinished( BasicWorker worker, @SuppressWarnings("unused") boolean ok) { if (!worker.shouldContinue()) { return; } if (worker.get() instanceof Integer) { Integer count = (Integer) worker.get(); if (count.intValue() == 0) { return; } } actionReload(); } }); dabWorker.start(); } /** * Action called when Test Automatic Fixing button is pressed. */ public void actionTestAutomaticFixing() { // Test replacements in the test pane String text = paneOriginal.getText(); if ((text != null) && (text.trim().length() > 0)) { List<AutomaticFixing> fixing = modelAutomaticFixing.getData(); List<String> replacements = new ArrayList<String>(); text = AutomaticFixing.apply(fixing, text, replacements); paneResult.setText(text); StringBuilder tmp = new StringBuilder(); tmp.append(GT._("The following replacements have been made:")); for (String replacement : replacements) { tmp.append("\n - "); tmp.append(replacement); } Utilities.displayInformationMessage(getParentComponent(), tmp.toString()); } // Test replacements in the page list List<Page> values = listPages.getSelectedValuesList(); if ((values != null) && (values.size() > 0)) { String message = GT._("Do you want to test the replacements on the pages ?"); int answer = Utilities.displayYesNoWarning(getParentComponent(), message); if (answer == JOptionPane.YES_OPTION) { runAutomaticFixing(false); } } } /** * Action called when Save Automatic Fixing button is pressed. */ public void actionSaveAutomaticFixing() { Configuration config = Configuration.getConfiguration(); List<AutomaticFixing> fixing = modelAutomaticFixing.getData(); Object[] replacements = (fixing != null) ? fixing.toArray() : null; config.addPojoArray( getPage().getWikipedia(), Configuration.POJO_AUTOMATIC_FIXING, replacements, getPage().getTitle()); } }