/*******************************************************************************
* Copyright (c) 2012 Google, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Google, Inc. - initial API and implementation
*******************************************************************************/
package com.windowtester.runtime.swt.internal.display;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.Callable;
import org.eclipse.swt.widgets.Display;
import com.windowtester.runtime.swt.internal.widgets.DisplayReference;
/**
* Display introspection works in two modes: synchronous and asynchronous.
*
* In the case of asynchronous mode (method asynchIntrospect) DisplayIntrospection will start
* a thread and will try to find Display from all existing threads in current process and it
* will notify all registered listeners about success or timeout.
*
* In the case of synchronous mode (method syncIntrospect) display introspection will return
* Display object or null after some predefined timeout.
*
*/
public class DisplayIntrospection implements Runnable {
private ArrayList<IDisplayIntrospectionListener> listeners = new ArrayList<IDisplayIntrospectionListener>();
private ThreadGroup root;
private long retryTime;
/** Shell that we want to introspect in the case of synchronous introspection */
//private Shell shell;
public DisplayIntrospection(long retryTime){
this.retryTime = retryTime;
// Find the root thread group
root = Thread.currentThread().getThreadGroup();
while (root.getParent() != null) {
root = root.getParent();
}
}
public void addIntrospectionListener(IDisplayIntrospectionListener listener){
listeners.add(listener);
}
public void removeIntrospectionListener(IDisplayIntrospectionListener listener){
listeners.remove(listener);
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
Display display = doRun();
if(display!=null){
for (Iterator<IDisplayIntrospectionListener> iter = listeners.iterator(); iter.hasNext();) {
IDisplayIntrospectionListener listener = iter.next();
listener.provideDisplay(display);
}
}else{
for (Iterator<IDisplayIntrospectionListener> iter = listeners.iterator(); iter.hasNext();) {
IDisplayIntrospectionListener listener = iter.next();
listener.timeout();
}
}
}
protected Display doRun(){
long start = System.currentTimeMillis();
Display display = null;
while ((System.currentTimeMillis() - start) < retryTime) {
display = findDisplay();
if(display!=null)
break;
try {
Thread.sleep(50); // be a good guy ...
} catch (InterruptedException e) {
}
}
if(display!=null)
ensureInitialized(display);
return display;
}
/**
* This method verifies and ensures display initialization. Verification
* is based on synchronous execution of empty Runnable.
*
* @param display the display to verify
*/
protected void ensureInitialized(Display display) {
// execute until display is initialized
while(!display.isDisposed()){
try {
// this empty execution is only required
// to verify that display is initialized
new DisplayReference(display).execute(new Callable<Boolean>() {
public Boolean call() throws Exception {
return true;
}
}, 30000);
break;
} catch (Throwable e) {
continue;
}
}
}
/**
* Finds Display in all threads of this process
*
* @return Display instance from UI thread
*/
protected Display findDisplay() {
return visit(root);
}
/**
* This method recursively visits all thread groups and tries to get
* Display in all thread it finds.
*
* @return The introspected Display from UI thread
*/
protected Display visit(ThreadGroup group) {
// Display to return
Display display = null;
// Get threads in 'group'
int numThreads = group.activeCount();
Thread[] threads = new Thread[numThreads*2];
numThreads = group.enumerate(threads, false);
// Enumerate each thread in 'group'
for (int i=0; i<numThreads; i++) {
// Get thread
Thread thread = threads[i];
display = Display.findDisplay(thread);
if(display!=null)
return display;
}
// Get thread subgroups of 'group'
int numGroups = group.activeGroupCount();
ThreadGroup[] groups = new ThreadGroup[numGroups*2];
numGroups = group.enumerate(groups, false);
// Recursively visit each subgroup
for (int i=0; i<numGroups; i++) {
display = visit(groups[i]);
if(display!=null)
return display;
}
return null;
}
public void asyncIntrospect(){
Thread t = new Thread(this, "Display Introspection Thread");
t.setDaemon(true);
t.start();
}
public Display syncIntrospect(){
return doRun();
}
}