Merge remote-tracking branch 'origin/GP-1-dragonmacher-help-validation-tweak'

This commit is contained in:
Ryan Kurtz
2021-10-25 13:32:48 -04:00
2 changed files with 50 additions and 39 deletions
@@ -32,20 +32,20 @@ import javax.swing.UIManager;
import docking.ComponentProvider;
import docking.action.DockingActionIf;
import generic.concurrent.GThreadPool;
import generic.util.WindowUtilities;
import ghidra.util.*;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskLauncher;
import ghidra.util.task.TaskMonitor;
import resources.ResourceManager;
import utilities.util.reflection.ReflectionUtilities;
/**
* Class that uses JavaHelp browser to show context sensitive help.
*
*
* <p>Note: this manager will validate all registered help when in development mode. In order
* to catch items that have not registered help at all, we rely on those items to register a
* to catch items that have not registered help at all, we rely on those items to register a
* default {@link HelpLocation} that will get flagged as invalid. Examples of this usage are
* the {@link DockingActionIf} and the {@link ComponentProvider} base classes.
*/
@@ -78,7 +78,7 @@ public class HelpManager implements HelpService {
/**
* Constructor.
*
*
* @param url url for the main HelpSet file for the application.
* @throws HelpSetException if HelpSet could not be created
*/
@@ -104,7 +104,7 @@ public class HelpManager implements HelpService {
/**
* Add the help set for the given URL.
*
*
* @param url url for the HelpSet (.hs) file
* @param classLoader the help classloader that knows how to find help modules in the classpath
* @throws HelpSetException if the help set could not be created from the given URL.
@@ -160,8 +160,8 @@ public class HelpManager implements HelpService {
}
// Implementation Note: the same object can have different help registered. For example,
// DockingActions do this as part of their construction process.
// These actions will register a default help location and then
// DockingActions do this as part of their construction process.
// These actions will register a default help location and then
// subclasses may change that location. We always use the most
// recently registered action.
helpLocations.put(helpObject, location);
@@ -188,6 +188,7 @@ public class HelpManager implements HelpService {
/**
* Returns the master help set (the one into which all other help sets are merged).
* @return the help set
*/
public GHelpSet getMasterHelpSet() {
return mainHS;
@@ -195,10 +196,10 @@ public class HelpManager implements HelpService {
/**
* Display the help page for the given URL. This is a specialty method for displaying
* help when a specific file is desired, like an introduction page. Showing help for
* objects within the system is accomplished by calling
* help when a specific file is desired, like an introduction page. Showing help for
* objects within the system is accomplished by calling
* {@link #showHelp(Object, boolean, Component)}.
*
*
* @param url the URL to display
* @see #showHelp(Object, boolean, Component)
*/
@@ -400,7 +401,7 @@ public class HelpManager implements HelpService {
((DefaultHelpBroker) mainHB).setActivationWindow(owner);
}
// make sure we are visible before we set data (prevents bugs in JavaHelp)
// make sure we are visible before we set data (prevents bugs in JavaHelp)
mainHB.setDisplayed(true);
if (!wasDisplayed) {
@@ -443,21 +444,20 @@ public class HelpManager implements HelpService {
return;
}
TaskLauncher.launchNonModal("Validating Help", monitor -> {
try {
printBadHelp(monitor);
}
catch (CancelledException e) {
// user cancelled; just exit
}
});
GThreadPool.runAsync(Swing.GSWING_THREAD_POOL_NAME, this::doPrintBadHelp);
}
private void printBadHelp(TaskMonitor monitor) throws CancelledException {
private void doPrintBadHelp() {
Map<Object, HelpLocation> badHelp = getInvalidHelpLocations(monitor);
if (badHelp.isEmpty()) {
return;
Map<Object, HelpLocation> badHelp;
try {
badHelp = getInvalidHelpLocations(TaskMonitor.DUMMY);
if (badHelp.isEmpty()) {
return;
}
}
catch (CancelledException e) {
return; // user cancelled
}
StringBuilder buffy = new StringBuilder();
@@ -474,7 +474,6 @@ public class HelpManager implements HelpService {
throws CancelledException {
Map<Object, HelpLocation> map = new WeakHashMap<>();
Map<Object, HelpLocation> helpLocationsCopy = copyHelpLocations();
monitor.initialize(helpLocationsCopy.size());
Set<Entry<Object, HelpLocation>> entries = helpLocationsCopy.entrySet();
@@ -493,7 +492,7 @@ public class HelpManager implements HelpService {
}
private Map<Object, HelpLocation> copyHelpLocations() {
// we must copy the help locations, since we are in a background thread and the
// we must copy the help locations, since we are in a background thread and the
// locations map is frequently updated by the Swing thread
return Swing.runNow(() -> new HashMap<>(helpLocations));
}
@@ -501,9 +500,9 @@ public class HelpManager implements HelpService {
//
// Warning!
// This code has timing implications. DockingActions register themselves with the help
// system as part of their construction. At that point, they are not usually fully
// system as part of their construction. At that point, they are not usually fully
// constructed, as most clients will use the newly constructed action to set the various
// toolbar/menu/popup data elements. For us to know if the action is really only for
// toolbar/menu/popup data elements. For us to know if the action is really only for
// keybinding purposes, we have to do this check after the action is fully constructed.
//
private boolean isKeybindingOnly(Object helpee) {
@@ -630,7 +629,7 @@ public class HelpManager implements HelpService {
}
// Note: not sure if we ever need to merge again after the initial load. If so, then
// this flag doesn't make sense. However, as of this writing, we do not discover
// this flag doesn't make sense. However, as of this writing, we do not discover
// new help sets on the fly.
hasMergedHelpSets = true;
helpSetsPendingMerge.clear();
@@ -657,7 +656,7 @@ public class HelpManager implements HelpService {
/**
* Create a new help set for the given url, if one does
* not already exist.
* @param classLoader
* @param classLoader the class loader
*/
private HelpSet createHelpSet(URL url, GHelpClassLoader classLoader) throws HelpSetException {
if (!urlToHelpSets.containsKey(url)) {
@@ -668,7 +667,7 @@ public class HelpManager implements HelpService {
return null;
}
/**
/**
* Set the color resources on the JEditorPane for selection so that
* you can see the highlights when you do a search in the JavaHelp.
*/
@@ -31,17 +31,17 @@ import ghidra.util.SystemUtilities;
* <p>
* The simple behavior for when new tasks are submitted:<br>
* 1) If there any idle threads, use that thread.<br>
* 2) If all existing threads are busy and the number of threads is less than max threads, add a
* 2) If all existing threads are busy and the number of threads is less than max threads, add a
* new thread and use it.<br>
* 3) if all threads are busy and there are max number of threads, queue the item until a thread
* becomes free.<br>
* <p>
* The simple behavior for when tasks are completed by a thread:<br>
* 1) If there are tasks in the queue, start processing a new item in the newly freed thread.<br>
* 2) if there are more threads that min threads, allow this thread to die if no new
* 2) if there are more threads that min threads, allow this thread to die if no new
* jobs arrive before
* the "KEEP ALIVE" time expires which is currently 15 seconds.<br>
* 3) if there are min threads or less, allow this thread to wait forever for a new job
* 3) if there are min threads or less, allow this thread to wait forever for a new job
* to arrive.<br>
*/
public class GThreadPool {
@@ -76,6 +76,18 @@ public class GThreadPool {
return threadPool;
}
/**
* Runs the given runnable in a background thread using a shared thread pool of the given name.
* @param poolName the thread pool name
* @param r the runnable
* @return the future
*/
public static CompletableFuture<Void> runAsync(String poolName, Runnable r) {
GThreadPool pool = getSharedThreadPool(poolName);
Executor ex = pool.getExecutor();
return CompletableFuture.runAsync(r, ex);
}
private GThreadPool(String name) {
this.name = name;
executor = new GThreadPoolExecutor();
@@ -157,7 +169,7 @@ public class GThreadPool {
/**
* Returns true if this is not a shared thread pool.
*
*
* @return true if this is not a shared thread pool.
*/
public boolean isPrivate() {
@@ -166,12 +178,12 @@ public class GThreadPool {
/**
* Returns the {@link Executor} used by this thread pool.
*
* <P>Note: normal usage of this thread pool contraindicates accessing the executor of
*
* <P>Note: normal usage of this thread pool contraindicates accessing the executor of
* this pool. For managing your own jobs, you should use the method on this class directly.
* The intent of this method is to provide access to the executor so that it may be
* The intent of this method is to provide access to the executor so that it may be
* passed to other asynchronous APIs, such as the {@link CompletableFuture}.
*
*
* @return the executor
*/
public Executor getExecutor() {
@@ -180,7 +192,7 @@ public class GThreadPool {
//==================================================================================================
// Inner Classes
//==================================================================================================
//==================================================================================================
private class GThreadPoolExecutor extends ThreadPoolExecutor {
private volatile int maxThreadCount = SystemUtilities.getDefaultThreadPoolSize();