JDK_1_5 - Merge with HEAD: v440c
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AttachSourceTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AttachSourceTests.java
index 0f3a499..e9079f0 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AttachSourceTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AttachSourceTests.java
@@ -12,6 +12,8 @@
 
 import junit.framework.Test;
 import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.Path;
@@ -300,6 +302,96 @@
 	testClassRetrieval();
 	testMethodRetrieval();
 }
+
+/*
+ * Ensures that having a project as a class folder and attaching its sources finds the source
+ * of a class in a non-default package.
+ * (regression test for https://bugs.eclipse.org/bugs/show_bug.cgi?id=65186)
+ */
+public void testProjectAsClassFolder1() throws CoreException {
+	try {
+		createJavaProject("P1");
+		createFolder("/P1/p");
+		createFile(
+			"/P1/p/X.java",
+			"package p;\n" +
+			"public class X {\n" +
+			"}"
+		);
+		IProject p1 = getProject("P1");
+		p1.build(IncrementalProjectBuilder.FULL_BUILD, null);
+		IJavaProject javaProject = createJavaProject("P2", new String[]{""}, new String[]{"/P1"}, null, null, "");
+		IPackageFragmentRoot root = javaProject.getPackageFragmentRoot(p1);
+		attachSource(root, "/P1", null);
+		IClassFile cf = root.getPackageFragment("p").getClassFile("X.class");
+		assertSourceEquals(
+			"Unexpected source for class file P1/p/X.class",
+			"package p;\n" +
+			"public class X {\n" +
+			"}",
+			cf.getSource());		
+	} finally {
+		deleteProject("P1");
+		deleteProject("P2");
+	}
+}
+
+/*
+ * Ensures that having a project as a class folder and attaching its sources finds the source
+ * of a class in the default package.
+ * (regression test for https://bugs.eclipse.org/bugs/show_bug.cgi?id=65186)
+ */
+public void testProjectAsClassFolder2() throws CoreException {
+	try {
+		createJavaProject("P1");
+		createFile(
+			"/P1/X.java",
+			"public class X {\n" +
+			"}"
+		);
+		IProject p1 = getProject("P1");
+		p1.build(IncrementalProjectBuilder.FULL_BUILD, null);
+		IJavaProject javaProject = createJavaProject("P2", new String[]{""}, new String[]{"/P1"}, null, null, "");
+		IPackageFragmentRoot root = javaProject.getPackageFragmentRoot(p1);
+		attachSource(root, "/P1", null);
+		IClassFile cf = root.getPackageFragment("").getClassFile("X.class");
+		assertSourceEquals(
+			"Unexpected source for class file P1/X.class",
+			"public class X {\n" +
+			"}",
+			cf.getSource());		
+	} finally {
+		deleteProject("P1");
+		deleteProject("P2");
+	}
+}
+
+/*
+ * Ensures that having a project as source attachement finds the source
+ * (regression test for https://bugs.eclipse.org/bugs/show_bug.cgi?id=65186)
+ */
+public void testProjectAsSourceAttachment() throws CoreException {
+	try {
+		IJavaProject javaProject = createJavaProject("Test", new String[]{""}, new String[]{"/AttachSourceTests/test.jar"}, null, null, "");
+		createFolder("/Test/test1");
+		createFile("/Test/test1/Test.java",
+			"package test1;\n" + 
+			"\n" + 
+			"public class Test {}");
+		IPackageFragmentRoot root = javaProject.getPackageFragmentRoot(getFile("/AttachSourceTests/test.jar"));
+		attachSource(root, "/Test", null);
+		IClassFile cf = root.getPackageFragment("test1").getClassFile("Test.class");
+		assertSourceEquals(
+			"Unexpected source for class file test1/Test.class",
+			"package test1;\n" + 
+			"\n" + 
+			"public class Test {}",
+			cf.getSource());		
+	} finally {
+		deleteProject("Test");
+	}
+}
+
 /**
  * Attaches a source zip to a jar.  The source zip has
  * a nested root structure and exists as a resource.  Tests that
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java
index 7c6586a..eb7d753 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java
@@ -2044,6 +2044,20 @@
 		this.deleteProject("P");
 	}
 }
+/*
+ * Ensures that an external class folder cannot be put on the classpath.
+ */
+public void testInvalidExternalClassFolder() throws CoreException {
+	try {
+		IJavaProject proj =  createJavaProject("P", new String[] {}, new String[] {EXTERNAL_JAR_DIR_PATH}, "bin");
+		assertMarkers(
+			"Unexpected markers",
+			"Required library cannot denote external folder: \'" + EXTERNAL_JAR_DIR_PATH + "\' for project P",
+			proj);
+	} finally {
+		deleteProject("P");
+	}
+}
 /**
  * Ensures that only one marker is created if building a project that is
  * missing its .classpath file multiple times.
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/EncodingTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/EncodingTests.java
index 696204b..749dc89 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/EncodingTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/EncodingTests.java
@@ -48,7 +48,7 @@
 	// All specified tests which do not belong to the class are skipped...
 	static {
 		// Names of tests to run: can be "testBugXXXX" or "BugXXXX")
-//		testsNames = new String[] { "testCreateCompilationUnitAndImportContainer" };
+//		testsNames = new String[] { "testBug66898" };
 		// Numbers of tests to run: "test<number>" will be run for each number of this array
 //		testsNumbers = new int[] { 2, 12 };
 		// Range numbers of tests to run: all tests between "test<first>" and "test<last>" will be run for { first, last }
@@ -577,4 +577,61 @@
 		String bomSource = bomSourceRef.getSource();
 		assertEquals("BOM UTF-8 source should be idtentical than UTF-8!", source, bomSource);
 	}	
+
+	/**
+	 * Test fix for bug 66898: refactor-rename: encoding is not preserved
+	 * @see <a href="http://bugs.eclipse.org/bugs/show_bug.cgi?id=66898">66898</a>
+	 */
+	public void testBug66898() throws JavaModelException, CoreException, UnsupportedEncodingException {
+
+		// Set file encoding
+		String encoding = "UTF-8".equals(vmEncoding) ? "Cp1252" : "UTF-8";
+		this.utf8File.setCharset(encoding);
+		
+		// Move file
+		String fileName = this.utf8File.getName();
+		ICompilationUnit cu = getCompilationUnit(this.utf8File.getFullPath().toString());
+		createFolder("/Encoding/src/tmp");
+		IPackageFragment packFrag = getPackageFragment("Encoding", "src", "tmp");
+		cu.move(packFrag, null, null, false, null);
+		ICompilationUnit destSource = packFrag.getCompilationUnit(fileName);
+		IFile destFile = (IFile) destSource.getUnderlyingResource();
+		assertEquals("Moved file should keep encoding", encoding, destFile.getCharset());
+
+		// Get source and compare with file contents
+		String source = destSource.getSource();
+		String systemSource = org.eclipse.jdt.core.tests.util.Util.convertToIndependantLineDelimiter(source);
+		String encodedContents = new String (Util.getResourceContentsAsCharArray(destFile));
+		encodedContents = org.eclipse.jdt.core.tests.util.Util.convertToIndependantLineDelimiter(encodedContents);
+		assertEquals("Encoded UTF-8 source should have been decoded the same way!", encodedContents, systemSource);
+		byte[] sourceBytes = source.getBytes(encoding);
+		byte[] encodedBytes = Util.getResourceContentsAsByteArray(destFile);
+		assertEquals("Wrong size of encoded string", encodedBytes.length, sourceBytes.length);
+		for (int i = 0, max = sourceBytes.length; i < max; i++) {
+			assertTrue("Wrong size of encoded character at " + i, sourceBytes[i] == encodedBytes[i]);
+		}
+		
+		// Rename file
+		destSource.rename("TestUTF8.java", false, null);
+		ICompilationUnit renamedSource = packFrag.getCompilationUnit("TestUTF8.java");
+		IFile renamedFile = (IFile) renamedSource.getUnderlyingResource();
+		assertEquals("Moved file should keep encoding", encoding, renamedFile.getCharset());
+		
+		// Compare contents again
+		String sourceRenamed = renamedSource.getSource();
+		String systemSourceRenamed = org.eclipse.jdt.core.tests.util.Util.convertToIndependantLineDelimiter(sourceRenamed);
+		String renamedContents = new String (Util.getResourceContentsAsCharArray(renamedFile));
+		renamedContents = org.eclipse.jdt.core.tests.util.Util.convertToIndependantLineDelimiter(renamedContents);
+		assertEquals("Encoded UTF-8 source should have been decoded the same way!", renamedContents, systemSourceRenamed);
+		byte[] renamedSourceBytes = sourceRenamed.getBytes(encoding);
+		byte[] renamedEncodedBytes = Util.getResourceContentsAsByteArray(renamedFile);
+		assertEquals("Wrong size of encoded string", renamedEncodedBytes.length, renamedSourceBytes.length);
+		for (int i = 0, max = renamedSourceBytes.length; i < max; i++) {
+			assertTrue("Wrong size of encoded character at " + i, renamedSourceBytes[i] == renamedEncodedBytes[i]);
+		}
+		
+		// Move back 
+		renamedFile.move(this.utf8File.getFullPath(), false, null);
+		assertEquals("Moved file should keep encoding", encoding, this.utf8File.getCharset());
+	}	
 }
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
index b11b635..3064fb8 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
@@ -12,7 +12,7 @@
 
 ### compiler 
 compiler.name = Eclipse Java Compiler
