diff options
author | Olivier Prouvost | 2021-11-18 18:34:57 +0000 |
---|---|---|
committer | Olivier Prouvost | 2021-11-30 22:28:03 +0000 |
commit | b23b559d77dc4024dafc98c7c9909de0cb0d5933 (patch) | |
tree | b93219d3d50fb869051a6a0ad843404016a7fa3d | |
parent | beb6c75755ab57863243c4b4836e74270fda6748 (diff) | |
download | eclipse.pde.ui-change/188307/20211130152446.tar.gz eclipse.pde.ui-change/188307/20211130152446.tar.xz eclipse.pde.ui-change/188307/20211130152446.zip |
Bug 577208 - Migrate the context spychange/188307/20211130152446
Change-Id: I3de773697657b3b07efcb86e183fcb9c59ddee83
Signed-off-by: Olivier Prouvost <olivier.prouvost@opcoach.com>
38 files changed, 1322 insertions, 0 deletions
@@ -77,6 +77,7 @@ <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.context</module> <module>ui/org.eclipse.pde.spy.preferences</module> <module>ui/org.eclipse.tools.layout.spy</module> <module>ui/org.eclipse.pde.ui.templates.tests</module> diff --git a/ui/org.eclipse.pde.spy.context/.classpath b/ui/org.eclipse.pde.spy.context/.classpath new file mode 100644 index 0000000000..e801ebfb46 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/.classpath @@ -0,0 +1,7 @@ +<?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"/> + <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.context/.project b/ui/org.eclipse.pde.spy.context/.project new file mode 100644 index 0000000000..7ee455081b --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.pde.spy.context</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.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/ui/org.eclipse.pde.spy.context/.settings/org.eclipse.jdt.core.prefs b/ui/org.eclipse.pde.spy.context/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..c9545f06a4 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,9 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.compliance=11 +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.context/META-INF/MANIFEST.MF b/ui/org.eclipse.pde.spy.context/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..b7328a1b85 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/META-INF/MANIFEST.MF @@ -0,0 +1,25 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %name +Bundle-SymbolicName: org.eclipse.pde.spy.context;singleton:=true +Bundle-Version: 0.12.100.qualifier +Bundle-Vendor: %provider-name +Automatic-Module-Name: org.eclipse.pde.spy.context +Bundle-RequiredExecutionEnvironment: JavaSE-11 +Require-Bundle: org.eclipse.jface;bundle-version="3.9.0", + org.eclipse.e4.core.contexts;bundle-version="1.2.0", + org.eclipse.e4.ui.model.workbench, + org.eclipse.core.runtime;bundle-version="3.9.0", + org.eclipse.e4.ui.workbench;bundle-version="1.0.0", + org.eclipse.e4.ui.di;bundle-version="1.0.0", + org.eclipse.e4.ui.services;bundle-version="1.0.0", + org.eclipse.e4.core.di;bundle-version="1.3.0", + org.eclipse.e4.core.services;bundle-version="1.1.0", + org.eclipse.e4.ui.workbench.swt, + org.eclipse.pde.spy.core;bundle-version="1.0.0" +Bundle-ActivationPolicy: lazy +Import-Package: javax.annotation;version="1.2.0", + javax.inject;version="1.0.0" +Bundle-Localization: plugin +Export-Package: org.eclipse.pde.spy.context + diff --git a/ui/org.eclipse.pde.spy.context/README.md b/ui/org.eclipse.pde.spy.context/README.md new file mode 100644 index 0000000000..ff41fc8394 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/README.md @@ -0,0 +1,13 @@ +org.eclipse.pde.spy.context +=================================== + + +For more information, refer to the [Eclipse e4 wiki page] [1]. + +License +------- + +[Eclipse Public License (EPL) v1.0][2] + +[1]: http://www.eclipse.org/e4/ +[2]: http://wiki.eclipse.org/EPL diff --git a/ui/org.eclipse.pde.spy.context/about.html b/ui/org.eclipse.pde.spy.context/about.html new file mode 100644 index 0000000000..824948673d --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/about.html @@ -0,0 +1,28 @@ +<!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>December 3, 2009</p> +<h3>License</h3> + +<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. +For purposes of the EPL, "Program" 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 ("Redistributor") 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.context/build.properties b/ui/org.eclipse.pde.spy.context/build.properties new file mode 100644 index 0000000000..66df5a7990 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/build.properties @@ -0,0 +1,10 @@ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml,\ + about.html,\ + OSGI-INF/,\ + icons/,\ + plugin.properties +source.. = src/ +src.includes = about.html diff --git a/ui/org.eclipse.pde.spy.context/icons/annotation_obj.png b/ui/org.eclipse.pde.spy.context/icons/annotation_obj.png Binary files differnew file mode 100644 index 0000000000..828da7aa31 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/annotation_obj.png diff --git a/ui/org.eclipse.pde.spy.context/icons/annotation_obj@2x.png b/ui/org.eclipse.pde.spy.context/icons/annotation_obj@2x.png Binary files differnew file mode 100644 index 0000000000..a53f05ca69 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/annotation_obj@2x.png diff --git a/ui/org.eclipse.pde.spy.context/icons/collapseall.png b/ui/org.eclipse.pde.spy.context/icons/collapseall.png Binary files differnew file mode 100644 index 0000000000..92cc57892b --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/collapseall.png diff --git a/ui/org.eclipse.pde.spy.context/icons/collapseall@2x.png b/ui/org.eclipse.pde.spy.context/icons/collapseall@2x.png Binary files differnew file mode 100644 index 0000000000..8769e04da0 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/collapseall@2x.png diff --git a/ui/org.eclipse.pde.spy.context/icons/contextfunction.png b/ui/org.eclipse.pde.spy.context/icons/contextfunction.png Binary files differnew file mode 100644 index 0000000000..7d9f913b4c --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/contextfunction.png diff --git a/ui/org.eclipse.pde.spy.context/icons/contextfunction@2x.png b/ui/org.eclipse.pde.spy.context/icons/contextfunction@2x.png Binary files differnew file mode 100644 index 0000000000..118737c3db --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/contextfunction@2x.png diff --git a/ui/org.eclipse.pde.spy.context/icons/expandall.png b/ui/org.eclipse.pde.spy.context/icons/expandall.png Binary files differnew file mode 100644 index 0000000000..08cb513f1c --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/expandall.png diff --git a/ui/org.eclipse.pde.spy.context/icons/expandall@2x.png b/ui/org.eclipse.pde.spy.context/icons/expandall@2x.png Binary files differnew file mode 100644 index 0000000000..fcbf24f455 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/expandall@2x.png diff --git a/ui/org.eclipse.pde.spy.context/icons/field_public_obj.png b/ui/org.eclipse.pde.spy.context/icons/field_public_obj.png Binary files differnew file mode 100644 index 0000000000..2b3e8e51b3 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/field_public_obj.png diff --git a/ui/org.eclipse.pde.spy.context/icons/field_public_obj@2x.png b/ui/org.eclipse.pde.spy.context/icons/field_public_obj@2x.png Binary files differnew file mode 100644 index 0000000000..e2695fae0f --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/field_public_obj@2x.png diff --git a/ui/org.eclipse.pde.spy.context/icons/inher_co.png b/ui/org.eclipse.pde.spy.context/icons/inher_co.png Binary files differnew file mode 100644 index 0000000000..a33523364e --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/inher_co.png diff --git a/ui/org.eclipse.pde.spy.context/icons/inher_co@2x.png b/ui/org.eclipse.pde.spy.context/icons/inher_co@2x.png Binary files differnew file mode 100644 index 0000000000..fb682c09d4 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/inher_co@2x.png diff --git a/ui/org.eclipse.pde.spy.context/icons/letter-l-icon.png b/ui/org.eclipse.pde.spy.context/icons/letter-l-icon.png Binary files differnew file mode 100644 index 0000000000..115dcefc3a --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/letter-l-icon.png diff --git a/ui/org.eclipse.pde.spy.context/icons/letter-l-icon@2x.png b/ui/org.eclipse.pde.spy.context/icons/letter-l-icon@2x.png Binary files differnew file mode 100644 index 0000000000..37868fc9c3 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/letter-l-icon@2x.png diff --git a/ui/org.eclipse.pde.spy.context/icons/methpub_obj.png b/ui/org.eclipse.pde.spy.context/icons/methpub_obj.png Binary files differnew file mode 100644 index 0000000000..a9be929f67 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/methpub_obj.png diff --git a/ui/org.eclipse.pde.spy.context/icons/methpub_obj@2x.png b/ui/org.eclipse.pde.spy.context/icons/methpub_obj@2x.png Binary files differnew file mode 100644 index 0000000000..fefe4e71a5 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/methpub_obj@2x.png diff --git a/ui/org.eclipse.pde.spy.context/icons/refresh.png b/ui/org.eclipse.pde.spy.context/icons/refresh.png Binary files differnew file mode 100644 index 0000000000..bee8fe7a32 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/refresh.png diff --git a/ui/org.eclipse.pde.spy.context/icons/refresh@2x.png b/ui/org.eclipse.pde.spy.context/icons/refresh@2x.png Binary files differnew file mode 100644 index 0000000000..89d4d48575 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/refresh@2x.png diff --git a/ui/org.eclipse.pde.spy.context/icons/splash.png b/ui/org.eclipse.pde.spy.context/icons/splash.png Binary files differnew file mode 100644 index 0000000000..ba755f9e35 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/splash.png diff --git a/ui/org.eclipse.pde.spy.context/icons/valueincontext.png b/ui/org.eclipse.pde.spy.context/icons/valueincontext.png Binary files differnew file mode 100644 index 0000000000..533cfcc3fd --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/valueincontext.png diff --git a/ui/org.eclipse.pde.spy.context/icons/valueincontext@2x.png b/ui/org.eclipse.pde.spy.context/icons/valueincontext@2x.png Binary files differnew file mode 100644 index 0000000000..de9d23daa5 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/icons/valueincontext@2x.png diff --git a/ui/org.eclipse.pde.spy.context/plugin.properties b/ui/org.eclipse.pde.spy.context/plugin.properties new file mode 100644 index 0000000000..c8536c2a43 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/plugin.properties @@ -0,0 +1,18 @@ +############################################################################### +# Copyright (c) 2021 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 - initial API and implementation +############################################################################### +# +# +name = Context Spy +description = Context Spy to understand what is behind the scene of injection +provider-name = Eclipse.org diff --git a/ui/org.eclipse.pde.spy.context/plugin.xml b/ui/org.eclipse.pde.spy.context/plugin.xml new file mode 100644 index 0000000000..3558708fb0 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/plugin.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<plugin> + <extension + point="org.eclipse.pde.spy.core.spyPart"> + <spyPart + description="%description" + icon="$nl$/icons/annotation_obj.png" + name="%name" + part="org.eclipse.pde.spy.context.ContextSpyPart" + shortcut="M2+M3+F10"> + </spyPart> + </extension> +</plugin> diff --git a/ui/org.eclipse.pde.spy.context/pom.xml b/ui/org.eclipse.pde.spy.context/pom.xml new file mode 100644 index 0000000000..f4cbf14ddb --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/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>eclipse.pde.ui</groupId> + <artifactId>org.eclipse.pde.spy.context</artifactId> + <version>0.12.100-SNAPSHOT</version> + <packaging>eclipse-plugin</packaging> +</project> diff --git a/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/internal/spy/context/ContextDataFilter.java b/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/internal/spy/context/ContextDataFilter.java new file mode 100644 index 0000000000..9f922be266 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/internal/spy/context/ContextDataFilter.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2014 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: + * OPCoach - initial API and implementation for bug #437478 + *******************************************************************************/ +package org.eclipse.pde.internal.spy.context; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.eclipse.e4.core.contexts.IEclipseContext; +import org.eclipse.e4.core.di.annotations.Creatable; +import org.eclipse.e4.core.internal.contexts.Computation; +import org.eclipse.e4.core.internal.contexts.EclipseContext; +import org.eclipse.e4.core.services.log.Logger; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; + +@SuppressWarnings("restriction") +@Creatable +@Singleton +public class ContextDataFilter extends ViewerFilter { + + @Inject + Logger log; + + private String pattern; + + // Implements the filter for the data table content + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + if ((element == ContextDataProvider.LOCAL_VALUE_NODE) + || (element == ContextDataProvider.INHERITED_INJECTED_VALUE_NODE)) + return true; + + // Must only select objects matching the pattern or objects under a kept + // node (to see where it is injected) + TreeViewer tv = (TreeViewer) viewer; + ContextDataProvider lpkey = (ContextDataProvider) tv.getLabelProvider(0); + ContextDataProvider lpval = (ContextDataProvider) tv.getLabelProvider(1); + + // If the text matches in one of the column, must keep it... + String skey = lpkey.getText(element); + String sval = lpval.getText(element); + + // Must also keep the listener elements if the parent is selected -> + // Must compute parent keys + String sparentkey = lpkey.getText(parentElement); + String sparentval = lpval.getText(parentElement); + + Set<Computation> listeners = lpkey.getListeners(parentElement); + boolean mustKeepParent = (matchText(sparentkey) || matchText(sparentval)) && (listeners != null) + && (listeners.size() > 0); + boolean mustKeepElement = matchText(skey) || matchText(sval); + + return mustKeepElement || (!mustKeepElement && mustKeepParent); + + } + + /** 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(); + } + + /** + * This method search for an object and check if it contains the text or a + * pattern matching this text + */ + public boolean containsText(IEclipseContext ctx) { + // It is useless to store the values in a map, because context changes + // everytime and it should be tracked. + Collection<String> values = computeValues(ctx); + + // Search if string is just in one of the values... manage ignore case + // and contain... + boolean found = false; + for (String s : values) { + if (matchText(s)) { + found = true; + break; + } + } + return found; + } + + public boolean matchText(String text) { + return ((text == null) || (pattern == null)) ? false : text.toLowerCase().contains(pattern); + } + + /** + * Extract all string values in context + * + * @param ctx + * @return + */ + private Collection<String> computeValues(IEclipseContext ctx) { + Collection<String> result = new ArrayList<String>(); + if (ctx instanceof EclipseContext) { + // Search for all strings in this context (values and context + // function) + + EclipseContext currentContext = (EclipseContext) ctx; + extractStringsFromMap(currentContext.localData(), result); + + // Search also in context functions + extractStringsFromMap(currentContext.localContextFunction(), result); + + // Search for the inherited values injected using this context but + // defined in + // parent + // Keep only the names that are not already displayed in local + // values + Collection<String> localKeys = currentContext.localData().keySet(); + Collection<String> localContextFunctionsKeys = currentContext.localContextFunction().keySet(); + + if (currentContext.getRawListenerNames() != null) { + for (String name : currentContext.getRawListenerNames()) { + if (!localKeys.contains(name) && !localContextFunctionsKeys.contains(name)) + result.add(name); + } + } + + } else { + log.warn("Warning : the received EclipseContext has not the expected type. It is a : " + + ctx.getClass().toString()); + } + + return result; + } + + /** + * + * @param map + * the map to extract the strings (keys and values) + * @param result + * the result to fill with strings + */ + private void extractStringsFromMap(Map<String, Object> map, Collection<String> result) { + for (Map.Entry<String, Object> entry : map.entrySet()) { + result.add(entry.getKey().toString()); + Object value = entry.getValue(); + if (value != null) { + result.add(value.toString()); + } + } + } + +} diff --git a/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/internal/spy/context/ContextDataPart.java b/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/internal/spy/context/ContextDataPart.java new file mode 100644 index 0000000000..868ca355a5 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/internal/spy/context/ContextDataPart.java @@ -0,0 +1,212 @@ +/******************************************************************************* + * Copyright (c) 2013 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: + * OPCoach - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.spy.context; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import javax.inject.Named; + +import org.eclipse.e4.core.contexts.ContextInjectionFactory; +import org.eclipse.e4.core.contexts.IEclipseContext; +import org.eclipse.e4.core.di.annotations.Optional; +import org.eclipse.e4.core.internal.contexts.EclipseContext; +import org.eclipse.e4.ui.di.Focus; +import org.eclipse.e4.ui.services.IServiceConstants; +import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.TreeViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.swt.SWT; +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.Composite; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeColumn; + +/** + * This part listen to selection, and if it is an EclipseContext, it displays + * its information It is used in the integrated ContextSpyPart and (in the + * future) it could be used outside to display the context of focused part for + * instance + */ +@SuppressWarnings("restriction") +public class ContextDataPart { + private TreeViewer contextDataViewer; + + private ContextDataProvider dataProvider; + + private ContextEntryComparator comparator; + + /** + * Create contents of the view part. + */ + @PostConstruct + public void createControls(Composite parent, IEclipseContext ctx) { + + parent.setLayout(new GridLayout(1, false)); + + // TreeViewer on the top + contextDataViewer = new TreeViewer(parent); + dataProvider = ContextInjectionFactory.make(ContextDataProvider.class, ctx); + contextDataViewer.setContentProvider(dataProvider); + contextDataViewer.setLabelProvider(dataProvider); + // contextContentTv.setSorter(new ViewerSorter()); + + final Tree cTree = contextDataViewer.getTree(); + cTree.setHeaderVisible(true); + cTree.setLinesVisible(true); + cTree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + // tv.setInput(a); + contextDataViewer.setInput("Foo"); // getElements starts alone + + // Add columns in the tree + // Create the first column for the key + TreeViewerColumn keyCol = new TreeViewerColumn(contextDataViewer, SWT.NONE); + keyCol.getColumn().setWidth(400); + keyCol.getColumn().setText("Key"); + ContextDataProvider keyLabelProvider = ContextInjectionFactory.make(ContextDataProvider.class, ctx); + keyLabelProvider.setDisplayKey(true); + keyCol.setLabelProvider(keyLabelProvider); + keyCol.getColumn().setToolTipText("Key in context"); + keyCol.getColumn().addSelectionListener( + getHeaderSelectionAdapter(contextDataViewer, keyCol.getColumn(), 0, keyLabelProvider)); + + comparator = new ContextEntryComparator(0, keyLabelProvider); + contextDataViewer.setComparator(comparator); + + // Create the second column for the value + TreeViewerColumn valueCol = new TreeViewerColumn(contextDataViewer, SWT.NONE); + valueCol.getColumn().setWidth(600); + valueCol.getColumn().setText("Value"); + ContextDataProvider valueLabelProvider = ContextInjectionFactory.make(ContextDataProvider.class, ctx); + valueCol.setLabelProvider(dataProvider); + valueCol.getColumn().addSelectionListener( + getHeaderSelectionAdapter(contextDataViewer, valueCol.getColumn(), 1, valueLabelProvider)); + + // Open all the tree + contextDataViewer.expandAll(); + + ColumnViewerToolTipSupport.enableFor(contextDataViewer); + + } + + @PreDestroy + public void dispose() { + } + + @Focus + public void setFocus() { + contextDataViewer.getControl().setFocus(); + } + + @Inject + @Optional + public void listenToContext(@Named(IServiceConstants.ACTIVE_SELECTION) EclipseContext ctx) { + // Must check if dataviewer is created or not (when we reopen the window + // @postconstruct has not been called yet) + if ((ctx == null) || (contextDataViewer == null)) { + return; + } + contextDataViewer.setInput(ctx); + contextDataViewer.expandToLevel(2); + } + + /** + * An entry comparator for the table, dealing with column index, keys and + * values + */ + public class ContextEntryComparator extends ViewerComparator { + private int columnIndex; + private int direction; + private ILabelProvider labelProvider; + + public ContextEntryComparator(int columnIndex, ILabelProvider defaultLabelProvider) { + this.columnIndex = columnIndex; + direction = SWT.UP; + labelProvider = defaultLabelProvider; + } + + public int getDirection() { + return direction; + } + + /** Called when click on table header, reverse order */ + public void setColumn(int column) { + if (column == columnIndex) { + // Same column as last sort; toggle the direction + direction = (direction == SWT.UP) ? SWT.DOWN : SWT.UP; + } else { + // New column; do a descending sort + columnIndex = column; + direction = SWT.DOWN; + } + } + + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + // For root elements at first level, we keep Local before Inherited + if ((e1 == ContextDataProvider.LOCAL_VALUE_NODE) || (e2 == ContextDataProvider.LOCAL_VALUE_NODE)) + return -1; + + // Now can compare the text from label provider. + String lp1 = labelProvider.getText(e1); + String lp2 = labelProvider.getText(e2); + String s1 = lp1 == null ? "" : lp1.toLowerCase(); + String s2 = lp2 == null ? "" : lp2.toLowerCase(); + int rc = s1.compareTo(s2); + // If descending order, flip the direction + return (direction == SWT.DOWN) ? -rc : rc; + } + + public void setLabelProvider(ILabelProvider textProvider) { + labelProvider = textProvider; + } + + } + + private SelectionAdapter getHeaderSelectionAdapter(final TreeViewer viewer, final TreeColumn column, + final int columnIndex, final ILabelProvider textProvider) { + SelectionAdapter selectionAdapter = new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + viewer.setComparator(comparator); + comparator.setColumn(columnIndex); + comparator.setLabelProvider(textProvider); + viewer.getTree().setSortDirection(comparator.getDirection()); + viewer.getTree().setSortColumn(column); + viewer.refresh(); + } + }; + return selectionAdapter; + } + + public void refresh(boolean refreshLabel) { + contextDataViewer.refresh(refreshLabel); + } + + private static final ViewerFilter[] NO_FILTER = new ViewerFilter[0]; + + public void setFilter(ViewerFilter filter) { + + contextDataViewer.setFilters((filter == null) ? NO_FILTER : new ViewerFilter[] { filter }); + } + +} diff --git a/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/internal/spy/context/ContextDataProvider.java b/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/internal/spy/context/ContextDataProvider.java new file mode 100644 index 0000000000..0913d26931 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/internal/spy/context/ContextDataProvider.java @@ -0,0 +1,366 @@ +/******************************************************************************* + * Copyright (c) 2013 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: + * OPCoach - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.spy.context; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; + +import org.eclipse.e4.core.internal.contexts.Computation; +import org.eclipse.e4.core.internal.contexts.EclipseContext; +import org.eclipse.jface.resource.FontRegistry; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; + +/** + * 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 + */ +@SuppressWarnings("restriction") +public class ContextDataProvider extends ColumnLabelProvider implements ITreeContentProvider { + + private static final String NO_VALUE_COULD_BE_COMPUTED = "No value could be yet computed"; + private static final Color COLOR_IF_FOUND = Display.getCurrent().getSystemColor(SWT.COLOR_BLUE); + private static final Color COLOR_IF_NOT_COMPUTED = Display.getCurrent().getSystemColor(SWT.COLOR_MAGENTA); + private static final Object[] EMPTY_RESULT = new Object[0]; + static final String LOCAL_VALUE_NODE = "Local values managed by this context"; + static final String INHERITED_INJECTED_VALUE_NODE = "Inherited values injected or updated using this context"; + + private static final String NO_VALUES_FOUND = "No values found"; + private static final String UPDATED_IN_CLASS = "Updated in class :"; + private static final String INJECTED_IN_FIELD = "Injected in field :"; + private static final String INJECTED_IN_METHOD = "Injected in method :"; + + // Image keys constants + private static final String PUBLIC_METHOD_IMG_KEY = "icons/methpub_obj.png"; + private static final String PUBLIC_FIELD_IMG_KEY = "icons/field_public_obj.png"; + private static final String VALUE_IN_CONTEXT_IMG_KEY = "icons/valueincontext.png"; + private static final String INHERITED_VARIABLE_IMG_KEY = "icons/inher_co.png"; + private static final String LOCAL_VARIABLE_IMG_KEY = "icons/letter-l-icon.png"; + private static final String CONTEXT_FUNCTION_IMG_KEY = "icons/contextfunction.png"; + private static final String INJECT_IMG_KEY = "icons/annotation_obj.png"; + + private ImageRegistry imgReg; + + @Inject + private ContextDataFilter contextFilter; + + /** Store the selected context (initialized in inputChanged) */ + private static EclipseContext selectedContext; + + private Font boldFont; + + private boolean displayKey = false; + + @Inject + public ContextDataProvider() { + super(); + initFonts(); + initializeImageRegistry(); + } + + @Override + public void dispose() { + selectedContext = null; + imgReg = null; + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + selectedContext = (newInput instanceof EclipseContext) ? (EclipseContext) newInput : null; + } + + @Override + public Object[] getElements(Object inputElement) { + return new String[] { LOCAL_VALUE_NODE, INHERITED_INJECTED_VALUE_NODE }; + } + + @Override + public Object[] getChildren(Object inputElement) { + if (selectedContext == null) + return EMPTY_RESULT; + + if (inputElement == LOCAL_VALUE_NODE) { + Collection<Object> result = new ArrayList<Object>(); + + result.addAll(selectedContext.localData().entrySet()); + + // For context function, we have to compute the value (if possible), + // and display it as a standard value + Map<String, Object> cfValues = new HashMap<String, Object>(); + for (String key : selectedContext.localContextFunction().keySet()) + try { + cfValues.put(key, selectedContext.get(key)); + } catch (Exception e) { + cfValues.put(key, NO_VALUE_COULD_BE_COMPUTED + " (Exception : " + e.getClass().getName() + ")"); + } + result.addAll(cfValues.entrySet()); + return result.toArray(); + + } else if (inputElement == INHERITED_INJECTED_VALUE_NODE) { + // Search for all values injected using this context but defined in + // parent + Collection<Object> result = new ArrayList<Object>(); + + // Keep only the names that are not already displayed in local + // values + Collection<String> localKeys = selectedContext.localData().keySet(); + Collection<String> localContextFunctionsKeys = selectedContext.localContextFunction().keySet(); + + if (selectedContext.getRawListenerNames() != null) { + for (String name : selectedContext.getRawListenerNames()) { + if (!localKeys.contains(name) && !localContextFunctionsKeys.contains(name)) + result.add(name); + } + } + return result.size() == 0 ? new String[] { NO_VALUES_FOUND } : result.toArray(); + + } else if (inputElement instanceof Map.Entry) { + Set<Computation> listeners = getListeners(inputElement); + return (listeners == null) ? null : listeners.toArray(); + } else if (inputElement instanceof String) { + // This is the name of a raw listener in the inherited injected + // value part + return selectedContext.getListeners((String) inputElement).toArray(); + } + + return EMPTY_RESULT; + } + + public void setDisplayKey(boolean k) { + displayKey = k; + } + + @Override + @SuppressWarnings("unchecked") + public String getText(Object element) { + if (selectedContext == null) + return null; + + if (element instanceof Map.Entry) { + Map.Entry<String, Object> mapEntry = (Map.Entry<String, Object>) element; + Object o = displayKey ? mapEntry.getKey() : mapEntry.getValue(); + return (o == null) ? "null" : o.toString(); + } else if (element instanceof Computation) { + // For a computation : display field or method in key column and the + // value in value + String txt = super.getText(element); + if (displayKey) { + if (txt.contains("#")) + return INJECTED_IN_METHOD; + else if (txt.contains("@")) + return UPDATED_IN_CLASS; + else + return INJECTED_IN_FIELD; + } else + return txt; + } + + return displayKey ? super.getText(element) : 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); + if ((s != null) && s.startsWith(NO_VALUE_COULD_BE_COMPUTED)) + return COLOR_IF_NOT_COMPUTED; + + // Return blue color if the string matches the search + return (contextFilter.matchText(s)) ? COLOR_IF_FOUND : null; + } + + /** Get the bold font for keys that are computed with ContextFunction */ + @Override + public Font getFont(Object element) { + return (element == LOCAL_VALUE_NODE || element == INHERITED_INJECTED_VALUE_NODE) ? boldFont : null; + + } + + @Override + public Image getImage(Object element) { + if (!displayKey) // No image in value column, only in key column + return null; + + if (element == LOCAL_VALUE_NODE) { + return selectedContext == null ? null : imgReg.get(LOCAL_VARIABLE_IMG_KEY); + + } else if (element == INHERITED_INJECTED_VALUE_NODE) { + return selectedContext == null ? null : imgReg.get(INHERITED_VARIABLE_IMG_KEY); + + } else if (element instanceof Computation) { + // For a computation : display field, method or class in key column + // and + // value in value column + String txt = super.getText(element); + + if (txt.contains("#")) + return imgReg.get(PUBLIC_METHOD_IMG_KEY); + else if (txt.contains("@")) + return imgReg.get(CONTEXT_FUNCTION_IMG_KEY); + else + return imgReg.get(PUBLIC_FIELD_IMG_KEY); + + } else if (element instanceof Map.Entry) { + if (isAContextKeyFunction(element)) + return imgReg.get(CONTEXT_FUNCTION_IMG_KEY); + else { + // It is a value. If it is injected somewhere, display the + // inject image + return hasChildren(element) ? imgReg.get(INJECT_IMG_KEY) : imgReg.get(VALUE_IN_CONTEXT_IMG_KEY); + } + + } + + return imgReg.get(INJECT_IMG_KEY); + + } + + @Override + public String getToolTipText(Object element) { + if (element == LOCAL_VALUE_NODE) { + return "This part contains values set in this context and then injected here or in children\n\n" + + "If the value is injected using this context, you can expand the node to see where\n\n" + + "If the value is injected using a child context you can find it in the second part for this child "; + } else if (element == INHERITED_INJECTED_VALUE_NODE) { + return "This part contains the values injected or updated using this context, but initialized in a parent context\n\n" + + "Expand nodes to see where values are injected or updated"; + } else if (isAContextKeyFunction(element)) { + String key = (String) ((Map.Entry<?, ?>) element).getKey(); + String fname = selectedContext.localContextFunction().get(key).getClass().getCanonicalName(); + + return "This value is created by the Context Function : " + fname; + } else { + if (hasChildren(element)) + return "Expand this node to see where this value is injected or updated"; + else { + if (element instanceof Map.Entry) + return "This value is set here but not injected using this context (look in children context)"; + } + + } + + return super.getToolTipText(element); + } + + @Override + public Image getToolTipImage(Object object) { + return getImage(object); + } + + @Override + public int getToolTipStyle(Object object) { + return SWT.SHADOW_OUT; + } + + /** + * Compute it the current entry in context is a context function + * + * @param element + * @return true if element is a context function + */ + @SuppressWarnings("unchecked") + boolean isAContextKeyFunction(Object element) { + if (selectedContext != null && element instanceof Map.Entry) { + // Just check if key in element is a key in the map of context + // functions. + Map.Entry<String, Object> mapEntry = (Map.Entry<String, Object>) element; + return (selectedContext.localContextFunction().containsKey(mapEntry.getKey())); + } + return false; + + } + + @Override + public Object getParent(Object element) { + if (element == LOCAL_VALUE_NODE || element == INHERITED_INJECTED_VALUE_NODE) + return null; + + // Not computed + return null; + + } + + @Override + public boolean hasChildren(Object element) { + if ((element == INHERITED_INJECTED_VALUE_NODE) || (element == LOCAL_VALUE_NODE)) { + return true; // Intermediate nodes returns true + } + + Set<Computation> listeners = getListeners(element); + return (listeners != null) && (listeners.size() > 0); + } + + @SuppressWarnings("unchecked") + Set<Computation> getListeners(Object element) { + if (selectedContext != null) { + if (element instanceof Map.Entry) { + // Ask the context to know if there are listeners for this value + Map.Entry<String, Object> mapEntry = (Map.Entry<String, Object>) element; + String key = mapEntry.getKey(); + return selectedContext.getListeners(key); + + } else if (element instanceof String) { + // Ask the context to know if there are listeners for this raw + // listener name + return selectedContext.getListeners((String) element); + } + } + return null; + + } + + private void initializeImageRegistry() { + Bundle b = FrameworkUtil.getBundle(this.getClass()); + imgReg = new ImageRegistry(); + + imgReg.put(CONTEXT_FUNCTION_IMG_KEY, ImageDescriptor.createFromURL(b.getEntry(CONTEXT_FUNCTION_IMG_KEY))); + imgReg.put(INJECT_IMG_KEY, ImageDescriptor.createFromURL(b.getEntry(INJECT_IMG_KEY))); + imgReg.put(PUBLIC_METHOD_IMG_KEY, ImageDescriptor.createFromURL(b.getEntry(PUBLIC_METHOD_IMG_KEY))); + imgReg.put(PUBLIC_FIELD_IMG_KEY, ImageDescriptor.createFromURL(b.getEntry(PUBLIC_FIELD_IMG_KEY))); + imgReg.put(PUBLIC_FIELD_IMG_KEY, ImageDescriptor.createFromURL(b.getEntry(PUBLIC_FIELD_IMG_KEY))); + imgReg.put(LOCAL_VARIABLE_IMG_KEY, ImageDescriptor.createFromURL(b.getEntry(LOCAL_VARIABLE_IMG_KEY))); + imgReg.put(VALUE_IN_CONTEXT_IMG_KEY, ImageDescriptor.createFromURL(b.getEntry(VALUE_IN_CONTEXT_IMG_KEY))); + imgReg.put(INHERITED_VARIABLE_IMG_KEY, ImageDescriptor.createFromURL(b.getEntry(INHERITED_VARIABLE_IMG_KEY))); + + } + + private void initFonts() { + FontData[] fontData = Display.getCurrent().getSystemFont().getFontData(); + String fontName = fontData[0].getName(); + FontRegistry registry = JFaceResources.getFontRegistry(); + boldFont = registry.getBold(fontName); + } + +} diff --git a/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/internal/spy/context/ContextSpyHelper.java b/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/internal/spy/context/ContextSpyHelper.java new file mode 100644 index 0000000000..32b4aed72d --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/internal/spy/context/ContextSpyHelper.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2013 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: + * OPCoach - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.spy.context; + +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import org.eclipse.e4.core.contexts.EclipseContextFactory; +import org.eclipse.e4.core.contexts.IEclipseContext; +import org.osgi.framework.BundleContext; + +/** + * A helper class to get information inside context management system. This + * class uses internal fields or methods defined in EclipseContext Could be + * updated in the future. + * + * @author olivier + * + */ +public class ContextSpyHelper { + + /** + * Get all the contexts created by EclipseContextFactory. It get values from + * field introspection. Should be rewritten if internal structure changes + * + * @return a collection of contexts created by EclipseContextFactory + */ + public static Collection<IEclipseContext> getAllBundleContexts() { + Collection<IEclipseContext> result = Collections.emptyList(); + try { + // Must use introspection to get the weak hash map (no getter). + Field f = EclipseContextFactory.class.getDeclaredField("serviceContexts"); + f.setAccessible(true); + @SuppressWarnings("unchecked") + Map<BundleContext, IEclipseContext> ctxs = (Map<BundleContext, IEclipseContext>) f.get(null); + result = ctxs.values(); + + } catch (SecurityException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + return result; + + } + +} diff --git a/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/internal/spy/context/ContextSpyProvider.java b/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/internal/spy/context/ContextSpyProvider.java new file mode 100644 index 0000000000..7440bdec68 --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/internal/spy/context/ContextSpyProvider.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2013 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: + * OPCoach - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.internal.spy.context; + +import java.util.Collection; + +import javax.inject.Inject; + +import org.eclipse.e4.core.contexts.IEclipseContext; +import org.eclipse.e4.core.internal.contexts.EclipseContext; +import org.eclipse.e4.ui.model.application.MApplication; +import org.eclipse.jface.viewers.IColorProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.widgets.Display; + +@SuppressWarnings("restriction") +/** + * This label and treecontent provider is used by ContextSpyPart to display + * available contexts. + * + * @author olivier + * + */ +public class ContextSpyProvider extends LabelProvider implements ITreeContentProvider, IColorProvider { + + @Inject + private ContextDataFilter contextFilter; + + @Inject + public ContextSpyProvider() { + + } + + @Override + public void dispose() { + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + @Override + public Object[] getElements(Object inputElement) { + if (inputElement instanceof MApplication) { + return new Object[] { ((MApplication) inputElement).getContext().getParent() }; + } else if (inputElement instanceof Collection<?>) { + return ((Collection<?>) inputElement).toArray(); + } + + return new Object[0]; + } + + @Override + public Object[] getChildren(Object parentElement) { + if (parentElement instanceof EclipseContext) { + return ((EclipseContext) parentElement).getChildren().toArray(); + } + return null; + } + + @Override + public Object getParent(Object element) { + if (element instanceof EclipseContext) { + return ((EclipseContext) element).getParent(); + } + return null; + } + + @Override + public boolean hasChildren(Object element) { + return ((element instanceof EclipseContext) && !(((EclipseContext) element).getChildren().isEmpty())); + } + + @Override + public String getText(Object element) { + return super.getText(element); + } + + @Override + public Color getForeground(Object element) { + // Return a color if a text contained in this node contains the text. + if (element instanceof IEclipseContext && contextFilter.containsText((IEclipseContext) element)) { + return Display.getCurrent().getSystemColor(SWT.COLOR_BLUE); + } + return null; + } + + @Override + public Color getBackground(Object element) { + return null; + } + +} diff --git a/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/spy/context/ContextSpyPart.java b/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/spy/context/ContextSpyPart.java new file mode 100644 index 0000000000..e07666ec1f --- /dev/null +++ b/ui/org.eclipse.pde.spy.context/src/org/eclipse/pde/spy/context/ContextSpyPart.java @@ -0,0 +1,224 @@ +/******************************************************************************* + * Copyright (c) 2013 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 + *******************************************************************************/ +package org.eclipse.pde.spy.context; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +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.e4.ui.model.application.MApplication; +import org.eclipse.e4.ui.workbench.modeling.ESelectionService; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.pde.internal.spy.context.ContextDataFilter; +import org.eclipse.pde.internal.spy.context.ContextDataPart; +import org.eclipse.pde.internal.spy.context.ContextSpyHelper; +import org.eclipse.pde.internal.spy.context.ContextSpyProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +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.Text; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; + +/** + * This class is the main part of the context spy. It creates a treeviewer and + * the context data part listening to context selection + */ +public class ContextSpyPart { + + private static final String ICON_COLLAPSEALL = "icons/collapseall.png"; + private static final String ICON_EXPANDALL = "icons/expandall.png"; + private static final String ICON_REFRESH = "icons/refresh.png"; + + // The ID for this part descriptor + static final String CONTEXT_SPY_VIEW_DESC = "org.eclipse.e4.tools.context.spy.view"; + + private TreeViewer contextTreeViewer; + + @Inject + private ESelectionService selService; + + private ContextSpyProvider treeContentProvider; + + private ImageRegistry imgReg; + + @Inject + private ContextDataFilter contextFilter; + + private ContextDataPart contextDataPart; + private Button showOnlyFilteredElements; + private Text filterText; + + /** Store the values to set it when it is reopened */ + private static String lastFilterText = null; + private static boolean lastShowFiltered = false; + + @Inject + private void initializeImageRegistry() { + Bundle b = FrameworkUtil.getBundle(this.getClass()); + imgReg = new ImageRegistry(); + imgReg.put(ICON_COLLAPSEALL, ImageDescriptor.createFromURL(b.getEntry(ICON_COLLAPSEALL))); + imgReg.put(ICON_EXPANDALL, ImageDescriptor.createFromURL(b.getEntry(ICON_EXPANDALL))); + imgReg.put(ICON_REFRESH, ImageDescriptor.createFromURL(b.getEntry(ICON_REFRESH))); + } + + /** + * Create contents of the view part. + */ + @PostConstruct + public void createControls(Composite parent, MApplication a, IEclipseContext ctx) { + parent.setLayout(new GridLayout(1, false)); + + final Composite comp = new Composite(parent, SWT.NONE); + comp.setLayout(new GridLayout(7, 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) { + contextTreeViewer.refresh(true); + contextDataPart.refresh(true); + } + }); + + Button expandAll = new Button(comp, SWT.FLAT); + expandAll.setImage(imgReg.get(ICON_EXPANDALL)); + expandAll.setToolTipText("Expand context nodes"); + expandAll.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + contextTreeViewer.expandAll(); + } + }); + Button collapseAll = new Button(comp, SWT.FLAT); + collapseAll.setImage(imgReg.get(ICON_COLLAPSEALL)); + collapseAll.setToolTipText("Collapse context nodes"); + collapseAll.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + contextTreeViewer.collapseAll(); + } + + }); + + 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 contexts where the contained objects contains this string pattern.\n" + + "Case is ignored."); + if (lastFilterText != null) + filterText.setText(lastFilterText); + contextFilter.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 + contextFilter.setPattern(textToSearch); + setFilter(); + contextTreeViewer.refresh(true); + contextDataPart.refresh(true); + } + + }); + + showOnlyFilteredElements = new Button(comp, SWT.CHECK); + showOnlyFilteredElements.setText("Show Only Filtered"); + showOnlyFilteredElements.setToolTipText("Show only the filtered items in the table view"); + showOnlyFilteredElements.setEnabled((lastFilterText != null) && (lastFilterText.length() > 0)); + showOnlyFilteredElements.setSelection(lastShowFiltered); + showOnlyFilteredElements.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + lastShowFiltered = showOnlyFilteredElements.getSelection(); + setFilter(); + } + }); + + SashForm sashForm = new SashForm(parent, SWT.VERTICAL | SWT.V_SCROLL | SWT.H_SCROLL); + sashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + // TreeViewer on the top + contextTreeViewer = new TreeViewer(sashForm); + treeContentProvider = ContextInjectionFactory.make(ContextSpyProvider.class, ctx); + contextTreeViewer.setContentProvider(treeContentProvider); + contextTreeViewer.setLabelProvider(treeContentProvider); + contextTreeViewer.setComparator(new ViewerComparator()); + + // tv.setInput(a); + contextTreeViewer.setInput(ContextSpyHelper.getAllBundleContexts()); + + contextTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() { + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection ss = (IStructuredSelection) event.getSelection(); + selService.setSelection((ss.size() == 1) ? ss.getFirstElement() : ss.toArray()); + } + }); + + IEclipseContext subCtx = ctx.createChild("Context for ContextDataPart"); + subCtx.set(Composite.class, sashForm); + contextDataPart = ContextInjectionFactory.make(ContextDataPart.class, subCtx); + setFilter(); + + // Set the correct weight for SashForm + sashForm.setWeights(new int[] { 35, 65 }); + + // Open all the tree + contextTreeViewer.expandAll(); + + } + + /** Set the filter on context data part */ + public void setFilter() { + if (showOnlyFilteredElements.isEnabled() && showOnlyFilteredElements.getSelection()) + contextDataPart.setFilter(contextFilter); + else + contextDataPart.setFilter(null); + } + + @PreDestroy + public void dispose() { + } + + @Focus + public void setFocus() { + contextTreeViewer.getControl().setFocus(); + } + +} |