org.languagetool.gui.ResultAreaHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.languagetool.gui.ResultAreaHelper.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 javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.html.HTMLDocument;

import org.apache.commons.lang3.StringUtils;
import org.languagetool.Language;
import org.languagetool.LanguageMaintainedState;
import org.languagetool.rules.ITSIssueType;
import org.languagetool.rules.Rule;
import org.languagetool.rules.RuleMatch;
import org.languagetool.tools.ContextTools;

/**
 * Helper for the JTextPane where the result of text checking is displayed.
 */
class ResultAreaHelper implements LanguageToolListener, HyperlinkListener {

    private static final String KEY = "org.languagetool.gui.ResultAreaHelper";
    private static final String EMPTY_PARA = "<p class=\"small\"></p>";
    private static final String HEADER = "header";
    private static final String MAIN = "maincontent";
    private static final String TEMPLATE = "<html>\n" + "  <head>\n" + "     <style type=\"text/css\">\n"
            + "       #" + HEADER + " {  }\n" + "       #" + MAIN + " { }\n"
            + "       p { font-family: Arial,Helvetica; padding: 1px; margin: 1px }\n"
            + "       p.small { font-size: 1px; }\n"
            + "       p.grayed { font-family: Arial,Helvetica; color: #666666 }\n" + "     </style>\n"
            + "  </head>\n" + "  <body>\n" + "    <div id=\"" + HEADER + "\">\n" + "    </div>\n" + "    <div id=\""
            + MAIN + "\">\n" + "    </div>\n" + "  </body>\n" + "</html>";
    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 long runTime;
    private final Object lock = new Object();
    private boolean enabled = false;

    static void install(ResourceBundle messages, LanguageToolSupport ltSupport, JTextPane pane) {
        Object prev = pane.getClientProperty(KEY);
        if (prev != null && prev instanceof ResultAreaHelper) {
            enable(pane);
            return;
        }
        ResultAreaHelper helper = new ResultAreaHelper(messages, ltSupport, pane);
        pane.putClientProperty(KEY, helper);
    }

    static void enable(JTextPane pane) {
        Object helper = pane.getClientProperty(KEY);
        if (helper != null && helper instanceof ResultAreaHelper) {
            ((ResultAreaHelper) helper).enable();
        }
    }

    static void disable(JTextPane pane) {
        Object helper = pane.getClientProperty(KEY);
        if (helper != null && helper instanceof ResultAreaHelper) {
            ((ResultAreaHelper) helper).disable();
        }
    }

    static void uninstall(JTextPane pane) {
        Object helper = pane.getClientProperty(KEY);
        if (helper != null && helper instanceof ResultAreaHelper) {
            ((ResultAreaHelper) helper).disable();
            pane.putClientProperty(KEY, null);
        }
    }

    private ResultAreaHelper(ResourceBundle messages, LanguageToolSupport ltSupport, JTextPane statusPane) {
        this.messages = messages;
        this.ltSupport = ltSupport;
        this.statusPane = statusPane;
        statusPane.setContentType("text/html");
        statusPane.setEditable(false);
        statusPane.setTransferHandler(new RetainLineBreakTransferHandler());
        enable();
    }

    private void enable() {
        synchronized (lock) {
            if (enabled) {
                return;
            }
            enabled = true;
            statusPane.setText(TEMPLATE);
            setHeader(messages.getString("resultAreaText"));
            statusPane.addHyperlinkListener(this);
            ltSupport.addLanguageToolListener(this);
        }
    }

    private void disable() {
        synchronized (lock) {
            if (!enabled) {
                return;
            }
            enabled = false;
            statusPane.setText(TEMPLATE);
            statusPane.removeHyperlinkListener(this);
            ltSupport.removeLanguageToolListener(this);
        }
    }

    @Override
    public void languageToolEventOccurred(LanguageToolEvent event) {
        if (event.getType() == LanguageToolEvent.Type.CHECKING_STARTED) {
            Language lang = ltSupport.getLanguage();
            String langName;
            if (lang.isExternal()) {
                langName = lang.getTranslatedName(messages) + Main.EXTERNAL_LANGUAGE_SUFFIX;
            } else {
                langName = lang.getTranslatedName(messages);
            }
            String msg = org.languagetool.tools.Tools.i18n(messages, "startChecking", langName) + "...";
            setHeader(msg);
            setMain(EMPTY_PARA);
            if (event.getCaller() == this) {
                statusPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            }
        } else if (event.getType() == LanguageToolEvent.Type.CHECKING_FINISHED) {
            setRunTime(event.getElapsedTime());
            String inputText = event.getSource().getTextComponent().getText();
            displayResult(inputText, event.getSource().getMatches());
            if (event.getCaller() == this) {
                statusPane.setCursor(Cursor.getDefaultCursor());
            }
        } else if (event.getType() == LanguageToolEvent.Type.RULE_DISABLED
                || event.getType() == LanguageToolEvent.Type.RULE_ENABLED) {
            String inputText = event.getSource().getTextComponent().getText();
            displayResult(inputText, event.getSource().getMatches());
        }
    }

    private void setHeader(String txt) {
        HTMLDocument d = (HTMLDocument) statusPane.getDocument();
        Element e = d.getElement(HEADER);
        try {
            d.setInnerHTML(e, "<p class=\"grayed\">" + txt + "</p>");
        } catch (BadLocationException ex) {
            Tools.showError(ex);
        } catch (IOException ex) {
            Tools.showError(ex);
        }
    }

