/*
* WPCleaner: A tool to help on Wikipedia maintenance tasks.
* Copyright (C) 2013 Nicolas Vervelle
*
* See README.txt file for licensing information.
*/
package org.wikipediacleaner.api.check.algorithm;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter.HighlightPainter;
import org.wikipediacleaner.api.check.CheckErrorResult;
import org.wikipediacleaner.api.check.CheckErrorResult.ErrorLevel;
import org.wikipediacleaner.api.check.SpecialCharacters;
import org.wikipediacleaner.api.constants.CWConfigurationError;
import org.wikipediacleaner.api.constants.EnumWikipedia;
import org.wikipediacleaner.api.constants.WPCConfigurationString;
import org.wikipediacleaner.api.data.MagicWord;
import org.wikipediacleaner.api.data.Page;
import org.wikipediacleaner.api.data.PageAnalysis;
import org.wikipediacleaner.gui.swing.component.MWPane;
import org.wikipediacleaner.i18n.GT;
/**
* Abstract base class for analyzing errors.
*/
public abstract class CheckErrorAlgorithmBase implements CheckErrorAlgorithm {
/**
* Configuration of the error.
*/
private CWConfigurationError configuration;
private final String name;
/**
* @param name Name of the error.
*/
public CheckErrorAlgorithmBase(String name) {
this.name = name;
}
/**
* @return Name of the error.
*/
public String getName() {
return name;
}
/**
* @return Textual representation of the object.
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return getShortDescriptionReplaced();
}
/**
* @return Flag indicating if this algorithm is available.
*/
@Override
public boolean isAvailable() {
return true;
}
/**
* @return True if the error has a list of pages.
*/
@Override
public boolean hasList() {
if (getErrorNumber() < MAX_ERROR_NUMBER_WITH_LIST) {
return true;
}
return hasSpecialList();
}
/**
* @return True if the error has a special list of pages.
*/
@Override
public boolean hasSpecialList() {
return false;
}
/**
* Retrieve the list of pages in error.
*
* @param wiki Wiki.
* @param limit Maximum number of pages to retrieve.
* @return List of pages in error.
*/
@Override
public List<Page> getSpecialList(EnumWikipedia wiki, int limit) {
return null;
}
/**
* @param configuration Configuration of the error.
* @see org.wikipediacleaner.api.check.algorithm.CheckErrorAlgorithm#setConfiguration(org.wikipediacleaner.api.constants.CWConfigurationError)
*/
@Override
public void setConfiguration(CWConfigurationError configuration) {
this.configuration = configuration;
}
/**
* @return Short description of the error.
* (See Check Wikipedia project for the description of errors)
*/
@Override
public String getShortDescription() {
return configuration.getShortDescription();
}
/**
* @return Short description of the error.
*/
@Override
public String getShortDescriptionReplaced() {
return configuration.getShortDescriptionReplaced();
}
/**
* @return Long description of the error.
* (See Check Wikipedia project for the description of errors)
*/
@Override
public String getLongDescription() {
return configuration.getLongDescription();
}
/**
* @return Link to error description.
*/
@Override
public String getLink() {
return configuration.getLink();
}
/**
* Tell if a page is among the white list.
*
* @param title Page title.
* @return Page among the white list ?
*/
@Override
public boolean isInWhiteList(String title) {
return configuration.isInWhiteList(title);
}
/**
* @return White list page name.
*/
@Override
public String getWhiteListPageName() {
return configuration.getWhiteListPageName();
}
/**
* Create a CheckErrorResult object.
*
* @param analysis Page analysis
* @param startPosition Start of the error.
* @param endPosition End of the error.
* @return new CheckErrorResult object.
*/
public CheckErrorResult createCheckErrorResult(
PageAnalysis analysis,
int startPosition, int endPosition) {
return createCheckErrorResult(
analysis,
startPosition, endPosition,
ErrorLevel.ERROR);
}
/**
* Create a CheckErrorResult object.
*
* @param analysis Page analysis
* @param startPosition Start of the error.
* @param endPosition End of the error.
* @param errorLevel Error level.
* @return new CheckErrorResult object.
*/
public CheckErrorResult createCheckErrorResult(
PageAnalysis analysis,
int startPosition, int endPosition,
ErrorLevel errorLevel) {
if ((!ErrorLevel.CORRECT.equals(errorLevel)) &&
(analysis != null) &&
(analysis.getPage() != null)) {
if (isInWhiteList(analysis.getPage().getTitle())) {
errorLevel = ErrorLevel.CORRECT;
}
}
return new CheckErrorResult(
this, analysis.getPage(),
startPosition, endPosition,
errorLevel);
}
/**
* @return Priority.
*/
@Override
public int getPriority() {
return configuration.getPriority();
}
/**
* @return Error number.
* (See Check Wikipedia project for the description of errors)
*/
@Override
public String getErrorNumberString() {
String baseName = CheckErrorAlgorithm.class.getName();
String className = getClass().getName();
if (className.startsWith(baseName)) {
return className.substring(baseName.length());
}
return "unknown";
}
/**
* @return Error number.
* (See Check Wikipedia project for the description of errors)
*/
@Override
public int getErrorNumber() {
int errorNumber = -1;
try {
String errorNumberString = getErrorNumberString();
errorNumber = Integer.parseInt(errorNumberString);
} catch (NumberFormatException e) {
//
}
return errorNumber;
}
/**
* @param property Property name.
* @param useWiki Flag indicating if wiki configuration can be used.
* @param useGeneral Flag indicating if general configuration can be used.
* @param acceptEmpty Flag indicating if empty strings are accepted.
* @return Property value.
*/
@Override
public String getSpecificProperty(
String property,
boolean useWiki, boolean useGeneral, boolean acceptEmpty) {
return configuration.getSpecificProperty(property, useWiki, useGeneral, useWiki, acceptEmpty);
}
/**
* Return the parameters used to configure the algorithm.
*
* @return Map of parameters (Name -> description).
*/
@Override
public Map<String, String> getParameters() {
Map<String, String> parameters = new Hashtable<String, String>();
parameters.put("link", GT._("Title of the article describing this type of error"));
parameters.put("noauto", GT._("Set to true to prevent automatic modifications for this type of error"));
parameters.put("whitelist", GT._("List of false positives for this type of error"));
parameters.put("whitelistpage", GT._("Page containing the list of false positives for this type of error"));
return parameters;
}
/**
* Automatic fixing of all the errors in the page.
*
* @param analysis Page analysis.
* @return Page contents after fix.
*/
@Override
public final String automaticFix(PageAnalysis analysis) {
if (configuration.getNoAuto()) {
return analysis.getContents();
}
return internalAutomaticFix(analysis);
}
/**
* Automatic fixing of all the errors in the page.
*
* @param analysis Page analysis.
* @return Page contents after fix.
*/
protected String internalAutomaticFix(PageAnalysis analysis) {
return analysis.getContents();
}
/**
* Bot fixing of all the errors in the page.
*
* @param analysis Page analysis.
* @return Page contents after fix.
*/
@Override
public final String botFix(PageAnalysis analysis) {
if (configuration.getNoAuto()) {
return analysis.getContents();
}
return internalBotFix(analysis);
}
/**
* Bot fixing of all the errors in the page.
*
* @param analysis Page analysis.
* @return Page contents after fix.
*/
protected String internalBotFix(PageAnalysis analysis) {
return internalAutomaticFix(analysis);
}
/**
* @return List of possible global fixes.
*/
@Override
public String[] getGlobalFixes() {
return null;
}
/**
* Fix all the errors in the page.
*
* @param fixName Fix name (extracted from getGlobalFixes()).
* @param analysis Page analysis.
* @param textPane Text pane.
* @return Page contents after fix.
*/
@Override
public String fix(String fixName, PageAnalysis analysis, MWPane textPane) {
throw new IllegalStateException("This algorithm has no global fixes");
}
/**
* Fix all the errors in the page by using the first replacement proposed.
*
* @param fixName Fix name (extracted from getGlobalFixes()).
* @param analysis Page analysis.
* @return Page contents after fix.
*/
public String fixUsingFirstReplacement(String fixName, PageAnalysis analysis) {
String result = analysis.getContents();
List<CheckErrorResult> errors = new ArrayList<CheckErrorResult>();
if (analyze(analysis, errors, false)) {
for (int i = errors.size(); i > 0; i--) {
CheckErrorResult errorResult = errors.get(i - 1);
String newText = errorResult.getFirstReplacement();
if (newText != null) {
String tmp =
result.substring(0, errorResult.getStartPosition()) +
newText +
result.substring(errorResult.getEndPosition());
result = tmp;
}
}
}
return result;
}
/**
* Fix all the errors in the page by using automatic replacement proposed.
*
* @param analysis Page analysis.
* @return Page contents after fix.
*/
public String fixUsingAutomaticReplacement(PageAnalysis analysis) {
String result = analysis.getContents();
List<CheckErrorResult> errors = new ArrayList<CheckErrorResult>();
if (analyze(analysis, errors, true)) {
Collections.sort(errors);
for (int i = errors.size(); i > 0; i--) {
CheckErrorResult errorResult = errors.get(i - 1);
String newText = errorResult.getAutomaticReplacement();
if (newText != null) {
String tmp =
result.substring(0, errorResult.getStartPosition()) +
newText +
result.substring(errorResult.getEndPosition());
result = tmp;
}
}
}
return result;
}
/**
* Fix all the errors in the page by using automatic bot replacement proposed.
*
* @param analysis Page analysis.
* @return Page contents after fix.
*/
public String fixUsingAutomaticBotReplacement(PageAnalysis analysis) {
String result = analysis.getContents();
List<CheckErrorResult> errors = new ArrayList<CheckErrorResult>();
if (analyze(analysis, errors, true)) {
Collections.sort(errors);
for (int i = errors.size(); i > 0; i--) {
CheckErrorResult errorResult = errors.get(i - 1);
String newText = errorResult.getAutomaticBotReplacement();
if (newText != null) {
String tmp =
result.substring(0, errorResult.getStartPosition()) +
newText +
result.substring(errorResult.getEndPosition());
result = tmp;
}
}
}
return result;
}
/**
* Fix all the errors in the page by removing.
*
* @param fixName Fix name (extracted from getGlobalFixes()).
* @param analysis Page analysis.
* @return Page contents after fix.
*/
public String fixUsingRemove(String fixName, PageAnalysis analysis) {
String result = analysis.getContents();
List<CheckErrorResult> errors = new ArrayList<CheckErrorResult>();
if (analyze(analysis, errors, false)) {
for (int i = errors.size(); i > 0; i--) {
CheckErrorResult errorResult = errors.get(i - 1);
String tmp =
result.substring(0, errorResult.getStartPosition()) +
result.substring(errorResult.getEndPosition());
result = tmp;
}
}
return result;
}
/**
* @param contents Contents.
* @param startIndex Index for starting the search.
* @param characters Authorized characters.
* @return First index after the start index that is not an authorized character.
*/
protected int getFirstIndexAfter(String contents, int startIndex, String characters) {
if ((contents == null) || (characters == null)) {
return startIndex;
}
int maxLength = contents.length();
while ((startIndex < maxLength) &&
(characters.indexOf(contents.charAt(startIndex)) >= 0)) {
startIndex++;
}
return startIndex;
}
/**
* @param contents Contents.
* @param startIndex Index for starting the search.
* @return First index after the start index that is not a space character.
*/
protected int getFirstIndexAfterSpace(String contents, int startIndex) {
return getFirstIndexAfter(contents, startIndex, " ");
}
protected int getLastIndexBefore(String contents, int startIndex, String characters) {
if ((contents == null) || (characters == null)) {
return startIndex;
}
while ((startIndex >= 0) &&
(characters.indexOf(contents.charAt(startIndex)) >= 0)) {
startIndex--;
}
return startIndex;
}
/**
* @param contents Contents.
* @param startIndex Index for starting the search.
* @return Last index before the start index that is not a space character.
*/
protected int getLastIndexBeforeSpace(String contents, int startIndex) {
return getLastIndexBefore(contents, startIndex, " ");
}
/**
* @param contents Contents.
* @param startIndex Index for starting the search.
* @return Last index before the start index that is not a whitespace character.
*/
protected int getLastIndexBeforeWhiteSpace(String contents, int startIndex) {
if (contents == null) {
return startIndex;
}
while ((startIndex >= 0) && (Character.isWhitespace(contents.charAt(startIndex)))) {
startIndex--;
}
return startIndex;
}
/**
* Search for simple text in page.
*
* @param pageAnalysis Page analysis.
* @param errors Errors found in the page.
* @param search Text to be searched.
* @return Flag indicating if the error was found.
*/
protected boolean simpleTextSearch(
PageAnalysis pageAnalysis,
Collection<CheckErrorResult> errors, String search) {
return simpleTextSearch(pageAnalysis, errors, search, (String[]) null);
}
/**
* Search for simple text in page.
*
* @param pageAnalysis Page analysis.
* @param errors Errors found in the page.
* @param search Text to be searched.
* @param replacement Text proposed as a replacement.
* @return Flag indicating if the error was found.
*/
protected boolean simpleTextSearch(
PageAnalysis pageAnalysis, Collection<CheckErrorResult> errors,
String search, String replacement) {
return simpleTextSearch(
pageAnalysis, errors, search,
(replacement != null) ? new String[] { replacement } : null);
}
/**
* Search for simple text in page.
*
* @param pageAnalysis Page analysis.
* @param begin Index in text to begin search.
* @param end Index in text to end search.
* @param errors Errors found in the page.
* @param search Text to be searched.
* @param replacement Text proposed as a replacement.
* @return Flag indicating if the error was found.
*/
protected boolean simpleTextSearch(
PageAnalysis pageAnalysis, int begin, int end,
Collection<CheckErrorResult> errors,
String search, String replacement) {
return simpleTextSearch(
pageAnalysis, begin, end, errors, search,
(replacement != null) ? new String[] { replacement } : null);
}
/**
* Search for simple text in page.
*
* @param pageAnalysis Page analysis.
* @param errors Errors found in the page.
* @param search Text to be searched.
* @param replacements Texts proposed as a replacement.
* @return Flag indicating if the error was found.
*/
protected boolean simpleTextSearch(
PageAnalysis pageAnalysis, Collection<CheckErrorResult> errors,
String search, String[] replacements) {
String contents = pageAnalysis.getContents();
return simpleTextSearch(
pageAnalysis, 0, contents.length(),
errors, search, replacements);
}
/**
* Search for simple text in page.
*
* @param analysis Page analysis.
* @param begin Index in text to begin search.
* @param end Index in text to end search.
* @param errors Errors found in the page.
* @param search Text to be searched.
* @param replacements Texts proposed as a replacement.
* @return Flag indicating if the error was found.
*/
protected boolean simpleTextSearch(
PageAnalysis analysis, int begin, int end,
Collection<CheckErrorResult> errors,
String search, String[] replacements) {
int startIndex = begin;
boolean result = false;
String contents = analysis.getContents();
while ((startIndex < contents.length()) && (startIndex < end)) {
startIndex = contents.indexOf(search, startIndex);
if ((startIndex >= 0) && (startIndex < end)) {
if (errors == null) {
return true;
}
result = true;
int endIndex = startIndex + search.length();
CheckErrorResult errorResult = createCheckErrorResult(
analysis, startIndex, endIndex);
if (replacements != null) {
for (int i = 0; i < replacements.length; i++) {
if (replacements[i] != null) {
errorResult.addReplacement(replacements[i]);
}
}
}
errors.add(errorResult);
startIndex = endIndex;
} else {
startIndex = contents.length();
}
}
return result;
}
/**
* Create a default DEFAULTSORT.
*
* @param analysis Page analysis.
* @return Default DEFAULTSORT.
*/
protected String createDefaultSort(PageAnalysis analysis) {
// Basic check
if ((analysis == null) ||
(analysis.getContents() == null)) {
return null;
}
String contents = analysis.getContents();
if ((analysis.getPage() == null) ||
(analysis.getPage().getTitle() == null)) {
return contents;
}
// Get DEFAULTSORT name
String defaultSort = "DEFAULTSORT:";
MagicWord magicWord = analysis.getWikiConfiguration().getMagicWordByName(MagicWord.DEFAULT_SORT);
if (magicWord != null) {
String value = analysis.getWPCConfiguration().getString(WPCConfigurationString.DEFAULTSORT);
if ((value != null) && (value.trim().length() > 0)) {
value = value.trim();
if (magicWord.isPossibleAlias(value)) {
defaultSort = value;
}
}
}
// Create DEFAULTSORT
StringBuilder buffer = new StringBuilder();
buffer.append("{{");
buffer.append(defaultSort);
// Remove special characters from title
String title = analysis.getPage().getTitle();
StringBuilder currentTitle = new StringBuilder();
EnumWikipedia wiki = analysis.getWikipedia();
for (int i = 0; i < title.length(); i++) {
char character = title.charAt(i);
if (!CheckErrorAlgorithms.isAlgorithmActive(wiki, 6) ||
SpecialCharacters.isAuthorized(character, wiki)) {
currentTitle.append(character);
} else {
currentTitle.append(SpecialCharacters.proposeReplacement(character, wiki));
}
}
// Manage capitals and small letters
title = currentTitle.toString();
currentTitle.setLength(0);
boolean previousSpace = true;
for (int i = 0; i < title.length(); i++) {
char character = title.charAt(i);
if (previousSpace) {
if (Character.isLowerCase(character) && (i == 0)) {
currentTitle.append(Character.toUpperCase(character));
} else {
currentTitle.append(character);
}
} else {
currentTitle.append(character);
}
previousSpace = (character == ' ');
}
// Finish
buffer.append(currentTitle);
buffer.append("}}");
return buffer.toString();
}
// ==========================================================================
// Manage highlights in the text
// ==========================================================================
/**
* A painter for highlighting text.
*/
private final static HighlightPainter highlighter =
new DefaultHighlighter.DefaultHighlightPainter(Color.YELLOW);
/**
* Add an highlight on the text pane.
*
* @param textPane Text pane.
* @param beginIndex Beginning of the highlight area.
* @param endIndex End of the highlight area.
* @return Highlight tag.
*/
protected Object addHighlight(
JTextPane textPane, int beginIndex, int endIndex) {
if (textPane != null) {
try {
return textPane.getHighlighter().addHighlight(
beginIndex, endIndex, highlighter);
} catch (BadLocationException e) {
//
}
}
return null;
}
/**
* Remove an highlight of the text pane.
*
* @param textPane Text pane.
* @param highlight Highlight tag.
*/
protected void removeHighlight(JTextPane textPane, Object highlight) {
if ((textPane != null) && (highlight != null)) {
textPane.getHighlighter().removeHighlight(highlight);
}
}
}