1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.springframework.richclient.util;
17
18 import java.awt.AWTEvent;
19 import java.awt.BorderLayout;
20 import java.awt.Component;
21 import java.awt.Dimension;
22 import java.awt.EventQueue;
23 import java.awt.LayoutManager;
24 import java.awt.Rectangle;
25 import java.awt.Toolkit;
26 import java.lang.ref.ReferenceQueue;
27 import java.lang.ref.PhantomReference;
28 import java.lang.reflect.InvocationTargetException;
29 import java.util.concurrent.FutureTask;
30 import java.util.concurrent.Callable;
31
32 import javax.swing.JComponent;
33 import javax.swing.JLabel;
34 import javax.swing.JPanel;
35 import javax.swing.JScrollPane;
36 import javax.swing.JTextField;
37 import javax.swing.Scrollable;
38 import javax.swing.JRootPane;
39 import javax.swing.JLayeredPane;
40 import javax.swing.SwingUtilities;
41
42 import org.springframework.richclient.test.SpringRichTestCase;
43
44
45
46
47
48 public class OverlayHelperTests extends SpringRichTestCase {
49
50
51
52
53
54
55
56
57
58
59 public void testRegressionScrollableProxy() throws Exception {
60 performScrollableTest();
61 performNonScrollableTest();
62 }
63
64
65
66
67
68
69
70
71 public void testRegressionOverlayHelperLeak() throws Exception {
72
73
74
75
76
77
78
79
80 JComponent component = createTestComponent();
81 JComponent overlay = createTestOverlay();
82 final ReferenceQueue rq = new ReferenceQueue();
83 final PhantomReference componentRef = new PhantomReference(component, rq);
84
85 final JRootPane rootPane = new JRootPane() {
86 public boolean isVisible() {
87 return true;
88 }
89 public boolean isShowing() {
90 return true;
91 }
92 protected JLayeredPane createLayeredPane() {
93 return new JLayeredPane() {
94 public boolean isVisible() {
95 return true;
96 }
97 public boolean isShowing() {
98 return true;
99 }
100 };
101 }
102 };
103
104 final int lpcount = rootPane.getLayeredPane().getComponentCountInLayer(JLayeredPane.PALETTE_LAYER.intValue());
105
106 OverlayHelper.attachOverlay(overlay, component, 0, 0, 0);
107
108 assertEquals(lpcount, rootPane.getLayeredPane().getComponentCountInLayer(JLayeredPane.PALETTE_LAYER.intValue()));
109 rootPane.getContentPane().add(component);
110
111 waitUntilEventQueueIsEmpty();
112 assertEquals(lpcount + 1, rootPane.getLayeredPane().getComponentCountInLayer(JLayeredPane.PALETTE_LAYER.intValue()));
113
114 rootPane.getContentPane().remove(component);
115
116
117 waitUntilEventQueueIsEmpty();
118
119
120 component = null;
121 overlay = null;
122
123
124 assertEquals("It appears the overlay was not removed from the layered pane when its component was removed from the content pane", lpcount, rootPane.getLayeredPane().getComponentCountInLayer(JLayeredPane.PALETTE_LAYER.intValue()));
125
126
127
128
129
130
131
132
133
134 PhantomReference pr;
135 final long end = System.currentTimeMillis() + 15000;
136 do {
137 System.gc();
138 } while((pr = (PhantomReference)rq.remove(100)) == null && System.currentTimeMillis() < end);
139 if(pr != null) {
140 pr.clear();
141 }
142 assertSame("Either something else is still holding a strong reference to the component, or the VM is not garbage collecting it. See comments in OverlayHelperTests.testRegressionOverlayHelperLeak() for more detail", pr, componentRef);
143 }
144
145
146
147
148
149
150 private void performScrollableTest() throws Exception {
151 final ScrollablePanel view = new ScrollablePanel(new BorderLayout());
152 view.setScrollableUnitIncrement(5);
153 view.setScrollableBlockIncrement(30);
154 view.setScrollableTracksViewportWidth(true);
155
156 final JComponent overlay = createTestOverlay();
157 final JComponent someField = createTestComponent();
158
159 OverlayHelper.attachOverlay(overlay, someField, 0, 0, 0);
160
161 view.add(someField);
162
163 final JScrollPane scrollPane = new JScrollPane(view);
164
165 waitUntilEventQueueIsEmpty();
166
167 final Component viewportView = scrollPane.getViewport().getView();
168
169
170
171
172
173
174 assertTrue(viewportView instanceof Scrollable);
175 assertTrue(((Scrollable) viewportView).getScrollableTracksViewportWidth());
176 assertFalse(((Scrollable) viewportView).getScrollableTracksViewportHeight());
177 assertEquals(5, ((Scrollable) viewportView).getScrollableUnitIncrement(null, 0, 0));
178 assertEquals(30, ((Scrollable) viewportView).getScrollableBlockIncrement(null, 0, 0));
179 assertEquals(view.getPreferredScrollableViewportSize(), ((Scrollable) viewportView)
180 .getPreferredScrollableViewportSize());
181 }
182
183 private void waitUntilEventQueueIsEmpty() throws InterruptedException, InvocationTargetException {
184
185 EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
186 AWTEvent peekEvent;
187 while((peekEvent = eventQueue.peekEvent()) != null) {
188 System.out.println("got event in queue: " + peekEvent);
189 Thread.currentThread().sleep(0);
190 }
191
192
193 SwingUtilities.invokeAndWait(new Runnable() {
194 public void run() { }
195 });
196 }
197
198
199
200
201
202
203
204 private void performNonScrollableTest() throws Exception {
205 final JPanel view = new JPanel(new BorderLayout());
206 final JComponent overlay = createTestOverlay();
207 final JComponent someField = createTestComponent();
208
209 OverlayHelper.attachOverlay(overlay, someField, 0, 0, 0);
210
211 view.add(someField);
212
213 final JScrollPane scrollPane = new JScrollPane(view);
214
215 waitUntilEventQueueIsEmpty();
216
217 final Component viewportView = scrollPane.getViewport().getView();
218
219 assertFalse(viewportView instanceof Scrollable);
220 }
221
222 public void testSwapScrollableForNonScrollable() throws Exception {
223 JComponent view = new ScrollablePanel(new BorderLayout());
224
225 final JComponent overlay = createTestOverlay();
226 final JComponent someField = createTestComponent();
227
228 OverlayHelper.attachOverlay(overlay, someField, 0, 0, 0);
229
230 view.add(someField);
231
232 final JScrollPane scrollPane = new JScrollPane(view);
233
234 waitUntilEventQueueIsEmpty();
235
236 Component viewportView = scrollPane.getViewport().getView();
237
238 assertTrue(viewportView instanceof Scrollable);
239
240 view.remove(someField);
241 view = new JPanel(new BorderLayout());
242 view.add(someField);
243 scrollPane.setViewportView(view);
244
245 waitUntilEventQueueIsEmpty();
246
247 viewportView = scrollPane.getViewport().getView();
248
249 assertFalse(viewportView instanceof Scrollable);
250
251 view.remove(someField);
252 view = new ScrollablePanel(new BorderLayout());
253 view.add(someField);
254 scrollPane.setViewportView(view);
255
256 waitUntilEventQueueIsEmpty();
257
258 viewportView = scrollPane.getViewport().getView();
259
260 assertTrue(viewportView instanceof Scrollable);
261 }
262
263 private JComponent createTestComponent() {
264 return new JTextField("Hello, world!") {
265
266
267 public boolean isVisible() {
268 return true;
269 }
270
271 public boolean isShowing() {
272 return true;
273 }
274 };
275 }
276
277 private JComponent createTestOverlay() {
278 final JComponent overlay = new JLabel("x") {
279
280
281 public boolean isVisible() {
282 return true;
283 }
284
285 public boolean isShowing() {
286 return true;
287 }
288 };
289 overlay.setOpaque(false);
290 return overlay;
291 }
292
293 public static class ScrollablePanel extends JPanel implements Scrollable {
294 private int scrollableUnitIncrement = 10;
295
296 private int scrollableBlockIncrement = 40;
297
298 private boolean scrollableTracksViewportWidth = false;
299
300 private boolean scrollableTracksViewportHeight = false;
301
302 public ScrollablePanel(LayoutManager layout, boolean isDoubleBuffered) {
303 super(layout, isDoubleBuffered);
304 }
305
306 public ScrollablePanel(LayoutManager layout) {
307 super(layout);
308 }
309
310 public ScrollablePanel(boolean isDoubleBuffered) {
311 super(isDoubleBuffered);
312 }
313
314 public ScrollablePanel() {
315 }
316
317 public void setScrollableUnitIncrement(final int scrollableUnitIncrement) {
318 this.scrollableUnitIncrement = scrollableUnitIncrement;
319 }
320
321 public void setScrollableBlockIncrement(final int scrollableBlockIncrement) {
322 this.scrollableBlockIncrement = scrollableBlockIncrement;
323 }
324
325 public void setScrollableTracksViewportWidth(final boolean scrollableTracksViewportWidth) {
326 this.scrollableTracksViewportWidth = scrollableTracksViewportWidth;
327 }
328
329 public void setScrollableTracksViewportHeight(final boolean scrollableTracksViewportHeight) {
330 this.scrollableTracksViewportHeight = scrollableTracksViewportHeight;
331 }
332
333 public Dimension getPreferredScrollableViewportSize() {
334 return getPreferredSize();
335 }
336
337 public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
338 return this.scrollableUnitIncrement;
339 }
340
341 public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
342 return this.scrollableBlockIncrement;
343 }
344
345 public boolean getScrollableTracksViewportWidth() {
346 return this.scrollableTracksViewportWidth;
347 }
348
349 public boolean getScrollableTracksViewportHeight() {
350 return this.scrollableTracksViewportHeight;
351 }
352 }
353 }