Bug 419219 - JavaCorePreferenceModifyListener pollutes exported
preferences

Change-Id: Ib25dabed1befc25cab8bbc83045c8c507e228377
Signed-off-by: Szymon Ptaszkiewicz <szymon.ptaszkiewicz@pl.ibm.com>
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java
index e8cd64a..2048e88 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java
@@ -189,6 +189,7 @@
 		UtilTests.class,
 		
 		JavaCoreOptionsTests.class,
+		JavaCorePreferenceModifyListenerTest.class,
 		
 		// Tests regarding null-annotations:
 		NullAnnotationModelTests.class,
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaCorePreferenceModifyListenerTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaCorePreferenceModifyListenerTest.java
new file mode 100644
index 0000000..bc53a89
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaCorePreferenceModifyListenerTest.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2014 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.core.tests.model;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import junit.framework.Test;
+
+import org.eclipse.core.internal.preferences.EclipsePreferences;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.ConfigurationScope;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.IExportedPreferences;
+import org.eclipse.core.runtime.preferences.IPreferencesService;
+import org.eclipse.jdt.core.tests.junit.extension.TestCase;
+import org.osgi.service.prefs.BackingStoreException;
+
+public class JavaCorePreferenceModifyListenerTest extends TestCase {
+	private static final String NODE_NAME = "bug419219";
+	private static final String KEY = "someKey";
+	private static final String VALUE = "someValue";
+
+	public static Test suite() {
+		return new JavaCorePreferenceModifyListenerTest("testPreApply");
+	}
+
+	public JavaCorePreferenceModifyListenerTest(String name) {
+		super(name);
+	}
+
+	public void testPreApply() throws BackingStoreException, CoreException {
+		// create a dummy node and export it to a stream
+		IEclipsePreferences toExport = ConfigurationScope.INSTANCE.getNode(NODE_NAME);
+		toExport.put(KEY, VALUE);
+		IPreferencesService service = Platform.getPreferencesService();
+		ByteArrayOutputStream stream = new ByteArrayOutputStream();
+		assertTrue(service.exportPreferences(toExport, stream, null).isOK());
+
+		// read preferences from a stream
+		IExportedPreferences exported = service.readPreferences(new ByteArrayInputStream(stream.toByteArray()));
+		exported = (IExportedPreferences) exported.node(ConfigurationScope.SCOPE).node(NODE_NAME);
+
+		// apply exported preferences to the global preferences hierarchy
+		assertTrue(service.applyPreferences(exported).isOK());
+
+		// verify that the node is not modified
+		String debugString = ((EclipsePreferences) exported.node("/")).toDeepDebugString();
+		assertFalse(debugString, exported.nodeExists("instance/org.eclipse.jdt.core"));
+		assertFalse(debugString, exported.nodeExists("/instance/org.eclipse.jdt.core"));
+	}
+}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaCorePreferenceModifyListener.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaCorePreferenceModifyListener.java
index 5fb1012..ffbc0d3 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaCorePreferenceModifyListener.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaCorePreferenceModifyListener.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * Copyright (c) 2000, 2014 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
@@ -13,7 +13,7 @@
 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
 import org.eclipse.core.runtime.preferences.InstanceScope;
 import org.eclipse.core.runtime.preferences.PreferenceModifyListener;
-import org.eclipse.jdt.core.*;
+import org.eclipse.jdt.core.JavaCore;
 import org.osgi.service.prefs.BackingStoreException;
 import org.osgi.service.prefs.Preferences;
 
@@ -26,8 +26,20 @@
 	 * @see org.eclipse.core.runtime.preferences.PreferenceModifyListener#preApply(org.eclipse.core.runtime.preferences.IEclipsePreferences)
 	 */
 	public IEclipsePreferences preApply(IEclipsePreferences node) {
-		Preferences instance = node.node(InstanceScope.SCOPE);
-		cleanJavaCore(instance.node(JavaCore.PLUGIN_ID));
+		// the node does not need to be the root of the hierarchy
+		Preferences root = node.node("/"); //$NON-NLS-1$
+		try {
+			// we must not create empty preference nodes, so first check if the node exists
+			if (root.nodeExists(InstanceScope.SCOPE)) {
+				Preferences instance = root.node(InstanceScope.SCOPE);
+				// we must not create empty preference nodes, so first check if the node exists
+				if (instance.nodeExists(JavaCore.PLUGIN_ID)) {
+					cleanJavaCore(instance.node(JavaCore.PLUGIN_ID));
+				}
+			}
+		} catch (BackingStoreException e) {
+			// do nothing
+		}
 		return super.preApply(node);
 	}