GT-2724,2216 - Table Chooser Dialog - Improvements: 1) objects instead

of row numbers are used to track work items, 2) added API methods for
things like removing items, and getting a dialog closed notification;
added tests
This commit is contained in:
dragonmacher 2019-04-09 18:22:01 -04:00
parent 49c2010b63
commit d474d83166
14 changed files with 670 additions and 361 deletions

View file

@ -1,43 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generic.platform;
import java.lang.reflect.InvocationHandler;
import org.apache.commons.lang3.reflect.MethodUtils;
/**
* A general interface for handle Mac Application callbacks. Some possible callbacks are:
* <ul>
* <li>quit</li>
* <li>about</li>
* <li>preferences</li>
* <li>file handling</li>
* </ul>
*
* see com.apple.eawt.Application
*/
abstract class AbstractMacHandler implements InvocationHandler {
protected Object getApplication() throws Exception {
Class<?> clazz = Class.forName("com.apple.eawt.Application");
Object application = MethodUtils.invokeExactStaticMethod(clazz, "getApplication");
return application;
}
}

View file

@ -1,77 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generic.platform;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.apache.commons.lang3.reflect.MethodUtils;
import ghidra.framework.OperatingSystem;
import ghidra.framework.Platform;
import ghidra.util.Msg;
/**
* A base implementation for creating an 'About' menu action callback. This is executed when
* the user presses the Dock's 'Ghidra->About' menu action.
* <p>
* Simply constructing this class will register it.
* <p>
* See
* com.apple.eawt.Application.setAboutHandler(AboutHandler)
* com.apple.eawt.AboutHandler.handleAbout(AboutEvent)
*/
public abstract class MacAboutHandler extends AbstractMacHandler {
public MacAboutHandler() {
addAboutApplicationListener();
}
public abstract void about();
private void addAboutApplicationListener() {
if (Platform.CURRENT_PLATFORM.getOperatingSystem() != OperatingSystem.MAC_OS_X) {
return;
}
try {
Object application = getApplication();
setAboutHandler(application);
}
catch (Exception e) {
Msg.error(this, "Unable to install Mac quit handler", e);
}
}
private void setAboutHandler(Object application) throws Exception {
Class<?> aboutHandlerClass = Class.forName("com.apple.eawt.AboutHandler");
Object aboutHandler = Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[] { aboutHandlerClass }, this);
MethodUtils.invokeMethod(application, "setAboutHandler", aboutHandler);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Args: AboutEvent
about(); // call our about() callback, ignoring the Application API
// the handleAbout() is void--return null
return null;
}
}

View file

@ -1,83 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generic.platform;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.apache.commons.lang3.reflect.MethodUtils;
import ghidra.framework.OperatingSystem;
import ghidra.framework.Platform;
import ghidra.util.Msg;
/**
* A base implementation for creating an 'Quit' menu action callback. This is executed when
* the user presses the Dock's 'Ghidra->Quit' menu action.
* <p>
* Simply constructing this class will register it.
* <p>
* See
* com.apple.eawt.Application.setQuitHandler(QuitHandler)
* com.apple.eawt.AboutHandler.handleQuitRequestWith(QuitEvent, QuitResponse)
*/
public abstract class MacQuitHandler extends AbstractMacHandler {
public MacQuitHandler() {
addQuitApplicationListener(this);
}
public abstract void quit();
private void addQuitApplicationListener(MacQuitHandler macQuitHandler) {
if (Platform.CURRENT_PLATFORM.getOperatingSystem() != OperatingSystem.MAC_OS_X) {
return;
}
try {
Object application = getApplication();
setQuitHandler(application);
}
catch (Exception e) {
Msg.error(this, "Unable to install Mac quit handler", e);
}
}
private void setQuitHandler(Object application) throws Exception {
Class<?> quitHandlerClass = Class.forName("com.apple.eawt.QuitHandler");
Object quitHandler = Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[] { quitHandlerClass }, this);
MethodUtils.invokeMethod(application, "setQuitHandler", quitHandler);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Args: QuitEvent event, QuitResponse response
// Call QuitResponse.cancelQuit(), as we will allow our tool to quit the application
// instead of the OS.
Object response = args[1];
MethodUtils.invokeExactMethod(response, "cancelQuit");
quit();
// the handleQuitRequestWith() is void--return null
return null;
}
}