-compiler.version = 0.440_Cheetah06
+compiler.version = 0.441_Cheetah06
 compiler.copyright = Copyright IBM Corp 2000, 2004. All rights reserved.
 
 ### scanning
diff --git a/org.eclipse.jdt.core/buildnotes_jdt-core.html b/org.eclipse.jdt.core/buildnotes_jdt-core.html
index 76f6cac..197b92c 100644
--- a/org.eclipse.jdt.core/buildnotes_jdt-core.html
+++ b/org.eclipse.jdt.core/buildnotes_jdt-core.html
@@ -159,6 +159,30 @@
 [1.5] ArrayStoreException in 1.5 parser
 
 
+<a name="v_441"></a>
+<p><hr><h1>
+Eclipse Platform Build Notes&nbsp;<br>
+Java Development Tooling Core</h1>
+Eclipse SDK 3.0RC3 Build - ?th June 2004
+<br>Project org.eclipse.jdt.core v_441
+(<a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.jdt.core/?only_with_tag=v_441">cvs</a>).
+<h2>
+What's new in this drop</h2>
+<ul>
+</ul>
+
+<h3>Problem Reports Fixed</h3>
+<a href="http://bugs.eclipse.org/bugs/show_bug.cgi?id=64646">64646</a>
+[Navigator] Navigator popup causes Eclipse to hang. 
+<br><a href="http://bugs.eclipse.org/bugs/show_bug.cgi?id=65186">65186</a>
+Can't attach source from project directory [build path] 
+<br><a href="http://bugs.eclipse.org/bugs/show_bug.cgi?id=66898">66898</a>
+refactor-rename: encoding is not preserved
+<br><a href="http://bugs.eclipse.org/bugs/show_bug.cgi?id=65831">65831</a>
+search for all types slow/memory intensive [search] 
+<br><a href="http://bugs.eclipse.org/bugs/show_bug.cgi?id=66675">66675</a>
+Extra period in the doc in 200406110010
+
 <a name="v_440"></a>
 <p><hr><h1>
 Eclipse Platform Build Notes&nbsp;<br>
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IType.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IType.java
index 2b90178..0c7ac6a 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IType.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IType.java
@@ -897,8 +897,7 @@
 	 * Note that if a working copy is empty, it will be as if the original compilation
 	 * unit had been deleted.
 	 * </p>
-	 * <p>. 
-	 * Multiple answers might be found in case there are ambiguous matches.
+	 * <p>Multiple answers might be found in case there are ambiguous matches.
 	 * </p>
 	 * <p>
 	 * Each matching type name is decomposed as an array of two strings, the first denoting the package
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathEntry.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathEntry.java
index 964917d..41ef429 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathEntry.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathEntry.java
@@ -1076,13 +1076,15 @@
 						IResource resolvedResource = (IResource) target;
 						switch(resolvedResource.getType()){
 							case IResource.FILE :
-								if (org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(resolvedResource.getFileExtension())) {
+								if (org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(resolvedResource.getName())) {
 									if (checkSourceAttachment 
 										&& sourceAttachment != null
 										&& !sourceAttachment.isEmpty()
 										&& JavaModel.getTarget(workspaceRoot, sourceAttachment, true) == null){
 										return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.unboundSourceAttachment", new String [] {sourceAttachment.makeRelative().toString(), path.makeRelative().toString(), projectName})); //$NON-NLS-1$
 									}
+								} else {
+									return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.illegalLibraryArchive", entryPathMsg, projectName)); //$NON-NLS-1$
 								}
 								break;
 							case IResource.FOLDER :	// internal binary folder
@@ -1094,12 +1096,17 @@
 								}
 						}
 					} else if (target instanceof File){
-						if (checkSourceAttachment 
-							&& sourceAttachment != null 
-							&& !sourceAttachment.isEmpty()
-							&& JavaModel.getTarget(workspaceRoot, sourceAttachment, true) == null){
-							return  new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.unboundSourceAttachment", new String [] {sourceAttachment.toString(), path.makeRelative().toString(), projectName})); //$NON-NLS-1$
-						}
+					    File file = (File) target;
+					    if (!file.isFile()) {
+							return  new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.illegalExternalFolder", path.toOSString(), projectName)); //$NON-NLS-1$
+					    } else if (!org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(file.getName())) {
+							return  new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.illegalLibraryArchive", path.toOSString(), projectName)); //$NON-NLS-1$
+					    } else if (checkSourceAttachment 
+								&& sourceAttachment != null 
+								&& !sourceAttachment.isEmpty()
+								&& JavaModel.getTarget(workspaceRoot, sourceAttachment, true) == null){
+								return  new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.unboundSourceAttachment", new String [] {sourceAttachment.toString(), path.makeRelative().toString(), projectName})); //$NON-NLS-1$
+					    }
 					} else {
 						return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.unboundLibrary", path.makeRelative().toString(), projectName)); //$NON-NLS-1$
 					}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/CopyResourceElementsOperation.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/CopyResourceElementsOperation.java
