Log in Help
Print
HomegatepluginsDeveloper_Toolssrcorgjdesktopswinghelperdebug 〉 CheckThreadViolationRepaintManager.java
 
/*
 * 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.jdesktop.swinghelper.debug;

import java.lang.ref.WeakReference;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;

/**
 * This class is used to detect Event Dispatch Thread rule violations<br>
 * See <a
 * href="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How
 * to Use Threads</a> for more info. This is a modification of original idea of
 * Scott Delap<br>
 * Initial version of ThreadCheckingRepaintManager can be found here<br>
 * <a href="http://www.clientjava.com/blog/2004/08/20/1093059428000.html">Easily
 * Find Swing Threading Mistakes</a> </p>
 * 
 * @author Scott Delap
 * @author Alexander Potochkin
 */
public class CheckThreadViolationRepaintManager extends RepaintManager {
  // it is recommended to pass the complete check
  private boolean completeCheck = true;

  private WeakReference<JComponent> lastComponent;

  public CheckThreadViolationRepaintManager(boolean completeCheck) {
    this.completeCheck = completeCheck;
  }

  public CheckThreadViolationRepaintManager() {
    this(true);
  }

  public boolean isCompleteCheck() {
    return completeCheck;
  }

  public void setCompleteCheck(boolean completeCheck) {
    this.completeCheck = completeCheck;
  }

  public synchronized void addInvalidComponent(JComponent component) {
    checkThreadViolations(component);
    super.addInvalidComponent(component);
  }

  public void addDirtyRegion(JComponent component, int x, int y, int w, int h) {
    checkThreadViolations(component);
    super.addDirtyRegion(component, x, y, w, h);
  }

  private void checkThreadViolations(JComponent c) {
    if(!SwingUtilities.isEventDispatchThread()
        && (completeCheck || c.isShowing())) {
      boolean repaint = false;
      boolean fromSwing = false;
      boolean imageUpdate = false;
      StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
      for(StackTraceElement st : stackTrace) {
        if(repaint && st.getClassName().startsWith("javax.swing.") &&
        // for details see
        // https://swinghelper.dev.java.net/issues/show_bug.cgi?id=1
            !st.getClassName().startsWith("javax.swing.SwingWorker")) {
          fromSwing = true;
        }
        if(repaint && "imageUpdate".equals(st.getMethodName())) {
          imageUpdate = true;
        }
        if("repaint".equals(st.getMethodName())) {
          repaint = true;
          fromSwing = false;
        }
      }
      if(imageUpdate) {
        // assuming it is java.awt.image.ImageObserver.imageUpdate(...)
        // image was asynchronously updated, that's ok
        return;
      }
      if(repaint && !fromSwing) {
        // no problems here, since repaint() is thread safe
        return;
      }
      // ignore the last processed component
      if(lastComponent != null && c == lastComponent.get()) { return; }
      lastComponent = new WeakReference<JComponent>(c);
      violationFound(c, stackTrace);
    }
  }

  protected void violationFound(JComponent c, StackTraceElement[] stackTrace) {
    System.out.println();
    System.out.println("EDT violation detected");
    System.out.println(c);
    for(StackTraceElement st : stackTrace) {
      System.out.println("\tat " + st);
    }
  }

  public static void main(String[] args) throws Exception {
    // set CheckThreadViolationRepaintManager
    RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager());
    // Valid code
    SwingUtilities.invokeAndWait(new Runnable() {
      public void run() {
        test();
      }
    });
    System.out.println("Valid code passed...");
    repaintTest();
    System.out.println("Repaint test - correct code");
    // Invalide code (stack trace expected)
    test();
  }

  static void test() {
    JFrame frame = new JFrame("Am I on EDT?");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(new JButton("JButton"));
    frame.pack();
    frame.setVisible(true);
    frame.dispose();
  }

  // this test must pass
  static void imageUpdateTest() {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JEditorPane editor = new JEditorPane();
    frame.setContentPane(editor);
    editor.setContentType("text/html");
    // it works with no valid image as well
    editor.setText("<html><img src=\"file:\\lala.png\"></html>");
    frame.setSize(300, 200);
    frame.setVisible(true);
  }

  private static JButton test;

  static void repaintTest() {
    try {
      SwingUtilities.invokeAndWait(new Runnable() {
        public void run() {
          test = new JButton();
          test.setSize(100, 100);
        }
      });
    } catch(Exception e) {
      e.printStackTrace();
    }
    // repaint(Rectangle) should be ok
    test.repaint(test.getBounds());
    test.repaint(0, 0, 100, 100);
    test.repaint();
  }
}