View file

@ -20,12 +20,15 @@ import static org.junit.Assert.fail;
import java.io.File;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
import org.junit.Assert;
import org.junit.rules.TestName;
import ghidra.framework.Application;
import ghidra.framework.TestApplicationUtils;
import ghidra.util.SystemUtilities;
import ghidra.util.UniversalIdGenerator;
@ -341,7 +344,24 @@ public abstract class AbstractGTest {
}
/**
* Waits for the given condition to return true.
* Waits for the given latch to be counted-down
*
* @param latch the latch to await
* @throws AssertionFailedError if the condition is not met within the timeout period
*/
public static void waitFor(CountDownLatch latch) {
try {
if (!latch.await(DEFAULT_WAIT_TIMEOUT, TimeUnit.MILLISECONDS)) {
throw new AssertionFailedError("Timed-out waiting for CountDownLatch");
}
}
catch (InterruptedException e) {
fail("Interrupted waiting for CountDownLatch");
}
}
/**
* Waits for the given condition to return true
*
* @param condition the condition that returns true when satisfied
* @throws AssertionFailedError if the condition is not met within the timeout period
@ -351,7 +371,7 @@ public abstract class AbstractGTest {
}
/**
* Waits for the given condition to return true.
* Waits for the given condition to return true
*
* @param condition the condition that returns true when satisfied
* @throws AssertionFailedError if the condition is not met within the timeout period
@ -380,7 +400,7 @@ public abstract class AbstractGTest {
*
* <P>Most clients should use {@link #waitForCondition(BooleanSupplier)}.
*
* @param condition the condition that returns true when satisfied
* @param supplier the supplier that returns true when satisfied
*/
public static void waitForConditionWithoutFailing(BooleanSupplier supplier) {
waitForCondition(supplier, false /*failOnTimeout*/, null /*failure message*/);
@ -465,7 +485,6 @@ public abstract class AbstractGTest {
* throwing an exception if that does not happen by the given timeout.
*
* @param supplier the supplier of the value
* @param timeoutMillis the timeout
* @param failureMessage the message to print upon the timeout being reached
* @param failOnTimeout if true, an exception will be thrown if the timeout is reached
* @return the value

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,13 +15,15 @@
*/
package ghidra.util.datastruct;
/**
* Factory for creating containers to use in various threading environments
*/
public class WeakDataStructureFactory {
/**
* Use when all access are on a single thread, such as the Swing thread.
*
* @return a new WeakSet
* @see CopyOnWriteReadWeakSet
*/
public static <T> WeakSet<T> createSingleThreadAccessWeakSet() {
return new ThreadUnsafeWeakSet<T>();
@ -32,7 +33,7 @@ public class WeakDataStructureFactory {
* Use when mutations outweigh iterations.
*
* @return a new WeakSet
* @see CopyOnWriteReadWeakSet
* @see CopyOnReadWeakSet
*/
public static <T> WeakSet<T> createCopyOnReadWeakSet() {
return new CopyOnReadWeakSet<T>();
@ -47,5 +48,4 @@ public class WeakDataStructureFactory {
public static <T> WeakSet<T> createCopyOnWriteWeakSet() {
return new CopyOnWriteWeakSet<T>();
}
}

View file

@ -76,17 +76,21 @@ public abstract class WeakSet<T> implements Iterable<T> {
//==================================================================================================
/**
* Add the given object to the set.
* Add the given object to the set
* @param t the object to add
*/
public abstract void add(T t);
/**
* Remove the given object from the data structure
* @param t the object to remove
*
*/
public abstract void remove(T t);
/**
* Returns true if the given object is in this data structure
* @return true if the given object is in this data structure
*/
public abstract boolean contains(T t);
@ -97,11 +101,13 @@ public abstract class WeakSet<T> implements Iterable<T> {
/**
* Return the number of objects contained within this data structure
* @return the size
*/
public abstract int size();
/**
* Return whether this data structure is empty.
* Return whether this data structure is empty
* @return whether this data structure is empty
*/
public abstract boolean isEmpty();
@ -119,4 +125,9 @@ public abstract class WeakSet<T> implements Iterable<T> {
public Stream<T> stream() {
return values().stream();
}
@Override
public String toString() {
return values().toString();
}
}