diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaIndexTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaIndexTests.java new file mode 100644 index 0000000..c15475f --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaIndexTests.java @@ -0,0 +1,765 @@ +/******************************************************************************* + * Copyright (c) 2011 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.File; +import java.io.IOException; + +import junit.framework.Test; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jdt.core.ClasspathContainerInitializer; +import org.eclipse.jdt.core.IClasspathAttribute; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.index.*; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.internal.core.JavaModelManager; +import org.eclipse.jdt.internal.core.UserLibraryClasspathContainer; +import org.osgi.service.prefs.BackingStoreException; + +public class JavaIndexTests extends AbstractJavaSearchTests { + + static { + // TESTS_NAMES = new String[] {"testPlatformIndexFile"}; + } + public JavaIndexTests(String name) { + super(name); + } + + public static Test suite() { + return buildModelTestSuite(JavaIndexTests.class); + } + // Test that the index file is really generated. + public void testGenerateIndex() throws IOException { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = getExternalResourcePath("Test.jar"); + try { + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + + JavaIndexer.generateIndexForJar(jarFilePath, indexFilePath); + assertTrue(new File(indexFilePath).exists()); + } finally { + new File(indexFilePath).delete(); + new File(jarFilePath).delete(); + } + } + + // Test that the index file and the jar can be deleted after the indexing is done + // This is to ensure that the files are closed + public void testDeleteIndexedFile() { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = getExternalResourcePath("Test.jar"); + try { + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + JavaIndexer.generateIndexForJar(jarFilePath, indexFilePath); + assertTrue("Could not delete the index file", new File(indexFilePath).delete()); + assertTrue("Could not delete the jar file", new File(jarFilePath).delete()); + } catch (IOException e) { + assertFalse("Test failed", true); + } + } + + // Test that search works fine with the index file + public void testUseIndex() throws CoreException, IOException { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = getExternalResourcePath("Test.jar"); + try { + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + JavaIndexer.generateIndexForJar(jarFilePath, indexFilePath); + long modified = new File(indexFilePath).lastModified(); + + IJavaProject p = createJavaProject("P"); + Path libPath = new Path(jarFilePath); + IClasspathAttribute attribute = JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, "file:///"+indexFilePath); + IClasspathEntry entry = JavaCore.newLibraryEntry(libPath, null, null, null, new IClasspathAttribute[]{attribute}, false); + setClasspath(p, new IClasspathEntry[] {entry}); + + waitUntilIndexesReady(); + + // Test that specified index file is really used + java.io.File indexFile = JavaModelManager.getIndexManager().getIndex(libPath, false, false).getIndexFile(); + assertEquals("Specified index file is not being used", indexFilePath,indexFile.toString()); + + // Test that search works properly + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + + // Ensure that the index file is not modified + assertEquals(modified, new File(indexFilePath).lastModified()); + } finally { + deleteProject("P"); + new File(indexFilePath).delete(); + new File(jarFilePath).delete(); + } + } + + // Test that the same index file is used even after restarting + public void testUseIndexAfterRestart() throws IOException, CoreException { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = getExternalResourcePath("Test.jar"); + try { + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + JavaIndexer.generateIndexForJar(jarFilePath, indexFilePath); + long modified = new File(indexFilePath).lastModified(); + IJavaProject p = createJavaProject("P"); + Path libPath = new Path(jarFilePath); + IClasspathAttribute attribute = JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, "file:///"+indexFilePath); + IClasspathEntry entry = JavaCore.newLibraryEntry(libPath, null, null, null, new IClasspathAttribute[]{attribute}, false); + setClasspath(p, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + + simulateExitRestart(); + getJavaModel().refreshExternalArchives(null, null); + waitUntilIndexesReady(); + + this.resultCollector = new JavaSearchResultCollector(); + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + + java.io.File indexFile = JavaModelManager.getIndexManager().getIndex(libPath, false, false).getIndexFile(); + assertEquals(indexFilePath,indexFile.toString()); + // Ensure that the file is not modified + assertEquals(modified, new File(indexFilePath).lastModified()); + } finally { + deleteProject("P"); + new File(indexFilePath).delete(); + new File(jarFilePath).delete(); + } + } + + // Test that the same index file is used even after restarting + public void testUseIndexInternalJarAfterRestart() throws IOException, CoreException { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = "/P/Test.jar"; + try { + IJavaProject p = createJavaProject("P"); + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + JavaIndexer.generateIndexForJar(jarFilePath, indexFilePath); + long modified = new File(indexFilePath).lastModified(); + IPath libPath = new Path(jarFilePath); + IClasspathAttribute attribute = JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, "file:///"+indexFilePath); + IClasspathEntry entry = JavaCore.newLibraryEntry(libPath, null, null, null, new IClasspathAttribute[]{attribute}, false); + setClasspath(p, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults("\\P\\Test.jar pkg.Test"); + + simulateExitRestart(); + getJavaModel().refreshExternalArchives(null, null); + waitUntilIndexesReady(); + + this.resultCollector = new JavaSearchResultCollector(); + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults("\\P\\Test.jar pkg.Test"); + + java.io.File indexFile = JavaModelManager.getIndexManager().getIndex(libPath, false, false).getIndexFile(); + assertEquals(indexFilePath,indexFile.toString()); + // Ensure that the file is not modified + assertEquals(modified, new File(indexFilePath).lastModified()); + } finally { + deleteProject("P"); + new File(indexFilePath).delete(); + } + } + + // Test that a jar file that gets modified after the index is created doesn't return new changes. + // This behavior might have to be modified but.. + public void testModifyJarAfterIndex() throws CoreException, IOException { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = getExternalResourcePath("Test.jar"); + try { + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + JavaIndexer.generateIndexForJar(jarFilePath, indexFilePath); + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}", + "pkg/NewTest.java", + "package pkg;\n" + + "public class NewTest {\n" + + " protected NewTest(int i) {}\n" + + "}"}, jarFilePath); + IJavaProject p = createJavaProject("P"); + Path libPath = new Path(jarFilePath); + IClasspathAttribute attribute = JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, "file:///"+indexFilePath); + IClasspathEntry entry = JavaCore.newLibraryEntry(libPath, null, null, null, new IClasspathAttribute[]{attribute}, false); + setClasspath(p, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + search("NewTest", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(""); + } finally { + deleteProject("P"); + new File(indexFilePath).delete(); + new File(jarFilePath).delete(); + } + } + + // test a non-existent index + public void testNonExistentIndex() throws CoreException, IOException { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = getExternalResourcePath("Test.jar"); + try { + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + IJavaProject p = createJavaProject("P"); + Path libPath = new Path(jarFilePath); + new File(indexFilePath).delete(); + IClasspathAttribute attribute = JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, "file:///"+indexFilePath); + IClasspathEntry entry = JavaCore.newLibraryEntry(libPath, null, null, null, new IClasspathAttribute[]{attribute}, false); + setClasspath(p, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + + } finally { + deleteProject("P"); + new File(indexFilePath).delete(); + new File(jarFilePath).delete(); + } + } + + // test a non-existent index + public void testNonExistentIndexRestart() throws CoreException, IOException { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = getExternalResourcePath("Test.jar"); + try { + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + + new File(indexFilePath).delete(); + + IJavaProject p = createJavaProject("P"); + Path libPath = new Path(jarFilePath); + IClasspathAttribute attribute = JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, "file:///"+indexFilePath); + IClasspathEntry entry = JavaCore.newLibraryEntry(libPath, null, null, null, new IClasspathAttribute[]{attribute}, false); + setClasspath(p, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + + java.io.File indexFile = JavaModelManager.getIndexManager().getIndex(libPath, false, false).getIndexFile(); + long modified = indexFile.lastModified(); + assertEquals(modified, indexFile.lastModified()); + + simulateExitRestart(); + getJavaModel().refreshExternalArchives(null,null); + waitUntilIndexesReady(); + + this.resultCollector = new JavaSearchResultCollector(); + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + + indexFile = JavaModelManager.getIndexManager().getIndex(libPath, false, false).getIndexFile(); + assertEquals("Index File should not have got modified",modified, indexFile.lastModified()); + } finally { + deleteProject("P"); + new File(indexFilePath).delete(); + new File(jarFilePath).delete(); + } + } + + // test that if the index is not existent after restart, it should build up a new index + public void testNonExistentIndexAfterRestart() throws CoreException, IOException { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = getExternalResourcePath("Test.jar"); + try { + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + "protected Test(int i) {}\n" + "}" + },jarFilePath); + JavaIndexer.generateIndexForJar(jarFilePath, indexFilePath); + + IJavaProject p = createJavaProject("P"); + Path libPath = new Path(jarFilePath); + IClasspathAttribute attribute = JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, "file:///"+indexFilePath); + IClasspathEntry entry = JavaCore.newLibraryEntry(libPath, null, null, null, new IClasspathAttribute[]{attribute}, false); + setClasspath(p, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + + simulateExitRestart(); + File indexFile = new File(indexFilePath); + indexFile.delete(); + assertTrue(!indexFile.exists()); + getJavaModel().refreshExternalArchives(null,null); + waitUntilIndexesReady(); + + this.resultCollector = new JavaSearchResultCollector(); + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + + } finally { + deleteProject("P"); + new File(indexFilePath).delete(); + new File(jarFilePath).delete(); + } + } + + // test a non-existent index which becomes existent after restart + public void testExistentIndexAfterRestart() throws CoreException, IOException { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = getExternalResourcePath("Test.jar"); + try { + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + + File indexFile = new File(indexFilePath); + indexFile.delete(); + assertTrue(!indexFile.exists()); + + IJavaProject p = createJavaProject("P"); + Path libPath = new Path(jarFilePath); + IClasspathAttribute attribute = JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, "file:///"+indexFilePath); + IClasspathEntry entry = JavaCore.newLibraryEntry(libPath, null, null, null, new IClasspathAttribute[]{attribute}, false); + setClasspath(p, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + + JavaIndexer.generateIndexForJar(jarFilePath, indexFilePath); + simulateExitRestart(); + getJavaModel().refreshExternalArchives(null,null); + waitUntilIndexesReady(); + + this.resultCollector = new JavaSearchResultCollector(); + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + + indexFile = JavaModelManager.getIndexManager().getIndex(libPath, false, false).getIndexFile(); + assertEquals(indexFilePath,indexFile.toString()); + } finally { + deleteProject("P"); + new File(indexFilePath).delete(); + new File(jarFilePath).delete(); + } + } + + // Test that the index file is not deleted when the project is deleted + public void testDeleteProject() throws CoreException, IOException { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = getExternalResourcePath("Test.jar"); + try { + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + JavaIndexer.generateIndexForJar(jarFilePath, indexFilePath); + IJavaProject p = createJavaProject("P"); + createExternalFolder("externalLib"); + Path libPath = new Path(jarFilePath); + IClasspathAttribute attribute = JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, "file:///"+indexFilePath); + IClasspathEntry entry = JavaCore.newLibraryEntry(libPath, null, null, null, new IClasspathAttribute[]{attribute}, false); + setClasspath(p, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + deleteProject("P"); + File f = new File(indexFilePath); + assertTrue(f.exists()); + } finally { + new File(indexFilePath).delete(); + new File(jarFilePath).delete(); + } + } + + + + // Test index file in platform + public void testPlatformIndexFile() throws CoreException, IOException { + String indexFilePath = null; + String jarFilePath = getExternalResourcePath("Test.jar"); + String indexUrl = "platform:/resource/P/Test.index"; + try { + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + + IJavaProject p = createJavaProject("P"); + indexFilePath = p.getProject().getLocation().append("Test.index").toFile().getAbsolutePath(); + JavaIndexer.generateIndexForJar(jarFilePath, indexFilePath); + long modified = new File(indexFilePath).lastModified(); + + Path libPath = new Path(jarFilePath); + IClasspathAttribute attribute = JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, indexUrl); + IClasspathEntry entry = JavaCore.newLibraryEntry(libPath, null, null, null, new IClasspathAttribute[]{attribute}, false); + setClasspath(p, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + + String indexFileName = JavaModelManager.getIndexManager().getIndex(libPath, false, false).getIndexFile().getName(); + assertEquals(indexFileName, "Test.index"); + + simulateExitRestart(); + getJavaModel().refreshExternalArchives(null,null); + waitUntilIndexesReady(); + + this.resultCollector = new JavaSearchResultCollector(); + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + + indexFileName = JavaModelManager.getIndexManager().getIndex(libPath, false, false).getIndexFile().getName(); + assertEquals(indexFileName, "Test.index"); + + assertEquals(modified, new File(indexFilePath).lastModified()); + } finally { + deleteProject("P"); + if (indexFilePath != null) new File(indexFilePath).delete(); + new File(jarFilePath).delete(); + } + } + + + public void testEditClasspath() throws CoreException, IOException { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = getExternalResourcePath("Test.jar"); + try { + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + JavaIndexer.generateIndexForJar(jarFilePath, indexFilePath); + File f = new File(indexFilePath); + long modified = f.lastModified(); + IJavaProject p = this.createJavaProject("P", new String[] {}, "bin"); + + String content = new String( + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "" + + "\n" + + " \n" + + "\n" + + "\n" + + "\n" + + "\n"); + + editFile("/P/.classpath", content); + p.open(null); + waitUntilIndexesReady(); + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + java.io.File indexFile = JavaModelManager.getIndexManager().getIndex(new Path(jarFilePath), false, false).getIndexFile(); + assertEquals(indexFilePath,indexFile.toString()); + f = new File(indexFilePath); + assertEquals(modified, f.lastModified()); + } finally { + deleteProject("P"); + new File(indexFilePath).delete(); + new File(jarFilePath).delete(); + } + } + + // Test changing the classpath + public void testChangeClasspath() throws CoreException, IOException { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = getExternalResourcePath("Test.jar"); + try { + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + JavaIndexer.generateIndexForJar(jarFilePath, indexFilePath); + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}", + "pkg/NewTest.java", + "package pkg;\n" + + "public class NewTest {\n" + + " protected NewTest(int i) {}\n" + + "}"}, jarFilePath); + IJavaProject p = createJavaProject("P"); + Path libPath = new Path(jarFilePath); + + IClasspathEntry entry = JavaCore.newLibraryEntry(libPath, null, null, null, null, false); + setClasspath(p, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + search("NewTest", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.NewTest"); + + IClasspathAttribute attribute = JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, "file:///"+indexFilePath); + entry = JavaCore.newLibraryEntry(libPath, null, null, null, new IClasspathAttribute[]{attribute}, false); + setClasspath(p, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + this.resultCollector = new JavaSearchResultCollector(); + search("NewTest", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(""); + + entry = JavaCore.newLibraryEntry(libPath, null, null, null, null, false); + setClasspath(p, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + this.resultCollector = new JavaSearchResultCollector(); + search("NewTest", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.NewTest"); + + + } finally { + deleteProject("P"); + new File(indexFilePath).delete(); + new File(jarFilePath).delete(); + } + } + + // Test changing the classpath + public void testChangeClasspathForInternalJar() throws CoreException, IOException { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = "/P/Test.jar"; + try { + IJavaProject p = createJavaProject("P"); + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + JavaIndexer.generateIndexForJar(jarFilePath, indexFilePath); + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}", + "pkg/NewTest.java", + "package pkg;\n" + + "public class NewTest {\n" + + " protected NewTest(int i) {}\n" + + "}"}, jarFilePath); + Path libPath = new Path(jarFilePath); + + IClasspathEntry entry = JavaCore.newLibraryEntry(libPath, null, null, null, null, false); + setClasspath(p, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + search("NewTest", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults("\\P\\Test.jar pkg.NewTest"); + + IClasspathAttribute attribute = JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, "file:///"+indexFilePath); + entry = JavaCore.newLibraryEntry(libPath, null, null, null, new IClasspathAttribute[]{attribute}, false); + setClasspath(p, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + this.resultCollector = new JavaSearchResultCollector(); + search("NewTest", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(""); + + entry = JavaCore.newLibraryEntry(libPath, null, null, null, null, false); + setClasspath(p, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + this.resultCollector = new JavaSearchResultCollector(); + search("NewTest", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults("\\P\\Test.jar pkg.NewTest"); + + + } finally { + deleteProject("P"); + new File(indexFilePath).delete(); + } + } + + public void testMultipleProjects() throws CoreException, IOException { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = getExternalResourcePath("Test.jar"); + try { + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + JavaIndexer.generateIndexForJar(jarFilePath, indexFilePath); + + IJavaProject p1 = createJavaProject("P1"); + Path libPath = new Path(jarFilePath); + IClasspathEntry entry = JavaCore.newLibraryEntry(libPath, null, null, null, null, false); + setClasspath(p1, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + + IJavaProject p2 = createJavaProject("P2"); + IClasspathAttribute attribute = JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, "file:///"+indexFilePath); + entry = JavaCore.newLibraryEntry(libPath, null, null, null, new IClasspathAttribute[]{attribute}, false); + setClasspath(p2, new IClasspathEntry[] {entry}); + waitUntilIndexesReady(); + + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p1})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + + File indexFile = JavaModelManager.getIndexManager().getIndex(libPath, false, false).getIndexFile(); + assertEquals(indexFilePath,indexFile.toString()); + + } finally { + deleteProject("P1"); + deleteProject("P2"); + new File(indexFilePath).delete(); + new File(jarFilePath).delete(); + } + } + + public void setContainerPath(IJavaProject p, IPath jarPath, String indexLocation) throws CoreException, BackingStoreException { + // Create new user library "SomeUserLibrary" + ClasspathContainerInitializer initializer= JavaCore.getClasspathContainerInitializer(JavaCore.USER_LIBRARY_CONTAINER_ID); + String libraryName = "SomeUserLibrary"; + IPath containerPath = new Path(JavaCore.USER_LIBRARY_CONTAINER_ID); + UserLibraryClasspathContainer containerSuggestion = new UserLibraryClasspathContainer(libraryName); + initializer.requestClasspathContainerUpdate(containerPath.append(libraryName), null, containerSuggestion); + + // Modify user library + IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode(JavaCore.PLUGIN_ID); + String propertyName = JavaModelManager.CP_USERLIBRARY_PREFERENCES_PREFIX+"SomeUserLibrary"; + StringBuffer propertyValue = new StringBuffer("\r\n\r\n\r\n"); + propertyValue.append(" \r\n"); + propertyValue.append(" \r\n\r\n"); + propertyValue.append("\r\n"); + propertyValue.append("\r\n"); + preferences.put(propertyName, propertyValue.toString()); + preferences.flush(); + + IClasspathEntry[] entries = p.getRawClasspath(); + int length = entries.length; + System.arraycopy(entries, 0, entries = new IClasspathEntry[length+1], 0, length); + entries[length] = JavaCore.newContainerEntry(containerSuggestion.getPath()); + p.setRawClasspath(entries, null); + + } + public void testUserLibraryIndex() throws IOException, CoreException, BackingStoreException { + String indexFilePath = getExternalResourcePath("Test.index"); + String jarFilePath = getExternalResourcePath("Test.jar"); + try { + createJar(new String[] { + "pkg/Test.java", + "package pkg;\n" + + "public class Test {\n" + + " protected Test(int i) {}\n" + + "}"}, jarFilePath); + JavaIndexer.generateIndexForJar(jarFilePath, indexFilePath); + long modified = new File(indexFilePath).lastModified(); + + IJavaProject p = createJavaProject("P"); + + Path libPath = new Path(jarFilePath); + setContainerPath(p, libPath, "file:///"+indexFilePath); + + waitUntilIndexesReady(); + + // Test that specified index file is really used + java.io.File indexFile = JavaModelManager.getIndexManager().getIndex(libPath, false, false).getIndexFile(); + assertEquals("Specified index file is not being used", indexFilePath,indexFile.toString()); + + // Test that search works properly + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + // Ensure that the index file is not modified + assertEquals(modified, new File(indexFilePath).lastModified()); + + simulateExitRestart(); + getJavaModel().refreshExternalArchives(null,null); + waitUntilIndexesReady(); + + // Test that specified index file is really used + indexFile = JavaModelManager.getIndexManager().getIndex(libPath, false, false).getIndexFile(); + assertEquals("Specified index file is not being used", indexFilePath,indexFile.toString()); + + // Test that search works properly + this.resultCollector = new JavaSearchResultCollector(); + search("Test", TYPE, DECLARATIONS, EXACT_RULE, SearchEngine.createJavaSearchScope(new IJavaElement[]{p})); + assertSearchResults(getExternalPath() + "Test.jar pkg.Test"); + // Ensure that the index file is not modified + assertEquals(modified, new File(indexFilePath).lastModified()); + } finally { + deleteProject("P"); + new File(indexFilePath).delete(); + new File(jarFilePath).delete(); + } + + } +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchScopeTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchScopeTests.java index deb70fd..52af315 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchScopeTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchScopeTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2008 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -1025,7 +1025,7 @@ // Index the output location as it is a library for the project IndexManager indexManager = JavaModelManager.getIndexManager(); - indexManager.indexLibrary(new Path("/P1/bin"), project.getProject()); + indexManager.indexLibrary(new Path("/P1/bin"), project.getProject(), null); waitUntilIndexesReady(); // Search for all types diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/RunJavaSearchTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/RunJavaSearchTests.java index 8917c29..2d7d864 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/RunJavaSearchTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/RunJavaSearchTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -62,6 +62,7 @@ allClasses.add(SearchTests.class); allClasses.add(JavaSearchScopeTests.class); allClasses.add(MatchingRegionsTest.class); + allClasses.add(JavaIndexTests.class); // Reset forgotten subsets of tests TestCase.TESTS_PREFIX = null; diff --git a/org.eclipse.jdt.core/META-INF/MANIFEST.MF b/org.eclipse.jdt.core/META-INF/MANIFEST.MF index 2df1e90..1cc07ca 100644 --- a/org.eclipse.jdt.core/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.core/META-INF/MANIFEST.MF @@ -17,6 +17,7 @@ org.eclipse.jdt.core.jdom, org.eclipse.jdt.core.search, org.eclipse.jdt.core.util, + org.eclipse.jdt.core.index, org.eclipse.jdt.internal.codeassist;x-internal:=true, org.eclipse.jdt.internal.codeassist.complete;x-internal:=true, org.eclipse.jdt.internal.codeassist.impl;x-internal:=true, diff --git a/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/BuildJarIndex.java b/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/BuildJarIndex.java new file mode 100644 index 0000000..dec3aaa --- /dev/null +++ b/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/BuildJarIndex.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2011 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; + +import java.io.IOException; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; +import org.eclipse.jdt.core.index.JavaIndexer; +import org.eclipse.jdt.internal.antadapter.AntAdapterMessages; + +/** + *

+ * An Ant task to generate the index file for the given jar path. + *

+ *

+ * <eclipse.buildJarIndex jarPath="Test.jar" indexPath="Test.index"/> + *

+ *

+ * For more information on Ant check out the website at http://jakarta.apache.org/ant/ . + *

+ *

+ * This is not intended to be subclassed by users. + *

+ * @since 3.8 + */ +public class BuildJarIndex extends Task { + + private String jarPath; + private String indexPath; + + public void execute() throws BuildException { + if (this.jarPath == null) { + throw new BuildException(AntAdapterMessages.getString("buildJarIndex.jarFile.cannot.be.null")); //$NON-NLS-1$ + } + if (this.indexPath == null) { + throw new BuildException(AntAdapterMessages.getString("buildJarIndex.indexFile.cannot.be.null")); //$NON-NLS-1$ + } + + try { + JavaIndexer.generateIndexForJar(this.jarPath, this.indexPath); + } catch (IOException e) { + throw new BuildException(AntAdapterMessages.getString("buildJarIndex.ioexception.occured", e.getLocalizedMessage())); //$NON-NLS-1$ + } + } + + public void setJarPath(String path) { + this.jarPath = path; + } + + public void setIndexPath(String path) { + this.indexPath = path; + } +} diff --git a/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/internal/antadapter/messages.properties b/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/internal/antadapter/messages.properties index df61656..9cb9be2 100644 --- a/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/internal/antadapter/messages.properties +++ b/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/internal/antadapter/messages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2000, 2006 IBM Corporation and others. +# Copyright (c) 2000, 2011 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 @@ -19,3 +19,7 @@ checkDebugAttributes.property.argument.cannot.be.null=The property argument cannot be null checkDebugAttributes.ioexception.occured=IOException occurred while reading checkDebugAttributes.file.argument.must.be.a.classfile.or.a.jarfile=The file argument must be a .class or a .jar file + +buildJarIndex.jarFile.cannot.be.null=The jar file argument cannot be null +buildJarIndex.indexFile.cannot.be.null=The index file argument cannot be null +buildJarIndex.ioexception.occured=IOException - {0} diff --git a/org.eclipse.jdt.core/build.properties b/org.eclipse.jdt.core/build.properties index ff520f9..8e3f678 100644 --- a/org.eclipse.jdt.core/build.properties +++ b/org.eclipse.jdt.core/build.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2000, 2010 IBM Corporation and others. +# Copyright (c) 2000, 2011 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 @@ -24,7 +24,8 @@ org.eclipse.jdt.core.jdom.*,\ org.eclipse.jdt.core.dom.*,\ org.eclipse.jdt.core.dom.rewrite.*,\ - org.eclipse.jdt.core.search.* + org.eclipse.jdt.core.search.*,\ + org.eclipse.jdt.core.index.* source.. = batch/,\ codeassist/,\ compiler/,\ diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IClasspathAttribute.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IClasspathAttribute.java index d90ab15..685c1a3 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IClasspathAttribute.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IClasspathAttribute.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2010 IBM Corporation and others. + * Copyright (c) 2005, 2011 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 @@ -58,6 +58,16 @@ * @since 3.1 */ String JAVADOC_LOCATION_ATTRIBUTE_NAME = "javadoc_location"; //$NON-NLS-1$ + + /** + * Constant for the name of the index location attribute. + * + *

The value for this attribute has to be the string representation of a URL. + * It should point to an existing index file in a folder or a jar. The URL can also be of platform protocol.

+ * + * @since 3.8 + */ + String INDEX_LOCATION_ATTRIBUTE_NAME = "index_location"; //$NON-NLS-1$ /** * Constant for the name of the optional attribute. The possible values diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathChange.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathChange.java index b96cd44..b4da4b3 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathChange.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathChange.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core; +import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -335,7 +336,7 @@ boolean found = false; for (int j = 0; j < accumulatedRoots.size(); j++) { IPackageFragmentRoot root = (IPackageFragmentRoot) accumulatedRoots.elementAt(j); - if (!root.getPath().equals(oldRoot.getPath())) { + if (root.getPath().equals(oldRoot.getPath())) { found = true; break; } @@ -511,7 +512,7 @@ for (int i = 0; i < newLength; i++) { int index = classpathContains(this.oldResolvedClasspath, newResolvedClasspath[i]); - if (index == -1) { + if (index == -1 || newResolvedClasspath[i].getEntryKind() == IClasspathEntry.CPE_LIBRARY) { // remote projects are not indexed in this project if (newResolvedClasspath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT){ continue; @@ -519,19 +520,24 @@ // Request indexing int entryKind = newResolvedClasspath[i].getEntryKind(); + URL newurl = ((ClasspathEntry)newResolvedClasspath[i]).getLibraryIndexLocation(); switch (entryKind) { case IClasspathEntry.CPE_LIBRARY: boolean pathHasChanged = true; IPath newPath = newResolvedClasspath[i].getPath(); - for (int j = 0; j < oldLength; j++) { - IClasspathEntry oldEntry = this.oldResolvedClasspath[j]; - if (oldEntry.getPath().equals(newPath)) { + if (index != -1) { + IClasspathEntry oldEntry = this.oldResolvedClasspath[index]; + URL oldurl = ((ClasspathEntry)oldEntry).getLibraryIndexLocation(); + if (oldurl == null && newurl == null) { pathHasChanged = false; - break; + } else if (oldurl != null && newurl != null) { + pathHasChanged = !(newurl.equals(oldurl)); + } else if (oldurl != null) { + indexManager.removeIndex(newPath); } } if (pathHasChanged) { - indexManager.indexLibrary(newPath, this.project.getProject()); + indexManager.indexLibrary(newPath, this.project.getProject(), newurl); } break; case IClasspathEntry.CPE_SOURCE: 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 5656323..810a6b2 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 @@ -17,6 +17,8 @@ import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -1524,6 +1526,33 @@ return JavaCore.getResolvedClasspathEntry(this); } + + /** + * This function computes the URL of the index location for this classpath entry. It returns null if the URL is + * invalid. + */ + public URL getLibraryIndexLocation() { + switch(getEntryKind()) { + case IClasspathEntry.CPE_LIBRARY : + case IClasspathEntry.CPE_VARIABLE : + break; + default : + return null; + } + if (this.extraAttributes == null) return null; + for (int i= 0; i < this.extraAttributes.length; i++) { + IClasspathAttribute attrib= this.extraAttributes[i]; + if (IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME.equals(attrib.getName())) { + String value = attrib.getValue(); + try { + return new URL(value); + } catch (MalformedURLException e) { + return null; + } + } + } + return null; + } /** * Validate a given classpath and output location for a project, using the following rules: diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java index 126fcc9..c36e288 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java @@ -13,6 +13,7 @@ package org.eclipse.jdt.internal.core; import java.io.File; +import java.net.URL; import java.util.*; import org.eclipse.core.resources.IContainer; @@ -997,8 +998,12 @@ // first remove the index so that it is forced to be re-indexed this.manager.indexManager.removeIndex(entryPath); // then index the jar - this.manager.indexManager.indexLibrary(entryPath, project.getProject()); + this.manager.indexManager.indexLibrary(entryPath, project.getProject(), ((ClasspathEntry)entries[j]).getLibraryIndexLocation()); } else { + URL indexLocation = ((ClasspathEntry)entries[j]).getLibraryIndexLocation(); + if (indexLocation != null) { // force reindexing, this could be faster rather than maintaining the list + this.manager.indexManager.indexLibrary(entryPath, project.getProject(), indexLocation); + } externalArchivesStatus.put(entryPath, EXTERNAL_JAR_UNCHANGED); } } else { @@ -1009,7 +1014,7 @@ this.state.getExternalLibTimeStamps().put(entryPath, new Long(newTimeStamp)); // index the new jar this.manager.indexManager.removeIndex(entryPath); - this.manager.indexManager.indexLibrary(entryPath, project.getProject()); + this.manager.indexManager.indexLibrary(entryPath, project.getProject(), ((ClasspathEntry)entries[j]).getLibraryIndexLocation()); } } } else { // internal JAR @@ -2629,13 +2634,13 @@ switch (delta.getKind()) { case IResourceDelta.ADDED: // index the new jar - indexManager.indexLibrary(jarPath, root.getJavaProject().getProject()); + indexManager.indexLibrary(jarPath, root.getJavaProject().getProject(), root.getIndexPath()); break; case IResourceDelta.CHANGED: // first remove the index so that it is forced to be re-indexed indexManager.removeIndex(jarPath); // then index the jar - indexManager.indexLibrary(jarPath, root.getJavaProject().getProject()); + indexManager.indexLibrary(jarPath, root.getJavaProject().getProject(), root.getIndexPath()); break; case IResourceDelta.REMOVED: // the jar was physically removed: remove the index diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JarPackageFragmentRoot.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JarPackageFragmentRoot.java index 5b18c19..3cd4d07 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JarPackageFragmentRoot.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JarPackageFragmentRoot.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core; +import java.net.URL; import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipException; @@ -292,4 +293,15 @@ return; super.toStringAncestors(buffer); } + + public URL getIndexPath() { + try { + IClasspathEntry entry = ((JavaProject) getParent()).getClasspathEntryFor(getPath()); + if (entry != null) return ((ClasspathEntry)entry).getLibraryIndexLocation(); + } catch (JavaModelException e) { + // ignore exception + } + return null; + } + } diff --git a/org.eclipse.jdt.core/plugin.xml b/org.eclipse.jdt.core/plugin.xml index 3784256..5d69788 100644 --- a/org.eclipse.jdt.core/plugin.xml +++ b/org.eclipse.jdt.core/plugin.xml @@ -1,7 +1,7 @@ @@ -196,6 +201,17 @@ + + + + + + + + + diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/index/JavaIndexer.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/index/JavaIndexer.java new file mode 100644 index 0000000..ba94871 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/index/JavaIndexer.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2011 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.index; + +import java.io.IOException; + +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.internal.core.search.indexing.DefaultJavaIndexer; + +/** + * {@link JavaIndexer} provides functionality to generate index files which can be used by the JDT {@link SearchEngine}. + * The generated index files can be used as a classpath attribute for the particular classpath entry. + * + *

The search engine indexes all the elements referred in the classpath entries of the project into + * index files. These index files are used to search the elements faster. Indexing for bigger jars could + * take some time. To avoid this time, one can generate the index file and specify it when the jar is added + * to the classpath of the project.

+ * + * @since 3.8 + */ +public final class JavaIndexer { + + /** + * Generates the index file for the specified jar. + * @param pathToJar The full path to the jar that needs to be indexed + * @param pathToIndexFile The full path to the index file that needs to be generated + * @throws IOException if the jar is not found or could not write into the index file + * @since 3.8 + */ + public static void generateIndexForJar(String pathToJar, String pathToIndexFile) throws IOException { + new DefaultJavaIndexer().generateIndexForJar(pathToJar, pathToIndexFile); + } + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/index/JavaIndexerApplication.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/index/JavaIndexerApplication.java new file mode 100644 index 0000000..8959bfb --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/index/JavaIndexerApplication.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2011 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.index; + +import java.io.File; +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; + +import org.eclipse.equinox.app.IApplication; +import org.eclipse.equinox.app.IApplicationContext; +import org.eclipse.osgi.util.NLS; + +/** + * Implements an Eclipse Application for {@link org.eclipse.jdt.core.index.JavaIndexer}. + * + *

+ * On MacOS, when invoked using the Eclipse executable, the "user.dir" property is set to the folder in which the + * eclipse.ini file is located. This makes it harder to use relative paths to point to the files to be jar'd or to + * the index file that is generated. + *

+ * + * + * @since 3.8 + * @noinstantiate This class is not intended to be instantiated by clients. + * @noextend This class is not intended to be subclassed by clients. + */ +public class JavaIndexerApplication implements IApplication { + + private final static class Messages extends NLS { + private static final String MESSAGES_NAME = "org.eclipse.jdt.core.index.messages";//$NON-NLS-1$ + + public static String CommandLineProcessing; + public static String CommandLineUsage; + public static String CommandLineOnlyOneOutputError; + public static String CommandLineOutputTakesArgs; + public static String CommandLineOnlyOneJarError; + public static String CommandLineJarNotSpecified; + public static String CommandLineIndexFileNotSpecified; + public static String CaughtException; + public static String CommandLineJarFileNotExist; + + static { + NLS.initializeMessages(MESSAGES_NAME, Messages.class); + } + + public static String bind(String message) { + return bind(message, null); + } + + public static String bind(String message, Object binding) { + return bind(message, new Object[] { binding }); + } + + public static String bind(String message, Object binding1, Object binding2) { + return bind(message, new Object[] { binding1, binding2 }); + } + + public static String bind(String message, Object[] bindings) { + return MessageFormat.format(message, bindings); + } + } + + private String jarToIndex; + private String indexFile; + private boolean verbose = false; + private static final String PDE_LAUNCH = "-pdelaunch"; //$NON-NLS-1$ + private static final String ARG_HELP = "-help"; //$NON-NLS-1$ + private static final String ARG_VERBOSE = "-verbose"; //$NON-NLS-1$ + private static final String ARG_OUTPUT = "-output"; //$NON-NLS-1$ + + private void displayHelp() { + System.out.println(Messages.bind(Messages.CommandLineUsage)); + } + + private void displayError(String message) { + System.out.println(message); + System.out.println(); + displayHelp(); + } + + private boolean processCommandLine(String[] argsArray) { + ArrayList args = new ArrayList(); + for (int i = 0, max = argsArray.length; i < max; i++) { + args.add(argsArray[i]); + } + int index = 0; + final int argCount = argsArray.length; + + loop: while (index < argCount) { + String currentArg = argsArray[index++]; + if (PDE_LAUNCH.equals(currentArg)) { + continue loop; + } else if (ARG_HELP.equals(currentArg)) { + displayHelp(); + return false; + } else if (ARG_VERBOSE.equals(currentArg)) { + this.verbose = true; + continue loop; + } else if (ARG_OUTPUT.equals(currentArg)) { + if (this.indexFile != null) { + displayError(Messages.bind(Messages.CommandLineOnlyOneOutputError)); + return false; + } else if (index == argCount) { + displayError(Messages.bind(Messages.CommandLineOutputTakesArgs)); + return false; + } + this.indexFile = argsArray[index++]; + } else { + if (this.jarToIndex != null) { + displayError(Messages.bind(Messages.CommandLineOnlyOneJarError)); + return false; + } + this.jarToIndex = currentArg; + } + } + return true; + } + + public Object start(IApplicationContext context) throws Exception { + boolean execute = processCommandLine((String[]) context.getArguments().get(IApplicationContext.APPLICATION_ARGS)); + if (execute) { + if (this.jarToIndex != null && this.indexFile != null) { + File f = new File(this.jarToIndex); + if (f.exists()) { + if (this.verbose) { + System.out.println(Messages.bind(Messages.CommandLineProcessing, this.indexFile, this.jarToIndex)); + } + try { + JavaIndexer.generateIndexForJar(this.jarToIndex, this.indexFile); + } catch (IOException e) { + System.out.println(Messages.bind(Messages.CaughtException, "IOException", e.getLocalizedMessage())); //$NON-NLS-1$ + } + } else { + System.out.println(Messages.bind(Messages.CommandLineJarFileNotExist, this.jarToIndex)); + } + } else if (this.jarToIndex == null) { + System.out.println(Messages.bind(Messages.CommandLineJarNotSpecified)); + } else if (this.indexFile == null) { + System.out.println(Messages.bind(Messages.CommandLineIndexFileNotSpecified)); + } + } + return IApplication.EXIT_OK; + } + + public void stop() { + // do nothing + } + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/index/messages.properties b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/index/messages.properties new file mode 100644 index 0000000..7b966bf --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/index/messages.properties @@ -0,0 +1,29 @@ +############################################################################### +# Copyright (c) 2000, 2006 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 +############################################################################### +CommandLineUsage=Usage: eclipse -application org.eclipse.jdt.core.JavaIndexerApplication [ OPTIONS ] -output \n\ +\n\ +\ -output Path to the index file to be generated.\n\ +\ +\ Path to the jar for which index needs to be generated.\n\ +\ +\n\ +\ OPTIONS:\n\ +\n\ +\ -help Display this message.\n\ +\ -verbose Be verbose about the job. +CommandLineProcessing=Generating index {0} for the jar {1}. +CommandLineOnlyOneOutputError=Only one output needs to be specified. +CommandLineOutputTakesArgs=-output should be followed by the path to the index file. +CommandLineOnlyOneJarError=Only one jar file needs to be specified. +CommandLineJarNotSpecified=No jar file is specified. +CommandLineIndexFileNotSpecified=No index file is specified. +CaughtException=Exception {0} - {1}. +CommandLineJarFileNotExist={0} does not exist. diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AddJarFileToIndex.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AddJarFileToIndex.java index 168904f..2a98d01 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AddJarFileToIndex.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/AddJarFileToIndex.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -42,14 +42,17 @@ private static final char JAR_SEPARATOR = IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR.charAt(0); IFile resource; Scanner scanner; + private IPath indexFileURL; - public AddJarFileToIndex(IFile resource, IndexManager manager) { + public AddJarFileToIndex(IFile resource, IPath indexFile, IndexManager manager) { super(resource.getFullPath(), manager); this.resource = resource; + this.indexFileURL = indexFile; } - public AddJarFileToIndex(IPath jarPath, IndexManager manager) { + public AddJarFileToIndex(IPath jarPath, IPath indexFile, IndexManager manager) { // external JAR scenario - no resource super(jarPath, manager); + this.indexFileURL = indexFile; } public boolean equals(Object o) { if (o instanceof AddJarFileToIndex) { @@ -70,6 +73,12 @@ public boolean execute(IProgressMonitor progressMonitor) { if (this.isCancelled || progressMonitor != null && progressMonitor.isCanceled()) return true; + + if (this.indexFileURL != null) { + boolean added = this.manager.addIndex(this.containerPath, this.indexFileURL); + if (added) return true; + this.indexFileURL = null; + } try { // if index is already cached, then do not perform any check @@ -192,7 +201,7 @@ return false; } index.separator = JAR_SEPARATOR; - + IPath indexPath = this.manager.computeIndexLocation(this.containerPath); for (Enumeration e = zip.entries(); e.hasMoreElements();) { if (this.isCancelled) { if (JobManager.VERBOSE) @@ -208,7 +217,7 @@ // index only classes coming from valid packages - https://bugs.eclipse.org/bugs/show_bug.cgi?id=293861 final byte[] classFileBytes = org.eclipse.jdt.internal.compiler.util.Util.getZipEntryByteContent(ze, zip); JavaSearchDocument entryDocument = new JavaSearchDocument(ze, zipFilePath, classFileBytes, participant); - this.manager.indexDocument(entryDocument, participant, index, this.containerPath); + this.manager.indexDocument(entryDocument, participant, index, indexPath); } } this.manager.saveIndex(index); diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/DefaultJavaIndexer.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/DefaultJavaIndexer.java new file mode 100644 index 0000000..54b4fa4 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/DefaultJavaIndexer.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2011 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.internal.core.search.indexing; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.core.search.SearchParticipant; +import org.eclipse.jdt.internal.compiler.util.Util; +import org.eclipse.jdt.internal.core.index.Index; +import org.eclipse.jdt.internal.core.search.JavaSearchDocument; + +public class DefaultJavaIndexer { + private static final char JAR_SEPARATOR = IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR.charAt(0); + + public void generateIndexForJar(String pathToJar, String pathToIndexFile) throws IOException { + File f = new File(pathToJar); + if (!f.exists()) { + throw new FileNotFoundException(pathToJar + " not found"); //$NON-NLS-1$ + } + Index index = new Index(pathToIndexFile, pathToJar, false /*reuse index file*/); + SearchParticipant participant = SearchEngine.getDefaultSearchParticipant(); + index.separator = JAR_SEPARATOR; + ZipFile zip = new ZipFile(pathToJar); + try { + for (Enumeration e = zip.entries(); e.hasMoreElements();) { + // iterate each entry to index it + ZipEntry ze = (ZipEntry) e.nextElement(); + String zipEntryName = ze.getName(); + if (Util.isClassFileName(zipEntryName)) { + final byte[] classFileBytes = org.eclipse.jdt.internal.compiler.util.Util.getZipEntryByteContent(ze, zip); + JavaSearchDocument entryDocument = new JavaSearchDocument(ze, new Path(pathToJar), classFileBytes, participant); + entryDocument.setIndex(index); + new BinaryIndexer(entryDocument).indexDocument(); + } + } + index.save(); + } finally { + zip.close(); + } + return; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexAllProject.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexAllProject.java index 4313621..e68230b 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexAllProject.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexAllProject.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -77,7 +77,7 @@ if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY && entry.getPath().equals(projectPath)) { // the project is also a library folder (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=89815) // ensure a job exists to index it as a binary folder - this.manager.indexLibrary(projectPath, this.project); + this.manager.indexLibrary(projectPath, this.project, ((ClasspathEntry)entry).getLibraryIndexLocation()); return true; } } 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 f08c249..2ab3653 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -11,10 +11,12 @@ package org.eclipse.jdt.internal.core.search.indexing; import java.io.*; +import java.net.URL; import java.util.*; import java.util.zip.CRC32; import org.eclipse.core.resources.*; +import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; @@ -59,6 +61,7 @@ public static final Integer UPDATING_STATE = new Integer(1); public static final Integer UNKNOWN_STATE = new Integer(2); public static final Integer REBUILDING_STATE = new Integer(3); + public static final Integer REUSE_STATE = new Integer(4); // search participants who register indexes with the index manager private SimpleLookupTable participantsContainers = null; @@ -73,7 +76,7 @@ IPath indexLocation = computeIndexLocation(containerPath); Object state = getIndexStates().get(indexLocation); Integer currentIndexState = state == null ? UNKNOWN_STATE : (Integer) state; - if (currentIndexState.equals(REBUILDING_STATE)) return; // already rebuilding the index + if (currentIndexState.compareTo(REBUILDING_STATE) >= 0) return; // already rebuilding the index int compare = newIndexState.compareTo(currentIndexState); if (compare > 0) { @@ -255,7 +258,7 @@ return index; } catch (IOException e) { // failed to read the existing file or its no longer compatible - if (currentIndexState != REBUILDING_STATE) { // rebuild index if existing file is corrupt, unless the index is already being rebuilt + if (currentIndexState != REBUILDING_STATE && currentIndexState != REUSE_STATE) { // rebuild index if existing file is corrupt, unless the index is already being rebuilt if (VERBOSE) Util.verbose("-> cannot reuse existing index: "+indexLocationString+" path: "+containerPathString); //$NON-NLS-1$ //$NON-NLS-2$ rebuildIndex(indexLocation, containerPath); @@ -265,6 +268,15 @@ } } if (currentIndexState == SAVED_STATE) { // rebuild index if existing file is missing + rebuildIndex(indexLocation, containerPath); + return null; + } + if (currentIndexState == REUSE_STATE) { + // supposed to be in reuse state but error in the index file, so reindex. + if (VERBOSE) + Util.verbose("-> cannot reuse given index: "+indexLocation+" path: "+containerPathString); //$NON-NLS-1$ //$NON-NLS-2$ + this.indexLocations.put(containerPath, null); + indexLocation = computeIndexLocation(containerPath); rebuildIndex(indexLocation, containerPath); return null; } @@ -474,7 +486,7 @@ for (int i = 0; i < entries.length; i++) { IClasspathEntry entry= entries[i]; if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) - indexLibrary(entry.getPath(), project); + indexLibrary(entry.getPath(), project, ((ClasspathEntry)entry).getLibraryIndexLocation()); } } catch(JavaModelException e){ // cannot retrieve classpath info } @@ -488,16 +500,24 @@ * Trigger addition of a library to an index * Note: the actual operation is performed in background */ -public void indexLibrary(IPath path, IProject requestingProject) { +public void indexLibrary(IPath path, IProject requestingProject, URL indexURL) { // requestingProject is no longer used to cancel jobs but leave it here just in case + IPath indexFile = null; + if (indexURL != null) { + try { + indexFile = new Path(FileLocator.resolve(indexURL).getPath()); + } catch (IOException e) { + if (VERBOSE) + Util.verbose("-> cannot resolve the url : "+ indexURL + " specified for " + path); //$NON-NLS-1$ //$NON-NLS-2$ + } + } if (JavaCore.getPlugin() == null) return; - - Object target = JavaModel.getTarget(path, true); IndexRequest request = null; + Object target = JavaModel.getTarget(path, true); if (target instanceof IFile) { - request = new AddJarFileToIndex((IFile) target, this); + request = new AddJarFileToIndex((IFile) target, indexFile, this); } else if (target instanceof File) { - request = new AddJarFileToIndex(path, this); + request = new AddJarFileToIndex(path, indexFile, this); } else if (target instanceof IContainer) { request = new IndexBinaryFolder((IContainer) target, this); } else { @@ -508,6 +528,18 @@ if (!isJobWaiting(request)) request(request); } + +synchronized boolean addIndex(IPath containerPath, IPath indexFile) { + this.indexStates.put(indexFile, REUSE_STATE); + this.indexLocations.put(containerPath, indexFile); + Index index = getIndex(containerPath, indexFile, true, false); + if (index == null) { + this.indexLocations.put(containerPath, null); + return false; + } + return true; +} + /** * Index the content of the given source folder. */ @@ -584,9 +616,9 @@ } else if (target instanceof IFolder) { request = new IndexBinaryFolder((IFolder) target, this); } else if (target instanceof IFile) { - request = new AddJarFileToIndex((IFile) target, this); + request = new AddJarFileToIndex((IFile) target, null, this); } else if (target instanceof File) { - request = new AddJarFileToIndex(containerPath, this); + request = new AddJarFileToIndex(containerPath, null, this); } if (request != null) request(request); @@ -643,7 +675,9 @@ } if (indexFile == null) indexFile = new File(indexLocation.toOSString()); // index is not cached yet, but still want to delete the file - if (indexFile.exists()) { + if (this.indexStates.get(indexLocation) == REUSE_STATE) { + this.indexLocations.put(containerPath, null); + } else if (indexFile.exists()) { if (DEBUG) Util.verbose("removing index file " + indexFile); //$NON-NLS-1$ indexFile.delete(); @@ -686,9 +720,15 @@ for (int i = 0; i < count; i++) this.indexes.removeKey(locations[i]); removeIndexesState(locations); - if (this.participantsContainers != null && this.participantsContainers.get(path.toOSString()) != null) { - this.participantsContainers.removeKey(path.toOSString()); - writeParticipantsIndexNamesFile(); + if (this.participantsContainers != null) { + boolean update = false; + for (int i = 0; i < count; i++) { + if (this.participantsContainers.get(locations[i]) != null) { + update = true; + this.participantsContainers.removeKey(locations[i]); + } + } + if (update) writeParticipantsIndexNamesFile(); } } }