index 30e32b8..7c77c38 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/CopyResourceElementsOperation.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/CopyResourceElementsOperation.java
@@ -235,6 +235,13 @@
 	
 		// copy resource
 		IFile sourceResource = (IFile)source.getResource();
+		String encoding = null;
+		try {
+			encoding = sourceResource.getCharset();
+		}
+		catch (CoreException ce) {
+			// use no encoding
+		}
 		IContainer destFolder = (IContainer)dest.getResource(); // can be an IFolder or an IProject
 		IFile destFile = destFolder.getFile(new Path(destName));
 		if (!destFile.equals(sourceResource)) {
@@ -269,13 +276,6 @@
 			if (newContent != null){
 				boolean wasReadOnly = destFile.isReadOnly();
 				try {
-					String encoding = null;
-					try {
-						encoding = destFile.getCharset();
-					}
-					catch (CoreException ce) {
-						// use no encoding
-					}
 					
 					// when the file was copied, its read-only flag was preserved -> temporary set it to false
 					// note this doesn't interfer with repository providers as this is a new resource that cannot be under
@@ -317,13 +317,6 @@
 			// see http://dev.eclipse.org/bugs/show_bug.cgi?id=9351
 			try {
 				if (newContent != null){
-					String encoding = null;
-					try {
-						encoding = destFile.getCharset();
-					}
-					catch (CoreException ce) {
-						// use no encoding
-					}
 					destFile.setContents(
 						new ByteArrayInputStream(encoding == null ? newContent.getBytes() : newContent.getBytes(encoding)), 
 						force ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY, 
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/PackageFragmentRoot.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/PackageFragmentRoot.java
index 4da7fe1..f68bcd0 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/PackageFragmentRoot.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/PackageFragmentRoot.java
@@ -368,15 +368,16 @@
 			entry = parentProject.getClasspathEntryFor(rootPath);
 			if (entry != null){
 				Object target = JavaModel.getTarget(workspaceRoot, entry.getSourceAttachmentPath(), true);
-				if (target instanceof IFile){
-					IFile file = (IFile) target;
-					if (org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(file.getName())){
+				if (target instanceof IResource) {
+					if (target instanceof IFile) {
+						IFile file = (IFile) target;
+						if (org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(file.getName())){
+							return entry;
+						}
+					} else if (target instanceof IContainer) {
 						return entry;
 					}
-				} else if (target instanceof IFolder) {
-					return entry;
-				}
-				if (target instanceof java.io.File){
+				} else if (target instanceof java.io.File){
 					java.io.File file = (java.io.File) target;
 					if (file.isFile()) {
 						if (org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(file.getName())){
@@ -402,15 +403,16 @@
 				entry = jProject.getClasspathEntryFor(rootPath);
 				if (entry != null){
 					Object target = JavaModel.getTarget(workspaceRoot, entry.getSourceAttachmentPath(), true);
-					if (target instanceof IFile){
-						IFile file = (IFile) target;
-						if (org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(file.getName())){
+					if (target instanceof IResource) {
+						if (target instanceof IFile){
+							IFile file = (IFile) target;
+							if (org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(file.getName())){
+								return entry;
+							}
+						} else if (target instanceof IContainer) {
 							return entry;
 						}
-					} else if (target instanceof IFolder) {
-						return entry;
-					}
-					if (target instanceof java.io.File){
+					} else if (target instanceof java.io.File){
 						java.io.File file = (java.io.File) target;
 						if (file.isFile()) {
 							if (org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(file.getName())){
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceMapper.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceMapper.java
index f95df25..72698df 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceMapper.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceMapper.java
@@ -120,7 +120,7 @@
 	/**
 	 * The unknown source range {-1, 0}
 	 */
-	protected static SourceRange fgUnknownRange = new SourceRange(-1, 0);
+	public static SourceRange fgUnknownRange = new SourceRange(-1, 0);
 
 	/**
 	 * The position within the source of the start of the
@@ -347,11 +347,11 @@
 			}
 		} else {
 			Object target = JavaModel.getTarget(ResourcesPlugin.getWorkspace().getRoot(), root.getPath(), true);
-			if (target instanceof IFolder) {
-				IResource resource = root.getResource();
-				if (resource.getType() == IResource.FOLDER) {
+			if (target instanceof IResource) {
+				IResource resource = (IResource) target;
+				if (resource instanceof IContainer) {
 					try {
-						IResource[] members = ((IFolder) resource).members();
+						IResource[] members = ((IContainer) resource).members();
 						for (int i = 0, max = members.length; i < max; i++) {
 							IResource member = members[i];
 							if (member.getType() == IResource.FOLDER) {
@@ -412,8 +412,10 @@
 			}
 		} else {
 			Object target = JavaModel.getTarget(ResourcesPlugin.getWorkspace().getRoot(), this.sourcePath, true);
-			if (target instanceof IFolder) {
-				computeRootPath((IFolder)target, firstLevelPackageNames, containsADefaultPackage);
+			if (target instanceof IResource) {
+				if (target instanceof IContainer) {
+					computeRootPath((IContainer)target, firstLevelPackageNames, containsADefaultPackage);
+				}
 			} else if (target instanceof File) {
 				File file = (File)target;
 				if (file.isDirectory()) {
@@ -454,16 +456,16 @@
 		}
 	}	
 
-	private void computeRootPath(IFolder directory, HashSet firstLevelPackageNames, boolean hasDefaultPackage) {
+	private void computeRootPath(IContainer container, HashSet firstLevelPackageNames, boolean hasDefaultPackage) {
 		try {
-			IResource[] resources = directory.members();
+			IResource[] resources = container.members();
 			boolean hasSubDirectories = false;
 			loop: for (int i = 0, max = resources.length; i < max; i++) {
 				IResource resource = resources[i];
 				if (resource.getType() == IResource.FOLDER) {
 					hasSubDirectories = true;
 					if (firstLevelPackageNames.contains(resource.getName())) {
-						IPath fullPath = resource.getParent().getFullPath();
+						IPath fullPath = container.getFullPath();
 						IPath rootPathEntry = fullPath.removeFirstSegments(this.sourcePath.segmentCount()).setDevice(null);
 						this.rootPaths.add(rootPathEntry.toString());
 						break loop;
@@ -472,12 +474,10 @@
 					}
 				}
 				if (i == max - 1 && !hasSubDirectories && hasDefaultPackage) {
-					IContainer container = resource.getParent();
 					// check if one member is a .java file
-					IResource[] members = container.members();
 					boolean hasJavaSourceFile = false;
-					for (int j = 0, max2 = members.length; j < max2; j++) {
-						if (Util.isJavaFileName(members[i].getName())) {
+					for (int j = 0; j < max; j++) {
+						if (Util.isJavaFileName(resources[i].getName())) {
 							hasJavaSourceFile = true;
 							break;
 						}
@@ -855,27 +855,22 @@
 			 * For example, A.class comes from A.java and p.A.class comes from a file A.java
 			 * in the folder p.
 			 */
-			try {
-				if (type.isMember()) {
-					IType enclosingType = type.getDeclaringType();
-					if (enclosingType == null) return null; // play it safe
-					while (enclosingType.getDeclaringType() != null) {
-						enclosingType = enclosingType.getDeclaringType();
-					}
-					return enclosingType.getElementName() + SUFFIX_STRING_java;
-				} else if (type.isLocal() || type.isAnonymous()){
-					String typeQualifiedName = type.getTypeQualifiedName();
-					return typeQualifiedName.substring(0, typeQualifiedName.indexOf('$')) + SUFFIX_STRING_java;
-				} else {
-					return type.getElementName() + SUFFIX_STRING_java;
+			if (info.isMember()) {
+				IType enclosingType = type.getDeclaringType();
+				if (enclosingType == null) return null; // play it safe
+				while (enclosingType.getDeclaringType() != null) {
+					enclosingType = enclosingType.getDeclaringType();
 				}
-			} catch (JavaModelException e) {
-				// ignore
+				return enclosingType.getElementName() + SUFFIX_STRING_java;
+			} else if (info.isLocal() || info.isAnonymous()){
+				String typeQualifiedName = type.getTypeQualifiedName();
+				return typeQualifiedName.substring(0, typeQualifiedName.indexOf('$')) + SUFFIX_STRING_java;
+			} else {
+				return type.getElementName() + SUFFIX_STRING_java;
 			}
 		} else {
 			return  new String(sourceFileName);
 		}
-		return null;
 	}
 
 	private char[] getSourceForRootPath(String currentRootPath, String name) {
@@ -913,24 +908,27 @@
 			}
 		} else {
 			Object target = JavaModel.getTarget(ResourcesPlugin.getWorkspace().getRoot(), this.sourcePath, true);
-			if (target instanceof IFolder) {
-				IFolder folder = (IFolder)target;
-				IResource res = folder.findMember(fullName);
-				if (res instanceof IFile) {
-					try {
-						source = org.eclipse.jdt.internal.core.util.Util.getResourceContentsAsCharArray((IFile)res);
-					} catch (JavaModelException e) {
-						// ignore
+			if (target instanceof IResource) {
+				if (target instanceof IContainer) {
+					IResource res = ((IContainer)target).findMember(fullName);
+					if (res instanceof IFile) {
+						try {
+							source = org.eclipse.jdt.internal.core.util.Util.getResourceContentsAsCharArray((IFile)res);
+						} catch (JavaModelException e) {
+							// ignore
+						}
 					}
 				}
 			} else if (target instanceof File) {
 				File file = (File)target;
 				if (file.isDirectory()) {
 					File sourceFile = new File(file, fullName);
-					try {
-						source = Util.getFileCharContent(sourceFile, this.encoding);
-					} catch (IOException e) {
-						// ignore
+					if (sourceFile.isFile()) {
+						try {
+							source = Util.getFileCharContent(sourceFile, this.encoding);
+						} catch (IOException e) {
+							// ignore
+						}
 					}
 				}
 			}
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchDocument.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchDocument.java
index 5cda669..7f06e61 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchDocument.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchDocument.java
@@ -56,6 +56,12 @@
 	 * in case of preprocessing.
 	 * <p>
 	 * This method must be implemented in subclasses.
+	 * </p><p>
+	 * Note: some implementation may choose to cache the contents directly on the
+	 * document for performance reason. However, this could induce scalability issues due
+	 * to the fact that collections of documents are manipulated throughout the search
+	 * operation, and cached contents would then consume lots of memory until they are 
+	 * all released at once in the end.
 	 * </p>
 	 * 
 	 * @return the contents of this document,
@@ -69,6 +75,12 @@
 	 * path due to preprocessing.
 	 * <p>
 	 * This method must be implemented in subclasses.
+	 * </p><p>
+	 * Note: some implementation may choose to cache the contents directly on the
+	 * document for performance reason. However, this could induce scalability issues due
+	 * to the fact that collections of documents are manipulated throughout the search
+	 * operation, and cached contents would then consume lots of memory until they are 
+	 * all released at once in the end.
 	 * </p>
 	 * 
 	 * @return the contents of this document,
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java
index 924d55b..5820b8d 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java
@@ -314,7 +314,11 @@
 	if (target instanceof IFile) {
 		request = new AddJarFileToIndex((IFile)target, this);
 	} else if (target instanceof java.io.File) {
-		request = new AddJarFileToIndex(path, this);
+		if (((java.io.File)target).isFile()) {
+			request = new AddJarFileToIndex(path, this);
+		} else {
+			return;
+		}
 	} else if (target instanceof IFolder) {
 		request = new IndexBinaryFolder((IFolder)target, this);
 	} else {
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java
index 5363313..ad34312 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java
@@ -365,7 +365,7 @@
  * Add the possibleMatch to the loop
  *  ->  build compilation unit declarations, their bindings and record their results.
  */
-protected void buildBindings(PossibleMatch possibleMatch) {
+protected void parseAndBuildBindings(PossibleMatch possibleMatch, boolean mustResolve) {
 	if (this.progressMonitor != null && this.progressMonitor.isCanceled())
 		throw new OperationCanceledException();
 
@@ -377,7 +377,7 @@
 		CompilationResult unitResult = new CompilationResult(possibleMatch, 1, 1, this.options.maxProblemsPerUnit);
 		CompilationUnitDeclaration parsedUnit = this.parser.dietParse(possibleMatch, unitResult);
 		if (parsedUnit != null) {
-			if (!parsedUnit.isEmpty())
+			if (mustResolve && !parsedUnit.isEmpty())
 				this.lookupEnvironment.buildTypeBindings(parsedUnit);
 
 			// add the possibleMatch with its parsedUnit to matchesToProcess
@@ -386,9 +386,6 @@
 			if (this.numberOfMatches == size)
 				System.arraycopy(this.matchesToProcess, 0, this.matchesToProcess = new PossibleMatch[size == 0 ? 1 : size * 2], 0, this.numberOfMatches);
 			this.matchesToProcess[this.numberOfMatches++] = possibleMatch;
-
-			if (this.progressMonitor != null)
-				this.progressMonitor.worked(4);
 		}
 	} finally {
 		this.parser.nodeSet = null;
@@ -708,11 +705,22 @@
 	initialize(javaProject, length);
 
 	// create and resolve binding (equivalent to beginCompilation() in Compiler)
-	boolean bindingsWereCreated = true;
+	boolean mustResolve = ((InternalSearchPattern)this.pattern).mustResolve;
+	boolean bindingsWereCreated = mustResolve;
 	try {
-		for (int i = start, maxUnits = start + length; i < maxUnits; i++)
-			buildBindings(possibleMatches[i]);
-		lookupEnvironment.completeTypeBindings();
+		for (int i = start, maxUnits = start + length; i < maxUnits; i++) {
+			PossibleMatch possibleMatch = possibleMatches[i];
+			try {
+				parseAndBuildBindings(possibleMatch, mustResolve);
+				if (!mustResolve)
+					process(possibleMatch, bindingsWereCreated);
+			} finally {
+				if (!mustResolve)
+					possibleMatch.cleanUp();
+			}
+		}
+		if (mustResolve)
+			lookupEnvironment.completeTypeBindings();
 
 		// create hierarchy resolver if needed
 		IType focusType = getFocusType();
@@ -726,6 +734,10 @@
 		bindingsWereCreated = false;
 	}
 
+	if (!mustResolve) {
+		return;
+	}
+	
 	// possible match resolution
 	for (int i = 0; i < this.numberOfMatches; i++) {
 		if (this.progressMonitor != null && this.progressMonitor.isCanceled())
@@ -750,11 +762,8 @@
 						String.valueOf(numberOfMatches),
 						new String(possibleMatch.parsedUnit.getFileName())}));
 			// cleanup compilation unit result
-			possibleMatch.parsedUnit.cleanUp();
-			possibleMatch.parsedUnit = null;
+			possibleMatch.cleanUp();
 		}
-		if (this.progressMonitor != null)
-			this.progressMonitor.worked(5);
 	}
 }
 /**
@@ -766,6 +775,8 @@
 		int max = Math.min(MAX_AT_ONCE, length - index);
 		locateMatches(javaProject, possibleMatches, index, max);
 		index += max;
+		if (this.progressMonitor != null)
+			this.progressMonitor.worked(max);
 	}
 }
 /**
@@ -801,14 +812,14 @@
 			this.handleFactory = new HandleFactory();
 
 		if (this.progressMonitor != null) {
-			// 1 for file path, 4 for parsing and binding creation, 5 for binding resolution? //$NON-NLS-1$
-			this.progressMonitor.beginTask("", searchDocuments.length * (((InternalSearchPattern)this.pattern).mustResolve ? 10 : 5)); //$NON-NLS-1$
+			this.progressMonitor.beginTask("", searchDocuments.length); //$NON-NLS-1$
 		}
 
 		// initialize pattern for polymorphic search (ie. method reference pattern)
 		this.patternLocator.initializePolymorphicSearch(this);
 
 		JavaProject previousJavaProject = null;
+		int skipped = 0;
 		PossibleMatchSet matchSet = new PossibleMatchSet();
 		Util.sort(searchDocuments, new Util.Comparer() {
 			public int compare(Object a, Object b) {
@@ -822,7 +833,10 @@
 			// skip duplicate paths
 			SearchDocument searchDocument = searchDocuments[i];
 			String pathString = searchDocument.getPath();
-			if (i > 0 && pathString.equals(searchDocuments[i - 1].getPath())) continue;
+			if (i > 0 && pathString.equals(searchDocuments[i - 1].getPath())) {
+				skipped++;
+				continue;
+			}
 
 			Openable openable;
 			org.eclipse.jdt.core.ICompilationUnit workingCopy = null;
@@ -848,14 +862,15 @@
 					} catch (JavaModelException e) {
 						// problem with classpath in this project -> skip it
 					}
+					if (this.progressMonitor != null)
+						this.progressMonitor.worked(skipped);
 					matchSet.reset();
 				}
 				previousJavaProject = javaProject;
+				skipped = 0;
 			}
 			matchSet.add(new PossibleMatch(this, resource, openable, searchDocument));
-
-			if (this.progressMonitor != null)
-				this.progressMonitor.worked(1);
+			skipped++;
 		}
 
 		// last project
@@ -865,6 +880,8 @@
 			} catch (JavaModelException e) {
 				// problem with classpath in last project -> ignore
 			}
+			if (this.progressMonitor != null)
+				this.progressMonitor.worked(skipped);
 		} 
 
 		if (this.progressMonitor != null)
@@ -1112,7 +1129,6 @@
 			throw e;
 		}
 	} finally {
-		this.currentPossibleMatch.cleanUp();
 		this.currentPossibleMatch = null;
 	}
 }
@@ -1278,9 +1294,9 @@
 
 }
 protected void reportBinaryMemberDeclaration(IResource resource, IMember binaryMember, IBinaryType info, int accuracy) throws CoreException {
-	ISourceRange range = binaryMember.getNameRange();
+	ClassFile classFile = (ClassFile) binaryMember.getClassFile();
+	ISourceRange range = classFile.isOpen() ? binaryMember.getNameRange() : SourceMapper.fgUnknownRange;
 	if (range.getOffset() == -1) {
-		ClassFile classFile = (ClassFile) binaryMember.getClassFile();
 		SourceMapper mapper = classFile.getSourceMapper();
 		if (mapper != null) {
 			IType type = classFile.getType();
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchingNodeSet.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchingNodeSet.java
index e1792af..37decce 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchingNodeSet.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchingNodeSet.java
@@ -28,8 +28,8 @@
  * Map of matching ast nodes that don't need to be resolved to their accuracy level.
  * Each node is removed as it is reported.
  */
-SimpleLookupTable matchingNodes = new SimpleLookupTable(3);
-private HashtableOfLong matchingNodesKeys = new HashtableOfLong(3);
+SimpleLookupTable matchingNodes = new SimpleLookupTable(3); // node -> accuracy
+private HashtableOfLong matchingNodesKeys = new HashtableOfLong(3); // sourceRange -> node
 static Integer EXACT_MATCH = new Integer(SearchMatch.A_ACCURATE);
 static Integer POTENTIAL_MATCH = new Integer(SearchMatch.A_INACCURATE);
 
@@ -138,6 +138,7 @@
 	return this.matchingNodes.removeKey(node);
 }
 public String toString() {
+	// TODO (jerome) should show both tables
 	StringBuffer result = new StringBuffer();
 	result.append("Exact matches:"); //$NON-NLS-1$
 	Object[] keyTable = this.matchingNodes.keyTable;
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatch.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatch.java
index 917049d..32ba475 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatch.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatch.java
@@ -43,6 +43,9 @@
 }
 public void cleanUp() {
 	this.source = null;
+	this.parsedUnit.cleanUp();
+	this.parsedUnit = null;
+	this.nodeSet = null;
 }
 public boolean equals(Object obj) {
 	if (this.compoundName == null) return super.equals(obj);