001/*
002 * This library is free software; you can redistribute it and/or
003 * modify it under the terms of the GNU Lesser General Public
004 * License as published by the Free Software Foundation; either
005 * version 2.1 of the License, or (at your option) any later version.
006 *
007 * This library is distributed in the hope that it will be useful,
008 * but WITHOUT ANY WARRANTY; without even the implied warranty of
009 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
010 * Lesser General Public License for more details.
011 *
012 * You should have received a copy of the GNU Lesser General Public
013 * License along with this library; if not, write to the Free Software
014 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
015 */
016package org.openstreetmap.josm.gui.util;
017
018import java.lang.ref.WeakReference;
019
020import javax.swing.JComponent;
021import javax.swing.RepaintManager;
022import javax.swing.SwingUtilities;
023
024import org.openstreetmap.josm.tools.Logging;
025
026/**
027 * <p>This class is used to detect Event Dispatch Thread rule violations</p>
028 *
029 * <p>This is a modification of original idea of Scott Delap.</p>
030 *
031 * @author Scott Delap
032 * @author Alexander Potochkin
033 */
034public class CheckThreadViolationRepaintManager extends RepaintManager {
035    private WeakReference<JComponent> lastComponent;
036
037    @Override
038    public synchronized void addInvalidComponent(JComponent component) {
039        checkThreadViolations(component);
040        super.addInvalidComponent(component);
041    }
042
043    @Override
044    public void addDirtyRegion(JComponent component, int x, int y, int w, int h) {
045        checkThreadViolations(component);
046        super.addDirtyRegion(component, x, y, w, h);
047    }
048
049    private void checkThreadViolations(JComponent c) {
050        if (!SwingUtilities.isEventDispatchThread()) {
051            boolean repaint = false;
052            boolean fromSwing = false;
053            boolean imageUpdate = false;
054            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
055            for (StackTraceElement st : stackTrace) {
056                if (repaint && st.getClassName().startsWith("javax.swing.") &&
057                    // for details see https://swinghelper.dev.java.net/issues/show_bug.cgi?id=1
058                    !st.getClassName().startsWith("javax.swing.SwingWorker")) {
059                    fromSwing = true;
060                }
061                if (repaint && "imageUpdate".equals(st.getMethodName())) {
062                    imageUpdate = true;
063                }
064                if ("repaint".equals(st.getMethodName())) {
065                    repaint = true;
066                    fromSwing = false;
067                }
068                if ("read".equals(st.getMethodName()) && "javax.swing.JEditorPane".equals(st.getClassName())) {
069                    // Swing reads html from a background thread
070                    return;
071                }
072            }
073            if (imageUpdate) {
074                //assuming it is java.awt.image.ImageObserver.imageUpdate(...)
075                //image was asynchronously updated, that's ok
076                return;
077            }
078            if (repaint && !fromSwing) {
079                //no problems here, since repaint() is thread safe
080                return;
081            }
082            //ignore the last processed component
083            if (lastComponent != null && c == lastComponent.get()) {
084                return;
085            }
086            lastComponent = new WeakReference<>(c);
087            violationFound(c, stackTrace);
088        }
089    }
090
091    protected static void violationFound(JComponent c, StackTraceElement[] stackTrace) {
092        Logging.error("");
093        Logging.error("EDT violation detected");
094        Logging.error(c.toString());
095        for (StackTraceElement st : stackTrace) {
096            Logging.error("\tat " + st);
097        }
098    }
099}