Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/eclipse/ui/internal/PluginAction.java


1   /*******************************************************************************
2    * Copyright (c) 2000, 2004 IBM Corporation and others.
3    * All rights reserved. This program and the accompanying materials 
4    * are made available under the terms of the Common Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/cpl-v10.html
7    * 
8    * Contributors:
9    *     IBM Corporation - initial API and implementation
10   *******************************************************************************/
11  package org.eclipse.ui.internal;
12  
13  import java.util.ArrayList;
14  import java.util.List;
15  
16  import org.eclipse.core.runtime.CoreException;
17  import org.eclipse.core.runtime.IAdaptable;
18  import org.eclipse.core.runtime.IConfigurationElement;
19  import org.eclipse.core.runtime.IStatus;
20  import org.eclipse.jface.action.Action;
21  import org.eclipse.jface.dialogs.MessageDialog;
22  import org.eclipse.jface.viewers.ISelection;
23  import org.eclipse.jface.viewers.ISelectionChangedListener;
24  import org.eclipse.jface.viewers.IStructuredSelection;
25  import org.eclipse.jface.viewers.SelectionChangedEvent;
26  import org.eclipse.jface.viewers.StructuredSelection;
27  import org.eclipse.swt.widgets.Display;
28  import org.eclipse.swt.widgets.Event;
29  import org.eclipse.ui.IActionDelegate;
30  import org.eclipse.ui.IActionDelegate2;
31  import org.eclipse.ui.IActionDelegateWithEvent;
32  import org.eclipse.ui.INullSelectionListener;
33  import org.eclipse.ui.IPluginContribution;
34  import org.eclipse.ui.ISelectionListener;
35  import org.eclipse.ui.IWorkbenchPart;
36  import org.eclipse.ui.SelectionEnabler;
37  import org.eclipse.ui.WorkbenchException;
38  import org.eclipse.ui.internal.misc.StatusUtil;
39  import org.eclipse.ui.internal.util.BundleUtility;
40  
41  /**
42   * A PluginAction is a proxy for an action extension.
43   *
44   * At startup we read the registry and create a PluginAction for each action extension.
45   * This plugin action looks like the real action ( label, icon, etc ) and acts as
46   * a proxy for the action until invoked.  At that point the proxy will instantiate 
47   * the real action and delegate the run method to the real action.
48   * This makes it possible to load the action extension lazily.
49   *
50   * Occasionally the class will ask if it is OK to 
51   * load the delegate (on selection changes).  If the plugin containing
52   * the action extension has been loaded then the action extension itself
53   * will be instantiated.
54   */
55  
56  public abstract class PluginAction extends Action 
57    implements ISelectionListener, ISelectionChangedListener, INullSelectionListener, IPluginContribution 
58  {
59    private IActionDelegate delegate;
60    private SelectionEnabler enabler;
61    private ISelection selection;
62    private IConfigurationElement configElement;
63    private String pluginId;
64    private String runAttribute = ActionDescriptor.ATT_CLASS;
65    private static int actionCount = 0;
66  
67    //a boolean that returns whether or not this action
68    //is Adaptable - i.e. is defined on a resource type
69    boolean isAdaptableAction = false;
70    boolean adaptableNotChecked = true;
71  
72    /**
73     * PluginAction constructor.
74     */
75    public PluginAction(IConfigurationElement actionElement, String id, int style) {
76      super(null, style);
77  
78      this.configElement = actionElement;
79      
80      if (id != null) {
81          setId(id);
82      }
83      else {
84        // Create unique action id.
85        setId("PluginAction." + Integer.toString(actionCount)); //$NON-NLS-1$
86        ++actionCount;
87      }
88  
89      String defId = actionElement.getAttribute(ActionDescriptor.ATT_DEFINITION_ID);
90      setActionDefinitionId(defId);
91          
92      pluginId = configElement.getDeclaringExtension().getNamespace();
93      
94      // Read enablement declaration.
95      if (configElement.getAttribute(PluginActionBuilder.ATT_ENABLES_FOR) != null) {
96        enabler = new SelectionEnabler(configElement);
97      } else {
98        IConfigurationElement[] kids = configElement.getChildren(PluginActionBuilder.TAG_ENABLEMENT);
99        if (kids.length > 0)
100         enabler = new SelectionEnabler(configElement);
101     }
102 
103     // Give enabler or delegate a chance to adjust enable state
104     selectionChanged(new StructuredSelection());
105   }
106 
107   /**
108    * Creates the delegate and refreshes its enablement.
109    */
110   protected final void createDelegate() {
111     // The runAttribute is null if delegate creation failed previously...
112     if (delegate == null && runAttribute != null) {
113       try {
114         Object obj = WorkbenchPlugin.createExtension(configElement, runAttribute);
115         delegate = validateDelegate(obj);
116         initDelegate();
117         refreshEnablement();
118       } catch (Throwable e) {
119         runAttribute = null;
120         IStatus status = null;
121         if (e instanceof CoreException) {
122           status = ((CoreException)e).getStatus();
123         } else {
124           status = StatusUtil.newStatus(IStatus.ERROR, "Internal plug-in action delegate error on creation.", e); //$NON-NLS-1$
125         }
126         String id = configElement.getAttribute(ActionDescriptor.ATT_ID);
127         WorkbenchPlugin.log("Could not create action delegate for id: " + id, status); //$NON-NLS-1$
128         return;
129       }
130     }
131   }
132 
133   /**
134    * Validates the object is a delegate of the expected type. Subclasses can
135    * override to check for specific delegate types.
136    * <p>
137    * <b>Note:</b> Calls to the object are not allowed during this method.
138    * </p>
139    *
140    * @param obj a possible action delegate implementation
141    * @return the <code>IActionDelegate</code> implementation for the object
142    * @throws a <code>WorkbenchException</code> if not expect delegate type
143    */
144   protected IActionDelegate validateDelegate(Object obj) throws WorkbenchException {
145     if (obj instanceof IActionDelegate)
146       return (IActionDelegate)obj;
147     else
148       throw new WorkbenchException("Action must implement IActionDelegate"); //$NON-NLS-1$
149   }
150 
151   /** 
152    * Initialize the action delegate by calling its lifecycle method.
153    * Subclasses may override but must call this implementation first.
154    */
155   protected void initDelegate() {
156     if (delegate instanceof IActionDelegate2)
157       ((IActionDelegate2)delegate).init(this);
158   }
159   
160   /**
161    * Returns the action delegate if created. Can be <code>null</code>
162    * if the delegate is not created yet or if previous delegate
163    * creation failed.
164    */
165   protected IActionDelegate getDelegate() {
166     return delegate;
167   }
168 
169   /**
170    * Returns true if the declaring plugin has been loaded
171    * and there is no need to delay creating the delegate
172    * any more.
173    */
174   protected boolean isOkToCreateDelegate() {
175     // test if the plugin has loaded
176     String bundleId = configElement.getDeclaringExtension().getNamespace();
177     return BundleUtility.isActive(bundleId);
178   }
179   
180   /**
181    * Return whether or not this action could have been registered
182    * due to an adaptable - i.e. it is a resource type.
183    */
184   private boolean hasAdaptableType() {
185     if (adaptableNotChecked) {
186       Object parentConfig = configElement.getParent();
187       String typeName = null;
188       if(parentConfig != null && parentConfig instanceof IConfigurationElement) 
189         typeName = ((IConfigurationElement) parentConfig).getAttribute("objectClass"); //$NON-NLS-1$
190       
191       //See if this is typed at all first
192       if(typeName == null){
193         adaptableNotChecked = false;
194         return false;
195       }
196       Class resourceClass = LegacyResourceSupport.getResourceClass();
197       if (resourceClass == null) {
198         // resources plug-in not even present
199         isAdaptableAction = false;
200         adaptableNotChecked = false;
201         return false;
202       }
203 
204       if (typeName.equals(resourceClass.getName())) {
205         isAdaptableAction = true;
206         adaptableNotChecked = false;
207         return isAdaptableAction;
208       }
209       Class[] children = resourceClass.getDeclaredClasses();
210       for (int i = 0; i < children.length; i++) {
211         if (children[i].getName().equals(typeName)) {
212           isAdaptableAction = true;
213           adaptableNotChecked = false;
214           return isAdaptableAction;
215         }        
216       }
217       adaptableNotChecked = false;
218     }
219     return isAdaptableAction;
220   }
221 
222   /**
223    * Refresh the action enablement.
224    */
225   protected void refreshEnablement() {
226     if (enabler != null) {
227       setEnabled(enabler.isEnabledForSelection(selection));
228     }
229     if (delegate != null) {
230       delegate.selectionChanged(this, selection);
231     }
232   }
233   
234   /* (non-Javadoc)
235    * Method declared on IAction.
236    */
237   public void run() {
238     runWithEvent(null);
239   }
240   
241   /* (non-Javadoc)
242    * Method declared on IAction.
243    */
244   public void runWithEvent(Event event) {
245     // this message dialog is problematic.
246     if (delegate == null) {
247       createDelegate();
248       if (delegate == null) {
249         MessageDialog.openInformation(
250           Display.getDefault().getActiveShell(),
251           WorkbenchMessages.getString("Information"), //$NON-NLS-1$
252           WorkbenchMessages.getString("PluginAction.operationNotAvailableMessage")); //$NON-NLS-1$
253         return;
254       }
255       if (!isEnabled()) {
256         MessageDialog.openInformation(
257           Display.getDefault().getActiveShell(),
258           WorkbenchMessages.getString("Information"), //$NON-NLS-1$
259           WorkbenchMessages.getString("PluginAction.disabledMessage")); //$NON-NLS-1$
260         return;
261       }
262     }
263 
264     if (event != null) {
265       if (delegate instanceof IActionDelegate2) {
266         ((IActionDelegate2)delegate).runWithEvent(this, event);
267         return;
268       }
269       // Keep for backward compatibility with R2.0
270       if (delegate instanceof IActionDelegateWithEvent) {
271         ((IActionDelegateWithEvent) delegate).runWithEvent(this, event);
272         return;
273       }
274     }
275 
276     delegate.run(this);
277   }
278   
279   /**
280    * Handles selection change. If rule-based enabled is
281    * defined, it will be first to call it. If the delegate
282    * is loaded, it will also be given a chance.
283    */
284   public void selectionChanged(ISelection newSelection) {
285     // Update selection.
286     selection = newSelection;
287     if (selection == null)
288       selection = StructuredSelection.EMPTY;
289     if (hasAdaptableType())
290       selection = getResourceAdapters(selection);
291       
292     // If the delegate can be loaded, do so.
293     // Otherwise, just update the enablement.
294     if (delegate == null && isOkToCreateDelegate())
295       createDelegate();
296     else 
297       refreshEnablement();
298   }
299   
300   /**
301    * The <code>SelectionChangedEventAction</code> implementation of this 
302    * <code>ISelectionChangedListener</code> method calls 
303    * <code>selectionChanged(IStructuredSelection)</code> when the selection is
304    * a structured one.
305    */
306   public void selectionChanged(SelectionChangedEvent event) {
307     ISelection sel = event.getSelection();
308     selectionChanged(sel);
309   }
310 
311   /**
312    * The <code>SelectionChangedEventAction</code> implementation of this 
313    * <code>ISelectionListener</code> method calls 
314    * <code>selectionChanged(IStructuredSelection)</code> when the selection is
315    * a structured one. Subclasses may extend this method to react to the change.
316    */
317   public void selectionChanged(IWorkbenchPart part, ISelection sel) {
318     selectionChanged(sel);
319   }
320   
321   /**
322    * Get a new selection with the resource adaptable version 
323    * of this selection
324    */
325   private ISelection getResourceAdapters(ISelection sel) {
326     if (sel instanceof IStructuredSelection) {
327       List adaptables = new ArrayList();
328       Object[] elements = ((IStructuredSelection)sel).toArray();
329       for (int i = 0; i < elements.length; i++) {
330         Object originalValue = elements[i];
331         if (originalValue instanceof IAdaptable) {
332           Class resourceClass = LegacyResourceSupport.getResourceClass();
333                     if (resourceClass != null) {
334              Object adaptedValue = ((IAdaptable)originalValue).getAdapter(resourceClass);
335              if (adaptedValue != null) {
336                adaptables.add(adaptedValue);
337                        }
338                     }
339         }
340       }
341       return new StructuredSelection(adaptables);
342     } else {
343       return sel;
344     }
345   }
346 
347   /**
348    * Returns the action identifier this action overrides.
349    * Default implementation returns <code>null</code>.
350    * 
351    * @return the action identifier to override or <code>null</code>
352    */
353   public String getOverrideActionId() {
354     return null;
355   }
356   
357   /**
358    * @return the IConfigurationElement used to create this PluginAction.
359    * 
360    * @since 3.0
361    */
362   protected IConfigurationElement getConfigElement() {
363     return configElement;
364   }
365 
366   /* (non-Javadoc)
367    * @see org.eclipse.ui.IPluginContribution#getLocalId()
368    */
369   public String getLocalId() {    
370     return getId();
371   }
372 
373   /* (non-Javadoc)
374    * @see org.eclipse.ui.IPluginContribution#getPluginId()
375    */
376   public String getPluginId() {    
377     return pluginId;
378   }
379 }