Package org.languagetool.openoffice

Source Code of org.languagetool.openoffice.Main$AboutDialogThread

/* LanguageTool, a natural language style checker
* Copyright (C) 2005 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.openoffice;

/** OpenOffice 3.x Integration
*
* @author Marcin MiƂkowski
*/
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Set;

import javax.swing.JOptionPane;
import javax.swing.UIManager;

import com.sun.star.lang.*;
import com.sun.star.lang.IllegalArgumentException;
import org.languagetool.JLanguageTool;
import org.languagetool.Language;
import org.languagetool.MultiThreadedJLanguageTool;
import org.languagetool.gui.AboutDialog;
import org.languagetool.gui.Configuration;
import org.languagetool.markup.AnnotatedText;
import org.languagetool.markup.AnnotatedTextBuilder;
import org.languagetool.rules.Rule;
import org.languagetool.rules.RuleMatch;
import org.languagetool.tools.StringTools;
import org.languagetool.tools.Tools;

import com.sun.star.beans.PropertyState;
import com.sun.star.beans.PropertyValue;
import com.sun.star.beans.XPropertySet;
import com.sun.star.frame.XDesktop;
import com.sun.star.frame.XModel;
import com.sun.star.lib.uno.helper.Factory;
import com.sun.star.lib.uno.helper.WeakBase;
import com.sun.star.linguistic2.ProofreadingResult;
import com.sun.star.linguistic2.SingleProofreadingError;
import com.sun.star.linguistic2.XLinguServiceEventBroadcaster;
import com.sun.star.linguistic2.XLinguServiceEventListener;
import com.sun.star.linguistic2.XProofreader;
import com.sun.star.registry.XRegistryKey;
import com.sun.star.task.XJobExecutor;
import com.sun.star.text.XTextViewCursor;
import com.sun.star.text.XTextViewCursorSupplier;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;

