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

View File

@@ -32,20 +32,20 @@ import javax.swing.UIManager;
import docking.ComponentProvider; import docking.ComponentProvider;
import docking.action.DockingActionIf; import docking.action.DockingActionIf;
import generic.concurrent.GThreadPool;
import generic.util.WindowUtilities; import generic.util.WindowUtilities;
import ghidra.util.*; import ghidra.util.*;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskLauncher;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
import resources.ResourceManager; import resources.ResourceManager;
import utilities.util.reflection.ReflectionUtilities; import utilities.util.reflection.ReflectionUtilities;
/** /**
* Class that uses JavaHelp browser to show context sensitive help. * 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 * <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 * default {@link HelpLocation} that will get flagged as invalid. Examples of this usage are
* the {@link DockingActionIf} and the {@link ComponentProvider} base classes. * the {@link DockingActionIf} and the {@link ComponentProvider} base classes.
*/ */
@@ -78,7 +78,7 @@ public class HelpManager implements HelpService {
/** /**
* Constructor. * Constructor.
* *
* @param url url for the main HelpSet file for the application. * @param url url for the main HelpSet file for the application.
* @throws HelpSetException if HelpSet could not be created * @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. * Add the help set for the given URL.
* *
* @param url url for the HelpSet (.hs) file * @param url url for the HelpSet (.hs) file
* @param classLoader the help classloader that knows how to find help modules in the classpath * @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. * @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, // Implementation Note: the same object can have different help registered. For example,
// DockingActions do this as part of their construction process. // DockingActions do this as part of their construction process.
// These actions will register a default help location and then // These actions will register a default help location and then
// subclasses may change that location. We always use the most // subclasses may change that location. We always use the most
// recently registered action. // recently registered action.
helpLocations.put(helpObject, location); 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). * Returns the master help set (the one into which all other help sets are merged).
* @return the help set
*/ */
public GHelpSet getMasterHelpSet() { public GHelpSet getMasterHelpSet() {
return mainHS; 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 * 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 * help when a specific file is desired, like an introduction page. Showing help for
* objects within the system is accomplished by calling * objects within the system is accomplished by calling
* {@link #showHelp(Object, boolean, Component)}. * {@link #showHelp(Object, boolean, Component)}.
* *
* @param url the URL to display * @param url the URL to display
* @see #showHelp(Object, boolean, Component) * @see #showHelp(Object, boolean, Component)
*/ */
@@ -400,7 +401,7 @@ public class HelpManager implements HelpService {
((DefaultHelpBroker) mainHB).setActivationWindow(owner); ((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); mainHB.setDisplayed(true);
if (!wasDisplayed) { if (!wasDisplayed) {
@@ -443,21 +444,20 @@ public class HelpManager implements HelpService {
return; return;
} }
TaskLauncher.launchNonModal("Validating Help", monitor -> { GThreadPool.runAsync(Swing.GSWING_THREAD_POOL_NAME, this::doPrintBadHelp);
try {
printBadHelp(monitor);
}
catch (CancelledException e) {
// user cancelled; just exit
}
});
} }
private void printBadHelp(TaskMonitor monitor) throws CancelledException { private void doPrintBadHelp() {
Map<Object, HelpLocation> badHelp = getInvalidHelpLocations(monitor); Map<Object, HelpLocation> badHelp;
if (badHelp.isEmpty()) { try {
return; badHelp = getInvalidHelpLocations(TaskMonitor.DUMMY);
if (badHelp.isEmpty()) {
return;
}
}
catch (CancelledException e) {
return; // user cancelled
} }
StringBuilder buffy = new StringBuilder(); StringBuilder buffy = new StringBuilder();
@@ -474,7 +474,6 @@ public class HelpManager implements HelpService {
throws CancelledException { throws CancelledException {
Map<Object, HelpLocation> map = new WeakHashMap<>(); Map<Object, HelpLocation> map = new WeakHashMap<>();
Map<Object, HelpLocation> helpLocationsCopy = copyHelpLocations(); Map<Object, HelpLocation> helpLocationsCopy = copyHelpLocations();
monitor.initialize(helpLocationsCopy.size()); monitor.initialize(helpLocationsCopy.size());
Set<Entry<Object, HelpLocation>> entries = helpLocationsCopy.entrySet(); Set<Entry<Object, HelpLocation>> entries = helpLocationsCopy.entrySet();
@@ -493,7 +492,7 @@ public class HelpManager implements HelpService {
} }
private Map<Object, HelpLocation> copyHelpLocations() { 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 // locations map is frequently updated by the Swing thread
return Swing.runNow(() -> new HashMap<>(helpLocations)); return Swing.runNow(() -> new HashMap<>(helpLocations));
} }
@@ -501,9 +500,9 @@ public class HelpManager implements HelpService {
// //
// Warning! // Warning!
// This code has timing implications. DockingActions register themselves with the help // 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 // 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. // keybinding purposes, we have to do this check after the action is fully constructed.
// //
private boolean isKeybindingOnly(Object helpee) { 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 // 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. // new help sets on the fly.
hasMergedHelpSets = true; hasMergedHelpSets = true;
helpSetsPendingMerge.clear(); helpSetsPendingMerge.clear();
@@ -657,7 +656,7 @@ public class HelpManager implements HelpService {
/** /**
* Create a new help set for the given url, if one does * Create a new help set for the given url, if one does
* not already exist. * not already exist.
* @param classLoader * @param classLoader the class loader
*/ */
private HelpSet createHelpSet(URL url, GHelpClassLoader classLoader) throws HelpSetException { private HelpSet createHelpSet(URL url, GHelpClassLoader classLoader) throws HelpSetException {
if (!urlToHelpSets.containsKey(url)) { if (!urlToHelpSets.containsKey(url)) {
@@ -668,7 +667,7 @@ public class HelpManager implements HelpService {
return null; return null;
} }
/** /**
* Set the color resources on the JEditorPane for selection so that * Set the color resources on the JEditorPane for selection so that
* you can see the highlights when you do a search in the JavaHelp. * you can see the highlights when you do a search in the JavaHelp.
*/ */

View File

@@ -31,17 +31,17 @@ import ghidra.util.SystemUtilities;
* <p> * <p>
* The simple behavior for when new tasks are submitted:<br> * The simple behavior for when new tasks are submitted:<br>
* 1) If there any idle threads, use that thread.<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> * 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 * 3) if all threads are busy and there are max number of threads, queue the item until a thread
* becomes free.<br> * becomes free.<br>
* <p> * <p>
* The simple behavior for when tasks are completed by a thread:<br> * 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> * 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 * jobs arrive before
* the "KEEP ALIVE" time expires which is currently 15 seconds.<br> * 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> * to arrive.<br>
*/ */
public class GThreadPool { public class GThreadPool {
@@ -76,6 +76,18 @@ public class GThreadPool {
return threadPool; 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) { private GThreadPool(String name) {
this.name = name; this.name = name;
executor = new GThreadPoolExecutor(); executor = new GThreadPoolExecutor();
@@ -157,7 +169,7 @@ public class GThreadPool {
/** /**
* Returns true if this is not a shared thread pool. * Returns true if this is not a shared thread pool.
* *
* @return true if this is not a shared thread pool. * @return true if this is not a shared thread pool.
*/ */
public boolean isPrivate() { public boolean isPrivate() {
@@ -166,12 +178,12 @@ public class GThreadPool {
/** /**
* Returns the {@link Executor} used by this thread pool. * 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. * 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}. * passed to other asynchronous APIs, such as the {@link CompletableFuture}.
* *
* @return the executor * @return the executor
*/ */
public Executor getExecutor() { public Executor getExecutor() {
@@ -180,7 +192,7 @@ public class GThreadPool {
//================================================================================================== //==================================================================================================
// Inner Classes // Inner Classes
//================================================================================================== //==================================================================================================
private class GThreadPoolExecutor extends ThreadPoolExecutor { private class GThreadPoolExecutor extends ThreadPoolExecutor {
private volatile int maxThreadCount = SystemUtilities.getDefaultThreadPoolSize(); private volatile int maxThreadCount = SystemUtilities.getDefaultThreadPoolSize();