    private void setMain(String html) {
        HTMLDocument d = (HTMLDocument) statusPane.getDocument();
        Element e = d.getElement(MAIN);
        try {
            d.setInnerHTML(e, html);
        } catch (BadLocationException ex) {
            Tools.showError(ex);
        } catch (IOException ex) {
            Tools.showError(ex);
        }
    }

    private void appendMain(String html) {
        HTMLDocument d = (HTMLDocument) statusPane.getDocument();
        Element e = d.getElement(MAIN);
        try {
            d.insertBeforeEnd(e, html);
        } catch (BadLocationException ex) {
            Tools.showError(ex);
        } catch (IOException ex) {
            Tools.showError(ex);
        }
    }

    private void getRuleMatchHtml(List<RuleMatch> ruleMatches, String text) {
        ContextTools contextTools = new ContextTools();
        StringBuilder sb = new StringBuilder(200);
        if (ltSupport.getLanguage().getMaintainedState() != LanguageMaintainedState.ActivelyMaintained) {
            sb.append("<p><b>").append(messages.getString("unsupportedWarning")).append("</b></p>\n");
        } else {
            sb.append(EMPTY_PARA);
        }
        setMain(sb.toString());
        sb.setLength(0);
        int i = 0;
        for (RuleMatch match : ruleMatches) {
            sb.append("<p>");
            String output = org.languagetool.tools.Tools.i18n(messages, "result1", i + 1, match.getLine() + 1,
                    match.getColumn());
            sb.append(output);
            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);
            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) {
                String replacement = String.join("; ", match.getSuggestedReplacements());
                sb.append("<b>").append(messages.getString("correctionMessage")).append("</b> ").append(replacement)
                        .append("<br>\n");
            }
            if (ITSIssueType.Misspelling == match.getRule().getLocQualityIssueType()) {
                contextTools.setErrorMarkerStart(SPELL_ERROR_MARKER_START);
            } else {
                contextTools.setErrorMarkerStart(LT_ERROR_MARKER_START);
            }
            String context = contextTools.getContext(match.getFromPos(), match.getToPos(), text);
            sb.append("<b>").append(messages.getString("errorContext")).append("</b> ").append(context);
            if ((match.getRule().getUrl() != null || match.getUrl() != null) && Desktop.isDesktopSupported()) {
                sb.append("<br>\n");
                sb.append("<b>").append(messages.getString("moreInfo")).append("</b> <a href=\"");
                String url;
                if (match.getUrl() != null) {
                    url = match.getUrl().toString();
                } else {
                    url = match.getRule().getUrl().toString();
                }
                sb.append(url);
                String shortUrl = StringUtils.abbreviate(url, 60);
                sb.append("\">").append(shortUrl).append("</a>\n");
            }
            sb.append("</p>");
            i++;
            appendMain(sb.toString());
            sb.setLength(0);
        }
        sb.append("<p class=\"grayed\">");
        sb.append(getDisabledRulesHtml());
        String checkDone = org.languagetool.tools.Tools.i18n(messages, "checkDone", ruleMatches.size(), runTime);
        sb.append("<br>\n").append(checkDone);
        sb.append("<br>\n").append(messages.getString("makeLanguageToolBetter"));
        sb.append("<br>\n");
        sb.append("</p>");
        appendMain(sb.toString());
    }

    private String getDisabledRulesHtml() {
        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;
            }
            Rule rule = ltSupport.getRuleForId(ruleId);
            if (rule == null || rule.isDefaultOff()) {
                continue;
            }
            if (i++ > 0) {
                sb.append(',');
            }
            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 setRunTime(long runTime) {
        this.runTime = runTime;
    }

    private void displayResult(String inputText, List<RuleMatch> matches) {
        List<RuleMatch> filtered = filterRuleMatches(matches);
        getRuleMatchHtml(filtered, inputText);
        statusPane.setCaretPosition(0);
    }

    private List<RuleMatch> filterRuleMatches(List<RuleMatch> matches) {
        List<RuleMatch> filtered = new ArrayList<>();
        Set<String> disabledRuleIds = ltSupport.getConfig().getDisabledRuleIds();
        for (RuleMatch ruleMatch : matches) {
            if (!disabledRuleIds.contains(ruleMatch.getRule().getId())) {
                filtered.add(ruleMatch);
            }
        }
        return filtered;
    }

    @Override
    public void hyperlinkUpdate(HyperlinkEvent e) {
        if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
            URL url = e.getURL();
            try {
                String uri = url.toURI().toString();
                if (uri.startsWith(DEACTIVATE_URL) || uri.startsWith(REACTIVATE_URL)) {
                    handleRuleLinkClick(uri);
                } else {
                    Tools.openURL(url);
                }
            } catch (Exception ex) {
                throw new RuntimeException("Could not handle URL click: " + url, ex);
            }
        }
    }

    private void handleRuleLinkClick(String uri) throws IOException {
        RuleLink ruleLink = RuleLink.getFromString(uri);
        String ruleId = ruleLink.getId();
        if (uri.startsWith(DEACTIVATE_URL)) {
            ltSupport.disableRule(ruleId);
        } else {
            ltSupport.enableRule(ruleId);
        }
        ltSupport.getConfig().saveConfiguration(ltSupport.getLanguage());
        ltSupport.checkImmediately(this);
    }

}