001    /*
002     * Copyright 2002-2004 the original author or authors.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.richclient.table;
017    
018    import java.awt.EventQueue;
019    import java.awt.Toolkit;
020    import java.lang.reflect.InvocationTargetException;
021    import java.util.List;
022    
023    import javax.swing.RepaintManager;
024    import javax.swing.SwingUtilities;
025    
026    /**
027     * Thread responsible for publishing changes to the Model. Sleeps for a defined
028     * amount of time, waits for no activity in the UI and then users invokeAndWait
029     * to publish changes.
030     */
031    public class TableUpdater extends Thread {
032        private int sleepTime = 3000;
033    
034        private int eqSleepTime = 1000;
035    
036        private boolean updatesEnabled = true;
037    
038        private Runnable publishRunnable;
039    
040        private Runnable emptyRunnable;
041    
042        private TableDataProvider tableDataProvider;
043    
044        private MutableTableModel tableModel;
045    
046        private boolean done;
047    
048        public TableUpdater(TableDataProvider provider, MutableTableModel tableModel) {
049            super();
050            setPriority(Thread.MIN_PRIORITY);
051            this.updatesEnabled = true;
052            this.tableDataProvider = provider;
053            this.tableModel = tableModel;
054    
055            // Runnable used to publish changes to the event dispatching thread
056            this.publishRunnable = new Runnable() {
057                public void run() {
058                    publishChangesOnEventDispatchingThread();
059                }
060            };
061    
062            // Empty runnable, used to wait until the event dispatching thread
063            // has finished processing any pending events.
064            this.emptyRunnable = new Runnable() {
065                public void run() {
066                }
067            };
068        }
069    
070        public void interrupt() {
071            done = true;
072            super.interrupt();
073        }
074    
075        public void run() {
076            while (!isInterrupted() && !done) {
077                try {
078                    sleep(sleepTime);
079                    waitForUpdatesEnabled();
080                    waitForIdleEventQueue();
081                    publishChanges();
082                }
083                catch (InterruptedException ie) {
084                }
085            }
086        }
087    
088        private void waitForUpdatesEnabled() {
089            synchronized (this) {
090                while (!this.updatesEnabled) {
091                    try {
092                        wait();
093                    }
094                    catch (InterruptedException ie) {
095                    }
096                }
097            }
098        }
099    
100        private void waitForIdleEventQueue() {
101            EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
102            while (queue.peekEvent() != null) {
103                try {
104                    sleep(eqSleepTime);
105                }
106                catch (InterruptedException ie) {
107                }
108            }
109        }
110    
111        /**
112         * Publishes changes on the event dispatching thread when the system isn't
113         * busy. This blocks the caller until the changes have been published.
114         */
115        private void publishChanges() {
116            // And wait until there are no pending events.
117            /*
118             * try { tableModel.lock(); } catch (InterruptedException e) {
119             * System.err.println("Table updater interrupted on table lock attempt.
120             * Nothing updated."); return; }
121             */
122    
123            try {
124                // publish the changes on the event dispatching thread
125                SwingUtilities.invokeAndWait(publishRunnable);
126            }
127            catch (InterruptedException ie) {
128            }
129            catch (InvocationTargetException ite) {
130            }
131    
132            try {
133                // Wait until the system has completed processing of any
134                // events we triggered as part of publishing changes.
135                SwingUtilities.invokeAndWait(emptyRunnable);
136            }
137            catch (InterruptedException ie) {
138            }
139            catch (InvocationTargetException ite) {
140            }
141    
142            /*
143             * tableModel.unlock();
144             */
145        }
146    
147        /**
148         * Does the actual publishing of changes.
149         */
150        private void publishChangesOnEventDispatchingThread() {
151            List newRows = tableDataProvider.takeData();
152            if (newRows.size() > 0) {
153                tableModel.addRows(newRows);
154                RepaintManager.currentManager(null).paintDirtyRegions();
155                newRows.clear();
156            }
157        }
158    
159        /**
160         * If enable is true, we are allowed to publish changes, otherwise we
161         * aren't.
162         */
163        public void setUpdatesEnabled(boolean enable) {
164            synchronized (this) {
165                updatesEnabled = enable;
166                if (updatesEnabled) {
167                    notify();
168                }
169            }
170        }
171    
172        public boolean getUpdatesEnabled() {
173            return updatesEnabled;
174        }
175    }