001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal;
011:
012: import java.util.ArrayList;
013:
014: import org.eclipse.core.runtime.IConfigurationElement;
015: import org.eclipse.core.runtime.Platform;
016: import org.eclipse.jface.action.AbstractGroupMarker;
017: import org.eclipse.jface.action.GroupMarker;
018: import org.eclipse.jface.action.IContributionItem;
019: import org.eclipse.jface.action.IContributionManager;
020: import org.eclipse.jface.action.IMenuManager;
021: import org.eclipse.jface.action.IToolBarManager;
022: import org.eclipse.jface.action.MenuManager;
023: import org.eclipse.jface.action.Separator;
024: import org.eclipse.ui.IWorkbenchActionConstants;
025: import org.eclipse.ui.PlatformUI;
026: import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
027: import org.eclipse.ui.internal.registry.RegistryReader;
028:
029: /**
030: * This class contains shared functionality for reading action contributions
031: * from plugins into workbench parts (both editors and views).
032: */
033: public abstract class PluginActionBuilder extends RegistryReader {
034: protected String targetID;
035:
036: protected String targetContributionTag;
037:
038: protected BasicContribution currentContribution;
039:
040: protected ArrayList cache;
041:
042: /**
043: * The default constructor.
044: */
045: public PluginActionBuilder() {
046: }
047:
048: /**
049: * Contributes submenus and/or actions into the provided menu and tool bar
050: * managers.
051: *
052: * @param menu the menu to contribute to
053: * @param toolbar the toolbar to contribute to
054: * @param appendIfMissing append containers if missing
055: */
056: public final void contribute(IMenuManager menu,
057: IToolBarManager toolbar, boolean appendIfMissing) {
058: if (cache == null) {
059: return;
060: }
061:
062: for (int i = 0; i < cache.size(); i++) {
063: BasicContribution contribution = (BasicContribution) cache
064: .get(i);
065: contribution.contribute(menu, appendIfMissing, toolbar,
066: appendIfMissing);
067: }
068: }
069:
070: /**
071: * This factory method returns a new ActionDescriptor for the
072: * configuration element. It should be implemented by subclasses.
073: */
074: protected abstract ActionDescriptor createActionDescriptor(
075: IConfigurationElement element);
076:
077: /**
078: * Factory method to create the helper contribution class that will hold
079: * onto the menus and actions contributed.
080: */
081: protected BasicContribution createContribution() {
082: return new BasicContribution();
083: }
084:
085: /**
086: * Returns the name of the part ID attribute that is expected
087: * in the target extension.
088: */
089: protected String getTargetID(IConfigurationElement element) {
090: String value = element
091: .getAttribute(IWorkbenchRegistryConstants.ATT_TARGET_ID);
092: return value != null ? value : "???"; //$NON-NLS-1$
093: }
094:
095: /**
096: * Returns the id of this contributions.
097: */
098: protected String getID(IConfigurationElement element) {
099: String value = element
100: .getAttribute(IWorkbenchRegistryConstants.ATT_ID);
101: return value != null ? value : "???"; //$NON-NLS-1$
102: }
103:
104: /**
105: * Reads the contributions from the registry for the provided workbench
106: * part and the provided extension point ID.
107: */
108: protected void readContributions(String id, String tag,
109: String extensionPoint) {
110: cache = null;
111: currentContribution = null;
112: targetID = id;
113: targetContributionTag = tag;
114: readRegistry(Platform.getExtensionRegistry(),
115: PlatformUI.PLUGIN_ID, extensionPoint);
116: }
117:
118: /**
119: * Implements abstract method to handle the provided XML element
120: * in the registry.
121: */
122: protected boolean readElement(IConfigurationElement element) {
123: String tag = element.getName();
124:
125: // Ignore all object contributions element as these
126: // are handled by the ObjectActionContributorReader.
127: if (tag
128: .equals(IWorkbenchRegistryConstants.TAG_OBJECT_CONTRIBUTION)) {
129: return true;
130: }
131:
132: // Found top level contribution element
133: if (tag.equals(targetContributionTag)) {
134: if (targetID != null) {
135: // Ignore contributions not matching target id
136: String id = getTargetID(element);
137: if (id == null || !id.equals(targetID)) {
138: return true;
139: }
140: }
141:
142: // Read its sub-elements
143: currentContribution = createContribution();
144: readElementChildren(element);
145: if (cache == null) {
146: cache = new ArrayList(4);
147: }
148: cache.add(currentContribution);
149: currentContribution = null;
150: return true;
151: }
152:
153: // Found menu contribution sub-element
154: if (tag.equals(IWorkbenchRegistryConstants.TAG_MENU)) {
155: currentContribution.addMenu(element);
156: return true;
157: }
158:
159: // Found action contribution sub-element
160: if (tag.equals(IWorkbenchRegistryConstants.TAG_ACTION)) {
161: currentContribution
162: .addAction(createActionDescriptor(element));
163: return true;
164: }
165:
166: return false;
167: }
168:
169: /**
170: * Helper class to collect the menus and actions defined within a
171: * contribution element.
172: */
173: protected static class BasicContribution {
174: protected ArrayList menus;
175:
176: protected ArrayList actions;
177:
178: /**
179: * Add a menu.
180: *
181: * @param element the element to base the menu on
182: */
183: public void addMenu(IConfigurationElement element) {
184: if (menus == null) {
185: menus = new ArrayList(1);
186: }
187: menus.add(element);
188: }
189:
190: /**
191: * Add an action.
192: *
193: * @param desc the descriptor
194: */
195: public void addAction(ActionDescriptor desc) {
196: if (actions == null) {
197: actions = new ArrayList(3);
198: }
199: actions.add(desc);
200: }
201:
202: /**
203: * Contributes submenus and/or actions into the provided menu and tool bar
204: * managers.
205: *
206: * The elements added are filtered based on activity enablement.
207: * @param menu the menu to contribute to
208: * @param menuAppendIfMissing whether to append missing groups to menus
209: * @param toolbar the toolbar to contribute to
210: * @param toolAppendIfMissing whether to append missing groups to toolbars
211: */
212: public void contribute(IMenuManager menu,
213: boolean menuAppendIfMissing, IToolBarManager toolbar,
214: boolean toolAppendIfMissing) {
215: if (menus != null && menu != null) {
216: for (int i = 0; i < menus.size(); i++) {
217: IConfigurationElement menuElement = (IConfigurationElement) menus
218: .get(i);
219: contributeMenu(menuElement, menu,
220: menuAppendIfMissing);
221: }
222: }
223:
224: if (actions != null) {
225: for (int i = 0; i < actions.size(); i++) {
226: ActionDescriptor ad = (ActionDescriptor) actions
227: .get(i);
228: if (menu != null) {
229: contributeMenuAction(ad, menu,
230: menuAppendIfMissing);
231: }
232: if (toolbar != null) {
233: contributeToolbarAction(ad, toolbar,
234: toolAppendIfMissing);
235: }
236: }
237: }
238: }
239:
240: /**
241: * Creates a menu from the information in the menu configuration element and
242: * adds it into the provided menu manager. If 'appendIfMissing' is true, and
243: * menu path slot is not found, it will be created and menu will be added
244: * into it. Otherwise, add operation will fail.
245: */
246: protected void contributeMenu(
247: IConfigurationElement menuElement, IMenuManager mng,
248: boolean appendIfMissing) {
249: // Get config data.
250: String id = menuElement
251: .getAttribute(IWorkbenchRegistryConstants.ATT_ID);
252: String label = menuElement
253: .getAttribute(IWorkbenchRegistryConstants.ATT_LABEL);
254: String path = menuElement
255: .getAttribute(IWorkbenchRegistryConstants.ATT_PATH);
256: if (label == null) {
257: WorkbenchPlugin
258: .log("Plugin \'" //$NON-NLS-1$
259: + menuElement.getContributor()
260: .getName()
261: + "\' invalid Menu Extension (label == null): " + id); //$NON-NLS-1$
262: return;
263: }
264:
265: // Calculate menu path and group.
266: String group = null;
267: if (path != null) {
268: int loc = path.lastIndexOf('/');
269: if (loc != -1) {
270: group = path.substring(loc + 1);
271: path = path.substring(0, loc);
272: } else {
273: // assume that path represents a slot
274: // so actual path portion should be null
275: group = path;
276: path = null;
277: }
278: }
279:
280: // Find parent menu.
281: IMenuManager parent = mng;
282: if (path != null) {
283: parent = mng.findMenuUsingPath(path);
284: if (parent == null) {
285: ideLog("Plugin \'" //$NON-NLS-1$
286: + menuElement.getContributor().getName()
287: + "\' invalid Menu Extension (Path \'" //$NON-NLS-1$
288: + path + "\' is invalid): " + id); //$NON-NLS-1$
289: return;
290: }
291: }
292:
293: // Find reference group.
294: if (group == null) {
295: group = IWorkbenchActionConstants.MB_ADDITIONS;
296: }
297: IContributionItem sep = parent.find(group);
298: if (sep == null) {
299: if (appendIfMissing) {
300: addGroup(parent, group);
301: } else {
302: WorkbenchPlugin.log("Plugin \'" //$NON-NLS-1$
303: + menuElement.getContributor().getName()
304: + "\' invalid Menu Extension (Group \'" //$NON-NLS-1$
305: + group + "\' is invalid): " + id); //$NON-NLS-1$
306: return;
307: }
308: }
309:
310: // If the menu does not exist create it.
311: IMenuManager newMenu = parent.findMenuUsingPath(id);
312: if (newMenu == null) {
313: newMenu = new MenuManager(label, id);
314: }
315:
316: // Add the menu
317: try {
318: insertAfter(parent, group, newMenu);
319: } catch (IllegalArgumentException e) {
320: WorkbenchPlugin.log("Plugin \'" //$NON-NLS-1$
321: + menuElement.getContributor().getName()
322: + "\' invalid Menu Extension (Group \'" //$NON-NLS-1$
323: + group + "\' is missing): " + id); //$NON-NLS-1$
324: }
325:
326: // Get the menu again as it may be wrapped, otherwise adding
327: // the separators and group markers below will not be wrapped
328: // properly if the menu was just created.
329: newMenu = parent.findMenuUsingPath(id);
330: if (newMenu == null) {
331: WorkbenchPlugin.log("Could not find new menu: " + id); //$NON-NLS-1$
332: }
333:
334: // Create separators.
335: IConfigurationElement[] children = menuElement
336: .getChildren();
337: for (int i = 0; i < children.length; i++) {
338: String childName = children[i].getName();
339: if (childName
340: .equals(IWorkbenchRegistryConstants.TAG_SEPARATOR)) {
341: contributeSeparator(newMenu, children[i]);
342: } else if (childName
343: .equals(IWorkbenchRegistryConstants.TAG_GROUP_MARKER)) {
344: contributeGroupMarker(newMenu, children[i]);
345: }
346: }
347: }
348:
349: /**
350: * Contributes action from action descriptor into the provided menu manager.
351: */
352: protected void contributeMenuAction(ActionDescriptor ad,
353: IMenuManager menu, boolean appendIfMissing) {
354: // Get config data.
355: String mpath = ad.getMenuPath();
356: String mgroup = ad.getMenuGroup();
357: if (mpath == null && mgroup == null) {
358: return;
359: }
360: // Find parent menu.
361: IMenuManager parent = menu;
362: if (mpath != null) {
363: parent = parent.findMenuUsingPath(mpath);
364: if (parent == null) {
365: ideLog("Plug-in '" + ad.getPluginId() + "' contributed an invalid Menu Extension (Path: '" + mpath + "' is invalid): " + ad.getId()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
366: return;
367: }
368: }
369:
370: // Find reference group.
371: if (mgroup == null) {
372: mgroup = IWorkbenchActionConstants.MB_ADDITIONS;
373: }
374: IContributionItem sep = parent.find(mgroup);
375: if (sep == null) {
376: if (appendIfMissing) {
377: addGroup(parent, mgroup);
378: } else {
379: WorkbenchPlugin
380: .log("Plug-in '" + ad.getPluginId() + "' contributed an invalid Menu Extension (Group: '" + mgroup + "' is invalid): " + ad.getId()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
381: return;
382: }
383: }
384:
385: // Add action.
386: try {
387: insertAfter(parent, mgroup, ad.getAction());
388: } catch (IllegalArgumentException e) {
389: WorkbenchPlugin
390: .log("Plug-in '" + ad.getPluginId() + "' contributed an invalid Menu Extension (Group: '" + mgroup + "' is missing): " + ad.getId()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
391: }
392: }
393:
394: /**
395: * Creates a named menu separator from the information in the configuration element.
396: * If the separator already exists do not create a second.
397: */
398: protected void contributeSeparator(IMenuManager menu,
399: IConfigurationElement element) {
400: String id = element
401: .getAttribute(IWorkbenchRegistryConstants.ATT_NAME);
402: if (id == null || id.length() <= 0) {
403: return;
404: }
405: IContributionItem sep = menu.find(id);
406: if (sep != null) {
407: return;
408: }
409: insertMenuGroup(menu, new Separator(id));
410: }
411:
412: /**
413: * Creates a named menu group marker from the information in the configuration element.
414: * If the marker already exists do not create a second.
415: */
416: protected void contributeGroupMarker(IMenuManager menu,
417: IConfigurationElement element) {
418: String id = element
419: .getAttribute(IWorkbenchRegistryConstants.ATT_NAME);
420: if (id == null || id.length() <= 0) {
421: return;
422: }
423: IContributionItem marker = menu.find(id);
424: if (marker != null) {
425: return;
426: }
427: insertMenuGroup(menu, new GroupMarker(id));
428: }
429:
430: /**
431: * Contributes action from the action descriptor into the provided tool bar manager.
432: */
433: protected void contributeToolbarAction(ActionDescriptor ad,
434: IToolBarManager toolbar, boolean appendIfMissing) {
435: // Get config data.
436: String tId = ad.getToolbarId();
437: String tgroup = ad.getToolbarGroupId();
438: if (tId == null && tgroup == null) {
439: return;
440: }
441:
442: // Find reference group.
443: if (tgroup == null) {
444: tgroup = IWorkbenchActionConstants.MB_ADDITIONS;
445: }
446: IContributionItem sep = null;
447: sep = toolbar.find(tgroup);
448: if (sep == null) {
449: if (appendIfMissing) {
450: addGroup(toolbar, tgroup);
451: } else {
452: WorkbenchPlugin.log("Plug-in '" + ad.getPluginId() //$NON-NLS-1$
453: + "' invalid Toolbar Extension (Group \'" //$NON-NLS-1$
454: + tgroup + "\' is invalid): " + ad.getId()); //$NON-NLS-1$
455: return;
456: }
457: }
458: // Add action to tool bar.
459: try {
460: insertAfter(toolbar, tgroup, ad.getAction());
461: } catch (IllegalArgumentException e) {
462: WorkbenchPlugin.log("Plug-in '" + ad.getPluginId() //$NON-NLS-1$
463: + "' invalid Toolbar Extension (Group \'" //$NON-NLS-1$
464: + tgroup + "\' is missing): " + ad.getId()); //$NON-NLS-1$
465: }
466: }
467:
468: /**
469: * Inserts the separator or group marker into the menu. Subclasses may override.
470: */
471: protected void insertMenuGroup(IMenuManager menu,
472: AbstractGroupMarker marker) {
473: menu.add(marker);
474: }
475:
476: /**
477: * Inserts an action after another named contribution item.
478: * Subclasses may override.
479: */
480: protected void insertAfter(IContributionManager mgr,
481: String refId, PluginAction action) {
482: insertAfter(mgr, refId, new PluginActionContributionItem(
483: action));
484: }
485:
486: /**
487: * Inserts a contribution item after another named contribution item.
488: * Subclasses may override.
489: */
490: protected void insertAfter(IContributionManager mgr,
491: String refId, IContributionItem item) {
492: mgr.insertAfter(refId, item);
493: }
494:
495: /**
496: * Adds a group to a contribution manager.
497: * Subclasses may override.
498: */
499: protected void addGroup(IContributionManager mgr, String name) {
500: mgr.add(new Separator(name));
501: }
502:
503: /**
504: * Disposes this contribution.
505: *
506: * @since 3.1
507: */
508: public void dispose() {
509: // do nothing
510: }
511:
512: /**
513: * Disposes the actions.
514: *
515: * @since 3.1
516: */
517: protected void disposeActions() {
518: if (actions != null) {
519: for (int i = 0; i < actions.size(); i++) {
520: PluginAction proxy = ((ActionDescriptor) actions
521: .get(i)).getAction();
522: proxy.dispose();
523: }
524: actions = null;
525: }
526: }
527: }
528:
529: private static boolean allowIdeLogging = false;
530:
531: /**
532: * If set to <code>false</code>, some of the logs that can be caused by
533: * use IDE plugins from an RCP app will be ignored.
534: *
535: * @param b
536: * Log the errors or not.
537: * @since 3.3
538: */
539: public static void setAllowIdeLogging(boolean b) {
540: allowIdeLogging = b;
541: }
542:
543: /**
544: * These are log messages that should be ignored by RCP apps when using the
545: * IDE plugins.
546: *
547: * @param msg
548: * @since 3.3
549: */
550: private static void ideLog(String msg) {
551: if (allowIdeLogging) {
552: WorkbenchPlugin.log(msg);
553: }
554: }
555: }
|