Revert "Revert "Bug 577207 - Migrate bundle spy""

This reverts commit 33882ac3c2ff848fc1acf301d91f61ef1f2df336.

Reason for revert: planned for 4.23

Change-Id: I243bdb79926a46e8b6c887962a15746771eedb43
Reviewed-on: https://git.eclipse.org/r/c/pde/eclipse.pde.ui/+/187129
Tested-by: PDE Bot <pde-bot@eclipse.org>
Reviewed-by: Lars Vogel <Lars.Vogel@vogella.com>
diff --git a/pom.xml b/pom.xml
index 55378e6..151460d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -74,6 +74,7 @@
     <module>ui/org.eclipse.pde.ui.tests</module>
     <module>ui/org.eclipse.pde.ui.tests.smartimport</module>
     <module>ui/org.eclipse.pde.spy.core</module>
+    <module>ui/org.eclipse.pde.spy.bundle</module>
     <module>ui/org.eclipse.pde.spy.css</module>
     <module>ui/org.eclipse.pde.spy.model</module> 
     <module>ui/org.eclipse.pde.spy.preferences</module> 
diff --git a/ui/org.eclipse.pde.spy.bundle/.classpath b/ui/org.eclipse.pde.spy.bundle/.classpath
new file mode 100644
index 0000000..4a00bec
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
+		<attributes>
+			<attribute name="module" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/ui/org.eclipse.pde.spy.bundle/.project b/ui/org.eclipse.pde.spy.bundle/.project
new file mode 100644
index 0000000..405de65
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.pde.spy.bundle</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.PluginNature</nature>
+	</natures>
+</projectDescription>
diff --git a/ui/org.eclipse.pde.spy.bundle/.settings/org.eclipse.jdt.core.prefs b/ui/org.eclipse.pde.spy.bundle/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..f2525a8
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,14 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=11
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=11
diff --git a/ui/org.eclipse.pde.spy.bundle/META-INF/MANIFEST.MF b/ui/org.eclipse.pde.spy.bundle/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..c417cb0
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %name
+Bundle-SymbolicName: org.eclipse.pde.spy.bundle;singleton:=true
+Bundle-Version: 0.12.100.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-11
+Automatic-Module-Name: org.eclipse.pde.spy.bundle
+Require-Bundle: org.eclipse.core.runtime;bundle-version="3.10.0",
+ org.eclipse.jface;bundle-version="3.10.1",
+ org.eclipse.e4.ui.model.workbench,
+ org.eclipse.e4.core.contexts,
+ org.eclipse.e4.ui.di,
+ org.eclipse.e4.core.di,
+ org.eclipse.e4.core.services,
+ org.eclipse.pde.spy.core;bundle-version="1.0.0"
+Bundle-Localization: plugin
+Import-Package: javax.annotation;version="1.3.5",
+ javax.inject;version="1.0.0"
+Bundle-Vendor: %provider-name
+Bundle-ActivationPolicy: lazy
diff --git a/ui/org.eclipse.pde.spy.bundle/about.html b/ui/org.eclipse.pde.spy.bundle/about.html
new file mode 100644
index 0000000..164f781
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/about.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>About</title>
+</head>
+<body lang="EN-US">
+	<h2>About This Content</h2>
+
+	<p>November 30, 2017</p>
+	<h3>License</h3>
+
+	<p>
+		The Eclipse Foundation makes available all content in this plug-in
+		(&quot;Content&quot;). Unless otherwise indicated below, the Content
+		is provided to you under the terms and conditions of the Eclipse
+		Public License Version 2.0 (&quot;EPL&quot;). A copy of the EPL is
+		available at <a href="http://www.eclipse.org/legal/epl-2.0">http://www.eclipse.org/legal/epl-2.0</a>.
+		For purposes of the EPL, &quot;Program&quot; will mean the Content.
+	</p>
+
+	<p>
+		If you did not receive this Content directly from the Eclipse
+		Foundation, the Content is being redistributed by another party
+		(&quot;Redistributor&quot;) and different terms and conditions may
+		apply to your use of any object code in the Content. Check the
+		Redistributor's license that was provided with the Content. If no such
+		license exists, contact the Redistributor. Unless otherwise indicated
+		below, the terms and conditions of the EPL still apply to any source
+		code in the Content and such source code may be obtained at <a
+			href="http://www.eclipse.org/">http://www.eclipse.org</a>.
+	</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/ui/org.eclipse.pde.spy.bundle/build.properties b/ui/org.eclipse.pde.spy.bundle/build.properties
new file mode 100644
index 0000000..a9b12c8
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/build.properties
@@ -0,0 +1,8 @@
+source.. = src/
+bin.includes = META-INF/,\
+               .,\
+               plugin.properties,\
+               plugin.xml,\
+               icons/,\
+               about.html
+src.includes = about.html
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/osgi.png b/ui/org.eclipse.pde.spy.bundle/icons/osgi.png
new file mode 100644
index 0000000..15cf6e3
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/osgi.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/osgi@2x.png b/ui/org.eclipse.pde.spy.bundle/icons/osgi@2x.png
new file mode 100644
index 0000000..5656bcf
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/osgi@2x.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/refresh.png b/ui/org.eclipse.pde.spy.bundle/icons/refresh.png
new file mode 100644
index 0000000..bee8fe7
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/refresh.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/refresh@2x.png b/ui/org.eclipse.pde.spy.bundle/icons/refresh@2x.png
new file mode 100644
index 0000000..89d4d48
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/refresh@2x.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/start.png b/ui/org.eclipse.pde.spy.bundle/icons/start.png
new file mode 100644
index 0000000..c06ce07
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/start.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/start@2x.png b/ui/org.eclipse.pde.spy.bundle/icons/start@2x.png
new file mode 100644
index 0000000..d1a4b91
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/start@2x.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/state_active.png b/ui/org.eclipse.pde.spy.bundle/icons/state_active.png
new file mode 100644
index 0000000..a9be929
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/state_active.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/state_active@2x.png b/ui/org.eclipse.pde.spy.bundle/icons/state_active@2x.png
new file mode 100644
index 0000000..fefe4e7
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/state_active@2x.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/state_installed.png b/ui/org.eclipse.pde.spy.bundle/icons/state_installed.png
new file mode 100644
index 0000000..cc3a72d
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/state_installed.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/state_installed@2x.png b/ui/org.eclipse.pde.spy.bundle/icons/state_installed@2x.png
new file mode 100644
index 0000000..65b2922
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/state_installed@2x.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/state_resolved.png b/ui/org.eclipse.pde.spy.bundle/icons/state_resolved.png
new file mode 100644
index 0000000..2edf65f
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/state_resolved.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/state_resolved@2x.png b/ui/org.eclipse.pde.spy.bundle/icons/state_resolved@2x.png
new file mode 100644
index 0000000..5411cac
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/state_resolved@2x.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/state_starting.png b/ui/org.eclipse.pde.spy.bundle/icons/state_starting.png
new file mode 100644
index 0000000..f2e6a03
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/state_starting.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/state_starting@2x.png b/ui/org.eclipse.pde.spy.bundle/icons/state_starting@2x.png
new file mode 100644
index 0000000..8e26ea4
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/state_starting@2x.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/state_stopping.png b/ui/org.eclipse.pde.spy.bundle/icons/state_stopping.png
new file mode 100644
index 0000000..9fcc646
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/state_stopping.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/state_stopping@2x.png b/ui/org.eclipse.pde.spy.bundle/icons/state_stopping@2x.png
new file mode 100644
index 0000000..d46b3a0
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/state_stopping@2x.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/state_uninstalled.png b/ui/org.eclipse.pde.spy.bundle/icons/state_uninstalled.png
new file mode 100644
index 0000000..fb407df
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/state_uninstalled.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/state_uninstalled@2x.png b/ui/org.eclipse.pde.spy.bundle/icons/state_uninstalled@2x.png
new file mode 100644
index 0000000..f0dc98a
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/state_uninstalled@2x.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/stop.png b/ui/org.eclipse.pde.spy.bundle/icons/stop.png
new file mode 100644
index 0000000..fe4c275
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/stop.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/icons/stop@2x.png b/ui/org.eclipse.pde.spy.bundle/icons/stop@2x.png
new file mode 100644
index 0000000..0787f19
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/icons/stop@2x.png
Binary files differ
diff --git a/ui/org.eclipse.pde.spy.bundle/plugin.properties b/ui/org.eclipse.pde.spy.bundle/plugin.properties
new file mode 100644
index 0000000..a230739
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/plugin.properties
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2021, vogella GmbH Corporation and others.
+#
+# This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License 2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+#     Lars Vogel - initial API and implementation
+###############################################################################
+#
+#
+name = Bundle Spy
+provider-name = Eclipse.org
+description = Bundle Spy to display all bundles and their states
\ No newline at end of file
diff --git a/ui/org.eclipse.pde.spy.bundle/plugin.xml b/ui/org.eclipse.pde.spy.bundle/plugin.xml
new file mode 100644
index 0000000..6c1460d
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/plugin.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin>
+   <extension point="org.eclipse.pde.spy.core.spyPart">
+      <spyPart
+            description="%description"
+            icon="$nl$/icons/osgi.png"
+            name="%name"
+            part="org.eclipse.pde.spy.bundle.BundleSpyPart"
+            shortcut="M2+M3+F12">
+      </spyPart>
+   </extension>
+</plugin>
diff --git a/ui/org.eclipse.pde.spy.bundle/pom.xml b/ui/org.eclipse.pde.spy.bundle/pom.xml
new file mode 100644
index 0000000..ed10982
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright (c) 2012, 2019 Eclipse Foundation and others.
+  All rights reserved. This program and the accompanying materials
+  are made available under the terms of the Eclipse Distribution License v1.0
+  which accompanies this distribution, and is available at
+  http://www.eclipse.org/org/documents/edl-v10.php
+
+  Contributors:
+     Igor Fedorenko - initial implementation
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>eclipse.pde.ui</artifactId>
+    <groupId>eclipse.pde.ui</groupId>
+    <version>4.23.0-SNAPSHOT</version>
+    <relativePath>../../</relativePath>
+  </parent>
+
+   <properties>
+    <skipAPIAnalysis>true</skipAPIAnalysis>
+  </properties>
+
+
+  <groupId>org.pde.ui</groupId>
+  <artifactId>org.eclipse.pde.spy.bundle</artifactId>
+  <version>0.12.100-SNAPSHOT</version>
+  <packaging>eclipse-plugin</packaging>
+</project>
diff --git a/ui/org.eclipse.pde.spy.bundle/src/org/eclipse/pde/spy/bundle/BundleSpyPart.java b/ui/org.eclipse.pde.spy.bundle/src/org/eclipse/pde/spy/bundle/BundleSpyPart.java
new file mode 100644
index 0000000..cf808b9
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/src/org/eclipse/pde/spy/bundle/BundleSpyPart.java
@@ -0,0 +1,317 @@
+/*******************************************************************************
+ * Copyright (c) 2015 OPCoach and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Olivier Prouvost <olivier.prouvost@opcoach.com> - initial API and implementation (bug #451116)
+ *     Simon Scholz <simon.scholz@vogella.com> - Bug 466785
+ *******************************************************************************/
+package org.eclipse.pde.spy.bundle;
+
+import java.util.Iterator;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+
+import org.eclipse.e4.core.contexts.ContextInjectionFactory;
+import org.eclipse.e4.core.contexts.IEclipseContext;
+import org.eclipse.e4.ui.di.Focus;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.pde.spy.bundle.internal.BundleDataFilter;
+import org.eclipse.pde.spy.bundle.internal.BundleDataProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ * This class is the main part of the bundle spy. It displays a tableviewer with
+ * all bundles
+ */
+public class BundleSpyPart {
+
+	private static final String ICON_REFRESH = "icons/refresh.png";
+	public static final String ICON_STATE_ACTIVE = "icons/state_active.png";
+	public static final String ICON_STATE_STARTING = "icons/state_starting.png";
+	public static final String ICON_STATE_STOPPING = "icons/state_stopping.png";
+	public static final String ICON_STATE_RESOLVED = "icons/state_resolved.png";
+	public static final String ICON_STATE_INSTALLED = "icons/state_installed.png";
+	public static final String ICON_STATE_UNINSTALLED = "icons/state_uninstalled.png";
+	public static final String ICON_START = "icons/start.png";
+	public static final String ICON_STOP = "icons/stop.png";
+
+	private TableViewer bundlesTableViewer;
+
+	private Text filterText;
+
+	private Button showOnlyFilteredElements;
+
+	private BundleDataFilter bundleFilter;
+
+	@Inject
+	private IEclipseContext ctx;
+
+	/** Store the values to set it when it is reopened */
+	private static String lastFilterText = null;
+	private static boolean lastShowFiltered = false;
+
+	/**
+	 * Create contents of the view part.
+	 */
+	@PostConstruct
+	public void createControls(Composite parent) {
+		ImageRegistry imgReg = initializeImageRegistry();
+
+		// Set a filter in context (-> null at the begining).
+		bundleFilter = new BundleDataFilter();
+		ctx.set(BundleDataFilter.class, bundleFilter);
+
+		parent.setLayout(new GridLayout(1, false));
+
+		final Composite comp = new Composite(parent, SWT.NONE);
+		comp.setLayout(new GridLayout(5, false));
+
+		Button refreshButton = new Button(comp, SWT.FLAT);
+		refreshButton.setImage(imgReg.get(ICON_REFRESH));
+		refreshButton.setToolTipText("Refresh the contexts");
+		refreshButton.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				bundlesTableViewer.refresh(true);
+			}
+		});
+
+		filterText = new Text(comp, SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL);
+		GridDataFactory.fillDefaults().hint(200, SWT.DEFAULT).applyTo(filterText);
+		filterText.setMessage("Search data");
+		filterText.setToolTipText(
+				"Highlight the bundles where the contained objects contains this string.\n" + "Case is ignored.");
+		if (lastFilterText != null)
+			filterText.setText(lastFilterText);
+		bundleFilter.setPattern(lastFilterText);
+		filterText.addKeyListener(new KeyAdapter() {
+			@Override
+			public void keyReleased(KeyEvent e) {
+				String textToSearch = filterText.getText();
+				lastFilterText = textToSearch;
+				boolean enableButton = textToSearch.length() > 0;
+				// Enable/disable button for filtering
+				showOnlyFilteredElements.setEnabled(enableButton);
+
+				// Then update filters and viewers
+				bundleFilter.setPattern(textToSearch);
+				setFilter();
+				bundlesTableViewer.refresh(true);
+			}
+
+		});
+
+		showOnlyFilteredElements = new Button(comp, SWT.CHECK);
+		showOnlyFilteredElements.setText("Show Only Filtered");
+		showOnlyFilteredElements.setToolTipText("Show only the filtered items in the bundle table ");
+		showOnlyFilteredElements.setEnabled((lastFilterText != null) && (lastFilterText.length() > 0));
+		showOnlyFilteredElements.setSelection(lastShowFiltered);
+		showOnlyFilteredElements.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				lastShowFiltered = showOnlyFilteredElements.getSelection();
+				setFilter();
+			}
+		});
+
+		startButton = new Button(comp, SWT.FLAT);
+		startButton.setImage(imgReg.get(ICON_START));
+		startButton.setToolTipText("Start the selected bundles not yet started");
+		startButton.setEnabled(false);
+		startButton.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				IStructuredSelection sel = (IStructuredSelection) bundlesTableViewer.getSelection();
+				Iterator<?> iter = sel.iterator();
+				while (iter.hasNext()) {
+					Bundle b = (Bundle) iter.next();
+					try {
+						b.start();
+					} catch (BundleException e1) {
+						e1.printStackTrace();
+					}
+				}
+				bundlesTableViewer.refresh();
+				updateButtonStatuses(sel);
+			}
+		});
+
+		stopButton = new Button(comp, SWT.FLAT);
+		stopButton.setImage(imgReg.get(ICON_STOP));
+		stopButton.setToolTipText("Stop the selected bundles not yet stopped");
+		stopButton.setEnabled(false);
+		stopButton.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				if (MessageDialog.openConfirm(((Control) e.getSource()).getShell(), "Confirm Bundle Stop",
+						"Stopping a bundle may cause problems in your current application.\nUse this button only for your bundles under testing\n\nDo you confirm you want to stop the selected started bundle(s) ? ")) {
+					IStructuredSelection sel = (IStructuredSelection) bundlesTableViewer.getSelection();
+					Iterator<?> iter = sel.iterator();
+					while (iter.hasNext()) {
+						Bundle b = (Bundle) iter.next();
+						try {
+							b.stop();
+						} catch (BundleException e1) {
+							e1.printStackTrace();
+						}
+					}
+					bundlesTableViewer.refresh();
+					updateButtonStatuses(sel);
+				}
+			}
+		});
+
+		// Create the customer table with 2 columns: firstname and name
+		bundlesTableViewer = new TableViewer(parent);
+		final Table cTable = bundlesTableViewer.getTable();
+		cTable.setHeaderVisible(true);
+		cTable.setLinesVisible(true);
+		GridData gd_cTable = new GridData(SWT.FILL, SWT.FILL, true, true);
+		// gd_cTable.verticalAlignment = SWT.TOP;
+		cTable.setLayoutData(gd_cTable);
+
+		// Create the first column for bundle name
+		addColumn(bundlesTableViewer, 35, "State", BundleDataProvider.COL_STATE);
+		addColumn(bundlesTableViewer, 200, "Bundle Name", BundleDataProvider.COL_NAME);
+		addColumn(bundlesTableViewer, 200, "Version", BundleDataProvider.COL_VERSION);
+
+		// Set input data and content provider (default ArrayContentProvider)
+		bundlesTableViewer.setContentProvider(ArrayContentProvider.getInstance());
+
+		// Get the list of bundles in platform using bundle context...
+		BundleContext bc = FrameworkUtil.getBundle(BundleSpyPart.class).getBundleContext();
+		bundlesTableViewer.setInput(bc.getBundles());
+
+		bundlesTableViewer.addSelectionChangedListener(event -> updateButtonStatuses((IStructuredSelection) event.getSelection()));
+
+		ColumnViewerToolTipSupport.enableFor(bundlesTableViewer);
+
+	}
+
+	/** Update the stop and start buttons depending on current selection */
+	protected void updateButtonStatuses(IStructuredSelection selection) {
+		// startButton is enabled if at least one bundle is not active
+		// stopButton is enabled if at least one bundle is active
+		boolean oneBundleIsActive = false;
+		boolean oneBundleIsNotActive = false;
+
+		Iterator<?> iter = selection.iterator();
+		while (iter.hasNext()) {
+			Bundle b = (Bundle) iter.next();
+			oneBundleIsActive = oneBundleIsActive || (b.getState() == Bundle.ACTIVE);
+			oneBundleIsNotActive = oneBundleIsNotActive || (b.getState() != Bundle.ACTIVE);
+		}
+		startButton.setEnabled(oneBundleIsNotActive);
+		stopButton.setEnabled(oneBundleIsActive);
+
+	}
+
+	private void addColumn(final TableViewer parentTable, int width, String title, final int column) {
+		TableViewerColumn col = new TableViewerColumn(bundlesTableViewer, SWT.NONE);
+		col.getColumn().setWidth(width);
+		col.getColumn().setText(title);
+
+		final BundleDataProvider bdp = ContextInjectionFactory.make(BundleDataProvider.class, ctx);
+		bdp.setColumn(column);
+		col.setLabelProvider(bdp);
+
+		col.getColumn().addSelectionListener(new SelectionAdapter() {
+
+			private int turnAround = 1;
+
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				turnAround *= -1;
+				parentTable.setComparator(new ViewerComparator() {
+					@Override
+					public int compare(Viewer viewer, Object e1, Object e2) {
+						if (BundleDataProvider.COL_STATE == column) {
+							Bundle b1 = (Bundle) e1;
+							Bundle b2 = (Bundle) e2;
+							return turnAround(Integer.compare(b1.getState(), b2.getState()));
+						}
+
+						return turnAround(bdp.getText(e1).compareTo(bdp.getText(e2)));
+					}
+				});
+			}
+
+			private int turnAround(int compare) {
+				return compare * turnAround;
+			}
+		});
+
+	}
+
+	private static final ViewerFilter[] NO_FILTER = new ViewerFilter[0];
+	private Button stopButton;
+	private Button startButton;
+
+	/** Set the filter on table */
+	public void setFilter() {
+
+		if (showOnlyFilteredElements.isEnabled() && showOnlyFilteredElements.getSelection()) {
+			bundlesTableViewer.setFilters(bundleFilter);
+		} else {
+			bundlesTableViewer.setFilters(NO_FILTER);
+		}
+	}
+
+	@Focus
+	public void setFocus() {
+		bundlesTableViewer.getControl().setFocus();
+	}
+
+	private ImageRegistry initializeImageRegistry() {
+		Bundle b = FrameworkUtil.getBundle(this.getClass());
+		ImageRegistry imgReg = new ImageRegistry();
+		imgReg.put(ICON_REFRESH, ImageDescriptor.createFromURL(b.getEntry(ICON_REFRESH)));
+		imgReg.put(ICON_STATE_ACTIVE, ImageDescriptor.createFromURL(b.getEntry(ICON_STATE_ACTIVE)));
+		imgReg.put(ICON_STATE_RESOLVED, ImageDescriptor.createFromURL(b.getEntry(ICON_STATE_RESOLVED)));
+		imgReg.put(ICON_STATE_STARTING, ImageDescriptor.createFromURL(b.getEntry(ICON_STATE_STARTING)));
+		imgReg.put(ICON_STATE_STOPPING, ImageDescriptor.createFromURL(b.getEntry(ICON_STATE_STOPPING)));
+		imgReg.put(ICON_STATE_INSTALLED, ImageDescriptor.createFromURL(b.getEntry(ICON_STATE_INSTALLED)));
+		imgReg.put(ICON_STATE_UNINSTALLED, ImageDescriptor.createFromURL(b.getEntry(ICON_STATE_UNINSTALLED)));
+		imgReg.put(ICON_START, ImageDescriptor.createFromURL(b.getEntry(ICON_START)));
+		imgReg.put(ICON_STOP, ImageDescriptor.createFromURL(b.getEntry(ICON_STOP)));
+
+		ctx.set(ImageRegistry.class, imgReg);
+
+		return imgReg;
+	}
+
+}
diff --git a/ui/org.eclipse.pde.spy.bundle/src/org/eclipse/pde/spy/bundle/internal/BundleDataFilter.java b/ui/org.eclipse.pde.spy.bundle/src/org/eclipse/pde/spy/bundle/internal/BundleDataFilter.java
new file mode 100644
index 0000000..28ece26
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/src/org/eclipse/pde/spy/bundle/internal/BundleDataFilter.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2015 OPCoach.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Olivier Prouvost <olivier.prouvost@opcoach.com> - initial API and implementation (bug #451116)
+ *******************************************************************************/
+package org.eclipse.pde.spy.bundle.internal;
+
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.osgi.framework.Bundle;
+
+public class BundleDataFilter extends ViewerFilter {
+	private String pattern;
+
+	// Implements the filter for the data table content
+	@Override
+	public boolean select(Viewer viewer, Object parentElement, Object element) {
+		Bundle b = (Bundle) element;
+
+		// Must only select objects matching the pattern -> get all text for one
+		// element and
+		// check if values are in pattern.
+		TableViewer tv = (TableViewer) viewer;
+		String bstring = getBundleStrings(b, tv.getTable().getColumnCount());
+
+		return matchText(bstring);
+
+	}
+
+	public String getBundleStrings(Bundle b, int nbColumn) {
+		StringBuilder sb = new StringBuilder();
+		for (int i = 0; i < nbColumn; i++)
+			sb.append(BundleDataProvider.getText(b, i)).append("  ");
+
+		return sb.toString();
+	}
+
+	/** Set the pattern and use it as lowercase */
+	public void setPattern(String newPattern) {
+		if ((newPattern == null) || (newPattern.length() == 0))
+			pattern = null;
+		else
+			pattern = newPattern.toLowerCase();
+	}
+
+	public boolean matchText(String text) {
+		return ((text == null) || (pattern == null)) ? false : text.toLowerCase().contains(pattern);
+	}
+
+}
diff --git a/ui/org.eclipse.pde.spy.bundle/src/org/eclipse/pde/spy/bundle/internal/BundleDataProvider.java b/ui/org.eclipse.pde.spy.bundle/src/org/eclipse/pde/spy/bundle/internal/BundleDataProvider.java
new file mode 100644
index 0000000..0c901a8
--- /dev/null
+++ b/ui/org.eclipse.pde.spy.bundle/src/org/eclipse/pde/spy/bundle/internal/BundleDataProvider.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (c) 2015 OPCoach.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Olivier Prouvost <olivier.prouvost@opcoach.com> - initial API and implementation (bug #451116)
+ *******************************************************************************/
+package org.eclipse.pde.spy.bundle.internal;
+
+import javax.inject.Inject;
+
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.pde.spy.bundle.BundleSpyPart;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+import org.osgi.framework.Bundle;
+
+/**
+ * The column Label and content Provider used to display information in context
+ * data TreeViewer. Two instances for label provider are created : one for key,
+ * one for values
+ *
+ * @see ContextDataPart
+ */
+public class BundleDataProvider extends ColumnLabelProvider {
+
+	public static final int COL_NAME = 0;
+	public static final int COL_VERSION = 1;
+	public static final int COL_STATE = 2;
+
+	private static final Color COLOR_IF_FOUND = Display.getCurrent().getSystemColor(SWT.COLOR_BLUE);
+
+	@Inject
+	private ImageRegistry imgReg;
+
+	// Only one bundle filter, injected for all columns.
+	@Inject
+	private BundleDataFilter bundleFilter;
+
+	// The column number this provider manages.
+	private int column;
+
+	@Inject
+	public BundleDataProvider() {
+		super();
+	}
+
+	@Override
+	public String getText(Object element) {
+		// Received element is a bundle...Text depends on column.
+		Bundle b = (Bundle) element;
+		String result = getText(b, column);
+		return (result == null) ? super.getText(element) : result;
+
+	}
+
+	public static String getText(Bundle b, int col) {
+		switch (col) {
+		case COL_NAME:
+			return b.getSymbolicName();
+		case COL_VERSION:
+			return b.getVersion().toString();
+		case COL_STATE:
+			return ""; // No text for state (see tooltip)
+
+		}
+		return null;
+	}
+
+	@Override
+	public Color getForeground(Object element) {
+		// Return magenta color if the value could not be yet computed (for
+		// context functions)
+		String s = getText(element);
+
+		// Return blue color if the string matches the search
+		return ((bundleFilter != null) && (bundleFilter.matchText(s))) ? COLOR_IF_FOUND : null;
+	}
+
+	@Override
+	public Image getImage(Object element) {
+		Bundle b = (Bundle) element;
+		if (column == COL_STATE) {
+
+			switch (b.getState()) {
+			case Bundle.ACTIVE:
+				return imgReg.get(BundleSpyPart.ICON_STATE_ACTIVE);
+			case Bundle.INSTALLED:
+				return imgReg.get(BundleSpyPart.ICON_STATE_INSTALLED);
+			case Bundle.RESOLVED:
+				return imgReg.get(BundleSpyPart.ICON_STATE_RESOLVED);
+			case Bundle.STARTING:
+				return imgReg.get(BundleSpyPart.ICON_STATE_STARTING);
+			case Bundle.STOPPING:
+				return imgReg.get(BundleSpyPart.ICON_STATE_STOPPING);
+			case Bundle.UNINSTALLED:
+				return imgReg.get(BundleSpyPart.ICON_STATE_UNINSTALLED);
+
+			}
+		}
+		return null;
+
+	}
+
+	@Override
+	public String getToolTipText(Object element) {
+		Bundle b = (Bundle) element;
+
+		switch (b.getState()) {
+		case Bundle.ACTIVE:
+			return "This bundle is Active";
+		case Bundle.INSTALLED:
+			return "This bundle is Installed";
+		case Bundle.RESOLVED:
+			return "This bundle is Resolved";
+		case Bundle.STARTING:
+			return "This bundle is Starting";
+		case Bundle.STOPPING:
+			return "This bundle is Stopping";
+		case Bundle.UNINSTALLED:
+			return "This bundle is Uninstalled";
+
+		}
+
+		return "This bundle is in state : " + b.getState();
+
+	}
+
+	@Override
+	public Image getToolTipImage(Object object) {
+		return getImage(object);
+	}
+
+	@Override
+	public int getToolTipStyle(Object object) {
+		return SWT.SHADOW_OUT;
+	}
+
+	public void setColumn(int col) {
+		column = col;
+
+	}
+
+}