/* LanguageTool, a natural language style checker
* Copyright (C) 2012 Daniel Naber (http://www.danielnaber.de)
*
* This library 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 library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
* USA
*/
package org.languagetool.gui;
import org.apache.commons.lang.StringUtils;
import org.languagetool.JLanguageTool;
import org.languagetool.rules.Rule;
import org.languagetool.rules.RuleMatch;
import org.languagetool.rules.spelling.SpellingCheckRule;
import org.languagetool.tools.ContextTools;
import org.languagetool.tools.StringTools;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import java.awt.*;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.List;
/**
* Area where the result of text checking is displayed.
*/
class ResultArea extends JTextPane {
private static final String DEACTIVATE_URL = "http://languagetool.org/deactivate/";
private static final String REACTIVATE_URL = "http://languagetool.org/reactivate/";
private static final String LT_ERROR_MARKER_START = "<b><font bgcolor=\"#d7d7ff\">";
private static final String SPELL_ERROR_MARKER_START = "<b><font bgcolor=\"#ffd7d7\">";
private final ResourceBundle messages;
private final JTextArea textArea;
private Configuration config;
private String inputText;
private String startText;
private List<RuleMatch> allRuleMatches;
private List<RuleMatch> ruleMatches; // will be filtered to not show disabled rules
private long runTime;
private JLanguageTool languageTool;
ResultArea(ResourceBundle messages, JTextArea textArea, Configuration config) {
this.messages = messages;
this.textArea = textArea;
this.config = config;
setContentType("text/html");
setText(Main.HTML_GREY_FONT_START + messages.getString("resultAreaText") + Main.HTML_FONT_END);
setEditable(false);
addHyperlinkListener(new MyHyperlinkListener());
setTransferHandler(new RetainLineBreakTransferHandler());
}
String getRuleMatchHtml(List<RuleMatch> ruleMatches, String text, String startCheckText) {
final ContextTools contextTools = new ContextTools();
final StringBuilder sb = new StringBuilder();
sb.append(startCheckText);
sb.append("<br>\n");
int i = 0;
for (final RuleMatch match : ruleMatches) {
final String output = Tools.makeTexti18n(messages, "result1", i + 1, match.getLine() + 1, match.getColumn());
sb.append(output);
final String msg = match.getMessage()
.replaceAll("<suggestion>", "<b>").replaceAll("</suggestion>", "</b>")
.replaceAll("<old>", "<b>").replaceAll("</old>", "</b>");
sb.append("<b>").append(messages.getString("errorMessage")).append("</b> ");
sb.append(msg);
final RuleLink ruleLink = RuleLink.buildDeactivationLink(match.getRule());
sb.append(" <a href=\"").append(ruleLink).append("\">").append(messages.getString("deactivateRule")).append("</a><br>\n");
if (match.getSuggestedReplacements().size() > 0) {
final String replacement = StringTools.listToString(match.getSuggestedReplacements(), "; ");
sb.append("<b>").append(messages.getString("correctionMessage")).append("</b> ").append(replacement).append("<br>\n");
}
if (match.getRule() instanceof SpellingCheckRule) {
contextTools.setErrorMarkerStart(SPELL_ERROR_MARKER_START);
} else {
contextTools.setErrorMarkerStart(LT_ERROR_MARKER_START);
}
final String context = contextTools.getContext(match.getFromPos(), match.getToPos(), text);
sb.append("<b>").append(messages.getString("errorContext")).append("</b> ").append(context);
sb.append("<br>\n");
if (match.getRule().getUrl() != null && Desktop.isDesktopSupported()) {
sb.append("<b>").append(messages.getString("moreInfo")).append("</b> <a href=\"");
final String url = match.getRule().getUrl().toString();
sb.append(url);
final String shortUrl = StringUtils.abbreviate(url, 60);
sb.append("\">").append(shortUrl).append("</a><br>\n");
}
i++;
}
sb.append(Main.HTML_GREY_FONT_START);
sb.append(getDisabledRulesHtml());
final String checkDone = Tools.makeTexti18n(messages, "checkDone", ruleMatches.size(), runTime);
sb.append("<br>\n").append(checkDone);
sb.append(Main.HTML_FONT_END).append("<br>\n");
return sb.toString();
}
private String getDisabledRulesHtml() {
final StringBuilder sb = new StringBuilder();
sb.append(messages.getString("deactivatedRulesText"));
int i = 0;
int deactivatedRuleCount = 0;
for (String ruleId : config.getDisabledRuleIds()) {
if (ruleId.trim().isEmpty()) {
continue;
}
final Rule rule = getRuleForId(ruleId);
if (rule == null || rule.isDefaultOff()) {
continue;
}
if (i++ > 0) {
sb.append(",");
}
final RuleLink reactivationLink = RuleLink.buildReactivationLink(rule);
sb.append(" <a href=\"").append(reactivationLink).append("\">").append(rule.getDescription()).append("</a>");
deactivatedRuleCount++;
}
sb.append("<br>");
if (deactivatedRuleCount == 0) {
return "";
} else {
return sb.toString();
}
}
private Rule getRuleForId(String ruleId) {
final List<Rule> allRules = languageTool.getAllRules();
for (Rule rule : allRules) {
if (rule.getId().equals(ruleId)) {
return rule;
}
}
return null;
}
void setInputText(String inputText) {
this.inputText = inputText;
}
void setStartText(String startText) {
this.startText = startText;
}
void setRunTime(long runTime) {
this.runTime = runTime;
}
void setRuleMatches(List<RuleMatch> ruleMatches) {
this.allRuleMatches = new ArrayList<RuleMatch>(ruleMatches);
this.ruleMatches = new ArrayList<RuleMatch>(ruleMatches);
}
void displayResult() {
ruleMatches = filterRuleMatches();
final String ruleMatchHtml = getRuleMatchHtml(ruleMatches, inputText, startText);
displayText(ruleMatchHtml);
}
void displayText(String text) {
setText(Main.HTML_FONT_START + text + Main.HTML_FONT_END);
setCaretPosition(0);
}
void setConfiguration(Configuration config) {
this.config = config;
}
void setLanguageTool(JLanguageTool languageTool) {
this.languageTool = languageTool;
}
private List<RuleMatch> filterRuleMatches() {
final List<RuleMatch> filtered = new ArrayList<RuleMatch>();
final Set<String> disabledRuleIds = config.getDisabledRuleIds();
for (RuleMatch ruleMatch : allRuleMatches) {
if (!disabledRuleIds.contains(ruleMatch.getRule().getId())) {
filtered.add(ruleMatch);
}
}
return filtered;
}
private class MyHyperlinkListener implements HyperlinkListener {
private MyHyperlinkListener() {
}
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
final URL url = e.getURL();
try {
final String uri = url.toURI().toString();
if (uri.startsWith(DEACTIVATE_URL) || uri.startsWith(REACTIVATE_URL)) {
handleRuleLinkClick(uri);
} else {
handleHttpClick(url);
}
} catch (Exception ex) {
throw new RuntimeException("Could not handle URL click: " + url, ex);
}
}
}
private void handleRuleLinkClick(String uri) throws IOException {
final Cursor prevCursor = getCursor();
setCursor(new Cursor(Cursor.WAIT_CURSOR));
try {
final RuleLink ruleLink = RuleLink.getFromString(uri);
final String ruleId = ruleLink.getId();
final Set<String> disabledRuleIds = config.getDisabledRuleIds();
if (uri.startsWith(DEACTIVATE_URL)) {
disabledRuleIds.add(ruleId);
languageTool.disableRule(ruleId);
} else {
disabledRuleIds.remove(ruleId);
languageTool.enableRule(ruleId);
}
config.setDisabledRuleIds(disabledRuleIds);
config.saveConfiguration(languageTool.getLanguage());
allRuleMatches = languageTool.check(textArea.getText());
reDisplayRuleMatches();
} finally {
setCursor(prevCursor);
}
}
private void reDisplayRuleMatches() {
ruleMatches = filterRuleMatches();
setInputText(textArea.getText());
displayResult();
}
private void handleHttpClick(URL url) {
if (Desktop.isDesktopSupported()) {
try {
final Desktop desktop = Desktop.getDesktop();
desktop.browse(url.toURI());
} catch (Exception ex) {
throw new RuntimeException("Could not open URL: " + url, ex);
}
}
}
}
}