/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.ui.common.actions; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.viewers.ISelection; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IPartListener; import org.eclipse.ui.ISelectionListener; import org.eclipse.ui.ISelectionService; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.internal.WorkbenchPage; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.ui.common.AbstractUiPlugin; import org.teiid.designer.ui.common.InternalUiConstants; /** * The <code>AbstractActionService</code> class is a base implementation of the <code>ActionService</code> interface. * * @since 8.0 */ public abstract class AbstractActionService implements ActionService, InternalUiConstants { /** The logging prefix. */ private static final String PREFIX = "AbstractActionService."; //$NON-NLS-1$ /** The workbench window action bars. */ private IActionBars actionBars; /** Map of registered actions. Key type is action <code>Class</code> name and value type is <code>IAction</code>. */ private Map<String, IAction> actionMap = new HashMap<String, IAction>(); /** The plugin associated with this service. */ private AbstractUiPlugin plugin; /** The window associated with this action service. */ private IWorkbenchWindow window; private IWorkbenchPage myPage; /** * Constructs and empty action registry associated with the given plugin. * * @param thePlugin the plugin */ public AbstractActionService( AbstractUiPlugin thePlugin, IWorkbenchPage page ) { plugin = thePlugin; CoreArgCheck.isNotNull(page); // page should never be null myPage = page; setWorkbenchWindow(page.getWorkbenchWindow()); // Assumption/Fact: It is OK to cast IWorkbenchPage to WorkbenchPage since that is the only impl // Bad assumption and definitely NOT a fact; this is an internal class that we may not have access to in the future (via // access restrictions) actionBars = ((WorkbenchPage)page).getActionBars(); } /** * @see org.teiid.designer.ui.common.actions.ActionService#addResourceChangeListener(org.eclipse.core.resources.IResourceChangeListener) */ @Override public void addResourceChangeListener( IResourceChangeListener theListener ) { IWorkspace workspace = ModelerCore.getWorkspace(); workspace.addResourceChangeListener(theListener); } /** * @see org.teiid.designer.ui.common.actions.ActionService#addWorkbenchSelectionListener(ISelectionListener) */ @Override public void addWorkbenchSelectionListener( ISelectionListener theListener ) { if (window == null) { throw new IllegalStateException(Util.getString(PREFIX + "WorkbenchWindowIsNullMessage")); //$NON-NLS-1$ } ISelectionService service = window.getSelectionService(); IWorkbenchPart activePart = window.getPartService().getActivePart(); service.addSelectionListener(theListener); theListener.selectionChanged(activePart, service.getSelection()); } /** * @see org.teiid.designer.ui.common.actions.ActionService#addWorkbenchSelectionListener(ISelectionListener) */ public void addPartListener( IPartListener theListener ) { if (window == null) { throw new IllegalStateException(Util.getString(PREFIX + "WorkbenchWindowIsNullMessage")); //$NON-NLS-1$ } window.getPartService().addPartListener(theListener); theListener.partActivated(window.getPartService().getActivePart()); } /** * @see org.teiid.designer.ui.common.actions.ActionService#contributeToContextMenu(org.eclipse.jface.action.IMenuManager, * org.teiid.designer.ui.common.actions.GlobalActionsMap, org.eclipse.jface.viewers.ISelection) */ @Override public void contributeToContextMenu( IMenuManager theMenuMgr, GlobalActionsMap theActionsMap, ISelection theSelection ) { // must be overriden in order to contribute } /** * @see org.teiid.designer.ui.common.actions.ActionService#contributePermanentActionsToContextMenu(org.eclipse.jface.action.IMenuManager, * org.eclipse.jface.viewers.ISelection) */ @Override public void contributePermanentActionsToContextMenu( IMenuManager theMenuMgr, ISelection theSelection ) { // must be overriden in order to contribute } /** * Returns the action.Adds this action to the registry if it is not already there. * * @param theActionId the fully qualified classname of the action * @see org.teiid.designer.ui.common.actions.ActionService#getAction(String) */ @Override public IAction getAction( String theActionId ) throws CoreException { CoreArgCheck.isNotNull(theActionId); IAction result = actionMap.get(theActionId); if (result == null) { Class actionClass = null; try { // must use the plugin's classloader since this class's loader might be different // this means that plugins can only load actions found in their class loader actionClass = plugin.getClass().getClassLoader().loadClass(theActionId); result = getAction(actionClass); } catch (ClassNotFoundException theException) { String msg = Util.getString(PREFIX + "ProblemFindingActionClassMessage", //$NON-NLS-1$ new Object[] {theActionId}); throw new CoreException(new Status(IStatus.ERROR, plugin.getBundle().getSymbolicName(), 0, // special status code // (not used) msg, theException)); } } return result; } /** * Retrieves the action associated with the specified action class. The class name is used as the action identifier. If the * action is not already in the service, the class name is used to construct one. to construct the action. * * @param theActionClass the action class associated with the action to retrieve * @return the requested action * @throws CoreException if action class can't be instantiated */ public IAction getAction( Class<? extends IAction> theActionClass ) throws CoreException { CoreArgCheck.isNotNull(theActionClass); // System.out.println("[AbstractActionService.getAction] Action is: " + theActionClass.getName() ); //$NON-NLS-1$ String actionName = theActionClass.getName(); IAction result = actionMap.get(actionName); if (result == null) { try { result = theActionClass.newInstance(); registerEventHandler(result); registerAction(actionName, result); } catch (Exception theException) { System.out.println("[AbstractActionService.getAction] Exception is: " + theException.getClass().getName()); //$NON-NLS-1$ if ((theException instanceof IllegalAccessException) || (theException instanceof InstantiationException) || (theException instanceof NullPointerException)) { String msg = Util.getString(PREFIX + "FailureConstructingActionMessage", //$NON-NLS-1$ new Object[] {theActionClass}); throw new CoreException(new Status(IStatus.ERROR, plugin.getBundle().getSymbolicName(), 0, // special status // code (not used) msg, theException)); } // unexpected exception // MAKE THIS BETTER!!!!!!! // Util. throw new RuntimeException(theException.getMessage()); } } return result; } /** * Gets the action to use for the given action identifier. Will either return the default action or the action supplied by the * given action map. * * @param theActionId the action identifier * @param theActionMap the action map used to store actions * @return the appropriate action to use */ protected IAction getAction( String theActionId, GlobalActionsMap theActionMap ) { IAction result = null; if (theActionMap.isUnsupportedAction(theActionId)) { result = theActionMap.getAction(theActionId); // need to set text, tooltip, icons to match default action IAction defaultAction = getDefaultAction(theActionId); result.setText(defaultAction.getText()); result.setToolTipText(defaultAction.getToolTipText()); result.setImageDescriptor(defaultAction.getImageDescriptor()); result.setHoverImageDescriptor(defaultAction.getHoverImageDescriptor()); result.setDisabledImageDescriptor(defaultAction.getDisabledImageDescriptor()); } else { result = (theActionMap.isDefaultAction(theActionId)) ? getDefaultAction(theActionId) : theActionMap.getAction(theActionId); } return result; } /** * Gets the <code>IActionBars</code> associated with the window of this service. * * @return the action bars */ protected IActionBars getActionBars() { return actionBars; } /** * Gets the plugin associated with this service. * * @return the plugin */ @Override public AbstractUiPlugin getPlugin() { return plugin; } /** * @see org.teiid.designer.ui.common.actions.ActionService#getWorkbenchWindow() */ @Override public IWorkbenchWindow getWorkbenchWindow() { if (window == null) { throw new IllegalStateException(Util.getString(PREFIX + "WorkbenchWindowIsNullMessage")); //$NON-NLS-1$ } return window; } protected IWorkbenchPage getPage() { return myPage; } /** * Adds this action to the registry if it is not already there. * * @param sActionKey the action's key * @param theAction the action to add */ @Override public boolean registerAction( String sActionKey, IAction theAction ) { if (actionMap.containsKey(sActionKey)) { return false; } if (!IAction.class.isAssignableFrom(theAction.getClass())) { CoreArgCheck.isTrue(IAction.class.isAssignableFrom(theAction.getClass()), Util.getString(PREFIX + "ClassNotAssignableToIActionMessage", //$NON-NLS-1$ new Object[] {theAction})); } actionMap.put(sActionKey, theAction); return true; } /** * Indicates if and action with the given identifier has been registered. * * @param theActionId the action identifier * @return <code>true</code> if the action is registered; <code>false</code> otherwise. */ @Override public boolean isRegistered( String theActionId ) { return actionMap.containsKey(theActionId); } /** * Registers the given action to receive {@link org.eclipse.jface.viewers.ISelection} events and * {@link org.eclipse.core.resources.IResourceChangeEvent}s if the action implements the appropriate interfaces. Subclasses * may add more event types. Also will call <code>actionBars.setGlobalActionHandler(IAction)</code> if the action implements * {@link org.eclipse.ui.ISelectionListener}. * * @param theAction the action being registered to receive events */ protected void registerEventHandler( IAction theAction ) { // workbench-level selection events if (theAction instanceof ISelectionListener) { addWorkbenchSelectionListener((ISelectionListener)theAction); actionBars.setGlobalActionHandler(theAction.getId(), theAction); } // resource changed events if (theAction instanceof IResourceChangeListener) { addResourceChangeListener((IResourceChangeListener)theAction); } // resource changed events if (theAction instanceof IPartListener) { addPartListener((IPartListener)theAction); } } /** * Removes the action with the given identifier from the action service. * * @param theActionId the identifier of the action being removed * @throws IllegalArgumentException if input is null */ @Override public void removeAction( String theActionId ) { CoreArgCheck.isNotNull(theActionId); actionMap.remove(theActionId); } /** * @see org.teiid.designer.ui.common.actions.ActionService#removeResourceChangeListener(org.eclipse.core.resources.IResourceChangeListener) */ @Override public void removeResourceChangeListener( IResourceChangeListener theListener ) { IWorkspace workspace = ModelerCore.getWorkspace(); workspace.removeResourceChangeListener(theListener); } /** * @see org.teiid.designer.ui.common.actions.ActionService#removeWorkbenchSelectionListener(ISelectionListener) */ @Override public void removeWorkbenchSelectionListener( ISelectionListener theListener ) { if (window == null) { throw new IllegalStateException(Util.getString(PREFIX + "WorkbenchWindowIsNullMessage")); //$NON-NLS-1$ } ISelectionService service = window.getSelectionService(); service.removeSelectionListener(theListener); } /** * @see org.teiid.designer.ui.common.actions.ActionService#removeWorkbenchSelectionListener(ISelectionListener) */ public void removePartListener( IPartListener theListener ) { if (window == null) { throw new IllegalStateException(Util.getString(PREFIX + "WorkbenchWindowIsNullMessage")); //$NON-NLS-1$ } window.getPartService().removePartListener(theListener); } /** * @see org.teiid.designer.ui.common.actions.ActionService#setWorkbenchWindow(IWorkbenchWindow) */ @Override public void setWorkbenchWindow( IWorkbenchWindow theWindow ) { window = theWindow; } /** * Performs shutdown by doing the following for each registered action: * <ol> * <li>unregisters the action if it is a {@link ISelectionListener}, * <li>unregisters the action if it is a {@link IResourceChangeListener}, and * <li>calls dispose on the action if it is an {@link AbstractAction}. * </ol> * * @see org.teiid.designer.ui.common.actions.ActionService#shutdown() */ @Override public void shutdown() { Iterator<IAction> itr = actionMap.values().iterator(); while (itr.hasNext()) { IAction action = itr.next(); unregisterEventHandler(action); if (action instanceof AbstractAction) { ((AbstractAction)action).dispose(); } } } /** * Unregisters the given action from receiving {@link org.eclipse.jface.viewers.ISelection} events and * {@link org.eclipse.core.resources.IResourceChangeEvent}s if the action implements the appropriate interfaces. Subclasses * may unregister other event types. * * @param theAction the action being unregistered to receive events */ protected void unregisterEventHandler( IAction theAction ) { // workbench-level selection events if (theAction instanceof ISelectionListener) { removeWorkbenchSelectionListener((ISelectionListener)theAction); } // resource changed events if (theAction instanceof IResourceChangeListener) { removeResourceChangeListener((IResourceChangeListener)theAction); } } }