class MessageHandler {
  
  private static final String logLineBreak = System.lineSeparator();  //  LineBreak in Log-File (MS-Windows compatible)
  
  private static boolean isOpen = false;
  private static boolean isInit = false;
  
  private static boolean testMode;
  
  MessageHandler(XComponentContext xContext) {
    initLogFile(xContext);
  }

  /**
   * Initialize log-file
   */
  private static void initLogFile(XComponentContext xContext) {
    if (!isInit) {
      isInit = true;
      try (OutputStream stream = new FileOutputStream(OfficeTools.getLogFilePath(xContext));
          OutputStreamWriter writer = new OutputStreamWriter(stream, StandardCharsets.UTF_8);
          BufferedWriter br = new BufferedWriter(writer)
          ) {
        Date date = new Date();
        OfficeProductInfo officeInfo = OfficeTools.getOfficeProductInfo(xContext);
        writer.write("LT office integration log from " + date + logLineBreak + logLineBreak);
        writer.write("LanguageTool " + JLanguageTool.VERSION + " (" + JLanguageTool.BUILD_DATE + ", " 
            + JLanguageTool.GIT_SHORT_ID + ")" + logLineBreak);
        writer.write("OS: " + System.getProperty("os.name") + " " 
            + System.getProperty("os.version") + " on " + System.getProperty("os.arch") + logLineBreak);
        if (officeInfo != null) { 
          writer.write(officeInfo.ooName + " " + officeInfo.ooVersion + officeInfo.ooExtension
              + " (" + officeInfo.ooVendor +"), " + officeInfo.ooLocale + logLineBreak);
        }
        writer.write(OfficeTools.getJavaInformation() + logLineBreak + logLineBreak);
      } catch (Throwable t) {
        showError(t);
      }
    }
  }
  
  /**
   * Initialize MessageHandler
   */
  static void init(XComponentContext xContext) {
    initLogFile(xContext);
  }

  /**
   * Show an error in a dialog
   */
  public static void showError(Throwable e) {
    printException(e);
    if (testMode) {
      throw new RuntimeException(e);
    }
    String msg = "An error has occurred in LanguageTool "
        + JLanguageTool.VERSION + " (" + JLanguageTool.BUILD_DATE + "):\n" + e + "\nStacktrace:\n";
    msg += Tools.getFullStackTrace(e);
    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;
    DialogThread dt = new DialogThread(msg, true);
    e.printStackTrace();
    dt.start();
  }

  /**
   * Write to log-file
   */
  public static void printToLogFile(String str) {
    try (OutputStream stream = new FileOutputStream(OfficeTools.getLogFilePath(), true);
        OutputStreamWriter writer = new OutputStreamWriter(stream, StandardCharsets.UTF_8);
        BufferedWriter br = new BufferedWriter(writer)
        ) {
      writer.write(str + logLineBreak);
    } catch (Throwable t) {
      showError(t);
    }
  }

  /** 
   * Prints Exception to log-file  
   */
  public static void printException(Throwable t) {
   printToLogFile(Tools.getFullStackTrace(t));
  }

  /**
   * Will throw exception instead of showing errors as dialogs - use only for test cases.
   */
  static void setTestMode(boolean mode) {
    testMode = mode;
  }

  /**
   * Shows a message in a dialog box
   * @param txt message to be shown
   */
  public static void showMessage(String txt) {
    showMessage(txt, true);
  }

  static void showMessage(String txt, boolean toLogFile) {
    if (toLogFile) {
      printToLogFile(txt);
    }
    DialogThread dt = new DialogThread(txt, false);
    dt.run();
  }
  
  public static void showFullStackMessage (String msg) {<FILL_FUNCTION_BODY>}

  /**
   * run an information message in a separate thread
   * closing if lost focus
   */
  static void showClosingInformationDialog(String text) {
    ClosingInformationThread informationDialog = new ClosingInformationThread(text);
    informationDialog.start();
  }
  
  /**
   * class to run a dialog in a separate thread
   */
  private static class DialogThread extends Thread {
    private final String text;
    private boolean isException;

    DialogThread(String text, boolean isException) {
      this.text = text;
      this.isException = isException;
    }

    @Override
    public void run() {
      if (isException) {
        if (!isOpen) {
          isOpen = true;
          JOptionPane.showMessageDialog(null, text);
          isOpen = false;
        }
      } else {
        JOptionPane.showMessageDialog(null, text);
      }
    }
  }
  
  /**
   * class to run a dialog in a separate thread
   * closing if lost focus
   */
  private static class ClosingInformationThread extends Thread {
    private final String text;
    JDialog dialog;

    ClosingInformationThread(String text) {
      this.text = text;
    }

    @Override
    public void run() {
      JOptionPane pane = new JOptionPane(text, JOptionPane.INFORMATION_MESSAGE);
      dialog = pane.createDialog(null, UIManager.getString("OptionPane.messageDialogTitle", null));
      dialog.setModal(false);
      dialog.setAutoRequestFocus(true);
      dialog.setAlwaysOnTop(true);
      dialog.addWindowFocusListener(new WindowFocusListener() {
        @Override
        public void windowGainedFocus(WindowEvent e) {
        }
        @Override
        public void windowLostFocus(WindowEvent e) {
          dialog.setVisible(false);
        }
      });
      dialog.setVisible(true);
    }
  }
  
}

    try {
      throw new RuntimeException(msg);
    } catch (Throwable t) {
      showError(t);
    }
  
<no_super_class>