diff options
Diffstat (limited to 'target_explorer/plugins/org.eclipse.tcf.te.runtime/src/org/eclipse/tcf/te/runtime/events/EventManager.java')
-rw-r--r-- | target_explorer/plugins/org.eclipse.tcf.te.runtime/src/org/eclipse/tcf/te/runtime/events/EventManager.java | 533 |
1 files changed, 533 insertions, 0 deletions
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.runtime/src/org/eclipse/tcf/te/runtime/events/EventManager.java b/target_explorer/plugins/org.eclipse.tcf.te.runtime/src/org/eclipse/tcf/te/runtime/events/EventManager.java new file mode 100644 index 000000000..3cd413306 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.runtime/src/org/eclipse/tcf/te/runtime/events/EventManager.java @@ -0,0 +1,533 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.te.runtime.events; + +import java.util.ArrayList; +import java.util.EventListener; +import java.util.EventObject; +import java.util.List; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.tcf.te.runtime.activator.CoreBundleActivator; +import org.eclipse.tcf.te.runtime.interfaces.events.IEventFireDelegate; +import org.eclipse.tcf.te.runtime.interfaces.events.IEventListener; +import org.eclipse.tcf.te.runtime.interfaces.tracing.ITraceIds; +import org.osgi.framework.Bundle; + + +/** + * The event manager implementation. + */ +public final class EventManager { + // Flag to remember if the extension point has been processed. + private boolean extensionPointProcessed; + // The list of registered listeners. + private final List<ListenerListEntry> listeners = new ArrayList<ListenerListEntry>(); + + /** + * Runnable implementation to fire a given event to a given listener. + */ + protected static class FireRunnable implements Runnable { + private final IEventListener listener; + private final EventObject event; + + /** + * Constructor. + * + * @param listener The event listener. Must not be <code>null</code>. + * @param event The event. Must not be <code>null</code>. + */ + public FireRunnable(IEventListener listener, EventObject event) { + Assert.isNotNull(listener); + Assert.isNotNull(event); + this.listener = listener; + this.event = event; + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + listener.eventFired(event); + } + } + + /** + * Listener list entry. + * <p> + * Each entry contains a reference to the listener and a list of valid source classes. + * If an event source can be casted to one of the classes the listener is invoked. + */ + private class ListenerListEntry { + private final IEventListener listener; + private final Object[] eventSources; + private final Class<?>[] eventTypes; + + /** + * Constructor. + * + * @param listener The listener. + * @param eventType The event type the listener is interested in. + * @param eventSource The source type for which events should be fired to the listener. + */ + protected ListenerListEntry(IEventListener listener, Class<?> eventType, Object eventSource) { + this(listener, eventType == null ? null : new Class[] { eventType }, eventSource == null ? null : new Object[] { eventSource }); + } + + /** + * Constructor. + * + * @param listener The listener. + * @param eventTypes The event types the listener is interested in. + * @param eventSources The source types for which events should be fired to the listener. + */ + protected ListenerListEntry(IEventListener listener, Class<?>[] eventTypes, Object[] eventSources) { + this.listener = listener; + if (eventTypes == null || eventTypes.length == 0) { + this.eventTypes = null; + } else { + this.eventTypes = eventTypes; + } + if (eventSources == null || eventSources.length == 0) { + this.eventSources = null; + } else { + this.eventSources = eventSources; + } + } + + /** + * Get the listener of this entry. + */ + protected EventListener getListener() { + return listener; + } + + /** + * Check whether the listener wants to be called for changes of the source. + * The check is made through <code>instanceof</code>. + * + * @param source The source of the event. + * @return True, if the source can be casted to one of the registered event source types + * or no event sources are registered. + */ + protected boolean listensTo(EventObject event) { + boolean types = (eventTypes == null || eventTypes.length == 0); + boolean sources = (eventSources == null || eventSources.length == 0); + + int t = 0; + while (!types && eventTypes != null && t < eventTypes.length) { + types = eventTypes[t].isInstance(event); + t++; + } + + int s = 0; + while (!sources && eventSources != null && s < eventSources.length) { + Object eventSource = eventSources[s]; + if (eventSource instanceof Class<?>) { + Class<?> eventSourceClass = (Class<?>)eventSource; + sources = eventSourceClass.isInstance(event.getSource()); + } else { + sources = eventSource == event.getSource(); + } + s++; + } + + return types && sources; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj != null && obj instanceof ListenerListEntry) { + ListenerListEntry other = (ListenerListEntry)obj; + return this.getListener() == other.getListener(); + } + return false; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return getClass().getName() + "{" + //$NON-NLS-1$ + "listener=" + listener + //$NON-NLS-1$ + ",eventTypes=" + eventTypes + //$NON-NLS-1$ + ",eventSources=" + eventSources + //$NON-NLS-1$ + "}"; //$NON-NLS-1$ + } + } + + /* + * Thread save singleton instance creation. + */ + private static class LazyInstance { + public static EventManager instance = new EventManager(); + } + + /** + * Private Constructor. + */ + EventManager() { + extensionPointProcessed = false; + } + + /** + * Returns the singleton instance for the event manager. + */ + public static EventManager getInstance() { + return LazyInstance.instance; + } + + /** + * Add a change listener to listen to a single event. + * + * @param listener The listener to add. + * @param eventType The event type this listeners wants to be invoked. + */ + public void addEventListener(IEventListener listener, Class<?> eventType) { + addEventListener(listener, eventType != null ? new Class[] { eventType } : null, null); + } + + /** + * Add a change listener to listen to multiple events. + * + * @param listener The listener to add. + * @param eventTypes The event types this listeners wants to be invoked. + */ + public void addEventListener(IEventListener listener, Class<?>[] eventTypes) { + addEventListener(listener, eventTypes, null); + } + + /** + * Add a change listener to listen to event from the specified event + * source. If the listener instance had been registered already, the listener + * event sources are updated + * + * @param listener The listener to add. + * @param eventType The event type this listeners wants to be invoked. + * @param eventSource The event source type this listeners wants to be invoked. + */ + public void addEventListener(IEventListener listener, Class<?> eventType, Object eventSource) { + addEventListener(listener, eventType != null ? new Class[] { eventType } : null, eventSource != null ? new Object[] { eventSource } : null); + } + + /** + * Add a change listener to listen to events from the specified event + * sources. If the listener instance had been registered already, the listener + * event sources are updated + * + * @param listener The listener to add. + * @param eventType The event type this listeners wants to be invoked. + * @param eventSources The event sources type this listeners wants to be invoked. + */ + public void addEventListener(IEventListener listener, Class<?> eventType, Object[] eventSources) { + addEventListener(listener, eventType != null ? new Class[] { eventType } : null, eventSources); + } + + /** + * Add a change listener to listen to event from the specified event + * sources. If the listener instance had been registered already, the listener + * event sources are updated + * + * @param listener The listener to add. + * @param eventTypes The event types this listeners wants to be invoked. + * @param eventSources The event source types this listeners wants to be invoked. + */ + public void addEventListener(IEventListener listener, Class<?>[] eventTypes, Object[] eventSources) { + ListenerListEntry listEntry = new ListenerListEntry(listener, eventTypes, eventSources); + // We must assure that the existing list entries can _never_ change! + synchronized (listeners) { + if (listeners.contains(listEntry)) { + listeners.remove(listEntry); + } + listeners.add(listEntry); + } + } + + /** + * Remove a change listener for all event types and sources. + * + * @param listener The listener to remove. + */ + public void removeEventListener(IEventListener listener) { + ListenerListEntry listEntry = new ListenerListEntry(listener, (Class<?>)null, (Object)null); + listeners.remove(listEntry); + } + + /** + * Remove all change listeners for all event types and sources. + */ + public void clear() { + listeners.clear(); + extensionPointProcessed = false; + } + + /** + * Notify all registered listeners. + * + * @param event The event. Must not be <code>null</code> + */ + public void fireEvent(final EventObject event) { + Assert.isNotNull(event); + + synchronized (this) { + // if the extension point has not been processed till here, now we have to do + if (!extensionPointProcessed) { + addExtensionPointNotificationListeners(); + extensionPointProcessed = true; + } + } + + // Based on the current listener listener list, compile a list of event + // listeners to where this event would have been send to in a synchronous invocation scheme. + List<ListenerListEntry> affected = new ArrayList<ListenerListEntry>(); + + // Get the array of registered event listeners. + ListenerListEntry[] registered = listeners.toArray(new ListenerListEntry[listeners.size()]); + + for (ListenerListEntry listEntry : registered) { + // ignore listeners not listening to the event type and source + if (listEntry.listensTo(event)) { + affected.add(listEntry); + } + } + + // If no current listener is affected, return now immediately + if (affected.size() == 0) { + return; + } + + // Loop over the list of affected listeners and fire the event. + // If the affected listener is a fire delegate -> use it itself to fire the event + for (ListenerListEntry listEntry : affected) { + if (!(listEntry.getListener() instanceof IEventListener)) { + continue; + } + // Create the runnable to use for executing the event firing + Runnable runnable = new FireRunnable((IEventListener)listEntry.getListener(), event); + // Check on how to fire the runnable + if (listEntry.getListener() instanceof IEventFireDelegate) { + // The listener is a fire delegate -> use it itself to fire the runnable + ((IEventFireDelegate)listEntry.getListener()).fire(runnable); + } else { + // Listener isn't a fire delegate -> fire the runnable directly + runnable.run(); + } + } + } + + /* + * Register change listeners defined by extension. + */ + private void addExtensionPointNotificationListeners() { + IExtensionPoint ep = Platform.getExtensionRegistry().getExtensionPoint("org.eclipse.tcf.te.runtime.eventListeners"); //$NON-NLS-1$ + if (ep != null) { + IExtension[] extensions = ep.getExtensions(); + if (extensions != null && extensions.length > 0) { + for (IExtension extension : extensions) { + IConfigurationElement[] configElements = extension.getConfigurationElements(); + if (configElements != null && configElements.length > 0) { + for (IConfigurationElement configElement : configElements) { + String name = configElement.getName(); + if ("eventListener".equals(name)) { //$NON-NLS-1$ + // try to read the "eventType" and "eventSourceType" configuration elements if any. + List<Class<?>> eventTypes = new ArrayList<Class<?>>(); + List<Class<?>> eventSourceTypes = new ArrayList<Class<?>>(); + + IConfigurationElement[] children = configElement.getChildren(); + for (IConfigurationElement child : children) { + if ("eventType".equals(child.getName())) { //$NON-NLS-1$ + // The event types, we have to instantiate here as we need the class object! + try { + // First we try to instantiate the class using our own local class loader. + // This trick can avoid activating the contributing plugin if we can load + // the class ourself. + // First we try to instantiate the class using our own context + String className = child.getAttribute("class"); //$NON-NLS-1$ + if (className == null || className.trim().length() == 0) { + continue; + } + + String bundleId = child.getAttribute("bundleId"); //$NON-NLS-1$ + + // If a bundle id got specified, use the specified bundle to load the service class + Bundle bundle = bundleId != null ? bundle = Platform.getBundle(bundleId) : null; + // If we don't have a bundle to load from yet, fallback to the declaring bundle + if (bundle == null) bundle = Platform.getBundle(child.getDeclaringExtension().getNamespaceIdentifier()); + // And finally, use our own bundle to load the class. This fallback is expected + // to never be used. + if (bundle == null) bundle = CoreBundleActivator.getContext().getBundle(); + + // Try to load the event type class now + Class<?> eventType = bundle != null ? bundle.loadClass(className) : Class.forName(className); + if (!eventTypes.contains(eventType)) { + eventTypes.add(eventType); + } + } catch (Exception ex) { + if (isTracingEnabled()) + CoreBundleActivator.getTraceHandler().trace("Error instantiating event listener event type object instance: " + child.getAttribute("class"), //$NON-NLS-1$ //$NON-NLS-2$ + 0, ITraceIds.TRACE_EVENTS, IStatus.ERROR, this); + } + } + + if ("eventSourceType".equals(child.getName())) { //$NON-NLS-1$ + // The event source types, we have to instantiate here as we need the class object! + try { + // First we try to instantiate the class using our own local class loader. + // This trick can avoid activating the contributing plugin if we can load + // the class ourself. + // First we try to instantiate the class using our own context + String className = child.getAttribute("class"); //$NON-NLS-1$ + if (className == null || className.trim().length() == 0) { + continue; + } + + String bundleId = child.getAttribute("bundleId"); //$NON-NLS-1$ + + // If a bundle id got specified, use the specified bundle to load the service class + Bundle bundle = bundleId != null ? bundle = Platform.getBundle(bundleId) : null; + // If we don't have a bundle to load from yet, fallback to the declaring bundle + if (bundle == null) bundle = Platform.getBundle(child.getDeclaringExtension().getNamespaceIdentifier()); + // And finally, use our own bundle to load the class. This fallback is expected + // to never be used. + if (bundle == null) bundle = CoreBundleActivator.getContext().getBundle(); + + // Try to load the event source type class now + Class<?> eventSourceType = bundle != null ? bundle.loadClass(className) : Class.forName(className); + if (!eventSourceTypes.contains(eventSourceType)) { + eventSourceTypes.add(eventSourceType); + } + } catch (Exception ex) { + if (isTracingEnabled()) + CoreBundleActivator.getTraceHandler().trace("Error instantiating event listener event source type object instance: " + child.getAttribute("class"), //$NON-NLS-1$ //$NON-NLS-2$ + 0, ITraceIds.TRACE_EVENTS, IStatus.ERROR, this); + } + } + } + + // For extension point contributed event listeners, we use delegating + // event listener instances + IEventListener listener = new EventListenerProxy(configElement); + addEventListener(listener, + !eventTypes.isEmpty() ? eventTypes.toArray(new Class[eventTypes.size()]) : null, + !eventSourceTypes.isEmpty() ? eventSourceTypes.toArray(new Class[eventSourceTypes.size()]) : null + ); + + if (isTracingEnabled()) + CoreBundleActivator.getTraceHandler().trace("Add extension point change listener: " + configElement.getAttribute("class"), //$NON-NLS-1$ //$NON-NLS-2$ + 0, ITraceIds.TRACE_EVENTS, IStatus.INFO, this); + } + } + } + } + } + } + } + + /** + * Internal class used to delay the instantiation and plugin activation of + * event listeners which are contributed via extension point till they + * are really fired. + */ + private class EventListenerProxy implements IEventListener, IEventFireDelegate { + private final IConfigurationElement configElement; + private IEventListener delegate; + + /** + * Constructor. + * + * @param configElement The contributing configuration element of the encapsulated event listener. + * Must not be <code>null</code>. + */ + public EventListenerProxy(IConfigurationElement configElement) { + Assert.isNotNull(configElement); + this.configElement = configElement; + delegate = null; + } + + /** + * Returns the event listener delegate and instantiate the delegate + * if not yet done. + * + * @return The event listener delegate or <code>null</code> if the instantiation fails. + */ + private IEventListener getDelegate() { + if (delegate == null) { + // Check the contributing plug-in state + boolean forcePluginActivation = Boolean.parseBoolean(configElement.getAttribute("forcePluginActivation")); //$NON-NLS-1$ + if (!forcePluginActivation) { + Bundle bundle = Platform.getBundle(configElement.getContributor().getName()); + forcePluginActivation = bundle != null ? bundle.getState() == Bundle.ACTIVE : false; + } + // Load the event listener implementation class if plugin activations is allowed. + if (forcePluginActivation) { + try { + Object executable = configElement.createExecutableExtension("class"); //$NON-NLS-1$ + if (executable instanceof IEventListener) { + delegate = (IEventListener)executable; + } + } catch (Exception ex) { + if (isTracingEnabled()) + CoreBundleActivator.getTraceHandler().trace("Error instantiating extension point event listener: " + configElement.getAttribute("class") //$NON-NLS-1$ //$NON-NLS-2$ + + "(Possible Cause: " + ex.getLocalizedMessage() + ")", //$NON-NLS-1$ //$NON-NLS-2$ + 0, ITraceIds.TRACE_EVENTS, IStatus.ERROR, this); + } + } + } + + return delegate; + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.runtime.interfaces.events.IEventListener#eventFired(java.util.EventObject) + */ + @Override + public void eventFired(EventObject event) { + Assert.isNotNull(event); + // Get the delegate (may force instantiation) + IEventListener delegate = getDelegate(); + // And pass on the event to the delegate if we got a valid delegate + if (delegate != null) delegate.eventFired(event); + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.runtime.interfaces.events.IEventFireDelegate#fire(java.lang.Runnable) + */ + @Override + public void fire(Runnable runnable) { + Assert.isNotNull(runnable); + // Pass on to the delegate if the delegate itself is an fire delegate, + if (getDelegate() instanceof IEventFireDelegate) { + ((IEventFireDelegate)getDelegate()).fire(runnable); + } + else { + runnable.run(); + } + } + } + + /** + * Return <code>true</code> if the tracing mode is enabled for the + * event manager and trace messages shall be printed to the debug console. + */ + public static boolean isTracingEnabled() { + return CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITraceIds.TRACE_EVENTS); + } + +} |