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 }