mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-01 23:08:41 +08:00
Merge remote-tracking branch 'origin/GT-3360-dragonmacher-event-manager-synchronization'
This commit is contained in:
+137
-138
@@ -15,38 +15,39 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.framework.plugintool.mgr;
|
package ghidra.framework.plugintool.mgr;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
import org.apache.commons.collections4.IterableUtils;
|
||||||
|
import org.apache.commons.collections4.map.LazyMap;
|
||||||
|
|
||||||
import ghidra.framework.model.ToolListener;
|
import ghidra.framework.model.ToolListener;
|
||||||
import ghidra.framework.plugintool.PluginEvent;
|
import ghidra.framework.plugintool.PluginEvent;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.framework.plugintool.util.PluginEventListener;
|
import ghidra.framework.plugintool.util.PluginEventListener;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.Swing;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to manage the events that plugins consume and produce.
|
* Helper class to manage the events that plugins consume and produce. This class keeps
|
||||||
* This class keeps track of the last events that went out so that when
|
* track of the last events that went out so that when a plugin is added, it receives those events.
|
||||||
* a plugin is added, it receives those events.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class EventManager {
|
public class EventManager {
|
||||||
private ArrayList<ToolListener> toolListeners = new ArrayList<>();
|
private List<ToolListener> toolListeners = new ArrayList<>();
|
||||||
private HashMap<Class<? extends PluginEvent>, Set<PluginEventListener>> pluginListenerMap =
|
private Map<Class<? extends PluginEvent>, Set<PluginEventListener>> listenersByEventType =
|
||||||
new HashMap<>();
|
LazyMap.lazyMap(new HashMap<>(), clazz -> new HashSet<>());
|
||||||
private HashMap<String, Counter> producerMap = new HashMap<>();
|
private Map<String, Counter> producerMap =
|
||||||
private HashMap<String, Counter> consumerMap = new HashMap<>();
|
LazyMap.lazyMap(new HashMap<>(), name -> new Counter());
|
||||||
private LinkedHashMap<Class<? extends PluginEvent>, PluginEvent> lastEvents =
|
private Map<String, Counter> consumerMap =
|
||||||
|
LazyMap.lazyMap(new HashMap<>(), name -> new Counter());
|
||||||
|
private LinkedHashMap<Class<? extends PluginEvent>, PluginEvent> lastEventsByType =
|
||||||
new LinkedHashMap<>();
|
new LinkedHashMap<>();
|
||||||
private LinkedList<PluginEvent> eventQ = new LinkedList<>();
|
private LinkedList<PluginEvent> eventQ = new LinkedList<>();
|
||||||
private Set<PluginEventListener> allEventListeners = new HashSet<>();
|
private Set<PluginEventListener> allEventListeners = new HashSet<>();
|
||||||
|
|
||||||
private PluginEvent currentEvent;
|
|
||||||
private Runnable sendEventsRunnable;
|
|
||||||
private PluginTool tool;
|
private PluginTool tool;
|
||||||
private boolean sendingToolEvent;
|
private PluginEvent currentEvent;
|
||||||
|
private final Runnable sendEventsRunnable = () -> sendEvents();
|
||||||
|
private volatile boolean sendingToolEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new EventManager.
|
* Construct a new EventManager.
|
||||||
@@ -54,8 +55,6 @@ public class EventManager {
|
|||||||
*/
|
*/
|
||||||
public EventManager(PluginTool tool) {
|
public EventManager(PluginTool tool) {
|
||||||
this.tool = tool;
|
this.tool = tool;
|
||||||
|
|
||||||
sendEventsRunnable = () -> sendEvents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,18 +65,11 @@ public class EventManager {
|
|||||||
*/
|
*/
|
||||||
public void addEventListener(Class<? extends PluginEvent> eventClass,
|
public void addEventListener(Class<? extends PluginEvent> eventClass,
|
||||||
PluginEventListener listener) {
|
PluginEventListener listener) {
|
||||||
Set<PluginEventListener> set = pluginListenerMap.get(eventClass);
|
Set<PluginEventListener> set = listenersByEventType.get(eventClass);
|
||||||
if (set == null) {
|
if (set.isEmpty()) {
|
||||||
set = new HashSet<>();
|
|
||||||
pluginListenerMap.put(eventClass, set);
|
|
||||||
String name = PluginEvent.lookupToolEventName(eventClass);
|
String name = PluginEvent.lookupToolEventName(eventClass);
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
Counter counter = consumerMap.get(name);
|
consumerMap.get(name).count++;
|
||||||
if (counter == null) {
|
|
||||||
counter = new Counter();
|
|
||||||
consumerMap.put(name, counter);
|
|
||||||
}
|
|
||||||
counter.count++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set.add(listener);
|
set.add(listener);
|
||||||
@@ -99,25 +91,27 @@ public class EventManager {
|
|||||||
*/
|
*/
|
||||||
public void removeEventListener(Class<? extends PluginEvent> eventClass,
|
public void removeEventListener(Class<? extends PluginEvent> eventClass,
|
||||||
PluginEventListener listener) {
|
PluginEventListener listener) {
|
||||||
Set<PluginEventListener> set = pluginListenerMap.get(eventClass);
|
Set<PluginEventListener> set = listenersByEventType.get(eventClass);
|
||||||
if (set != null) {
|
set.remove(listener);
|
||||||
set.remove(listener);
|
if (set.isEmpty()) {
|
||||||
if (set.size() == 0) {
|
eventConsumerRemoved(eventClass);
|
||||||
pluginListenerMap.remove(eventClass);
|
}
|
||||||
String name = PluginEvent.lookupToolEventName(eventClass);
|
}
|
||||||
if (name != null) {
|
|
||||||
Counter counter = consumerMap.get(name);
|
private void eventConsumerRemoved(Class<? extends PluginEvent> eventClass) {
|
||||||
if (counter != null && --counter.count == 0) {
|
String name = PluginEvent.lookupToolEventName(eventClass);
|
||||||
consumerMap.remove(name);
|
if (name == null) {
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
Counter counter = consumerMap.get(name);
|
||||||
|
if (--counter.count == 0) {
|
||||||
|
consumerMap.remove(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the given tool listener to a list of tool listeners notified
|
* Add the given tool listener to be notified notified when tool events are generated
|
||||||
* when tool events are generated.
|
|
||||||
* @param listener listener to add
|
* @param listener listener to add
|
||||||
*/
|
*/
|
||||||
public void addToolListener(ToolListener listener) {
|
public void addToolListener(ToolListener listener) {
|
||||||
@@ -125,7 +119,7 @@ public class EventManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the given tool listener from the list of tool listeners.
|
* Remove the given tool listener from the list of tool listeners
|
||||||
* @param listener listener to remove
|
* @param listener listener to remove
|
||||||
*/
|
*/
|
||||||
public void removeToolListener(ToolListener listener) {
|
public void removeToolListener(ToolListener listener) {
|
||||||
@@ -133,25 +127,22 @@ public class EventManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether there are any registered tool listeners for the
|
* Return whether there are any registered tool listeners for the tool associated with class
|
||||||
* tool associated with this EventManager.
|
*
|
||||||
|
* @return true if there are any listeners
|
||||||
*/
|
*/
|
||||||
public boolean hasToolListeners() {
|
public boolean hasToolListeners() {
|
||||||
return !toolListeners.isEmpty();
|
return !toolListeners.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the class for the PluginEvent that a plugin will produce.
|
* Add the class for the PluginEvent that a plugin will produce
|
||||||
* @param eventClass class for the PluginEvent
|
* @param eventClass class for the PluginEvent
|
||||||
*/
|
*/
|
||||||
public void addEventProducer(Class<? extends PluginEvent> eventClass) {
|
public void addEventProducer(Class<? extends PluginEvent> eventClass) {
|
||||||
String name = PluginEvent.lookupToolEventName(eventClass);
|
String name = PluginEvent.lookupToolEventName(eventClass);
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
Counter counter = producerMap.get(name);
|
Counter counter = producerMap.get(name);
|
||||||
if (counter == null) {
|
|
||||||
counter = new Counter();
|
|
||||||
producerMap.put(name, counter);
|
|
||||||
}
|
|
||||||
counter.count++;
|
counter.count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,11 +153,13 @@ public class EventManager {
|
|||||||
*/
|
*/
|
||||||
public void removeEventProducer(Class<? extends PluginEvent> eventClass) {
|
public void removeEventProducer(Class<? extends PluginEvent> eventClass) {
|
||||||
String name = PluginEvent.lookupToolEventName(eventClass);
|
String name = PluginEvent.lookupToolEventName(eventClass);
|
||||||
if (name != null) {
|
if (name == null) {
|
||||||
Counter counter = producerMap.get(name);
|
return;
|
||||||
if (counter != null && --counter.count == 0) {
|
}
|
||||||
producerMap.remove(name);
|
|
||||||
}
|
Counter counter = producerMap.get(name);
|
||||||
|
if (--counter.count == 0) {
|
||||||
|
producerMap.remove(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,117 +188,129 @@ public class EventManager {
|
|||||||
|
|
||||||
synchronized (eventQ) {
|
synchronized (eventQ) {
|
||||||
if (currentEvent != null) {
|
if (currentEvent != null) {
|
||||||
if (validateEventChain(event)) {
|
if (validateEventChain(currentEvent, event)) {
|
||||||
|
|
||||||
|
// note: it is a bit odd that we assume any event passed to this method is
|
||||||
|
// triggered by that event. This may not be the case if we are on a
|
||||||
|
// background thread.
|
||||||
event.setTriggerEvent(currentEvent);
|
event.setTriggerEvent(currentEvent);
|
||||||
eventQ.add(event);
|
eventQ.add(event);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
|
return; // allow the current event processing to finish
|
||||||
}
|
}
|
||||||
currentEvent = event;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
// no event processing running right now; start it
|
||||||
sendEvents();
|
eventQ.add(event);
|
||||||
}
|
Swing.runNow(sendEventsRunnable);
|
||||||
else {
|
}
|
||||||
try {
|
|
||||||
SwingUtilities.invokeAndWait(sendEventsRunnable);
|
private boolean validateEventChain(PluginEvent startEvent, PluginEvent newEvent) {
|
||||||
}
|
while (startEvent != null) {
|
||||||
catch (InterruptedException e) {
|
if (startEvent.getClass().isAssignableFrom(newEvent.getClass()) &&
|
||||||
}
|
startEvent.getEventName().equals(newEvent.getEventName())) {
|
||||||
catch (InvocationTargetException e) {
|
return false;
|
||||||
}
|
}
|
||||||
|
startEvent = startEvent.getTriggerEvent();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the given tool event to a plugin event, and notify the
|
* Convert the given tool event to a plugin event; notify the appropriate plugin listeners.
|
||||||
* appropriate plugin event listeners.
|
* This method allows one tool's event manager to send events to another connected tool.
|
||||||
* @param event tool event
|
* @param event tool event
|
||||||
*/
|
*/
|
||||||
public void processToolEvent(PluginEvent event) {
|
public void processToolEvent(PluginEvent event) {
|
||||||
|
// only process the event if we are the receiving tool
|
||||||
if (!sendingToolEvent) {
|
if (!sendingToolEvent) {
|
||||||
fireEvent(event);
|
fireEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the list of last plugin events fired.
|
* Clear the list of last plugin events fired
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public void clearLastEvents() {
|
public void clearLastEvents() {
|
||||||
lastEvents.clear();
|
lastEventsByType.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an array of the last plugin events fired. EventManager
|
* Return an array of the last plugin events fired. EventManager maps the event class to the
|
||||||
* maps the event class to the last event fired.
|
* last event fired.
|
||||||
|
*
|
||||||
* @return array of plugin events
|
* @return array of plugin events
|
||||||
*/
|
*/
|
||||||
public PluginEvent[] getLastEvents() {
|
public PluginEvent[] getLastEvents() {
|
||||||
return lastEvents.values().toArray(new PluginEvent[lastEvents.size()]);
|
return lastEventsByType.values().toArray(new PluginEvent[lastEventsByType.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Send all events on the queue
|
|
||||||
*/
|
|
||||||
private void sendEvents() {
|
private void sendEvents() {
|
||||||
|
|
||||||
|
Swing.assertThisIsTheSwingThread("Events must be sent on the Swing thread");
|
||||||
|
|
||||||
|
synchronized (eventQ) {
|
||||||
|
currentEvent = eventQ.poll();
|
||||||
|
}
|
||||||
|
|
||||||
while (currentEvent != null) {
|
while (currentEvent != null) {
|
||||||
Class<? extends PluginEvent> eventClass = currentEvent.getClass();
|
Class<? extends PluginEvent> eventClass = currentEvent.getClass();
|
||||||
lastEvents.remove(eventClass);
|
lastEventsByType.put(eventClass, currentEvent);
|
||||||
lastEvents.put(eventClass, currentEvent);
|
|
||||||
|
|
||||||
Set<PluginEventListener> set = pluginListenerMap.get(eventClass);
|
for (PluginEventListener listener : getListeners(eventClass)) {
|
||||||
if (set != null) {
|
try {
|
||||||
for (PluginEventListener listener : set) {
|
listener.eventSent(currentEvent);
|
||||||
try {
|
}
|
||||||
listener.eventSent(currentEvent);
|
catch (Throwable t) {
|
||||||
}
|
Msg.showError(this, tool.getToolFrame(), "Plugin Event Error",
|
||||||
catch (Throwable t) {
|
"Error in plugin event listener", t);
|
||||||
Msg.showError(this, tool.getToolFrame(), "Plugin Event Error",
|
|
||||||
"Error in plugin event listener", t);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (PluginEventListener pluginEventListener : allEventListeners) {
|
|
||||||
pluginEventListener.eventSent(currentEvent);
|
sendToolEvent(currentEvent);
|
||||||
}
|
|
||||||
sendToolEvent();
|
|
||||||
synchronized (eventQ) {
|
synchronized (eventQ) {
|
||||||
currentEvent = eventQ.isEmpty() ? null : (PluginEvent) eventQ.removeFirst();
|
currentEvent = eventQ.poll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tool.contextChanged(null);
|
tool.contextChanged(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendToolEvent() {
|
private Iterable<PluginEventListener> getListeners(Class<? extends PluginEvent> eventClass) {
|
||||||
if (!toolListeners.isEmpty() && currentEvent.isToolEvent()) {
|
Set<PluginEventListener> specificListeners = listenersByEventType.get(eventClass);
|
||||||
sendingToolEvent = true;
|
return IterableUtils.chainedIterable(specificListeners, allEventListeners);
|
||||||
try {
|
|
||||||
currentEvent.setSourceName(PluginEvent.EXTERNAL_SOURCE_NAME);
|
|
||||||
currentEvent.setTriggerEvent(null);
|
|
||||||
for (int i = 0; i < toolListeners.size(); i++) {
|
|
||||||
ToolListener tl = toolListeners.get(i);
|
|
||||||
tl.processToolEvent(currentEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
sendingToolEvent = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateEventChain(PluginEvent event) {
|
// note: this is expected to be on the Swing thread, called from sendEvent()
|
||||||
PluginEvent tempEvent = currentEvent;
|
private void sendToolEvent(PluginEvent event) {
|
||||||
while (tempEvent != null) {
|
if (toolListeners.isEmpty()) {
|
||||||
if (tempEvent.getClass().isAssignableFrom(event.getClass()) &&
|
return;
|
||||||
tempEvent.getEventName().equals(event.getEventName())) {
|
}
|
||||||
return false;
|
|
||||||
}
|
if (!event.isToolEvent()) {
|
||||||
tempEvent = tempEvent.getTriggerEvent();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendingToolEvent = true;
|
||||||
|
try {
|
||||||
|
event.setSourceName(PluginEvent.EXTERNAL_SOURCE_NAME);
|
||||||
|
event.setTriggerEvent(null);
|
||||||
|
for (int i = 0; i < toolListeners.size(); i++) {
|
||||||
|
ToolListener tl = toolListeners.get(i);
|
||||||
|
|
||||||
|
try {
|
||||||
|
tl.processToolEvent(event);
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
Msg.showError(this, tool.getToolFrame(), "Plugin Event Error",
|
||||||
|
"Error sending event to connected tool", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
sendingToolEvent = false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -314,35 +319,29 @@ public class EventManager {
|
|||||||
* @param className class name of the plugin (event listener)
|
* @param className class name of the plugin (event listener)
|
||||||
*/
|
*/
|
||||||
public void removeEventListener(String className) {
|
public void removeEventListener(String className) {
|
||||||
ArrayList<Class<? extends PluginEvent>> unusedList =
|
|
||||||
new ArrayList<>();
|
|
||||||
|
|
||||||
Iterator<Class<? extends PluginEvent>> iter = pluginListenerMap.keySet().iterator();
|
List<Class<? extends PluginEvent>> unusedList = new ArrayList<>();
|
||||||
|
|
||||||
|
Iterator<Class<? extends PluginEvent>> iter = listenersByEventType.keySet().iterator();
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
Class<? extends PluginEvent> eventClass = iter.next();
|
Class<? extends PluginEvent> eventClass = iter.next();
|
||||||
Set<PluginEventListener> set = pluginListenerMap.get(eventClass);
|
Set<PluginEventListener> set = listenersByEventType.get(eventClass);
|
||||||
Iterator<PluginEventListener> iterator = set.iterator();
|
Iterator<PluginEventListener> it = set.iterator();
|
||||||
for (; iterator.hasNext();) {
|
while (it.hasNext()) {
|
||||||
PluginEventListener listener = iterator.next();
|
PluginEventListener listener = it.next();
|
||||||
if (listener.getClass().getName().equals(className)) {
|
if (listener.getClass().getName().equals(className)) {
|
||||||
iterator.remove();
|
it.remove();
|
||||||
if (set.size() == 0) {
|
if (set.isEmpty()) {
|
||||||
unusedList.add(eventClass);
|
unusedList.add(eventClass);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < unusedList.size(); i++) {
|
for (int i = 0; i < unusedList.size(); i++) {
|
||||||
Class<? extends PluginEvent> eventClass = unusedList.get(i);
|
Class<? extends PluginEvent> eventClass = unusedList.get(i);
|
||||||
pluginListenerMap.remove(eventClass);
|
eventConsumerRemoved(eventClass);
|
||||||
String name = PluginEvent.lookupToolEventName(eventClass);
|
|
||||||
if (name != null) {
|
|
||||||
Counter counter = consumerMap.get(name);
|
|
||||||
if (counter != null && --counter.count == 0) {
|
|
||||||
consumerMap.remove(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user