Bug 544924 - Update plugin classpath when annotation provider changes
Whenever relevant changes happen in plugin registry, recompute the
classpath contributions of affected projects.
Change-Id: I034f43c6c39eb6676262c2a9484046fe48a23946
Signed-off-by: Peter Nehrer <pnehrer@eclipticalsoftware.com>
diff --git a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/Activator.java b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/Activator.java
index af3b20a..601462e 100644
--- a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/Activator.java
+++ b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/Activator.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2017 Ecliptical Software Inc. and others.
+ * Copyright (c) 2012, 2019 Ecliptical Software Inc. and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -61,6 +61,7 @@
@Override
public void stop(BundleContext context) throws Exception {
+ DSLibPluginModelListener.dispose();
dsPrefListener.dispose();
synchronized (projectPrefListeners) {
diff --git a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationClasspathContributor.java b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationClasspathContributor.java
index fa153c6..a49e9c5 100644
--- a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationClasspathContributor.java
+++ b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationClasspathContributor.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2015, 2017 Ecliptical Software Inc. and others.
+ * Copyright (c) 2015, 2019 Ecliptical Software Inc. and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -48,44 +48,47 @@
IPluginModelBase model = PluginRegistry.findModel(project);
if (model != null) {
IResource resource = model.getUnderlyingResource();
- if (resource != null && !WorkspaceModelManager.isBinaryProject(resource.getProject())) {
- IPreferencesService prefs = Platform.getPreferencesService();
- IScopeContext[] scope = new IScopeContext[] { new ProjectScope(resource.getProject()),
- InstanceScope.INSTANCE, DefaultScope.INSTANCE };
- boolean enabled = prefs.getBoolean(Activator.PLUGIN_ID, Activator.PREF_ENABLED, false, scope);
- if (enabled) {
- boolean autoClasspath = prefs.getBoolean(Activator.PLUGIN_ID, Activator.PREF_CLASSPATH, true,
- scope);
- if (autoClasspath) {
- DSAnnotationVersion specVersion;
- try {
- specVersion = DSAnnotationVersion.valueOf(prefs.getString(Activator.PLUGIN_ID,
- Activator.PREF_SPEC_VERSION, DSAnnotationVersion.V1_3.name(), scope));
- } catch (IllegalArgumentException e) {
- specVersion = DSAnnotationVersion.V1_3;
- }
+ if (resource == null || WorkspaceModelManager.isBinaryProject(resource.getProject())) {
+ return Collections.emptyList();
+ }
- String libBundleName;
- if (specVersion == DSAnnotationVersion.V1_3) {
- libBundleName = "org.eclipse.pde.ds.lib"; //$NON-NLS-1$
- } else {
- libBundleName = "org.eclipse.pde.ds1_2.lib"; //$NON-NLS-1$
- }
+ IPreferencesService prefs = Platform.getPreferencesService();
+ IScopeContext[] scope = new IScopeContext[] { new ProjectScope(resource.getProject()),
+ InstanceScope.INSTANCE, DefaultScope.INSTANCE };
+ boolean enabled = prefs.getBoolean(Activator.PLUGIN_ID, Activator.PREF_ENABLED, false, scope);
+ if (enabled) {
+ boolean autoClasspath = prefs.getBoolean(Activator.PLUGIN_ID, Activator.PREF_CLASSPATH, true, scope);
+ if (autoClasspath) {
+ DSAnnotationVersion specVersion;
+ try {
+ specVersion = DSAnnotationVersion.valueOf(prefs.getString(Activator.PLUGIN_ID,
+ Activator.PREF_SPEC_VERSION, DSAnnotationVersion.V1_3.name(), scope));
+ } catch (IllegalArgumentException e) {
+ specVersion = DSAnnotationVersion.V1_3;
+ }
- IPluginModelBase bundle = PluginRegistry.findModel(libBundleName);
- if (bundle != null && bundle.isEnabled()) {
- String location = bundle.getInstallLocation();
- if (location != null) {
- IPath srcPath = getSrcPath(libBundleName);
- IClasspathEntry entry = JavaCore.newLibraryEntry(
- new Path(location + "/annotations.jar"), srcPath, Path.ROOT, //$NON-NLS-1$
- ANNOTATION_ACCESS_RULES, DS_ATTRS, false);
- return Collections.singletonList(entry);
- }
+ String libBundleName;
+ if (specVersion == DSAnnotationVersion.V1_3) {
+ libBundleName = "org.eclipse.pde.ds.lib"; //$NON-NLS-1$
+ } else {
+ libBundleName = "org.eclipse.pde.ds1_2.lib"; //$NON-NLS-1$
+ }
+
+ IPluginModelBase bundle = PluginRegistry.findModel(libBundleName);
+ if (bundle != null && bundle.isEnabled()) {
+ String location = bundle.getInstallLocation();
+ if (location != null) {
+ IPath srcPath = getSrcPath(libBundleName);
+ IClasspathEntry entry = JavaCore.newLibraryEntry(new Path(location + "/annotations.jar"), //$NON-NLS-1$
+ srcPath, Path.ROOT, ANNOTATION_ACCESS_RULES, DS_ATTRS, false);
+ DSLibPluginModelListener.addProject(JavaCore.create(resource.getProject()), libBundleName);
+ return Collections.singletonList(entry);
}
}
}
}
+
+ DSLibPluginModelListener.removeProject(JavaCore.create(resource.getProject()));
}
return Collections.emptyList();
diff --git a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationPropertyPage.java b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationPropertyPage.java
index 1080fe1..1134dc3 100644
--- a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationPropertyPage.java
+++ b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSAnnotationPropertyPage.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2017 Ecliptical Software Inc. and others.
+ * Copyright (c) 2012, 2019 Ecliptical Software Inc. and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -428,12 +428,13 @@
public boolean performOk() {
IEclipsePreferences prefs;
if (isProjectPreferencePage()) {
- prefs = wcManager.getWorkingCopy(new ProjectScope(getProject()).getNode(Activator.PLUGIN_ID));
- if (!useProjectSettings()) {
+ IProject project = getProject();
+ prefs = wcManager.getWorkingCopy(new ProjectScope(project).getNode(Activator.PLUGIN_ID));
+ if (useProjectSettings()) {
+ Activator.getDefault().listenForClasspathPreferenceChanges(JavaCore.create(project));
+ } else {
try {
- for (String key : prefs.keys()) {
- prefs.remove(key);
- }
+ prefs.clear();
} catch (BackingStoreException e) {
Activator.log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unable to reset project preferences.", e)); //$NON-NLS-1$
}
@@ -476,11 +477,6 @@
return false;
}
- IProject project = getProject();
- if (project != null) {
- Activator.getDefault().listenForClasspathPreferenceChanges(JavaCore.create(project));
- }
-
return true;
}
}
\ No newline at end of file
diff --git a/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSLibPluginModelListener.java b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSLibPluginModelListener.java
new file mode 100644
index 0000000..a383061
--- /dev/null
+++ b/ds/org.eclipse.pde.ds.annotations/src/org/eclipse/pde/ds/internal/annotations/DSLibPluginModelListener.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Ecliptical Software Inc. 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:
+ * Ecliptical Software Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.pde.ds.internal.annotations;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.eclipse.core.resources.WorkspaceJob;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.MultiRule;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.pde.core.plugin.ModelEntry;
+import org.eclipse.pde.internal.core.IPluginModelListener;
+import org.eclipse.pde.internal.core.PluginModelDelta;
+import org.eclipse.pde.internal.core.PluginModelManager;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+
+@SuppressWarnings("restriction")
+public class DSLibPluginModelListener implements IPluginModelListener {
+
+ private static DSLibPluginModelListener INSTANCE;
+
+ private final HashMap<IJavaProject, String> projects = new HashMap<>();
+
+ private final HashMap<String, Integer> counts = new HashMap<>();
+
+ private DSLibPluginModelListener() {
+ PluginModelManager.getInstance().addPluginModelListener(this);
+ }
+
+ private synchronized static DSLibPluginModelListener getInstance(boolean create) {
+ if (create && INSTANCE == null) {
+ INSTANCE = new DSLibPluginModelListener();
+ }
+
+ return INSTANCE;
+ }
+
+ private void decrementCount(String modelId) {
+ Integer oldCount = counts.get(modelId);
+ if (oldCount != null) {
+ if (oldCount.intValue() <= 1) {
+ counts.remove(modelId);
+ } else {
+ counts.put(modelId, oldCount.intValue() - 1);
+ }
+ }
+ }
+
+ public static void addProject(IJavaProject project, String modelId) {
+ DSLibPluginModelListener instance = getInstance(true);
+ synchronized (instance.projects) {
+ String oldModelId = instance.projects.put(project, modelId);
+ Integer count = instance.counts.getOrDefault(modelId, Integer.valueOf(0));
+ instance.counts.put(modelId, count.intValue() + 1);
+ if (oldModelId != null) {
+ instance.decrementCount(oldModelId);
+ }
+ }
+ }
+
+ public static void removeProject(IJavaProject project) {
+ DSLibPluginModelListener instance = getInstance(false);
+ if (instance != null) {
+ synchronized (instance.projects) {
+ String oldModelId = instance.projects.remove(project);
+ if (oldModelId != null) {
+ instance.decrementCount(oldModelId);
+ }
+ }
+ }
+ }
+
+ private boolean containsModel(ModelEntry[] entries, String id) {
+ for (ModelEntry entry : entries) {
+ if ("org.eclipse.pde.ds.lib".equals(entry.getId())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public void modelsChanged(PluginModelDelta delta) {
+ synchronized (projects) {
+ HashSet<String> modelIds = new HashSet<>(2);
+ for (String modelId : counts.keySet()) {
+ if (((delta.getKind() & PluginModelDelta.ADDED) != 0 && containsModel(delta.getAddedEntries(), modelId))
+ || ((delta.getKind() & PluginModelDelta.CHANGED) != 0
+ && containsModel(delta.getChangedEntries(), modelId))
+ || ((delta.getKind() & PluginModelDelta.REMOVED) != 0
+ && containsModel(delta.getRemovedEntries(), modelId))) {
+ modelIds.add(modelId);
+ }
+ }
+
+ ArrayList<IJavaProject> toUpdate = new ArrayList<>(projects.size());
+ if (!modelIds.isEmpty()) {
+ for (Map.Entry<IJavaProject, String> entry : projects.entrySet()) {
+ IJavaProject project = entry.getKey();
+ String modelId = entry.getValue();
+ if (modelIds.contains(modelId)) {
+ toUpdate.add(project);
+ }
+ }
+ }
+
+ if (!toUpdate.isEmpty()) {
+ requestClasspathUpdate(toUpdate);
+ }
+ }
+ }
+
+ private void requestClasspathUpdate(final Collection<IJavaProject> changedProjects) {
+ WorkspaceJob job = new WorkspaceJob(Messages.ProjectClasspathPreferenceChangeListener_jobName) {
+ @Override
+ public IStatus runInWorkspace(IProgressMonitor monitor) {
+ SubMonitor progress = SubMonitor.convert(monitor, Messages.DSAnnotationPreferenceListener_taskName,
+ changedProjects.size());
+ for (IJavaProject project : changedProjects) {
+ ProjectClasspathPreferenceChangeListener.updateClasspathContainer(project, progress.newChild(1));
+ }
+
+ return Status.OK_STATUS;
+ };
+ };
+
+ job.setSystem(true);
+
+ ISchedulingRule[] rules = changedProjects.stream().map(IJavaProject::getProject)
+ .toArray(size -> new ISchedulingRule[size]);
+ job.setRule(new MultiRule(rules));
+
+ Display display = Display.getCurrent();
+ if (display != null) {
+ PlatformUI.getWorkbench().getProgressService().showInDialog(display.getActiveShell(), job);
+ }
+
+ job.schedule();
+ }
+
+ public static void dispose() {
+ DSLibPluginModelListener instance = getInstance(false);
+ if (instance != null) {
+ PluginModelManager.getInstance().removePluginModelListener(instance);
+ }
+ }
+}