Bug 489540 - Error marker for unknown nature referenced by project
Change-Id: I2aaac4bf01dd3099ec926faebd88b854c6b58476
Signed-off-by: Mickael Istria <mistria@redhat.com>
diff --git a/bundles/org.eclipse.core.resources/plugin.properties b/bundles/org.eclipse.core.resources/plugin.properties
index 1c45ea3..1497e27 100644
--- a/bundles/org.eclipse.core.resources/plugin.properties
+++ b/bundles/org.eclipse.core.resources/plugin.properties
@@ -38,3 +38,4 @@
regexFilterProvider.name = Regular Expression
trace.component.label = Platform Core Resources
+unknownNatureMarkerName=Unknown nature
diff --git a/bundles/org.eclipse.core.resources/plugin.xml b/bundles/org.eclipse.core.resources/plugin.xml
index 7f8663c..bdf6dfc 100644
--- a/bundles/org.eclipse.core.resources/plugin.xml
+++ b/bundles/org.eclipse.core.resources/plugin.xml
@@ -257,4 +257,18 @@
</bundle>
</component>
</extension>
+ <extension
+ id="unknownNature"
+ name="%unknownNatureMarkerName"
+ point="org.eclipse.core.resources.markers">
+ <super
+ type="org.eclipse.core.resources.problemmarker">
+ </super>
+ <persistent
+ value="true">
+ </persistent>
+ <attribute
+ name="natureId">
+ </attribute>
+ </extension>
</plugin>
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/CheckMissingNaturesListener.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/CheckMissingNaturesListener.java
new file mode 100644
index 0000000..4e19902
--- /dev/null
+++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/CheckMissingNaturesListener.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Mickael Istria (Red Hat Inc.) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.osgi.util.NLS;
+
+public class CheckMissingNaturesListener implements IResourceChangeListener {
+
+ public static final String MARKER_TYPE = ResourcesPlugin.getPlugin().getBundle().getSymbolicName() + ".unknownNature"; //$NON-NLS-1$
+ public static final String NATURE_ID_ATTRIBUTE = "natureId"; //$NON-NLS-1$
+
+ @Override
+ public void resourceChanged(IResourceChangeEvent event) {
+ if (event.getDelta() == null) {
+ return;
+ }
+ try {
+ event.getDelta().accept(new IResourceDeltaVisitor() {
+ @Override
+ public boolean visit(IResourceDelta delta) throws CoreException {
+ if (delta.getResource() != null && delta.getResource().getType() == IResource.PROJECT && (delta.getKind() == IResourceDelta.ADDED || delta.getKind() == IResourceDelta.CHANGED)) {
+ final IProject project = (IProject) delta.getResource();
+ if (!project.isAccessible()) {
+ return false;
+ }
+ int severity = getMissingNatureSeverity(project);
+ if (severity < 0) {
+ return false;
+ }
+
+ final Set<String> missingNatures = new HashSet<>();
+ for (String natureId : project.getDescription().getNatureIds()) {
+ if (project.getWorkspace().getNatureDescriptor(natureId) == null) {
+ missingNatures.add(natureId);
+ }
+ }
+ final Set<IMarker> toRemove = new HashSet<>();
+ for (IMarker existingMarker : project.findMarkers(MARKER_TYPE, true, IResource.DEPTH_ZERO)) {
+ String markerNature = existingMarker.getAttribute(NATURE_ID_ATTRIBUTE, ""); //$NON-NLS-1$
+ if (!missingNatures.contains(markerNature)) {
+ toRemove.add(existingMarker);
+ } else {
+ // no need to create a new marker
+ missingNatures.remove(markerNature);
+ }
+ }
+ if (!toRemove.isEmpty() || !missingNatures.isEmpty()) {
+ WorkspaceJob workspaceJob = new WorkspaceJob(NLS.bind(Messages.addingMissingNatureMarkersOnProject, project.getName())) {
+ @Override
+ public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
+ for (IMarker marker : toRemove) {
+ marker.delete();
+ }
+ for (String natureId : missingNatures) {
+ IMarker marker = project.createMarker(MARKER_TYPE);
+ marker.setAttribute(IMarker.SEVERITY, severity);
+ marker.setAttribute(IMarker.MESSAGE, NLS.bind(Messages.natures_missingNature, natureId));
+ marker.setAttribute(NATURE_ID_ATTRIBUTE, natureId);
+ }
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public boolean belongsTo(Object family) {
+ return super.belongsTo(family) || MARKER_TYPE.equals(family);
+ }
+ };
+ workspaceJob.setUser(false);
+ workspaceJob.setSystem(true);
+ workspaceJob.setPriority(Job.DECORATE);
+ workspaceJob.schedule();
+ }
+ }
+ return delta.getResource() == null || delta.getResource().getType() == IResource.ROOT;
+ }
+
+ private int getMissingNatureSeverity(final IProject project) {
+ int severity = PreferenceInitializer.PREF_MISSING_NATURE_MARKER_SEVERITY_DEFAULT;
+ IEclipsePreferences node = InstanceScope.INSTANCE.getNode(ResourcesPlugin.PI_RESOURCES);
+ if (node != null) {
+ severity = node.getInt(ResourcesPlugin.PREF_MISSING_NATURE_MARKER_SEVERITY, PreferenceInitializer.PREF_MISSING_NATURE_MARKER_SEVERITY_DEFAULT);
+ }
+ return severity;
+ }
+ });
+ } catch (CoreException e) {
+ ResourcesPlugin.getPlugin().getLog().log(new Status(IStatus.ERROR, ResourcesPlugin.getPlugin().getBundle().getSymbolicName(), e.getMessage(), e));
+ }
+
+ }
+
+}
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/PreferenceInitializer.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/PreferenceInitializer.java
index 3ae12e1..8cd0510 100644
--- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/PreferenceInitializer.java
+++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/PreferenceInitializer.java
@@ -21,7 +21,7 @@
// internal preference keys
public static final String PREF_OPERATIONS_PER_SNAPSHOT = "snapshots.operations"; //$NON-NLS-1$
- public static final String PREF_DELTA_EXPIRATION = "delta.expiration"; //$NON-NLS-1$
+ public static final String PREF_DELTA_EXPIRATION = "delta.expiration"; //$NON-NLS-1$
// DEFAULTS
public static final boolean PREF_AUTO_REFRESH_DEFAULT = false;
@@ -39,6 +39,13 @@
public static final long PREF_MAX_FILE_STATE_SIZE_DEFAULT = 1024 * 1024l; // 1 MB
public static final int PREF_MAX_FILE_STATES_DEFAULT = 50;
public static final long PREF_DELTA_EXPIRATION_DEFAULT = 30 * 24 * 3600 * 1000l; // 30 days
+ /**
+ * Default setting for {@value ResourcesPlugin#PREF_MISSING_NATURE_MARKER_SEVERITY}.
+ * Currently -1/ignore, but very likely to change.
+ *
+ * @since 3.12
+ */
+ public static final int PREF_MISSING_NATURE_MARKER_SEVERITY_DEFAULT = -1;
public PreferenceInitializer() {
super();
@@ -59,6 +66,7 @@
node.put(ResourcesPlugin.PREF_BUILD_ORDER, PREF_BUILD_ORDER_DEFAULT);
node.putInt(ResourcesPlugin.PREF_MAX_BUILD_ITERATIONS, PREF_MAX_BUILD_ITERATIONS_DEFAULT);
node.putBoolean(ResourcesPlugin.PREF_DEFAULT_BUILD_ORDER, PREF_DEFAULT_BUILD_ORDER_DEFAULT);
+ node.putInt(ResourcesPlugin.PREF_MISSING_NATURE_MARKER_SEVERITY, PREF_MISSING_NATURE_MARKER_SEVERITY_DEFAULT);
// history store defaults
node.putBoolean(ResourcesPlugin.PREF_APPLY_FILE_STATE_POLICY, PREF_APPLY_FILE_STATE_POLICY_DEFAULT);
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/Messages.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/Messages.java
index 34e8a2b..735b53b 100644
--- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/Messages.java
+++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/Messages.java
@@ -326,6 +326,9 @@
public static String WM_nativeErr;
public static String WM_mutexAbandoned;
+ public static String cleanMarkersOnProject;
+ public static String addingMissingNatureMarkersOnProject;
+
static {
// initialize resource bundles
NLS.initializeMessages(BUNDLE_NAME, Messages.class);
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/messages.properties b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/messages.properties
index eadd09c..94e9d75 100644
--- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/messages.properties
+++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/utils/messages.properties
@@ -118,6 +118,9 @@
preferences_saveProblems=Exception occurred while saving project preferences: {0}.
preferences_syncException=Exception occurred while synchronizing node: {0}.
+cleanMarkersOnProject=Clear natures markers on {0}
+addingMissingNatureMarkersOnProject=Add missing natures markers on {0}
+
projRead_badLinkName = Names ''{0}'' and ''{1}'' detected for a single link. Using ''{0}''.
projRead_badLinkType2 = Types ''{0}'' and ''{1}'' detected for a single link. Using ''{0}''.
projRead_badLocation = Locations ''{0}'' and ''{1}'' detected for a single link. Using ''{0}''.
diff --git a/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/ResourcesPlugin.java b/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/ResourcesPlugin.java
index 59d44a9..a186eef 100644
--- a/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/ResourcesPlugin.java
+++ b/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/ResourcesPlugin.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -327,6 +327,14 @@
public static final boolean DEFAULT_PREF_SEPARATE_DERIVED_ENCODINGS = false;
/**
+ * Name of a preference for configuring the marker severity in case project
+ * description references an unknown nature.
+ *
+ * @since 3.12
+ */
+ public static final String PREF_MISSING_NATURE_MARKER_SEVERITY = "missingNatureMarkerSeverity"; //$NON-NLS-1$
+
+ /**
* The single instance of this plug-in runtime class.
*/
private static ResourcesPlugin plugin;
@@ -340,6 +348,8 @@
private ServiceRegistration<IWorkspace> workspaceRegistration;
private ServiceRegistration<DebugOptionsListener> debugRegistration;
+ private CheckMissingNaturesListener checkMissingNaturesListener;
+
/**
* Constructs an instance of this plug-in runtime class.
* <p>
@@ -429,6 +439,7 @@
if (workspace == null) {
return;
}
+ workspace.removeResourceChangeListener(checkMissingNaturesListener);
if (workspaceRegistration != null) {
workspaceRegistration.unregister();
}
@@ -467,6 +478,8 @@
IStatus result = workspace.open(null);
if (!result.isOK())
getLog().log(result);
+ checkMissingNaturesListener = new CheckMissingNaturesListener();
+ workspace.addResourceChangeListener(checkMissingNaturesListener, IResourceChangeEvent.POST_CHANGE);
workspaceRegistration = context.registerService(IWorkspace.class, workspace, null);
}
diff --git a/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/NatureTest.java b/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/NatureTest.java
index a2b4c4e..36c5fb5 100644
--- a/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/NatureTest.java
+++ b/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/NatureTest.java
@@ -22,7 +22,9 @@
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.core.tests.internal.resources.SimpleNature;
+import org.junit.Assert;
/**
* Tests all aspects of project natures. These tests only
@@ -30,6 +32,8 @@
* APIs on IWorkspace are tested by IWorkspaceTest.
*/
public class NatureTest extends ResourceTest {
+ private static final String MISSING_NATURE_MARKER_ID = "org.eclipse.core.resources.unknownNature";
+
/**
* Constructor for NatureTest.
*/
@@ -84,11 +88,20 @@
@Override
protected void tearDown() throws Exception {
+ InstanceScope.INSTANCE.getNode(ResourcesPlugin.PI_RESOURCES).remove(ResourcesPlugin.PREF_MISSING_NATURE_MARKER_SEVERITY);
+ InstanceScope.INSTANCE.getNode(ResourcesPlugin.PI_RESOURCES).flush();
super.tearDown();
getWorkspace().getRoot().refreshLocal(IResource.DEPTH_INFINITE, null);
ensureDoesNotExistInWorkspace(getWorkspace().getRoot());
}
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ InstanceScope.INSTANCE.getNode(ResourcesPlugin.PI_RESOURCES).putInt(ResourcesPlugin.PREF_MISSING_NATURE_MARKER_SEVERITY, IMarker.SEVERITY_WARNING);
+ InstanceScope.INSTANCE.getNode(ResourcesPlugin.PI_RESOURCES).flush();
+ }
+
/**
* Tests invalid additions to the set of natures for a project.
*/
@@ -421,4 +434,33 @@
assertTrue("4.0", project.hasNature(NATURE_SIMPLE));
assertTrue("5.0", project.isNatureEnabled(NATURE_SIMPLE));
}
+
+ public void testMissingNatureAddsMarker() throws Exception {
+ IWorkspace ws = ResourcesPlugin.getWorkspace();
+ IProject project = ws.getRoot().getProject(getUniqueString());
+ ensureExistsInWorkspace(project, true);
+ IProjectDescription desc = project.getDescription();
+ desc.setNatureIds(new String[] {NATURE_MISSING});
+ project.setDescription(desc, IResource.FORCE | IResource.AVOID_NATURE_CONFIG, getMonitor());
+ project.refreshLocal(IResource.DEPTH_INFINITE, getMonitor());
+ project.build(IncrementalProjectBuilder.FULL_BUILD, getMonitor());
+ Job.getJobManager().join(MISSING_NATURE_MARKER_ID, getMonitor());
+ IMarker[] markers = project.findMarkers(MISSING_NATURE_MARKER_ID, false, IResource.DEPTH_ZERO);
+ Assert.assertEquals(1, markers.length);
+ IMarker marker = markers[0];
+ Assert.assertEquals(NATURE_MISSING, marker.getAttribute("natureId"));
+ }
+
+ public void testKnownNatureDoesntAddMarker() throws Exception {
+ IWorkspace ws = ResourcesPlugin.getWorkspace();
+ IProject project = ws.getRoot().getProject(getUniqueString());
+ ensureExistsInWorkspace(project, true);
+ IProjectDescription desc = project.getDescription();
+ desc.setNatureIds(new String[] {NATURE_SIMPLE});
+ project.setDescription(desc, getMonitor());
+ project.refreshLocal(IResource.DEPTH_INFINITE, getMonitor());
+ project.build(IncrementalProjectBuilder.FULL_BUILD, getMonitor());
+ Job.getJobManager().join(MISSING_NATURE_MARKER_ID, getMonitor());
+ Assert.assertEquals(0, project.findMarkers(MISSING_NATURE_MARKER_ID, false, IResource.DEPTH_ZERO).length);
+ }
}