001: /*******************************************************************************
002: * Copyright (c) 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.pde.internal.ui.views.dependencies;
011:
012: import java.util.ArrayList;
013:
014: import org.eclipse.jdt.ui.ISharedImages;
015: import org.eclipse.jdt.ui.JavaUI;
016: import org.eclipse.jface.action.Action;
017: import org.eclipse.jface.action.ActionContributionItem;
018: import org.eclipse.jface.action.IAction;
019: import org.eclipse.jface.action.IContributionItem;
020: import org.eclipse.jface.action.IMenuListener;
021: import org.eclipse.jface.action.IMenuManager;
022: import org.eclipse.jface.action.IStatusLineManager;
023: import org.eclipse.jface.action.IToolBarManager;
024: import org.eclipse.jface.action.MenuManager;
025: import org.eclipse.jface.action.Separator;
026: import org.eclipse.jface.dialogs.IDialogSettings;
027: import org.eclipse.jface.util.IPropertyChangeListener;
028: import org.eclipse.jface.util.PropertyChangeEvent;
029: import org.eclipse.jface.viewers.DoubleClickEvent;
030: import org.eclipse.jface.viewers.IDoubleClickListener;
031: import org.eclipse.jface.viewers.IStructuredSelection;
032: import org.eclipse.jface.viewers.ITreeContentProvider;
033: import org.eclipse.jface.viewers.LabelProvider;
034: import org.eclipse.jface.viewers.StructuredSelection;
035: import org.eclipse.jface.viewers.TreeViewer;
036: import org.eclipse.jface.viewers.Viewer;
037: import org.eclipse.jface.viewers.ViewerFilter;
038: import org.eclipse.osgi.service.resolver.BundleDelta;
039: import org.eclipse.osgi.service.resolver.BundleDescription;
040: import org.eclipse.osgi.service.resolver.BundleSpecification;
041: import org.eclipse.osgi.service.resolver.ExportPackageDescription;
042: import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
043: import org.eclipse.osgi.service.resolver.ResolverError;
044: import org.eclipse.osgi.service.resolver.State;
045: import org.eclipse.osgi.service.resolver.StateDelta;
046: import org.eclipse.osgi.service.resolver.VersionConstraint;
047: import org.eclipse.osgi.util.NLS;
048: import org.eclipse.pde.core.plugin.PluginRegistry;
049: import org.eclipse.pde.internal.core.IPluginModelListener;
050: import org.eclipse.pde.internal.core.IStateDeltaListener;
051: import org.eclipse.pde.internal.core.PDECore;
052: import org.eclipse.pde.internal.core.PluginModelDelta;
053: import org.eclipse.pde.internal.ui.IPreferenceConstants;
054: import org.eclipse.pde.internal.ui.PDELabelProvider;
055: import org.eclipse.pde.internal.ui.PDEPlugin;
056: import org.eclipse.pde.internal.ui.PDEPluginImages;
057: import org.eclipse.pde.internal.ui.PDEUIMessages;
058: import org.eclipse.pde.internal.ui.editor.plugin.ManifestEditor;
059: import org.eclipse.pde.internal.ui.elements.DefaultContentProvider;
060: import org.eclipse.pde.internal.ui.util.SharedLabelProvider;
061: import org.eclipse.swt.SWT;
062: import org.eclipse.swt.graphics.Image;
063: import org.eclipse.swt.layout.GridData;
064: import org.eclipse.swt.layout.GridLayout;
065: import org.eclipse.swt.widgets.Composite;
066: import org.eclipse.swt.widgets.Control;
067: import org.eclipse.swt.widgets.Layout;
068: import org.eclipse.swt.widgets.Menu;
069: import org.eclipse.ui.IWorkbenchActionConstants;
070: import org.eclipse.ui.dialogs.FilteredTree;
071: import org.eclipse.ui.dialogs.PatternFilter;
072: import org.eclipse.ui.part.Page;
073: import org.osgi.framework.Version;
074:
075: public class StateViewPage extends Page implements IStateDeltaListener,
076: IPluginModelListener {
077:
078: private IPropertyChangeListener fPropertyListener;
079: private FilteredTree fFilteredTree = null;
080: private TreeViewer fTreeViewer = null;
081: private DependenciesView fView;
082: private Composite fComposite;
083: private Action fOpenAction;
084:
085: private static final String HIDE_RESOLVED = "hideResolved"; //$NON-NLS-1$
086: private static final String SHOW_NONLEAF = "hideNonLeaf"; //$NON-NLS-1$
087:
088: private ViewerFilter fHideResolvedFilter = new ViewerFilter() {
089: public boolean select(Viewer viewer, Object parentElement,
090: Object element) {
091: return ((element instanceof BundleDescription && !((BundleDescription) element)
092: .isResolved()) || parentElement instanceof BundleDescription
093: && !((BundleDescription) parentElement)
094: .isResolved());
095: }
096: };
097:
098: private ViewerFilter fShowLeaves = new ViewerFilter() {
099: public boolean select(Viewer viewer, Object parentElement,
100: Object element) {
101: if (element instanceof BundleDescription) {
102: return ((BundleDescription) element).getDependents().length == 0;
103: }
104: return true;
105: }
106: };
107:
108: class FocusOnAction extends Action {
109: public FocusOnAction(String text) {
110: super (text);
111: setDescription(PDEUIMessages.StateViewPage_focusActionDescription);
112: setToolTipText(PDEUIMessages.StateViewPage_focusActionToolTip);
113: }
114:
115: public void run() {
116: setFocusOnSelection();
117: }
118:
119: }
120:
121: class DependencyGroup {
122: Object[] dependencies;
123:
124: public DependencyGroup(Object[] constraints) {
125: dependencies = constraints;
126: }
127:
128: public Object[] getChildren() {
129: return dependencies;
130: }
131:
132: public String toString() {
133: return (dependencies[0] instanceof BundleSpecification) ? PDEUIMessages.StateViewPage_requiredBundles
134: : PDEUIMessages.StateViewPage_importedPackages;
135: }
136: }
137:
138: class StateContentProvider extends DefaultContentProvider implements
139: ITreeContentProvider {
140:
141: public Object[] getChildren(Object parentElement) {
142: if (parentElement instanceof BundleDescription) {
143: BundleDescription desc = (BundleDescription) parentElement;
144: if (desc.isResolved()) {
145: Object[] required = getResolvedDependencies(desc
146: .getRequiredBundles());
147: Object[] imported = getResolvedDependencies(desc
148: .getImportPackages());
149: ArrayList list = new ArrayList(2);
150: if (required.length > 0)
151: list.add(new DependencyGroup(required));
152: if (imported.length > 0)
153: list.add(new DependencyGroup(imported));
154: return list.toArray();
155: }
156: return desc.getContainingState()
157: .getResolverErrors(desc);
158: } else if (parentElement instanceof DependencyGroup) {
159: return ((DependencyGroup) parentElement).getChildren();
160: }
161: return new Object[0];
162: }
163:
164: public Object getParent(Object element) {
165: return null;
166: }
167:
168: public boolean hasChildren(Object element) {
169: return getChildren(element).length > 0;
170: }
171:
172: public Object[] getElements(Object inputElement) {
173: if (inputElement instanceof State)
174: return ((State) inputElement).getBundles();
175: return new Object[0];
176: }
177:
178: private Object[] getResolvedDependencies(
179: VersionConstraint[] constraints) {
180: ArrayList list = new ArrayList(constraints.length);
181: for (int i = 0; i < constraints.length; i++)
182: if (constraints[i].isResolved())
183: list.add(constraints[i]);
184: return list.toArray();
185: }
186:
187: }
188:
189: class StateLabelProvider extends LabelProvider {
190: private PDELabelProvider fSharedProvider;
191:
192: public StateLabelProvider() {
193: fSharedProvider = PDEPlugin.getDefault().getLabelProvider();
194: fSharedProvider.connect(this );
195: }
196:
197: public void dispose() {
198: fSharedProvider.disconnect(this );
199: super .dispose();
200: }
201:
202: public Image getImage(Object element) {
203: if (element instanceof DependencyGroup)
204: element = ((DependencyGroup) element).getChildren()[0];
205: if (element instanceof BundleSpecification)
206: element = ((BundleSpecification) element).getSupplier();
207: if (element instanceof BundleDescription) {
208: int flags = ((BundleDescription) element).isResolved() ? 0
209: : SharedLabelProvider.F_ERROR;
210: return (((BundleDescription) element).getHost() == null) ? fSharedProvider
211: .get(PDEPluginImages.DESC_PLUGIN_OBJ, flags)
212: : fSharedProvider.get(
213: PDEPluginImages.DESC_FRAGMENT_OBJ,
214: flags);
215: }
216: if (element instanceof ImportPackageSpecification)
217: return JavaUI.getSharedImages().getImage(
218: ISharedImages.IMG_OBJS_PACKAGE);
219: if (element instanceof ResolverError) {
220: if (((ResolverError) element).getType() == ResolverError.PLATFORM_FILTER)
221: return fSharedProvider
222: .get(PDEPluginImages.DESC_OPERATING_SYSTEM_OBJ);
223: return fSharedProvider.getImage(element);
224: }
225: return null;
226: }
227:
228: public String getText(Object element) {
229: StringBuffer buffer = new StringBuffer();
230: if (element instanceof ImportPackageSpecification) {
231: ImportPackageSpecification spec = (ImportPackageSpecification) element;
232: buffer.append(spec.getName());
233: ExportPackageDescription supplier = (ExportPackageDescription) spec
234: .getSupplier();
235: if (isJREPackage(supplier))
236: return buffer.append(
237: PDEUIMessages.StateViewPage_suppliedByJRE)
238: .toString();
239: element = supplier.getSupplier();
240: buffer.append(PDEUIMessages.StateViewPage_suppliedBy);
241: }
242: if (element instanceof BundleSpecification)
243: element = ((BundleSpecification) element).getSupplier();
244: if (element instanceof BundleDescription) {
245: buffer.append(fSharedProvider
246: .getObjectText((BundleDescription) element));
247: Version version = ((BundleDescription) element)
248: .getVersion();
249: // Bug 183417 - Bidi3.3: Elements' labels in the extensions page in the fragment manifest characters order is incorrect
250: // Use the PDELabelProvider.formatVersion function to properly format the version for all languages including bidi
251: return buffer.append(' ').append(
252: PDELabelProvider.formatVersion(version
253: .toString())).toString();
254: }
255: return element.toString();
256: }
257:
258: }
259:
260: private boolean isJREPackage(ExportPackageDescription supplier) {
261: // check for runtime's non-API directive. This may change in the future
262: return (((Integer) supplier.getDirective("x-equinox-ee")).intValue() > 0); //$NON-NLS-1$
263: }
264:
265: public StateViewPage(DependenciesView view) {
266: fView = view;
267: fPropertyListener = new IPropertyChangeListener() {
268: public void propertyChange(PropertyChangeEvent event) {
269: String property = event.getProperty();
270: if (property
271: .equals(IPreferenceConstants.PROP_SHOW_OBJECTS)) {
272: fTreeViewer.refresh();
273: }
274: }
275: };
276: }
277:
278: public void createControl(Composite parent) {
279: fComposite = new Composite(parent, SWT.NONE);
280: GridLayout layout = new GridLayout();
281: layout.marginHeight = layout.marginWidth = 0;
282: fComposite.setLayout(layout);
283: fComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
284:
285: fFilteredTree = new FilteredTree(fComposite, SWT.MULTI
286: | SWT.V_SCROLL | SWT.H_SCROLL | SWT.SINGLE,
287: new PatternFilter());
288: fFilteredTree.setBackground(parent.getDisplay().getSystemColor(
289: SWT.COLOR_LIST_BACKGROUND));
290:
291: // need to give filter Textbox some space from the border
292: Layout filterLayout = fFilteredTree.getFilterControl()
293: .getParent().getLayout();
294: if (filterLayout instanceof GridLayout) {
295: ((GridLayout) filterLayout).marginHeight = 4;
296: ((GridLayout) filterLayout).marginWidth = 3;
297: }
298:
299: fTreeViewer = fFilteredTree.getViewer();
300: fTreeViewer.setContentProvider(new StateContentProvider());
301: fTreeViewer.setLabelProvider(new StateLabelProvider());
302: fTreeViewer.setComparator(DependenciesViewComparator
303: .getViewerComparator());
304: fTreeViewer.addDoubleClickListener(new IDoubleClickListener() {
305:
306: public void doubleClick(DoubleClickEvent event) {
307: handleDoubleClick();
308: }
309:
310: });
311:
312: if (getSettings().getBoolean(HIDE_RESOLVED))
313: fTreeViewer.addFilter(fHideResolvedFilter);
314: if (getSettings().getBoolean(SHOW_NONLEAF))
315: fTreeViewer.addFilter(fShowLeaves);
316:
317: PDEPlugin.getDefault().getPreferenceStore()
318: .addPropertyChangeListener(fPropertyListener);
319: getSite().setSelectionProvider(fTreeViewer);
320: PDECore.getDefault().getModelManager().addStateDeltaListener(
321: this );
322: }
323:
324: public Control getControl() {
325: return fComposite;
326: }
327:
328: public void setFocus() {
329: if (fFilteredTree != null) {
330: Control c = fFilteredTree.getFilterControl();
331: if (!c.isFocusControl()) {
332: c.setFocus();
333: }
334: }
335: }
336:
337: protected void handleDoubleClick() {
338: StructuredSelection selection = (StructuredSelection) fTreeViewer
339: .getSelection();
340: if (selection.size() == 1) {
341: BundleDescription desc = getBundleDescription(selection
342: .getFirstElement());
343: if (desc != null)
344: ManifestEditor.openPluginEditor(desc.getSymbolicName());
345: }
346: }
347:
348: private BundleDescription getBundleDescription(Object obj) {
349: if (obj instanceof BundleSpecification)
350: obj = ((BundleSpecification) obj).getSupplier();
351: else if (obj instanceof ImportPackageSpecification)
352: obj = ((ExportPackageDescription) ((ImportPackageSpecification) obj)
353: .getSupplier()).getSupplier();
354: if (obj instanceof BundleDescription)
355: return (BundleDescription) obj;
356: return null;
357: }
358:
359: protected void setActive(boolean active) {
360: if (active) {
361: fView.updateTitle(PDEUIMessages.StateViewPage_title);
362: State state = PDECore.getDefault().getModelManager()
363: .getState().getState();
364: state.resolve(true);
365: fTreeViewer.setInput(state);
366: PDECore.getDefault().getModelManager()
367: .addPluginModelListener(this );
368: } else {
369: PDECore.getDefault().getModelManager()
370: .removePluginModelListener(this );
371: }
372: }
373:
374: public void makeContributions(IMenuManager menuManager,
375: IToolBarManager toolBarManager,
376: IStatusLineManager statusLineManager) {
377: super .makeContributions(menuManager, toolBarManager,
378: statusLineManager);
379: Action filterResolved = new Action(
380: PDEUIMessages.StateViewPage_showOnlyUnresolved_label,
381: IAction.AS_CHECK_BOX) {
382: public void run() {
383: getSettings().put(HIDE_RESOLVED, isChecked());
384: if (isChecked())
385: fTreeViewer.addFilter(fHideResolvedFilter);
386: else
387: fTreeViewer.removeFilter(fHideResolvedFilter);
388: }
389: };
390: Action filterLeaves = new Action(
391: PDEUIMessages.StateViewPage_showLeaves,
392: IAction.AS_CHECK_BOX) {
393: public void run() {
394: getSettings().put(SHOW_NONLEAF, isChecked());
395: if (isChecked())
396: fTreeViewer.addFilter(fShowLeaves);
397: else
398: fTreeViewer.removeFilter(fShowLeaves);
399: }
400: };
401:
402: filterResolved.setChecked(getSettings().getBoolean(
403: HIDE_RESOLVED));
404: filterLeaves.setChecked(getSettings().getBoolean(SHOW_NONLEAF));
405: menuManager.add(filterResolved);
406: menuManager.add(filterLeaves);
407:
408: Action action = new FocusOnAction(
409: PDEUIMessages.StateViewPage_focusOnTitle);
410: action.setImageDescriptor(PDEPluginImages.DESC_FOCUS_ON);
411: if (toolBarManager.find(DependenciesView.TREE_ACTION_GROUP) != null)
412: toolBarManager.prependToGroup(
413: DependenciesView.TREE_ACTION_GROUP, action);
414: else
415: toolBarManager.add(action);
416:
417: hookContextMenu();
418: }
419:
420: private void hookContextMenu() {
421: MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
422: menuMgr.setRemoveAllWhenShown(true);
423: menuMgr.addMenuListener(new IMenuListener() {
424: public void menuAboutToShow(IMenuManager manager) {
425: fillContextMenu(manager);
426: }
427: });
428: Menu menu = menuMgr.createContextMenu(fTreeViewer.getControl());
429: fTreeViewer.getControl().setMenu(menu);
430:
431: getSite().registerContextMenu(fView.getSite().getId(), menuMgr,
432: fTreeViewer);
433: }
434:
435: private void fillContextMenu(IMenuManager menu) {
436: IStructuredSelection selection = (IStructuredSelection) fTreeViewer
437: .getSelection();
438: BundleDescription desc = getBundleDescription(selection
439: .getFirstElement());
440: if (desc != null) {
441: if (fOpenAction == null) {
442: fOpenAction = new Action(
443: PDEUIMessages.StateViewPage_openItem) {
444: public void run() {
445: handleDoubleClick();
446: }
447: };
448: }
449: menu.add(fOpenAction);
450: menu.add(new Separator());
451: String name = ((LabelProvider) fTreeViewer
452: .getLabelProvider()).getText(desc);
453: menu
454: .add(new FocusOnAction(
455: NLS
456: .bind(
457: PDEUIMessages.StateViewPage_focusOnSelection,
458: name)));
459:
460: menu.add(new Separator());
461: menu.add(new Separator(
462: IWorkbenchActionConstants.MB_ADDITIONS));
463: }
464: }
465:
466: public void dispose() {
467: PDECore.getDefault().getModelManager()
468: .removeStateDeltaListener(this );
469: super .dispose();
470: }
471:
472: public void stateResolved(final StateDelta delta) {
473: if (!fView.getCurrentPage().equals(this ) || fTreeViewer == null
474: || fTreeViewer.getTree().isDisposed())
475: // if this page is not active, then wait until we call refresh on next activation
476: return;
477: fTreeViewer.getTree().getDisplay().asyncExec(new Runnable() {
478: public void run() {
479: if (delta == null) {
480: fTreeViewer.refresh();
481: } else {
482: BundleDelta[] deltas = delta.getChanges();
483: for (int i = 0; i < deltas.length; i++) {
484: int type = deltas[i].getType();
485: if (type == BundleDelta.REMOVED
486: || type == BundleDelta.RESOLVED
487: || type == BundleDelta.ADDED
488: || type == BundleDelta.UNRESOLVED) {
489: fTreeViewer.refresh();
490: break;
491: }
492: }
493: }
494: }
495: });
496: }
497:
498: public void stateChanged(final State newState) {
499: if (!fView.getCurrentPage().equals(this ) || fTreeViewer == null
500: || fTreeViewer.getTree().isDisposed())
501: // if this page is not active, then wait until we call refresh on next activation
502: return;
503: fTreeViewer.getTree().getDisplay().asyncExec(new Runnable() {
504: public void run() {
505: fTreeViewer.setInput(newState);
506: }
507: });
508: }
509:
510: // Changes State view to dependencies view and sets the input as the corresponding selected item in the tree viewer.
511: private void setFocusOnSelection() {
512: // first, find the Show State Action from the toolbar
513: IContributionItem item = getSite().getActionBars()
514: .getToolBarManager().find(
515: DependenciesView.SHOW_STATE_ACTION_ID);
516: if (item != null && item instanceof ActionContributionItem) {
517: // then get selection item
518: IStructuredSelection selection = (IStructuredSelection) fTreeViewer
519: .getSelection();
520: if (selection.isEmpty())
521: return;
522: BundleDescription desc = getBundleDescription(selection
523: .getFirstElement());
524: if (desc != null) {
525: IAction action = ((ActionContributionItem) item)
526: .getAction();
527: // deselect the action to show the state
528: action.setChecked(false);
529: // run the action to change the view back to traditional dependencies view
530: action.run();
531: // set the traditional view to focus on selected object from state view
532: fView.openTo(PluginRegistry.findModel(desc));
533: }
534: }
535: }
536:
537: private IDialogSettings getSettings() {
538: IDialogSettings master = PDEPlugin.getDefault()
539: .getDialogSettings();
540: IDialogSettings section = master.getSection("dependenciesView"); //$NON-NLS-1$
541: if (section == null) {
542: section = master.addNewSection("dependenciesView"); //$NON-NLS-1$
543: }
544: return section;
545: }
546:
547: public void modelsChanged(PluginModelDelta delta) {
548: if (fTreeViewer == null || fTreeViewer.getTree().isDisposed())
549: return;
550: if (delta.getAddedEntries().length > 0
551: || delta.getChangedEntries().length > 0
552: || delta.getRemovedEntries().length > 0)
553: fTreeViewer.getTree().getDisplay().asyncExec(
554: new Runnable() {
555: public void run() {
556: fTreeViewer.refresh();
557: }
558: });
559: }
560:
561: }
|