public class Main extends WeakBase implements XJobExecutor,
    XServiceDisplayName, XServiceInfo, XProofreader,
    XLinguServiceEventBroadcaster {

  // Service name required by the OOo API && our own name.
  private static final String[] SERVICE_NAMES = {
          "com.sun.star.linguistic2.Proofreader",
          "org.languagetool.openoffice.Main" };

  // use a different name than the stand-alone version to avoid conflicts:
  private static final String CONFIG_FILE = ".languagetool-ooo.cfg";

  private static final ResourceBundle MESSAGES = JLanguageTool.getMessageBundle();

  // LibreOffice (since 4.2.0) special tag for locale with variant
  // e.g. language ="qlt" country="ES" variant="ca-ES-valencia":
  private static final String LIBREOFFICE_SPECIAL_LANGUAGE_TAG = "qlt";

  private Configuration config;
  private JLanguageTool langTool;
  private Language docLanguage;
  private String docID;

  // Rules disabled using the config dialog box rather than Spelling dialog box
  // or the context menu.
  private Set<String> disabledRules;
  private Set<String> disabledRulesUI;

  private List<XLinguServiceEventListener> xEventListeners;

  // Make another instance of JLanguageTool and assign it to langTool if true.
  private boolean recheck;

  /**
   * Sentence tokenization-related members.
   */
  private String currentPara;
  private List<String> tokenizedSentences;
  private int position;
  private List<RuleMatch> paragraphMatches;
  private XComponentContext xContext;

  public Main(final XComponentContext xCompContext) {
    changeContext(xCompContext);
    xEventListeners = new ArrayList<>();
  }

  private void prepareConfig(final Language lang) {
    try {
      final File homeDir = getHomeDir();
      config = new Configuration(homeDir, CONFIG_FILE, lang);
      disabledRules = config.getDisabledRuleIds();
      if (disabledRules == null) {
        disabledRules = new HashSet<>();
      }
      disabledRulesUI = new HashSet<>(disabledRules);
    } catch (final Throwable t) {
      showError(t);
    }
  }

  public final void changeContext(final XComponentContext xCompContext) {
    xContext = xCompContext;
  }

  private XComponent getXComponent() {
    try {
      final XMultiComponentFactory xMCF = xContext.getServiceManager();
      final Object desktop = xMCF.createInstanceWithContext("com.sun.star.frame.Desktop", xContext);
      final XDesktop xDesktop = UnoRuntime.queryInterface(XDesktop.class, desktop);
      return xDesktop.getCurrentComponent();
    } catch (final Throwable t) {
      showError(t);
      return null;
    }
  }

  /**
   * Checks the language under the cursor. Used for opening the configuration
   * dialog.
   * @return the language under the visible cursor
   */
  private Language getLanguage() {
    final XComponent xComponent = getXComponent();
    final Locale charLocale;
    final XPropertySet xCursorProps;
    try {
      final XModel model = UnoRuntime.queryInterface(XModel.class, xComponent);
      final XTextViewCursorSupplier xViewCursorSupplier =
          UnoRuntime.queryInterface(XTextViewCursorSupplier.class, model.getCurrentController());
      final XTextViewCursor xCursor = xViewCursorSupplier.getViewCursor();
      if (xCursor.isCollapsed()) { // no text selection
        xCursorProps = UnoRuntime.queryInterface(XPropertySet.class, xCursor);
      } else { // text is selected, need to create another cursor
        // as multiple languages can occur here - we care only
        // about character under the cursor, which might be wrong
        // but it applies only to the checking dialog to be removed
        xCursorProps = UnoRuntime.queryInterface(
            XPropertySet.class,
            xCursor.getText().createTextCursorByRange(xCursor.getStart()));
      }

      // The CharLocale and CharLocaleComplex properties may both be set, so we still cannot know
      // whether the text is e.g. Khmer or Tamil (the only "complex text layout (CTL)" languages we support so far).
      // Thus we check the text itself:
      if (new KhmerDetector().isThisLanguage(xCursor.getText().getString())) {
        return Language.getLanguageForShortName("km");
      }
      if (new TamilDetector().isThisLanguage(xCursor.getText().getString())) {
        return Language.getLanguageForShortName("ta");
      }

      final Object obj = xCursorProps.getPropertyValue("CharLocale");
      if (obj == null) {
        return Language.getLanguageForShortName("en-US");
      }
      charLocale = (Locale) obj;
      boolean langIsSupported = false;
      for (Language element : Language.LANGUAGES) {
        if (charLocale.Language.equalsIgnoreCase(LIBREOFFICE_SPECIAL_LANGUAGE_TAG)
            && element.getShortNameWithCountryAndVariant().equalsIgnoreCase(charLocale.Variant)) {
          langIsSupported = true;
          break;
        }
        if (element.getShortName().equals(charLocale.Language)) {
          langIsSupported = true;
          break;
        }
      }
      if (!langIsSupported) {
        final String message = org.languagetool.gui.Tools.makeTexti18n(
            MESSAGES, "language_not_supported", charLocale.Language);
        JOptionPane.showMessageDialog(null, message);
        return null;
      }
    } catch (final Throwable t) {
      showError(t);
      return null;
    }
    return getLanguage(charLocale);
  }

  private Language getLanguage(Locale locale) {
    try {
      if (locale.Language.equalsIgnoreCase(LIBREOFFICE_SPECIAL_LANGUAGE_TAG)) {
        return Language.getLanguageForShortName(locale.Variant);
      } else {
        return Language.getLanguageForShortName(locale.Language + "-" + locale.Country);
      }
    } catch (java.lang.IllegalArgumentException e) {
      return Language.getLanguageForShortName(locale.Language);
    }
  }

  /**
   * Runs the grammar checker on paragraph text.
   *
   * @param docID document ID
   * @param paraText paragraph text
   * @param locale Locale the text Locale
   * @param startOfSentencePos start of sentence position
   * @param nSuggestedBehindEndOfSentencePosition end of sentence position
   * @return ProofreadingResult containing the results of the check.
   */
  @Override
  public final ProofreadingResult doProofreading(final String docID,
      final String paraText, final Locale locale, final int startOfSentencePos,
      final int nSuggestedBehindEndOfSentencePosition,
      final PropertyValue[] propertyValues) {
    final ProofreadingResult paRes = new ProofreadingResult();
    try {
      paRes.nStartOfSentencePosition = startOfSentencePos;
      paRes.xProofreader = this;
      paRes.aLocale = locale;
      paRes.aDocumentIdentifier = docID;
      paRes.aText = paraText;
      paRes.aProperties = propertyValues;
      int[] footnotePositions = getPropertyValues("FootnotePositions", propertyValues)// since LO 4.3
      return doGrammarCheckingInternal(paraText, locale, paRes, footnotePositions);
    } catch (final Throwable t) {
      showError(t);
      return paRes;
    }
  }

  private int[] getPropertyValues(String propName, PropertyValue[] propertyValues) {
    for (PropertyValue propertyValue : propertyValues) {
      if (propName.equals(propertyValue.Name)) {
        if (propertyValue.Value instanceof int[]) {
          return (int[]) propertyValue.Value;
        } else {
          System.err.println("Not of expected type int[]: " + propertyValue.Name + ": " + propertyValue.Value.getClass());
        }
      }
    }
    return new int[]{}// e.g. for LO/OO < 4.3 and the 'FootnotePositions' property
  }

  private synchronized ProofreadingResult doGrammarCheckingInternal(
      final String paraText, final Locale locale, final ProofreadingResult paRes, int[] footnotePositions) {

    if (!StringTools.isEmpty(paraText) && hasLocale(locale)) {
      Language langForShortName = getLanguage(locale);
      if (!langForShortName.equals(docLanguage) || langTool == null || recheck) {
        docLanguage = langForShortName;
        initLanguageTool();
      }

      final Set<String> disabledRuleIds = config.getDisabledRuleIds();
      if (disabledRuleIds != null) {
        // copy as the config thread may access this as well
        final List<String> list = new ArrayList<>(disabledRuleIds);
        for (final String id : list) {
          langTool.disableRule(id);
        }
      }
      final Set<String> disabledCategories = config.getDisabledCategoryNames();
      if (disabledCategories != null) {
        // copy as the config thread may access this as well
        final List<String> list = new ArrayList<>(disabledCategories);
        for (final String categoryName : list) {
          langTool.disableCategory(categoryName);
        }
      }
      final Set<String> enabledRuleIds = config.getEnabledRuleIds();
      if (enabledRuleIds != null) {
        // copy as the config thread may access this as well
        final List<String> list = new ArrayList<>(enabledRuleIds);
        for (String ruleName : list) {
          langTool.enableDefaultOffRule(ruleName);
          langTool.enableRule(ruleName);
        }
      }
      try {
        final String sentence = getSentence(paraText,
            paRes.nStartOfSentencePosition);
        paRes.nStartOfSentencePosition = position;
        paRes.nStartOfNextSentencePosition = position + sentence.length();
        paRes.nBehindEndOfSentencePosition = paRes.nStartOfNextSentencePosition;
        if (!StringTools.isEmpty(sentence)) {
          AnnotatedText annotatedText = getAnnotatedText(sentence, footnotePositions, paRes);
          final List<RuleMatch> ruleMatches = langTool.check(annotatedText, false,
              JLanguageTool.ParagraphHandling.ONLYNONPARA);
          final SingleProofreadingError[] pErrors = checkParaRules(paraText,
                  paRes.nStartOfSentencePosition,
              paRes.nStartOfNextSentencePosition, paRes.aDocumentIdentifier);
          int pErrorCount = 0;
          if (pErrors != null) {
            pErrorCount = pErrors.length;
          }
          if (!ruleMatches.isEmpty()) {
            final SingleProofreadingError[] errorArray =
                    new SingleProofreadingError[ruleMatches.size() + pErrorCount];
            int i = 0;
            for (final RuleMatch myRuleMatch : ruleMatches) {
              errorArray[i] = createOOoError(myRuleMatch, paRes.nStartOfSentencePosition);
              i++;
            }
            // add para matches
            if (pErrors != null) {
              for (SingleProofreadingError paraError : pErrors) {
                if (paraError != null) {
                  errorArray[i] = paraError;
                  i++;
                }
              }
            }
            Arrays.sort(errorArray, new ErrorPositionComparator());
            paRes.aErrors = errorArray;

          } else {
            if (pErrors != null) {
              paRes.aErrors = pErrors;
            }
          }
        }
      } catch (final Throwable t) {
        showError(t);
        paRes.nBehindEndOfSentencePosition = paraText.length();
      }
    }
    return paRes;
  }

  private AnnotatedText getAnnotatedText(String sentence, int[] footnotePos, ProofreadingResult paRes) {
    Set<Integer> correctedPos = new HashSet<>();
    for (int pos : footnotePos) {
      correctedPos.add(pos - paRes.nStartOfSentencePosition);
    }
    AnnotatedTextBuilder annotations = new AnnotatedTextBuilder();
    // not very efficient but simple implementation:
    for (int i = 0; i < sentence.length(); i++) {
      if (correctedPos.contains(i)) {
        annotations.addMarkup("\u200B");
      } else {
        annotations.addText(String.valueOf(sentence.charAt(i)));
      }
    }
    return annotations.build();
  }

  private void initLanguageTool() {
    try {
      prepareConfig(docLanguage);
      langTool = new MultiThreadedJLanguageTool(docLanguage, config.getMotherTongue());
      langTool.activateDefaultPatternRules();
      langTool.activateDefaultFalseFriendRules();
      for (Rule rule : langTool.getAllActiveRules()) {
        if (rule.isDictionaryBasedSpellingRule()) {
          langTool.disableRule(rule.getId());
        }
        if (rule.useInOffice()) {
          langTool.enableRule(rule.getId());
        }
      }
      recheck = false;
    } catch (final Throwable t) {
      showError(t);
    }
  }

  private synchronized String getSentence(final String paraText,
      final int startPos) {
    if (paraText.equals(currentPara) && tokenizedSentences != null) {
      int i = 0;
      int index = -1;
      while (index < startPos && i < tokenizedSentences.size()) {
        index += tokenizedSentences.get(i).length();
        if (index < startPos) {
          i++;
        }
      }
      position = index + 1;
      if (i < tokenizedSentences.size()) {
        position -= tokenizedSentences.get(i).length();
        return tokenizedSentences.get(i);
      }
      return "";
    }
    currentPara = paraText;
    tokenizedSentences = langTool.sentenceTokenize(cleanFootnotes(paraText));
    position = 0;
    if (!tokenizedSentences.isEmpty()) {
      return tokenizedSentences.get(0);
    }
    return "";
  }

  // Fix numbers that are (probably) foot notes.
  // See https://bugs.freedesktop.org/show_bug.cgi?id=69416
  // non-private for test case
  String cleanFootnotes(String paraText) {
    return paraText.replaceAll("([^\\d][.!?])\\d ", "$1Âč ");
  }

  private synchronized SingleProofreadingError[] checkParaRules(
      final String paraText, final int startPos,
      final int endPos, final String docID) {
    if (startPos == 0) {
      try {
        paragraphMatches = langTool.check(paraText, false,
            JLanguageTool.ParagraphHandling.ONLYPARA);
        this.docID = docID;
      } catch (final Throwable t) {
        showError(t);
      }
    }
    if (paragraphMatches != null && !paragraphMatches.isEmpty() && docID.equals(this.docID)) {
      final List<SingleProofreadingError> errorList = new ArrayList<>(paragraphMatches.size());
      for (final RuleMatch myRuleMatch : paragraphMatches) {
        final int startErrPos = myRuleMatch.getFromPos();
        final int endErrPos = myRuleMatch.getToPos();
        if (startErrPos >= startPos && startErrPos < endPos
            && endErrPos >= startPos && endErrPos < endPos) {
          errorList.add(createOOoError(myRuleMatch, 0));
        }
      }
      if (!errorList.isEmpty()) {
        final SingleProofreadingError[] errorArray = errorList
            .toArray(new SingleProofreadingError[errorList.size()]);
        Arrays.sort(errorArray, new ErrorPositionComparator());
        return errorArray;
      }
    }
    return null;
  }

  /**
   * Creates a SingleGrammarError object for use in LO/OO.
   */
  private SingleProofreadingError createOOoError(final RuleMatch ruleMatch,
      final int startIndex) {
    final SingleProofreadingError aError = new SingleProofreadingError();
    aError.nErrorType = com.sun.star.text.TextMarkupType.PROOFREADING;
    // the API currently has no support for formatting text in comments
    aError.aFullComment = ruleMatch.getMessage()
        .replaceAll("<suggestion>", "\"").replaceAll("</suggestion>", "\"")
        .replaceAll("([\r]*\n)", " ");
    // not all rules have short comments
    if (!StringTools.isEmpty(ruleMatch.getShortMessage())) {
      aError.aShortComment = ruleMatch.getShortMessage();
    } else {
      aError.aShortComment = aError.aFullComment;
    }
    aError.aShortComment = org.languagetool.gui.Tools
        .shortenComment(aError.aShortComment);

    aError.aSuggestions = ruleMatch.getSuggestedReplacements().toArray(
        new String[ruleMatch.getSuggestedReplacements().size()]);
    aError.nErrorStart = ruleMatch.getFromPos() + startIndex;
    aError.nErrorLength = ruleMatch.getToPos() - ruleMatch.getFromPos();
    aError.aRuleIdentifier = ruleMatch.getRule().getId();
    // LibreOffice since version 3.5 supports an URL that provides more
    // information about the error,
    // older version will simply ignore the property:
    if (ruleMatch.getRule().getUrl() != null) {
      aError.aProperties = new PropertyValue[] { new PropertyValue(
          "FullCommentURL", -1, ruleMatch.getRule().getUrl().toString(),
          PropertyState.DIRECT_VALUE) };
    } else {
      aError.aProperties = new PropertyValue[0];
    }
    return aError;
  }

  /**
   * We leave spell checking to OpenOffice/LibreOffice.
   * @return false
   */
  @Override
  public final boolean isSpellChecker() {
    return false;
  }

  /**
   * Runs LT options dialog box.
   */
  public final void runOptionsDialog() {
    final Language lang = getLanguage();
    if (lang == null) {
      return;
    }
    prepareConfig(lang);
    final ConfigThread configThread = new ConfigThread(lang, config, this);
    configThread.start();
  }

  /**
   * @return An array of Locales supported by LT
   */
  @Override
  public final Locale[] getLocales() {
    try {
      List<Locale> locales = new ArrayList<>();
      for (final Language lang : Language.LANGUAGES) {
        if (lang.getCountries().length == 0) {
          // e.g. Esperanto
          if (lang.getVariant() != null) {
            locales.add(new Locale(LIBREOFFICE_SPECIAL_LANGUAGE_TAG, "", lang.getShortNameWithCountryAndVariant()));
          } else {
            locales.add(new Locale(lang.getShortName(), "", ""));
          }
        } else {
          for (final String country : lang.getCountries()) {
            if (lang.getVariant() != null) {
              locales.add(new Locale(LIBREOFFICE_SPECIAL_LANGUAGE_TAG, country, lang.getShortNameWithCountryAndVariant()));
            } else {
              locales.add(new Locale(lang.getShortName(), country, ""));
            }
          }
        }
      }
      return locales.toArray(new Locale[locales.size()]);
    } catch (final Throwable t) {
      showError(t);
      return new Locale[0];
    }
  }

  /**
   * @return true if LT supports the language of a given locale
   * @param locale The Locale to check
   */
  @Override
  public final boolean hasLocale(final Locale locale) {
    try {
      for (final Language element : Language.LANGUAGES) {
        if (locale.Language.equalsIgnoreCase(LIBREOFFICE_SPECIAL_LANGUAGE_TAG)
            && element.getShortNameWithCountryAndVariant().equals(locale.Variant)) {
          return true;
        }
        if (element.getShortName().equals(locale.Language)) {
          return true;
        }
      }
    } catch (final Throwable t) {
      showError(t);
    }
    return false;
  }

  /**
   * Add a listener that allow re-checking the document after changing the
   * options in the configuration dialog box.
   *
   * @param eventListener the listener to be added
   * @return true if listener is non-null and has been added, false otherwise
   */
  @Override
  public final boolean addLinguServiceEventListener(
      final XLinguServiceEventListener eventListener) {
    if (eventListener == null) {
      return false;
    }
    xEventListeners.add(eventListener);
    return true;
  }

  /**
   * Remove a listener from the event listeners list.
   *
   * @param eventListener the listener to be removed
   * @return true if listener is non-null and has been removed, false otherwise
   */
  @Override
  public final boolean removeLinguServiceEventListener(
      final XLinguServiceEventListener eventListener) {
    if (eventListener == null) {
      return false;
    }
    if (xEventListeners.contains(eventListener)) {
      xEventListeners.remove(eventListener);
      return true;
    }
    return false;
  }

  /**
   * Inform listener (grammar checking iterator) that options have changed and
   * the doc should be rechecked.
   */
  public final void resetDocument() {
    if (!xEventListeners.isEmpty()) {
      for (final XLinguServiceEventListener xEvLis : xEventListeners) {
        if (xEvLis != null) {
          final com.sun.star.linguistic2.LinguServiceEvent xEvent = new com.sun.star.linguistic2.LinguServiceEvent();
          xEvent.nEvent = com.sun.star.linguistic2.LinguServiceEventFlags.PROOFREAD_AGAIN;
          xEvLis.processLinguServiceEvent(xEvent);
        }
      }
      recheck = true;
      disabledRules = config.getDisabledRuleIds();
      if (disabledRules == null) {
        disabledRules = new HashSet<>();
      }
    }
  }

  @Override
  public String[] getSupportedServiceNames() {
    return getServiceNames();
  }

  public static String[] getServiceNames() {
    return SERVICE_NAMES;
  }

  @Override
  public boolean supportsService(final String sServiceName) {
    for (final String sName : SERVICE_NAMES) {
      if (sServiceName.equals(sName)) {
        return true;
      }
    }
    return false;
  }

  @Override
  public String getImplementationName() {
    return Main.class.getName();
  }

  public static XSingleComponentFactory __getComponentFactory(
      final String sImplName) {
    SingletonFactory xFactory = null;
    if (sImplName.equals(Main.class.getName())) {
      xFactory = new SingletonFactory();
    }
    return xFactory;
  }

  public static boolean __writeRegistryServiceInfo(final XRegistryKey regKey) {
    return Factory.writeRegistryServiceInfo(Main.class.getName(), Main.getServiceNames(), regKey);
  }

  @Override
  public void trigger(final String sEvent) {
    if (Thread.currentThread().getContextClassLoader() == null) {
      Thread.currentThread().setContextClassLoader(Main.class.getClassLoader());
    }
    if (!javaVersionOkay()) {
      return;
    }
    try {
      if ("configure".equals(sEvent)) {
        runOptionsDialog();
      } else if ("about".equals(sEvent)) {
        final AboutDialogThread aboutThread = new AboutDialogThread(MESSAGES);
        aboutThread.start();
      } else {
        System.err.println("Sorry, don't know what to do, sEvent = " + sEvent);
      }
    } catch (final Throwable e) {
      showError(e);
    }
  }

  private boolean javaVersionOkay() {
    final String version = System.getProperty("java.version");
    if (version != null
        && (version.startsWith("1.0") || version.startsWith("1.1")
            || version.startsWith("1.2") || version.startsWith("1.3")
            || version.startsWith("1.4") || version.startsWith("1.5")
            || version.startsWith("1.6"))) {
      final DialogThread dt = new DialogThread(
          "Error: LanguageTool requires Java 7.0 or later. Current version: " + version);
      dt.start();
      return false;
    }
    try {
      // do not set look and feel for on Mac OS X as it causes the following error:
      // soffice[2149:2703] Apple AWT Java VM was loaded on first thread -- can't start AWT.
      if (!System.getProperty("os.name").contains("OS X")) {
        for (UIManager.LookAndFeelInfo info : UIManager
            .getInstalledLookAndFeels()) {
          if ("Nimbus".equals(info.getName())) {
            UIManager.setLookAndFeel(info.getClassName());
            break;
          }
        }
      }
    } catch (Exception ignored) {
      // Well, what can we do...
    }
    return true;
  }

  static void showError(final Throwable e) {
    String msg = "An error has occurred in LanguageTool "
        + JLanguageTool.VERSION + ":\n" + e.toString() + "\nStacktrace:\n";
    msg += Tools.getFullStackTrace(e);
    final String metaInfo = "OS: " + System.getProperty("os.name") + " on "
        + System.getProperty("os.arch") + ", Java version "
        + System.getProperty("java.version") + " from "
        + System.getProperty("java.vm.vendor");
    msg += metaInfo;
    final DialogThread dt = new DialogThread(msg);
    e.printStackTrace()// without this, we see no exception if a test case fails
    dt.start();
  }

  private File getHomeDir() {
    final String homeDir = System.getProperty("user.home");
    if (homeDir == null) {
      @SuppressWarnings("ThrowableInstanceNeverThrown")
      final RuntimeException ex = new RuntimeException("Could not get home directory");
      showError(ex);
    }
    return new File(homeDir);
  }

  private class AboutDialogThread extends Thread {

    private final ResourceBundle messages;

    AboutDialogThread(final ResourceBundle messages) {
      this.messages = messages;
    }

    @Override
    public void run() {
      // TODO: null can cause the dialog to appear on the wrong screen in a
      // multi-monitor setup, but we just don't have a proper java.awt.Component
      // here which we could use instead:
      final AboutDialog about = new AboutDialog(messages, null);
      about.show();
    }
  }

  /**
   * Called when "Ignore" is selected e.g. in the context menu for an error.
   */
  @Override
  public void ignoreRule(final String ruleId, final Locale locale)
      throws IllegalArgumentException {
    // TODO: config should be locale-dependent
    disabledRulesUI.add(ruleId);
    config.setDisabledRuleIds(disabledRulesUI);
    try {
      config.saveConfiguration(langTool.getLanguage());
    } catch (final Throwable t) {
      showError(t);
    }
    recheck = true;
  }

  /**
   * Called on rechecking the document - resets the ignore status for rules that
   * was set in the spelling dialog box or in the context menu.
   *
   * The rules disabled in the config dialog box are left as intact.
   */
  @Override
  public void resetIgnoreRules() {
    config.setDisabledRuleIds(disabledRules);
    try {
      config.saveConfiguration(langTool.getLanguage());
    } catch (final Throwable t) {
      showError(t);
    }
    recheck = true;
  }

  @Override
  public String getServiceDisplayName(Locale locale) {
    return "LanguageTool";
  }

}

