Bug 266962: JUnit to consume hamcrest.core from orbit.
diff --git a/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/JUnitPreferencesConstants.java b/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/JUnitPreferencesConstants.java
index d31c097..4af67e5 100644
--- a/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/JUnitPreferencesConstants.java
+++ b/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/JUnitPreferencesConstants.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * Copyright (c) 2000, 2009 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
@@ -64,6 +64,11 @@
 	 */
 	public static final String JUNIT4_JAVADOC= JUnitPlugin.PLUGIN_ID + ".junit4.javadoclocation"; //$NON-NLS-1$
 
+	/**
+	 * Javadoc location for org.hamcrest.core (JUnit 4)
+	 */
+	public static final String HAMCREST_CORE_JAVADOC= JUnitPlugin.PLUGIN_ID + ".junit4.hamcrest.core.javadoclocation"; //$NON-NLS-1$
+	
 
 	private static final String[] fgDefaultFilterPatterns= new String[] {
 		"org.eclipse.jdt.internal.junit.runner.*", //$NON-NLS-1$
diff --git a/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/BuildPathSupport.java b/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/BuildPathSupport.java
index c399e32..eb35522 100644
--- a/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/BuildPathSupport.java
+++ b/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/BuildPathSupport.java
@@ -11,12 +11,20 @@
 package org.eclipse.jdt.internal.junit.buildpath;
 
 
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
 import org.osgi.framework.Version;
 
 import org.eclipse.equinox.internal.provisional.frameworkadmin.BundleInfo;
 import org.eclipse.osgi.service.resolver.VersionRange;
 
+import org.eclipse.core.runtime.FileLocator;
 import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.URIUtil;
 
 import org.eclipse.jdt.core.IAccessRule;
 import org.eclipse.jdt.core.IClasspathAttribute;
@@ -33,68 +41,139 @@
 	public static class JUnitPluginDescription {
 		
 		private final String bundleId;
-		private final String sourceBundleId;
 		private final VersionRange versionRange;
+		private final String sourceBundleId;
+		private final String bundleRoot;
+		private final String repositorySource;
+		private final String javadocPreferenceKey;
 
-		public JUnitPluginDescription(String bundleId, String sourceBundleId, VersionRange versionRange) {
+		public JUnitPluginDescription(String bundleId, VersionRange versionRange, String bundleRoot, String sourceBundleId, String repositorySource, String javadocPreferenceKey) {
 			this.bundleId= bundleId;
-			this.sourceBundleId= sourceBundleId;
 			this.versionRange= versionRange;
+			this.sourceBundleId= sourceBundleId;
+			this.bundleRoot= bundleRoot;
+			this.repositorySource= repositorySource;
+			this.javadocPreferenceKey= javadocPreferenceKey;
+		}
+		
+		public IPath getBundleLocation() {
+			return P2Utils.getBundleLocationPath(P2Utils.findBundle(bundleId, versionRange, false));
+		}
+		
+		public IPath getSourceBundleLocation() {
+			return getSourceLocation(P2Utils.findBundle(bundleId, versionRange, false));
+		}
+		
+		//XXX: Official API has been requested for the provisional p2 APIs: https://bugs.eclipse.org/bugs/show_bug.cgi?id=269496
+		
+		public IClasspathEntry getLibraryEntry() {
+			BundleInfo bundleInfo= P2Utils.findBundle(bundleId, versionRange, false);
+			IPath bundleLocation= P2Utils.getBundleLocationPath(bundleInfo);
+			if (bundleLocation != null) {
+				
+				IPath bundleRootLocation= bundleLocation;
+				if (bundleRoot != null)
+					bundleRootLocation= bundleLocation.append(bundleRoot);
+				
+				IPath srcLocation= getSourceLocation(bundleInfo);
+				
+				IAccessRule[] accessRules= { };
+
+				String javadocLocation= JUnitPlugin.getDefault().getPreferenceStore().getString(javadocPreferenceKey);
+				IClasspathAttribute[] attributes;
+				if (javadocLocation.length() == 0) {
+					attributes= new IClasspathAttribute[0];
+				} else {
+					attributes= new IClasspathAttribute[] { JavaCore.newClasspathAttribute(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, javadocLocation) };
+				}
+				
+				return JavaCore.newLibraryEntry(bundleRootLocation, srcLocation, null, accessRules, attributes, false);
+			}
+			return null;
+		}
+
+		private IPath getSourceLocation(BundleInfo bundleInfo) {
+			if (bundleInfo == null)
+				return null;
+			
+			IPath srcLocation= null;
+			if (repositorySource != null) {
+				// Try source in workspace (from repository)
+				try {
+					URL bundleUrl= FileLocator.toFileURL(URIUtil.toURL(bundleInfo.getLocation()));
+					File bundleFile= new File(bundleUrl.getFile());
+					if (bundleFile.isDirectory()) {
+						File srcFile= new File(bundleFile, repositorySource);
+						if (srcFile.exists()) {
+							srcLocation= new Path(srcFile.getPath());
+							if (srcFile.isDirectory()) {
+								srcLocation= srcLocation.addTrailingSeparator();
+							}
+						}
+					}
+				} catch (MalformedURLException e) {
+					//continue
+				} catch (IOException e) {
+					//continue
+				}
+			}
+			
+			if (srcLocation == null) {
+				// Try exact version
+				BundleInfo sourceBundleInfo= P2Utils.findBundle(sourceBundleId, new Version(bundleInfo.getVersion()), true);
+				if (sourceBundleInfo == null) {
+					// Try version range
+					sourceBundleInfo= P2Utils.findBundle(sourceBundleId, versionRange, true);
+				}
+				srcLocation= P2Utils.getBundleLocationPath(sourceBundleInfo);
+			}
+			return srcLocation;
 		}
 	}
 
 	
-	public static final JUnitPluginDescription JUNIT3_PLUGIN= new JUnitPluginDescription("org.junit", "org.junit.source", new VersionRange("[3.8.2,3.9)")); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
-	public static final JUnitPluginDescription JUNIT4_PLUGIN= new JUnitPluginDescription("org.junit4", "org.junit4.source", new VersionRange("[4.5.0,5.0.0)")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+	public static final JUnitPluginDescription JUNIT3_PLUGIN= new JUnitPluginDescription(
+			"org.junit", new VersionRange("[3.8.2,3.9)"), "junit.jar", "org.junit.source", "source-bundle/", JUnitPreferencesConstants.JUNIT3_JAVADOC); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+	
+	private static final JUnitPluginDescription JUNIT4_PLUGIN= new JUnitPluginDescription(
+			"org.junit4", new VersionRange("[4.5.0,5.0.0)"), "junit.jar", "org.junit4.source", "junitsrc.zip", JUnitPreferencesConstants.JUNIT4_JAVADOC); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+	
+	private static final JUnitPluginDescription HAMCREST_CORE_PLUGIN= new JUnitPluginDescription(
+			"org.hamcrest.core", new VersionRange("[1.1.0,2.0.0)"), null, "org.hamcrest.core.source", "source-bundle/", JUnitPreferencesConstants.HAMCREST_CORE_JAVADOC); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
 
-	public static IPath getBundleLocation(JUnitPluginDescription pluginDesc) {
-		return P2Utils.getBundleLocationPath(P2Utils.findBundle(pluginDesc.bundleId, pluginDesc.versionRange, false));
-	}
-
-	public static IPath getSourceBundleLocation(JUnitPluginDescription pluginDesc) {
-		return P2Utils.getBundleLocationPath(P2Utils.findBundle(pluginDesc.sourceBundleId, pluginDesc.versionRange, true));
-	}
-
+	/**
+	 * @return the JUnit3 classpath container
+	 */
 	public static IClasspathEntry getJUnit3ClasspathEntry() {
 		return JavaCore.newContainerEntry(JUnitContainerInitializer.JUNIT3_PATH);
 	}
 
+	/**
+	 * @return the JUnit4 classpath container
+	 */
 	public static IClasspathEntry getJUnit4ClasspathEntry() {
 		return JavaCore.newContainerEntry(JUnitContainerInitializer.JUNIT4_PATH);
 	}
 
+	/**
+	 * @return the org.junit library
+	 */
 	public static IClasspathEntry getJUnit3LibraryEntry() {
-		return getJUnitLibraryEntry(JUNIT3_PLUGIN, JUnitPreferencesConstants.JUNIT3_JAVADOC);
+		return JUNIT3_PLUGIN.getLibraryEntry();
 	}
 
+	/**
+	 * @return the org.junit4 library
+	 */
 	public static IClasspathEntry getJUnit4LibraryEntry() {
-		return getJUnitLibraryEntry(JUNIT4_PLUGIN, JUnitPreferencesConstants.JUNIT4_JAVADOC);
+		return JUNIT4_PLUGIN.getLibraryEntry();
 	}
 
-	// Official API has been requested for the provisional p2 APIs: https://bugs.eclipse.org/bugs/show_bug.cgi?id=269496
-	private static IClasspathEntry getJUnitLibraryEntry(JUnitPluginDescription pluginDesc, String javadocPreferenceKey) {
-		BundleInfo bundleInfo= P2Utils.findBundle(pluginDesc.bundleId, pluginDesc.versionRange, false);
-		IPath bundleBase= P2Utils.getBundleLocationPath(bundleInfo);
-		if (bundleBase != null) {
-
-			// Try exact version
-			BundleInfo sourceBundleInfo= P2Utils.findBundle(pluginDesc.sourceBundleId, new Version(bundleInfo.getVersion()), true);
-			if (sourceBundleInfo == null) {
-				// Try exact version range
-				sourceBundleInfo= P2Utils.findBundle(pluginDesc.sourceBundleId, pluginDesc.versionRange, true);
-			}
-
-			IPath jarLocation= bundleBase.append("junit.jar"); //$NON-NLS-1$
-			IPath srcLocation= P2Utils.getBundleLocationPath(sourceBundleInfo);
-
-			IAccessRule[] accessRules= { };
-
-			String javadocLocation= JUnitPlugin.getDefault().getPreferenceStore().getString(javadocPreferenceKey);
-			IClasspathAttribute[] attributes= { JavaCore.newClasspathAttribute(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, javadocLocation) };
-
-			return JavaCore.newLibraryEntry(jarLocation, srcLocation, null, accessRules, attributes, false);
-		}
-		return null;
+	/**
+	 * @return the org.hamcrest.core library
+	 */
+	public static IClasspathEntry getHamcrestCoreLibraryEntry() {
+		return HAMCREST_CORE_PLUGIN.getLibraryEntry();
 	}
 
 }
diff --git a/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/JUnitContainerInitializer.java b/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/JUnitContainerInitializer.java
index 56daa86..670075f 100644
--- a/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/JUnitContainerInitializer.java
+++ b/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/JUnitContainerInitializer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2006, 2008 IBM Corporation and others.
+ * Copyright (c) 2006, 2009 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
@@ -95,18 +95,21 @@
 
 	private static JUnitContainer getNewContainer(IPath containerPath) {
 		IClasspathEntry entry= null;
+		IClasspathEntry entry2= null;
 		String version= containerPath.segment(1);
 		if (JUNIT3_8_1.equals(version) || JUNIT3.equals(version)) {
 			entry= BuildPathSupport.getJUnit3LibraryEntry();
 		} else if (JUNIT4.equals(version)) {
 			entry= BuildPathSupport.getJUnit4LibraryEntry();
+			entry2= BuildPathSupport.getHamcrestCoreLibraryEntry();
 		}
-
 		IClasspathEntry[] entries;
-		if (entry != null) {
+		if (entry == null) {
+			entries= new IClasspathEntry[] { };
+		} else if (entry2 == null) {
 			entries= new IClasspathEntry[] { entry };
 		} else {
-			entries= new IClasspathEntry[] { };
+			entries= new IClasspathEntry[] { entry, entry2 };
 		}
 		return new JUnitContainer(containerPath, entries);
 	}
@@ -152,29 +155,59 @@
 	 * @see org.eclipse.jdt.core.ClasspathContainerInitializer#requestClasspathContainerUpdate(org.eclipse.core.runtime.IPath, org.eclipse.jdt.core.IJavaProject, org.eclipse.jdt.core.IClasspathContainer)
 	 */
 	public void requestClasspathContainerUpdate(IPath containerPath, IJavaProject project, IClasspathContainer containerSuggestion) throws CoreException {
+		IPreferenceStore preferenceStore= JUnitPlugin.getDefault().getPreferenceStore();
+		
 		IClasspathEntry[] entries= containerSuggestion.getClasspathEntries();
-		if (entries.length == 1 && isValidJUnitContainerPath(containerPath)) {
+		if (entries.length >= 1 && isValidJUnitContainerPath(containerPath)) {
 			String version= containerPath.segment(1);
-
-			// only modifiable entry in Javadoc location
-			IClasspathAttribute[] extraAttributes= entries[0].getExtraAttributes();
-			for (int i= 0; i < extraAttributes.length; i++) {
-				IClasspathAttribute attrib= extraAttributes[i];
-				if (attrib.getName().equals(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME)) {
-
-					IPreferenceStore preferenceStore= JUnitPlugin.getDefault().getPreferenceStore();
-					if (JUNIT3.equals(version)) {
-						preferenceStore.setValue(JUnitPreferencesConstants.JUNIT3_JAVADOC, attrib.getValue());
-					} else if (JUNIT4.equals(version)) {
-						preferenceStore.setValue(JUnitPreferencesConstants.JUNIT4_JAVADOC, attrib.getValue());
+			
+			// only modifiable entry is Javadoc location
+			for (int i= 0; i < entries.length; i++) {
+				IClasspathEntry entry= entries[i];
+				String preferenceKey= getPreferenceKey(entry, version);
+				
+				IClasspathAttribute[] extraAttributes= entry.getExtraAttributes();
+				if (extraAttributes.length == 0) {
+					// Revert to default
+					if (!preferenceStore.isDefault(preferenceKey)) {
+						preferenceStore.setToDefault(preferenceKey);
 					}
-					break;
+					
+					/* 
+					 * The following would be correct, but would not allow to revert to the default.
+					 * There's no concept of "default value" for a classpath attribute, see
+					 * org.eclipse.jdt.internal.ui.preferences.JavadocConfigurationBlock.performDefaults()
+					 */
+					// preferenceStore.setValue(preferenceKey, "");
+				} else {
+					for (int j= 0; j < extraAttributes.length; j++) {
+						IClasspathAttribute attrib= extraAttributes[j];
+						if (attrib.getName().equals(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME)) {
+							if (preferenceKey != null) {
+								preferenceStore.setValue(preferenceKey, attrib.getValue());
+							}
+							break;
+						}
+					}
 				}
 			}
 			rebindClasspathEntries(project.getJavaModel(), containerPath);
 		}
 	}
 
+	private String getPreferenceKey(IClasspathEntry entry, String version) {
+		if (JUNIT3.equals(version)) {
+			return JUnitPreferencesConstants.JUNIT3_JAVADOC;
+		} else if (JUNIT4.equals(version)) {
+			if (entry.getPath().lastSegment().indexOf("junit") != -1) { //$NON-NLS-1$
+				return JUnitPreferencesConstants.JUNIT4_JAVADOC;
+			} else {
+				return JUnitPreferencesConstants.HAMCREST_CORE_JAVADOC;
+			}
+		}
+		return null;
+	}
+
 	private static void rebindClasspathEntries(IJavaModel model, IPath containerPath) throws JavaModelException {
 		ArrayList affectedProjects= new ArrayList();
 
diff --git a/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/JUnitHomeInitializer.java b/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/JUnitHomeInitializer.java
index b695c07..c19a1fb 100644
--- a/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/JUnitHomeInitializer.java
+++ b/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/JUnitHomeInitializer.java
@@ -34,7 +34,7 @@
 
 	private void initializeHome() {
 		try {
-			IPath location= BuildPathSupport.getBundleLocation(BuildPathSupport.JUNIT3_PLUGIN);
+			IPath location= BuildPathSupport.JUNIT3_PLUGIN.getBundleLocation();
 			if (location != null) {
 				JavaCore.setClasspathVariable(JUnitPlugin.JUNIT_HOME, location, null);
 			} else {
@@ -47,7 +47,7 @@
 
 	private void initializeSource() {
 		try {
-			IPath sourceLocation= BuildPathSupport.getSourceBundleLocation(BuildPathSupport.JUNIT3_PLUGIN);
+			IPath sourceLocation= BuildPathSupport.JUNIT3_PLUGIN.getSourceBundleLocation();
 			if (sourceLocation != null) {
 				JavaCore.setClasspathVariable(JUnitPlugin.JUNIT_SRC_HOME, sourceLocation, null);
 			} else {
diff --git a/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/P2Utils.java b/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/P2Utils.java
index a3388e5..e324189 100644
--- a/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/P2Utils.java
+++ b/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/buildpath/P2Utils.java
@@ -177,7 +177,7 @@
 	/**
 	 * Returns the bundle location.
 	 * 
-	 * @param bundleInfo the bundle info
+	 * @param bundleInfo the bundle info or <code>null</code>
 	 * @return the bundle location or <code>null</code> if it is not possible to convert to a path
 	 */
 	public static IPath getBundleLocationPath(BundleInfo bundleInfo) {
diff --git a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/BuildPathSupport.java b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/BuildPathSupport.java
index c399e32..eb35522 100644
--- a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/BuildPathSupport.java
+++ b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/BuildPathSupport.java
@@ -11,12 +11,20 @@
 package org.eclipse.jdt.internal.junit.buildpath;
 
 
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
 import org.osgi.framework.Version;
 
 import org.eclipse.equinox.internal.provisional.frameworkadmin.BundleInfo;
 import org.eclipse.osgi.service.resolver.VersionRange;
 
+import org.eclipse.core.runtime.FileLocator;
 import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.URIUtil;
 
 import org.eclipse.jdt.core.IAccessRule;
 import org.eclipse.jdt.core.IClasspathAttribute;
@@ -33,68 +41,139 @@
 	public static class JUnitPluginDescription {
 		
 		private final String bundleId;
-		private final String sourceBundleId;
 		private final VersionRange versionRange;
+		private final String sourceBundleId;
+		private final String bundleRoot;
+		private final String repositorySource;
+		private final String javadocPreferenceKey;
 
-		public JUnitPluginDescription(String bundleId, String sourceBundleId, VersionRange versionRange) {
+		public JUnitPluginDescription(String bundleId, VersionRange versionRange, String bundleRoot, String sourceBundleId, String repositorySource, String javadocPreferenceKey) {
 			this.bundleId= bundleId;
-			this.sourceBundleId= sourceBundleId;
 			this.versionRange= versionRange;
+			this.sourceBundleId= sourceBundleId;
+			this.bundleRoot= bundleRoot;
+			this.repositorySource= repositorySource;
+			this.javadocPreferenceKey= javadocPreferenceKey;
+		}
+		
+		public IPath getBundleLocation() {
+			return P2Utils.getBundleLocationPath(P2Utils.findBundle(bundleId, versionRange, false));
+		}
+		
+		public IPath getSourceBundleLocation() {
+			return getSourceLocation(P2Utils.findBundle(bundleId, versionRange, false));
+		}
+		
+		//XXX: Official API has been requested for the provisional p2 APIs: https://bugs.eclipse.org/bugs/show_bug.cgi?id=269496
+		
+		public IClasspathEntry getLibraryEntry() {
+			BundleInfo bundleInfo= P2Utils.findBundle(bundleId, versionRange, false);
+			IPath bundleLocation= P2Utils.getBundleLocationPath(bundleInfo);
+			if (bundleLocation != null) {
+				
+				IPath bundleRootLocation= bundleLocation;
+				if (bundleRoot != null)
+					bundleRootLocation= bundleLocation.append(bundleRoot);
+				
+				IPath srcLocation= getSourceLocation(bundleInfo);
+				
+				IAccessRule[] accessRules= { };
+
+				String javadocLocation= JUnitPlugin.getDefault().getPreferenceStore().getString(javadocPreferenceKey);
+				IClasspathAttribute[] attributes;
+				if (javadocLocation.length() == 0) {
+					attributes= new IClasspathAttribute[0];
+				} else {
+					attributes= new IClasspathAttribute[] { JavaCore.newClasspathAttribute(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, javadocLocation) };
+				}
+				
+				return JavaCore.newLibraryEntry(bundleRootLocation, srcLocation, null, accessRules, attributes, false);
+			}
+			return null;
+		}
+
+		private IPath getSourceLocation(BundleInfo bundleInfo) {
+			if (bundleInfo == null)
+				return null;
+			
+			IPath srcLocation= null;
+			if (repositorySource != null) {
+				// Try source in workspace (from repository)
+				try {
+					URL bundleUrl= FileLocator.toFileURL(URIUtil.toURL(bundleInfo.getLocation()));
+					File bundleFile= new File(bundleUrl.getFile());
+					if (bundleFile.isDirectory()) {
+						File srcFile= new File(bundleFile, repositorySource);
+						if (srcFile.exists()) {
+							srcLocation= new Path(srcFile.getPath());
+							if (srcFile.isDirectory()) {
+								srcLocation= srcLocation.addTrailingSeparator();
+							}
+						}
+					}
+				} catch (MalformedURLException e) {
+					//continue
+				} catch (IOException e) {
+					//continue
+				}
+			}
+			
+			if (srcLocation == null) {
+				// Try exact version
+				BundleInfo sourceBundleInfo= P2Utils.findBundle(sourceBundleId, new Version(bundleInfo.getVersion()), true);
+				if (sourceBundleInfo == null) {
+					// Try version range
+					sourceBundleInfo= P2Utils.findBundle(sourceBundleId, versionRange, true);
+				}
+				srcLocation= P2Utils.getBundleLocationPath(sourceBundleInfo);
+			}
+			return srcLocation;
 		}
 	}
 
 	
-	public static final JUnitPluginDescription JUNIT3_PLUGIN= new JUnitPluginDescription("org.junit", "org.junit.source", new VersionRange("[3.8.2,3.9)")); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
-	public static final JUnitPluginDescription JUNIT4_PLUGIN= new JUnitPluginDescription("org.junit4", "org.junit4.source", new VersionRange("[4.5.0,5.0.0)")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+	public static final JUnitPluginDescription JUNIT3_PLUGIN= new JUnitPluginDescription(
+			"org.junit", new VersionRange("[3.8.2,3.9)"), "junit.jar", "org.junit.source", "source-bundle/", JUnitPreferencesConstants.JUNIT3_JAVADOC); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+	
+	private static final JUnitPluginDescription JUNIT4_PLUGIN= new JUnitPluginDescription(
+			"org.junit4", new VersionRange("[4.5.0,5.0.0)"), "junit.jar", "org.junit4.source", "junitsrc.zip", JUnitPreferencesConstants.JUNIT4_JAVADOC); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+	
+	private static final JUnitPluginDescription HAMCREST_CORE_PLUGIN= new JUnitPluginDescription(
+			"org.hamcrest.core", new VersionRange("[1.1.0,2.0.0)"), null, "org.hamcrest.core.source", "source-bundle/", JUnitPreferencesConstants.HAMCREST_CORE_JAVADOC); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
 
-	public static IPath getBundleLocation(JUnitPluginDescription pluginDesc) {
-		return P2Utils.getBundleLocationPath(P2Utils.findBundle(pluginDesc.bundleId, pluginDesc.versionRange, false));
-	}
-
-	public static IPath getSourceBundleLocation(JUnitPluginDescription pluginDesc) {
-		return P2Utils.getBundleLocationPath(P2Utils.findBundle(pluginDesc.sourceBundleId, pluginDesc.versionRange, true));
-	}
-
+	/**
+	 * @return the JUnit3 classpath container
+	 */
 	public static IClasspathEntry getJUnit3ClasspathEntry() {
 		return JavaCore.newContainerEntry(JUnitContainerInitializer.JUNIT3_PATH);
 	}
 
+	/**
+	 * @return the JUnit4 classpath container
+	 */
 	public static IClasspathEntry getJUnit4ClasspathEntry() {
 		return JavaCore.newContainerEntry(JUnitContainerInitializer.JUNIT4_PATH);
 	}
 
+	/**
+	 * @return the org.junit library
+	 */
 	public static IClasspathEntry getJUnit3LibraryEntry() {
-		return getJUnitLibraryEntry(JUNIT3_PLUGIN, JUnitPreferencesConstants.JUNIT3_JAVADOC);
+		return JUNIT3_PLUGIN.getLibraryEntry();
 	}
 
+	/**
+	 * @return the org.junit4 library
+	 */
 	public static IClasspathEntry getJUnit4LibraryEntry() {
-		return getJUnitLibraryEntry(JUNIT4_PLUGIN, JUnitPreferencesConstants.JUNIT4_JAVADOC);
+		return JUNIT4_PLUGIN.getLibraryEntry();
 	}
 
-	// Official API has been requested for the provisional p2 APIs: https://bugs.eclipse.org/bugs/show_bug.cgi?id=269496
-	private static IClasspathEntry getJUnitLibraryEntry(JUnitPluginDescription pluginDesc, String javadocPreferenceKey) {
-		BundleInfo bundleInfo= P2Utils.findBundle(pluginDesc.bundleId, pluginDesc.versionRange, false);
-		IPath bundleBase= P2Utils.getBundleLocationPath(bundleInfo);
-		if (bundleBase != null) {
-
-			// Try exact version
-			BundleInfo sourceBundleInfo= P2Utils.findBundle(pluginDesc.sourceBundleId, new Version(bundleInfo.getVersion()), true);
-			if (sourceBundleInfo == null) {
-				// Try exact version range
-				sourceBundleInfo= P2Utils.findBundle(pluginDesc.sourceBundleId, pluginDesc.versionRange, true);
-			}
-
-			IPath jarLocation= bundleBase.append("junit.jar"); //$NON-NLS-1$
-			IPath srcLocation= P2Utils.getBundleLocationPath(sourceBundleInfo);
-
-			IAccessRule[] accessRules= { };
-
-			String javadocLocation= JUnitPlugin.getDefault().getPreferenceStore().getString(javadocPreferenceKey);
-			IClasspathAttribute[] attributes= { JavaCore.newClasspathAttribute(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, javadocLocation) };
-
-			return JavaCore.newLibraryEntry(jarLocation, srcLocation, null, accessRules, attributes, false);
-		}
-		return null;
+	/**
+	 * @return the org.hamcrest.core library
+	 */
+	public static IClasspathEntry getHamcrestCoreLibraryEntry() {
+		return HAMCREST_CORE_PLUGIN.getLibraryEntry();
 	}
 
 }
diff --git a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/JUnitContainerInitializer.java b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/JUnitContainerInitializer.java
index 56daa86..670075f 100644
--- a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/JUnitContainerInitializer.java
+++ b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/JUnitContainerInitializer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2006, 2008 IBM Corporation and others.
+ * Copyright (c) 2006, 2009 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
@@ -95,18 +95,21 @@
 
 	private static JUnitContainer getNewContainer(IPath containerPath) {
 		IClasspathEntry entry= null;
+		IClasspathEntry entry2= null;
 		String version= containerPath.segment(1);
 		if (JUNIT3_8_1.equals(version) || JUNIT3.equals(version)) {
 			entry= BuildPathSupport.getJUnit3LibraryEntry();
 		} else if (JUNIT4.equals(version)) {
 			entry= BuildPathSupport.getJUnit4LibraryEntry();
+			entry2= BuildPathSupport.getHamcrestCoreLibraryEntry();
 		}
-
 		IClasspathEntry[] entries;
-		if (entry != null) {
+		if (entry == null) {
+			entries= new IClasspathEntry[] { };
+		} else if (entry2 == null) {
 			entries= new IClasspathEntry[] { entry };
 		} else {
-			entries= new IClasspathEntry[] { };
+			entries= new IClasspathEntry[] { entry, entry2 };
 		}
 		return new JUnitContainer(containerPath, entries);
 	}
@@ -152,29 +155,59 @@
 	 * @see org.eclipse.jdt.core.ClasspathContainerInitializer#requestClasspathContainerUpdate(org.eclipse.core.runtime.IPath, org.eclipse.jdt.core.IJavaProject, org.eclipse.jdt.core.IClasspathContainer)
 	 */
 	public void requestClasspathContainerUpdate(IPath containerPath, IJavaProject project, IClasspathContainer containerSuggestion) throws CoreException {
+		IPreferenceStore preferenceStore= JUnitPlugin.getDefault().getPreferenceStore();
+		
 		IClasspathEntry[] entries= containerSuggestion.getClasspathEntries();
-		if (entries.length == 1 && isValidJUnitContainerPath(containerPath)) {
+		if (entries.length >= 1 && isValidJUnitContainerPath(containerPath)) {
 			String version= containerPath.segment(1);
-
-			// only modifiable entry in Javadoc location
-			IClasspathAttribute[] extraAttributes= entries[0].getExtraAttributes();
-			for (int i= 0; i < extraAttributes.length; i++) {
-				IClasspathAttribute attrib= extraAttributes[i];
-				if (attrib.getName().equals(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME)) {
-
-					IPreferenceStore preferenceStore= JUnitPlugin.getDefault().getPreferenceStore();
-					if (JUNIT3.equals(version)) {
-						preferenceStore.setValue(JUnitPreferencesConstants.JUNIT3_JAVADOC, attrib.getValue());
-					} else if (JUNIT4.equals(version)) {
-						preferenceStore.setValue(JUnitPreferencesConstants.JUNIT4_JAVADOC, attrib.getValue());
+			
+			// only modifiable entry is Javadoc location
+			for (int i= 0; i < entries.length; i++) {
+				IClasspathEntry entry= entries[i];
+				String preferenceKey= getPreferenceKey(entry, version);
+				
+				IClasspathAttribute[] extraAttributes= entry.getExtraAttributes();
+				if (extraAttributes.length == 0) {
+					// Revert to default
+					if (!preferenceStore.isDefault(preferenceKey)) {
+						preferenceStore.setToDefault(preferenceKey);
 					}
-					break;
+					
+					/* 
+					 * The following would be correct, but would not allow to revert to the default.
+					 * There's no concept of "default value" for a classpath attribute, see
+					 * org.eclipse.jdt.internal.ui.preferences.JavadocConfigurationBlock.performDefaults()
+					 */
+					// preferenceStore.setValue(preferenceKey, "");
+				} else {
+					for (int j= 0; j < extraAttributes.length; j++) {
+						IClasspathAttribute attrib= extraAttributes[j];
+						if (attrib.getName().equals(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME)) {
+							if (preferenceKey != null) {
+								preferenceStore.setValue(preferenceKey, attrib.getValue());
+							}
+							break;
+						}
+					}
 				}
 			}
 			rebindClasspathEntries(project.getJavaModel(), containerPath);
 		}
 	}
 
+	private String getPreferenceKey(IClasspathEntry entry, String version) {
+		if (JUNIT3.equals(version)) {
+			return JUnitPreferencesConstants.JUNIT3_JAVADOC;
+		} else if (JUNIT4.equals(version)) {
+			if (entry.getPath().lastSegment().indexOf("junit") != -1) { //$NON-NLS-1$
+				return JUnitPreferencesConstants.JUNIT4_JAVADOC;
+			} else {
+				return JUnitPreferencesConstants.HAMCREST_CORE_JAVADOC;
+			}
+		}
+		return null;
+	}
+
 	private static void rebindClasspathEntries(IJavaModel model, IPath containerPath) throws JavaModelException {
 		ArrayList affectedProjects= new ArrayList();
 
diff --git a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/JUnitHomeInitializer.java b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/JUnitHomeInitializer.java
index b695c07..c19a1fb 100644
--- a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/JUnitHomeInitializer.java
+++ b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/JUnitHomeInitializer.java
@@ -34,7 +34,7 @@
 
 	private void initializeHome() {
 		try {
-			IPath location= BuildPathSupport.getBundleLocation(BuildPathSupport.JUNIT3_PLUGIN);
+			IPath location= BuildPathSupport.JUNIT3_PLUGIN.getBundleLocation();
 			if (location != null) {
 				JavaCore.setClasspathVariable(JUnitPlugin.JUNIT_HOME, location, null);
 			} else {
@@ -47,7 +47,7 @@
 
 	private void initializeSource() {
 		try {
-			IPath sourceLocation= BuildPathSupport.getSourceBundleLocation(BuildPathSupport.JUNIT3_PLUGIN);
+			IPath sourceLocation= BuildPathSupport.JUNIT3_PLUGIN.getSourceBundleLocation();
 			if (sourceLocation != null) {
 				JavaCore.setClasspathVariable(JUnitPlugin.JUNIT_SRC_HOME, sourceLocation, null);
 			} else {
diff --git a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/P2Utils.java b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/P2Utils.java
index a3388e5..e324189 100644
--- a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/P2Utils.java
+++ b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/buildpath/P2Utils.java
@@ -177,7 +177,7 @@
 	/**
 	 * Returns the bundle location.
 	 * 
-	 * @param bundleInfo the bundle info
+	 * @param bundleInfo the bundle info or <code>null</code>
 	 * @return the bundle location or <code>null</code> if it is not possible to convert to a path
 	 */
 	public static IPath getBundleLocationPath(BundleInfo bundleInfo) {
diff --git a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitPreferencesConstants.java b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitPreferencesConstants.java
index d31c097..4af67e5 100644
--- a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitPreferencesConstants.java
+++ b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/JUnitPreferencesConstants.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * Copyright (c) 2000, 2009 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
@@ -64,6 +64,11 @@
 	 */
 	public static final String JUNIT4_JAVADOC= JUnitPlugin.PLUGIN_ID + ".junit4.javadoclocation"; //$NON-NLS-1$
 
+	/**
+	 * Javadoc location for org.hamcrest.core (JUnit 4)
+	 */
+	public static final String HAMCREST_CORE_JAVADOC= JUnitPlugin.PLUGIN_ID + ".junit4.hamcrest.core.javadoclocation"; //$NON-NLS-1$
+	
 
 	private static final String[] fgDefaultFilterPatterns= new String[] {
 		"org.eclipse.jdt.internal.junit.runner.*", //$NON-NLS-1$
diff --git a/org.junit4/.classpath b/org.junit4/.classpath
index 7f0bfca..bb5bc27 100644
--- a/org.junit4/.classpath
+++ b/org.junit4/.classpath
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-	<classpathentry exported="true" kind="lib" path="junit.jar" sourcepath="junitsrc.zip"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry exported="true" kind="lib" path="junit.jar" sourcepath="junitsrc.zip"/>
 	<classpathentry kind="output" path=""/>
 </classpath>
diff --git a/org.junit4/META-INF/MANIFEST.MF b/org.junit4/META-INF/MANIFEST.MF
index d979a24..f595fe0 100644
--- a/org.junit4/META-INF/MANIFEST.MF
+++ b/org.junit4/META-INF/MANIFEST.MF
@@ -11,9 +11,6 @@
  junit.framework;version="4.5.0",
  junit.runner;version="4.5.0",
  junit.textui;version="4.5.0",
- org.hamcrest;version="1.1.0",
- org.hamcrest.core;version="1.1.0",
- org.hamcrest.internal;version="1.1.0";x-internal:=true,
  org.junit;version="4.5.0",
  org.junit.experimental.results;version="4.5.0",
  org.junit.experimental.runners;version="4.5.0",
@@ -33,3 +30,4 @@
  org.junit.runner.notification;version="4.5.0",
  org.junit.runners;version="4.5.0",
  org.junit.runners.model;version="4.5.0"
+Require-Bundle: org.hamcrest.core;bundle-version="1.1.0";visibility:=reexport
diff --git a/org.junit4/about.html b/org.junit4/about.html
index 3e67898..aac880c 100644
--- a/org.junit4/about.html
+++ b/org.junit4/about.html
@@ -8,7 +8,7 @@
 <body lang="EN-US">
 <h2>About This Content</h2>
  
-<p>September 4, 2008</p>	
+<p>April 17, 2009</p>	
 <h3>License</h3>
 
 <p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  Unless otherwise 
@@ -40,44 +40,5 @@
 Common Public License Version 1.0 (&quot;CPL&quot;).  A copy of the CPL is available at <a href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>.
 The binary code is located in junit.jar and the source code is located in junitsrc.zip.</p>
 
-<h4>Hamcrest Core 1.1</h4>
-
-<p>The plug-in is accompanied by software developed by Hamcrest (<a href="http://code.google.com/p/hamcrest/">http://code.google.com/p/hamcrest/</a>).
-The Hamcrest Core 1.1 code included within the JUnit 4.5 Jar includes no modifications.
-Your use of Hamcrest Core 1.1 in both source and binary code form contained in the plug-in is subject to the terms and conditions of the 
-New BSD License.
-The binary code is located in junit.jar/org/hamcrest and the source code is located in junitsrc.zip/org/hamcrest.</p>
-
-<p>The Hamcrest New BSD License:</p>
-<pre>
-BSD License
-
-Copyright (c) 2000-2006, www.hamcrest.org
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this list of
-conditions and the following disclaimer. Redistributions in binary form must reproduce
-the above copyright notice, this list of conditions and the following disclaimer in
-the documentation and/or other materials provided with the distribution.
-
-Neither the name of Hamcrest nor the names of its contributors may be used to endorse
-or promote products derived from this software without specific prior written
-permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
-SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
-WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGE.
-</pre>
-
 </body>
 </html>
\ No newline at end of file
diff --git a/org.junit4/junit.jar b/org.junit4/junit.jar
index 7339216..4cef6c0 100644
--- a/org.junit4/junit.jar
+++ b/org.junit4/junit.jar
Binary files differ
diff --git a/org.junit4/junitsrc.zip b/org.junit4/junitsrc.zip
index 772a425..14fa2ea 100644
--- a/org.junit4/junitsrc.zip
+++ b/org.junit4/junitsrc.zip
Binary files differ