org.languagetool.gui.ResultArea.java Source code

Java tutorial

Introduction

Here is the source code for org.languagetool.gui.ResultArea.java

Source

/* 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 java.awt.Cursor;
import java.awt.Desktop;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Set;

import javax.swing.JTextPane;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;

import org.apache.commons.lang.StringUtils;
import org.languagetool.Language;
import org.languagetool.rules.ITSIssueType;
import org.languagetool.rules.Rule;
import org.languagetool.rules.RuleMatch;
import org.languagetool.tools.ContextTools;
import org.languagetool.tools.StringTools;

/**
 * Area where the result of text checking is displayed.
 */
class ResultArea {

    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 JTextPane statusPane;
    private final LanguageToolSupport ltSupport;
    private final Object marker = new Object();

    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;

    ResultArea(final ResourceBundle messages, final LanguageToolSupport ltSupport, final JTextPane statusPane) {
        this.messages = messages;
        this.ltSupport = ltSupport;
        this.statusPane = statusPane;
        statusPane.setContentType("text/html");
        statusPane.setText(Main.HTML_GREY_FONT_START + messages.getString("resultAreaText") + Main.HTML_FONT_END);
        statusPane.setEditable(false);
        statusPane.addHyperlinkListener(new MyHyperlinkListener());
        statusPane.setTransferHandler(new RetainLineBreakTransferHandler());
        ltSupport.addLanguageToolListener(new LanguageToolListener() {
            @Override
            public void languageToolEventOccurred(LanguageToolEvent event) {
                if (event.getType() == LanguageToolEvent.Type.CHECKING_STARTED) {
                    final Language lang = ltSupport.getLanguage();
                    final String langName;
                    if (lang.isExternal()) {
                        langName = lang.getTranslatedName(messages) + Main.EXTERNAL_LANGUAGE_SUFFIX;
                    } else {
                        langName = lang.getTranslatedName(messages);
                    }
                    final String startCheckText = Main.HTML_GREY_FONT_START
                            + Tools.makeTexti18n(messages, "startChecking", langName) + "..." + Main.HTML_FONT_END;
                    statusPane.setText(startCheckText);
                    setStartText(startCheckText);
                    if (event.getCaller() == marker) {
                        statusPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
                    }
                } else if (event.getType() == LanguageToolEvent.Type.CHECKING_FINISHED) {
                    inputText = event.getSource().getTextComponent().getText();
                    setRuleMatches(event.getSource().getMatches());
                    if (event.getCaller() == marker || event.getCaller() == null) {
                        displayResult();
                        if (event.getCaller() == marker) {
                            statusPane.setCursor(Cursor.getDefaultCursor());
                        }
                    }
                } else if (event.getType() == LanguageToolEvent.Type.RULE_DISABLED
                        || event.getType() == LanguageToolEvent.Type.RULE_ENABLED) {
                    inputText = event.getSource().getTextComponent().getText();
                    setRuleMatches(event.getSource().getMatches());
                    displayResult();
                }
            }
        });
    }

    private String getRuleMatchHtml(List<RuleMatch> ruleMatches, String text, String startCheckText) {
        final ContextTools contextTools = new ContextTools();
        final StringBuilder sb = new StringBuilder(200);
        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 (ITSIssueType.Misspelling.equals(match.getRule().getLocQualityIssueType())) {
                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("<br>\n").append(messages.getString("makeLanguageToolBetter"));
        sb.append(Main.HTML_FONT_END).append("<br>\n");
        return sb.toString();
    }

    private String getDisabledRulesHtml() {
        final StringBuilder sb = new StringBuilder(40);
        sb.append(messages.getString("deactivatedRulesText"));
        int i = 0;
        int deactivatedRuleCount = 0;
        for (String ruleId : ltSupport.getConfig().getDisabledRuleIds()) {
            if (ruleId.trim().isEmpty()) {
                continue;
            }
            final Rule rule = ltSupport.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 void setStartText(String startText) {
        this.startText = startText;
    }

    void setRunTime(long runTime) {
        this.runTime = runTime;
    }

    void setRuleMatches(List<RuleMatch> ruleMatches) {
        this.allRuleMatches = new ArrayList<>(ruleMatches);
        this.ruleMatches = new ArrayList<>(ruleMatches);
    }

    void displayResult() {
        ruleMatches = filterRuleMatches();
        final String ruleMatchHtml = getRuleMatchHtml(ruleMatches, inputText, startText);
        displayText(ruleMatchHtml);
    }

    private void displayText(String text) {
        // TODO: use a JTable for faster rendering
        statusPane.setText(Main.HTML_FONT_START + text + Main.HTML_FONT_END);
        statusPane.setCaretPosition(0);
    }

    private List<RuleMatch> filterRuleMatches() {
        final List<RuleMatch> filtered = new ArrayList<>();
        final Set<String> disabledRuleIds = ltSupport.getConfig().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 RuleLink ruleLink = RuleLink.getFromString(uri);
            final String ruleId = ruleLink.getId();
            if (uri.startsWith(DEACTIVATE_URL)) {
                ltSupport.disableRule(ruleId);
            } else {
                ltSupport.enableRule(ruleId);
            }
            ltSupport.getConfig().saveConfiguration(ltSupport.getLanguage());
            ltSupport.checkImmediately(marker);
        }

        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);
                }
            }
        }
    }
}