/**
* A simple comparator for sorting errors by their position.
*/
class ErrorPositionComparator implements Comparator<SingleProofreadingError> {

  @Override
  public int compare(final SingleProofreadingError match1,
      final SingleProofreadingError match2) {
    if (match1.aSuggestions.length == 0 && match2.aSuggestions.length > 0) {
      return 1;
    }
    if (match2.aSuggestions.length == 0 && match1.aSuggestions.length > 0) {
      return -1;
    }
    final int error1pos = match1.nErrorStart;
    final int error2pos = match2.nErrorStart;
    if (error1pos > error2pos) {
      return 1;
    } else if (error1pos < error2pos) {
      return -1;
    } else {
      if (match1.aSuggestions.length != 0 && match2.aSuggestions.length != 0
          && match1.aSuggestions.length != match2.aSuggestions.length) {
        return ((Integer) (match1.aSuggestions.length))
            .compareTo(match2.aSuggestions.length);
      }
    }
    return match1.aRuleIdentifier.compareTo(match2.aRuleIdentifier);
  }
}

class DialogThread extends Thread {
  private final String text;

  DialogThread(final String text) {
    this.text = text;
  }

  @Override
  public void run() {
    JOptionPane.showMessageDialog(null, text);
  }
}
TOP

Related Classes of org.languagetool.openoffice.Main$AboutDialogThread

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.