diff --git a/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/internal/tests/PositionTrackerTests.java b/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/internal/tests/PositionTrackerTests.java index cf638e3b447..688384fd14f 100644 --- a/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/internal/tests/PositionTrackerTests.java +++ b/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/internal/tests/PositionTrackerTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2007 Wind River Systems, Inc. and others. + * Copyright (c) 2006, 2008 Wind River Systems, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -23,7 +23,7 @@ import org.eclipse.jface.text.Region; public class PositionTrackerTests extends TestCase { public static Test suite() { - return new TestSuite(PositionTrackerTests.class, "PositionTrackerTests"); + return new TestSuite(PositionTrackerTests.class); } public void testInitialFailures() { diff --git a/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/internal/tests/ResourceLookupTests.java b/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/internal/tests/ResourceLookupTests.java new file mode 100644 index 00000000000..0adba55e2de --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/internal/tests/ResourceLookupTests.java @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Markus Schorn - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.internal.tests; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.eclipse.cdt.internal.core.resources.ResourceLookup; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; + +public class ResourceLookupTests extends TestCase { + public static Test suite() { + return new TestSuite(ResourceLookupTests.class); + } + + private IProject fProject; + + @Override + protected void setUp() { + final IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + fProject= root.getProject("reslookup"); + } + + @Override + protected void tearDown() throws Exception { + fProject.delete(true, new NullProgressMonitor()); + } + + protected IFolder createFolder(IProject project, String filename) throws CoreException { + IFolder folder= project.getFolder(filename); + folder.create(true, false, new NullProgressMonitor()); + return folder; + } + + protected IFile createFile(IProject project, String filename) throws CoreException { + IFile file= project.getFile(filename); + file.create(new InputStream() { + @Override + public int read() throws IOException { + return -1; + }}, true, new NullProgressMonitor()); + return file; + } + + public void testNameLookup() throws CoreException { + IProject[] prjs= new IProject[]{fProject}; + + fProject.create(new NullProgressMonitor()); + fProject.open(new NullProgressMonitor()); + createFolder(fProject, "folder1"); + createFolder(fProject, "folder2"); + createFile(fProject, "abc.h"); + createFile(fProject, "folder1/abc.h"); + createFile(fProject, "folder2/abC.h"); + + IFile[] files= ResourceLookup.findFilesByName(new Path("abc.h"), prjs, false); + assertEquals(2, files.length); + files= ResourceLookup.findFilesByName(new Path("bla/../abc.h"), prjs, false); + assertEquals(2, files.length); + files= ResourceLookup.findFilesByName(new Path("../abc.h"), prjs, false); + assertEquals(2, files.length); + files= ResourceLookup.findFilesByName(new Path("../../abc.h"), prjs, false); + assertEquals(2, files.length); + + files= ResourceLookup.findFilesByName(new Path("abc.h"), prjs, true); + assertEquals(3, files.length); + + files= ResourceLookup.findFilesByName(new Path("folder1/abc.h"), prjs, false); + assertEquals(1, files.length); + files= ResourceLookup.findFilesByName(new Path("folder1/abC.h"), prjs, false); + assertEquals(0, files.length); + files= ResourceLookup.findFilesByName(new Path("fOlder1/abc.h"), prjs, false); + assertEquals(0, files.length); + + files= ResourceLookup.findFilesByName(new Path("folder1/abc.h"), prjs, true); + assertEquals(1, files.length); + files= ResourceLookup.findFilesByName(new Path("folder1/abC.h"), prjs, true); + assertEquals(1, files.length); + files= ResourceLookup.findFilesByName(new Path("fOlder1/abc.h"), prjs, true); + assertEquals(1, files.length); + + files= ResourceLookup.findFilesByName(new Path("bla/../abc.h"), prjs, true); + assertEquals(3, files.length); + } + + public void testResourceDelta() throws CoreException { + IProject[] prjs= new IProject[]{fProject}; + fProject.create(new NullProgressMonitor()); + fProject.open(new NullProgressMonitor()); + + IFile[] files= ResourceLookup.findFilesByName(new Path("abc.h"), prjs, true); + assertEquals(0, files.length); + + IFolder f1= createFolder(fProject, "folder1"); + createFolder(fProject, "folder2"); + IFile f2= createFile(fProject, "abc.h"); + files= ResourceLookup.findFilesByName(new Path("abc.h"), prjs, true); + assertEquals(1, files.length); + + createFile(fProject, "folder1/abc.h"); + files= ResourceLookup.findFilesByName(new Path("abc.h"), prjs, true); + assertEquals(2, files.length); + + createFile(fProject, "folder2/abC.h"); + files= ResourceLookup.findFilesByName(new Path("abc.h"), prjs, true); + assertEquals(3, files.length); + + f1.delete(true, new NullProgressMonitor()); + files= ResourceLookup.findFilesByName(new Path("abc.h"), prjs, true); + assertEquals(2, files.length); + + f2.delete(true, new NullProgressMonitor()); + files= ResourceLookup.findFilesByName(new Path("abc.h"), prjs, true); + assertEquals(1, files.length); + } + + public void testDeref() throws CoreException { + IProject[] prjs= new IProject[]{fProject}; + + fProject.create(new NullProgressMonitor()); + fProject.open(new NullProgressMonitor()); + createFolder(fProject, "folder1"); + createFolder(fProject, "folder2"); + createFile(fProject, "abc.h"); + IFile[] files= ResourceLookup.findFilesByName(new Path("abc.h"), prjs, true); + assertEquals(1, files.length); + + ResourceLookup.unrefNodeMap(); + createFile(fProject, "folder1/abc.h"); + createFile(fProject, "folder2/abC.h"); + + files= ResourceLookup.findFilesByName(new Path("abc.h"), prjs, true); + assertEquals(3, files.length); + + ResourceLookup.unrefNodeMap(); + files= ResourceLookup.findFilesByName(new Path("abc.h"), prjs, true); + assertEquals(3, files.length); + } + + public void testCollected() throws CoreException { + IProject[] prjs= new IProject[]{fProject}; + + fProject.create(new NullProgressMonitor()); + fProject.open(new NullProgressMonitor()); + createFolder(fProject, "folder1"); + createFolder(fProject, "folder2"); + createFile(fProject, "abc.h"); + IFile[] files= ResourceLookup.findFilesByName(new Path("abc.h"), prjs, true); + assertEquals(1, files.length); + + ResourceLookup.simulateNodeMapCollection(); + createFile(fProject, "folder1/abc.h"); + createFile(fProject, "folder2/abC.h"); + + files= ResourceLookup.findFilesByName(new Path("abc.h"), prjs, true); + assertEquals(3, files.length); + + ResourceLookup.simulateNodeMapCollection(); + files= ResourceLookup.findFilesByName(new Path("abc.h"), prjs, true); + assertEquals(3, files.length); + } + + public void testFindFilesByLocation() throws Exception { + IProject[] prjs= new IProject[]{fProject}; + + fProject.create(new NullProgressMonitor()); + fProject.open(new NullProgressMonitor()); + createFolder(fProject, "folder1"); + createFolder(fProject, "folder2"); + IFile file= createFile(fProject, "abc.h"); + createFile(fProject, "folder1/abc.h"); + createFile(fProject, "folder2/abC.h"); + + URI uri= file.getLocationURI(); + IFile[] files= ResourceLookup.findFilesForLocation(uri, prjs); + assertEquals(1, files.length); + + if (new File("a").equals(new File("A"))) { + URI upperCase= new URI(uri.getScheme(), uri.getSchemeSpecificPart().toUpperCase(), uri.getFragment()); + files= ResourceLookup.findFilesForLocation(upperCase, prjs); + assertEquals(1, files.length); + } + } +} diff --git a/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/internal/tests/StringBuilderTest.java b/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/internal/tests/StringBuilderTest.java index 513becdf63c..cfd4af72a0c 100644 --- a/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/internal/tests/StringBuilderTest.java +++ b/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/internal/tests/StringBuilderTest.java @@ -19,7 +19,7 @@ import junit.framework.TestSuite; public class StringBuilderTest extends TestCase { public static Test suite() { - return new TestSuite(StringBuilderTest.class, "StringBuilderTest"); + return new TestSuite(StringBuilderTest.class); } public void testSafe() { diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/LocationMapTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/LocationMapTests.java index 95cf7a19c6a..f37ba6b952e 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/LocationMapTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/LocationMapTests.java @@ -334,8 +334,8 @@ public class LocationMapTests extends BaseTestCase { public void testIncludes() { init(DIGITS); - fLocationMap.encounterPoundInclude(0, 0, 0, 0, "n1".toCharArray(), null, true, false); - fLocationMap.encounterPoundInclude(0, 1, 3, 16, "n2".toCharArray(), "f2", false , true); + fLocationMap.encounterPoundInclude(0, 0, 0, 0, "n1".toCharArray(), null, true, false, false); + fLocationMap.encounterPoundInclude(0, 1, 3, 16, "n2".toCharArray(), "f2", false , true, false); IASTPreprocessorIncludeStatement[] includes= fLocationMap.getIncludeDirectives(); assertEquals(2, includes.length); checkInclude(includes[0], "", "", "n1", "", true, false, FN, 0, 0, 1, 0, 0); @@ -495,11 +495,11 @@ public class LocationMapTests extends BaseTestCase { assertEquals(FN, fLocationMap.getCurrentFilePath()); fLocationMap.encounteredComment(0,2,true); // number: [6,15)[25,26) - ILocationCtx i1= fLocationMap.pushInclusion(0, 2, 4, 6, "b1b2b3b4b5".toCharArray(), "pre1", "pre1".toCharArray(), false); + ILocationCtx i1= fLocationMap.pushInclusion(0, 2, 4, 6, "b1b2b3b4b5".toCharArray(), "pre1", "pre1".toCharArray(), false, false); assertEquals("pre1", fLocationMap.getCurrentFilePath()); fLocationMap.encounteredComment(2,4,true); // number: [15,25) - ILocationCtx i2= fLocationMap.pushInclusion(6, 7, 8, 9, "c1c2c3c4c5".toCharArray(), "pre11", "pre11".toCharArray(), false); + ILocationCtx i2= fLocationMap.pushInclusion(6, 7, 8, 9, "c1c2c3c4c5".toCharArray(), "pre11", "pre11".toCharArray(), false, false); assertEquals("pre11", fLocationMap.getCurrentFilePath()); fLocationMap.encounteredComment(2,6,true); fLocationMap.popContext(i2); @@ -512,7 +512,7 @@ public class LocationMapTests extends BaseTestCase { fLocationMap.popContext(pre2); assertEquals(FN, fLocationMap.getCurrentFilePath()); // number [36, 46) - ILocationCtx i3= fLocationMap.pushInclusion(0, 2, 4, 6, "d1d2d3d4d5".toCharArray(), "pre2", "pre2".toCharArray(), false); + ILocationCtx i3= fLocationMap.pushInclusion(0, 2, 4, 6, "d1d2d3d4d5".toCharArray(), "pre2", "pre2".toCharArray(), false, false); assertEquals("pre2", fLocationMap.getCurrentFilePath()); fLocationMap.encounteredComment(0,2,true); fLocationMap.popContext(i3); diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexBugsTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexBugsTests.java index 240594951b4..ce5c3f7f2f0 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexBugsTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexBugsTests.java @@ -1350,4 +1350,53 @@ public class IndexBugsTests extends BaseTestCase { fIndex.releaseReadLock(); } } + + + // #include + // #define _CONCAT(x,y) x##y + // #define CONCAT(x,y) _CONCAT(x,y) + public void testIncludeHeuristics_Bug213562() throws Exception { + String contents= getContentsForTest(1)[0]; + final IIndexManager indexManager = CCorePlugin.getIndexManager(); + TestSourceReader.createFile(fCProject.getProject(), "f1/g/header.h", "#define ID one\n"); + TestSourceReader.createFile(fCProject.getProject(), "f2/header.h", "#define ID two\n"); + TestSourceReader.createFile(fCProject.getProject(), "f1/g/h/header.h", "#define ID three\n"); + TestSourceReader.createFile(fCProject.getProject(), "f1/g/source.cpp", contents + "int CONCAT(one, ID);\n"); + TestSourceReader.createFile(fCProject.getProject(), "f2/g/source.cpp", contents + "int CONCAT(two, ID);\n"); + TestSourceReader.createFile(fCProject.getProject(), "f1/g/h/source.cpp", contents + "int CONCAT(three, ID);\n"); + indexManager.reindex(fCProject); + waitForIndexer(); + fIndex.acquireReadLock(); + try { + IIndexBinding[] bindings= fIndex.findBindings("oneone".toCharArray(), IndexFilter.ALL_DECLARED, new NullProgressMonitor()); + assertEquals(1, bindings.length); + bindings= fIndex.findBindings("twotwo".toCharArray(), IndexFilter.ALL_DECLARED, new NullProgressMonitor()); + assertEquals(1, bindings.length); + bindings= fIndex.findBindings("threethree".toCharArray(), IndexFilter.ALL_DECLARED, new NullProgressMonitor()); + assertEquals(1, bindings.length); + } finally { + fIndex.releaseReadLock(); + } + } + + public void testIncludeHeuristicsFlag_Bug213562() throws Exception { + final IIndexManager indexManager = CCorePlugin.getIndexManager(); + TestSourceReader.createFile(fCProject.getProject(), "f1/header.h", ""); + IFile f1= TestSourceReader.createFile(fCProject.getProject(), "source1.cpp", "#include \"header.h\"\n"); + IFile f2= TestSourceReader.createFile(fCProject.getProject(), "source2.cpp", "#include \"f1/header.h\"\n"); + indexManager.reindex(fCProject); + waitForIndexer(); + fIndex.acquireReadLock(); + try { + IIndexFile f= fIndex.getFile(ILinkage.CPP_LINKAGE_ID, IndexLocationFactory.getWorkspaceIFL(f1)); + IIndexInclude i= f.getIncludes()[0]; + assertTrue(i.isResolvedByHeuristics()); + + f= fIndex.getFile(ILinkage.CPP_LINKAGE_ID, IndexLocationFactory.getWorkspaceIFL(f2)); + i= f.getIncludes()[0]; + assertFalse(i.isResolvedByHeuristics()); + } finally { + fIndex.releaseReadLock(); + } + } } \ No newline at end of file diff --git a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java index 571a9aea566..66074479d89 100644 --- a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java +++ b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java @@ -18,6 +18,7 @@ import junit.framework.TestSuite; import org.eclipse.cdt.core.cdescriptor.tests.CDescriptorTests; import org.eclipse.cdt.core.internal.errorparsers.tests.ErrorParserTests; import org.eclipse.cdt.core.internal.tests.PositionTrackerTests; +import org.eclipse.cdt.core.internal.tests.ResourceLookupTests; import org.eclipse.cdt.core.internal.tests.StringBuilderTest; import org.eclipse.cdt.core.language.AllLanguageTests; import org.eclipse.cdt.core.model.tests.AllCoreTests; @@ -61,6 +62,7 @@ public class AutomatedIntegrationSuite extends TestSuite { suite.addTest(ElementDeltaTests.suite()); suite.addTest(WorkingCopyTests.suite()); suite.addTest(PositionTrackerTests.suite()); + suite.addTest(ResourceLookupTests.suite()); suite.addTest(StringBuilderTest.suite()); suite.addTest(AllLanguageTests.suite()); suite.addTest(RewriteTests.suite()); diff --git a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF index c785d94a4bc..56a5cc21242 100644 --- a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF @@ -71,6 +71,7 @@ Export-Package: org.eclipse.cdt.core, org.eclipse.cdt.internal.core.pdom.dom.cpp;x-internal:=true, org.eclipse.cdt.internal.core.pdom.export;x-internal:=true, org.eclipse.cdt.internal.core.pdom.indexer;x-friends:="org.eclipse.cdt.ui", + org.eclipse.cdt.internal.core.resources;x-internal:=true, org.eclipse.cdt.internal.core.util;x-internal:=true, org.eclipse.cdt.internal.errorparsers;x-internal:=true, org.eclipse.cdt.internal.formatter;x-internal:=true, diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/TranslationUnit.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/TranslationUnit.java index fbf880cf229..95faec1b33f 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/TranslationUnit.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/TranslationUnit.java @@ -70,6 +70,7 @@ import org.eclipse.cdt.internal.core.dom.NullCodeReaderFactory; import org.eclipse.cdt.internal.core.dom.SavedCodeReaderFactory; import org.eclipse.cdt.internal.core.index.IndexBasedCodeReaderFactory; import org.eclipse.cdt.internal.core.parser.ParserLogService; +import org.eclipse.cdt.internal.core.pdom.indexer.ProjectIndexerIncludeResolutionHeuristics; import org.eclipse.cdt.internal.core.pdom.indexer.ProjectIndexerInputAdapter; import org.eclipse.cdt.internal.core.util.ICanceler; import org.eclipse.cdt.internal.core.util.MementoTokenizer; @@ -835,15 +836,20 @@ public class TranslationUnit extends Openable implements ITranslationUnit { } private ICodeReaderFactory getCodeReaderFactory(int style, IIndex index, int linkageID) { + final ICProject cprj= getCProject(); + final ProjectIndexerInputAdapter pathResolver = new ProjectIndexerInputAdapter(cprj); + final ProjectIndexerIncludeResolutionHeuristics heuristics = new ProjectIndexerIncludeResolutionHeuristics(cprj.getProject(), pathResolver); ICodeReaderFactory codeReaderFactory; if ((style & AST_SKIP_NONINDEXED_HEADERS) != 0) { codeReaderFactory= NullCodeReaderFactory.getInstance(); } else { - codeReaderFactory= SavedCodeReaderFactory.getInstance(); + codeReaderFactory= SavedCodeReaderFactory.createInstance(heuristics); } if (index != null && (style & AST_SKIP_INDEXED_HEADERS) != 0) { - IndexBasedCodeReaderFactory ibcf= new IndexBasedCodeReaderFactory(index, new ProjectIndexerInputAdapter(getCProject()), linkageID, codeReaderFactory); + IndexBasedCodeReaderFactory ibcf= new IndexBasedCodeReaderFactory(index, + heuristics, + pathResolver, linkageID, codeReaderFactory); if ((style & AST_CONFIGURE_USING_SOURCE_CONTEXT) != 0) { ibcf.setSupportFillGapFromContextToHeader(true); } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTPreprocessorIncludeStatement.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTPreprocessorIncludeStatement.java index 94b2eac5c11..7770098f7b4 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTPreprocessorIncludeStatement.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTPreprocessorIncludeStatement.java @@ -1,20 +1,20 @@ /******************************************************************************* - * Copyright (c) 2004, 2007 IBM Corporation and others. + * Copyright (c) 2004, 2008 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 - Initial API and implementation - * Markus Schorn (Wind River Systems) + * IBM - Initial API and implementation + * Markus Schorn (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.core.dom.ast; /** * This interface represent a preprocessor #include statement. * - * @author jcamelon + * @noimplement This interface is not intended to be implemented by clients. */ public interface IASTPreprocessorIncludeStatement extends IASTPreprocessorStatement { @@ -57,4 +57,10 @@ public interface IASTPreprocessorIncludeStatement extends * @since 4.0 */ public boolean isResolved(); + + /** + * Returns whether the inclusion was resolved using a heuristics. + * @since 5.1 + */ + public boolean isResolvedByHeuristics(); } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/index/IIndexInclude.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/index/IIndexInclude.java index fe134135140..39f5267bde5 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/index/IIndexInclude.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/index/IIndexInclude.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2007 Wind River Systems, Inc. and others. + * Copyright (c) 2006, 2008 Wind River Systems, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -9,23 +9,14 @@ * Markus Schorn - initial API and implementation * Andrew Ferguson (Symbian) *******************************************************************************/ - package org.eclipse.cdt.core.index; import org.eclipse.core.runtime.CoreException; /** * Interface for an include directive stored in the index. - *

- * This interface is not intended to be implemented by clients. - *

- *

- * EXPERIMENTAL. This class or interface has been added as - * part of a work in progress. There is no guarantee that this API will work or - * that it will remain the same. Please do not use this API without consulting - * with the CDT team. - *

* + * @noimplement This interface is not intended to be implemented by clients. * @since 4.0 */ public interface IIndexInclude { @@ -106,4 +97,11 @@ public interface IIndexInclude { * @throws CoreException */ boolean isResolved() throws CoreException; + + /** + * Tests whether this include has been resolved using a heuristics rather than relying on + * the include search path. + * @since 5.1 + */ + boolean isResolvedByHeuristics() throws CoreException; } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IndexBasedCodeReaderFactory.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IndexBasedCodeReaderFactory.java index 6c0b7ad20ef..60903447ad6 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IndexBasedCodeReaderFactory.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IndexBasedCodeReaderFactory.java @@ -34,6 +34,8 @@ import org.eclipse.cdt.core.index.IIndexMacro; import org.eclipse.cdt.core.parser.CodeReader; import org.eclipse.cdt.core.parser.ICodeReaderCache; import org.eclipse.cdt.core.parser.ParserUtil; +import org.eclipse.cdt.internal.core.dom.AbstractCodeReaderFactory; +import org.eclipse.cdt.internal.core.dom.IIncludeFileResolutionHeuristics; import org.eclipse.cdt.internal.core.parser.scanner.IIndexBasedCodeReaderFactory; import org.eclipse.cdt.internal.core.parser.scanner.IncludeFileContent; import org.eclipse.cdt.internal.core.parser.scanner.IncludeFileContent.InclusionKind; @@ -46,7 +48,7 @@ import org.eclipse.core.runtime.CoreException; * Code reader factory, that fakes code readers for header files already stored in the * index. */ -public final class IndexBasedCodeReaderFactory implements IIndexBasedCodeReaderFactory { +public final class IndexBasedCodeReaderFactory extends AbstractCodeReaderFactory implements IIndexBasedCodeReaderFactory { private static final class NeedToParseException extends Exception {} private static final String GAP = "__gap__"; //$NON-NLS-1$ @@ -59,13 +61,15 @@ public final class IndexBasedCodeReaderFactory implements IIndexBasedCodeReaderF private final AbstractIndexerTask fRelatedIndexerTask; private boolean fSupportFillGapFromContextToHeader= false; - public IndexBasedCodeReaderFactory(IIndex index, ASTFilePathResolver pathResolver, int linkage, - ICodeReaderFactory fallbackFactory) { - this(index, pathResolver, linkage, fallbackFactory, null); + public IndexBasedCodeReaderFactory(IIndex index, IIncludeFileResolutionHeuristics heuristics, + ASTFilePathResolver pathResolver, int linkage, ICodeReaderFactory fallbackFactory) { + this(index, heuristics, pathResolver, linkage, fallbackFactory, null); } - public IndexBasedCodeReaderFactory(IIndex index, ASTFilePathResolver pathResolver, int linkage, + public IndexBasedCodeReaderFactory(IIndex index, IIncludeFileResolutionHeuristics heuristics, + ASTFilePathResolver pathResolver, int linkage, ICodeReaderFactory fallbackFactory, AbstractIndexerTask relatedIndexerTask) { + super(heuristics); fIndex= index; fFallBackFactory= fallbackFactory; fPathResolver= pathResolver; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/indexer/StandaloneFastIndexerTask.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/indexer/StandaloneFastIndexerTask.java index 64dc7feca98..6114a471cd4 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/indexer/StandaloneFastIndexerTask.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/indexer/StandaloneFastIndexerTask.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2007 QNX Software Systems and others. + * Copyright (c) 2006, 2008 QNX Software Systems 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 @@ -16,6 +16,7 @@ package org.eclipse.cdt.internal.core.indexer; import java.util.List; import org.eclipse.cdt.core.dom.ICodeReaderFactory; +import org.eclipse.cdt.internal.core.dom.IIncludeFileResolutionHeuristics; /** * A task for index updates. @@ -38,4 +39,9 @@ public class StandaloneFastIndexerTask extends StandaloneIndexerTask { protected ICodeReaderFactory createReaderFactory() { return new StandaloneIndexerFallbackReaderFactory(); } + + @Override + protected IIncludeFileResolutionHeuristics createIncludeHeuristics() { + return null; + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/indexer/StandaloneFullIndexerTask.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/indexer/StandaloneFullIndexerTask.java index 7a4d9642557..de3d76a3d4c 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/indexer/StandaloneFullIndexerTask.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/indexer/StandaloneFullIndexerTask.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2007 QNX Software Systems and others. + * Copyright (c) 2006, 2008 QNX Software Systems 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 @@ -16,6 +16,7 @@ package org.eclipse.cdt.internal.core.indexer; import java.util.List; import org.eclipse.cdt.core.dom.ICodeReaderFactory; +import org.eclipse.cdt.internal.core.dom.IIncludeFileResolutionHeuristics; /** * A task for index updates. @@ -39,4 +40,9 @@ public class StandaloneFullIndexerTask extends StandaloneIndexerTask { protected ICodeReaderFactory createReaderFactory() { return ((StandaloneFullIndexer)fIndexer).getCodeReaderFactory(); } + + @Override + protected IIncludeFileResolutionHeuristics createIncludeHeuristics() { + return null; + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java index 5dc1d3d2e52..e62f74a19c5 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java @@ -212,16 +212,18 @@ class ASTInclusionStatement extends ASTPreprocessorNode implements IASTPreproces private final boolean fIsActive; private final boolean fIsResolved; private final boolean fIsSystemInclude; + private final boolean fFoundByHeuristics; public ASTInclusionStatement(IASTTranslationUnit parent, int startNumber, int nameStartNumber, int nameEndNumber, int endNumber, - char[] headerName, String filePath, boolean userInclude, boolean active) { + char[] headerName, String filePath, boolean userInclude, boolean active, boolean heuristic) { super(parent, IASTTranslationUnit.PREPROCESSOR_STATEMENT, startNumber, endNumber); fName= new ASTPreprocessorName(this, IASTPreprocessorIncludeStatement.INCLUDE_NAME, nameStartNumber, nameEndNumber, headerName, null); fPath= filePath == null ? "" : filePath; //$NON-NLS-1$ fIsActive= active; fIsResolved= filePath != null; fIsSystemInclude= !userInclude; + fFoundByHeuristics= heuristic; } public IASTName getName() { @@ -249,6 +251,10 @@ class ASTInclusionStatement extends ASTPreprocessorNode implements IASTPreproces super.findNode(nodeSpec); nodeSpec.visit(fName); } + + public boolean isResolvedByHeuristics() { + return fFoundByHeuristics; + } } class ASTMacroDefinition extends ASTPreprocessorNode implements IASTPreprocessorObjectStyleMacroDefinition { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java index 43774647f4e..f2fa958cdac 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java @@ -44,6 +44,7 @@ import org.eclipse.cdt.core.parser.ParserLanguage; import org.eclipse.cdt.core.parser.util.CharArrayIntMap; import org.eclipse.cdt.core.parser.util.CharArrayMap; import org.eclipse.cdt.core.parser.util.CharArrayUtils; +import org.eclipse.cdt.internal.core.dom.IIncludeFileResolutionHeuristics; import org.eclipse.cdt.internal.core.parser.scanner.ExpressionEvaluator.EvalException; import org.eclipse.cdt.internal.core.parser.scanner.IncludeFileContent.InclusionKind; import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions; @@ -87,21 +88,29 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { private static final DynamicMacro __LINE__ = new LineMacro("__LINE__".toCharArray()); //$NON-NLS-1$ private interface IIncludeFileTester { - T checkFile(String path, String fileName); + T checkFile(String path, String fileName, boolean isHeuristicMatch); } final private IIncludeFileTester createCodeReaderTester= new IIncludeFileTester() { - public IncludeFileContent checkFile(String path, String fileName) { - String finalPath = ScannerUtility.createReconciledPath(path, fileName); - return fCodeReaderFactory.getContentForInclusion(finalPath); + public IncludeFileContent checkFile(String path, String fileName, boolean isHeuristicMatch) { + final String finalPath = ScannerUtility.createReconciledPath(path, fileName); + final IncludeFileContent fc= fCodeReaderFactory.getContentForInclusion(finalPath); + if (fc != null) { + fc.setFoundByHeuristics(isHeuristicMatch); + } + return fc; } }; - final private IIncludeFileTester createPathTester= new IIncludeFileTester() { - public String checkFile(String path, String fileName) { + private static class IncludeResolution {String fLocation; boolean fHeuristic;} + final private IIncludeFileTester createPathTester= new IIncludeFileTester() { + public IncludeResolution checkFile(String path, String fileName, boolean isHeuristicMatch) { String finalPath= ScannerUtility.createReconciledPath(path, fileName); if (fCodeReaderFactory.getInclusionExists(finalPath)) { - return finalPath; + IncludeResolution res= new IncludeResolution(); + res.fHeuristic= isHeuristicMatch; + res.fLocation= finalPath; + return res; } return null; } @@ -109,6 +118,8 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { final private IParserLogService fLog; final private IIndexBasedCodeReaderFactory fCodeReaderFactory; + + private IIncludeFileResolutionHeuristics fIncludeFileResolutionHeuristics; private final ExpressionEvaluator fExpressionEvaluator; private final MacroDefinitionParser fMacroDefinitionParser; private final MacroExpander fMacroExpander; @@ -142,7 +153,6 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { private Token fPrefetchedTokens; private Token fLastToken; - public CPreprocessor(CodeReader reader, IScannerInfo info, ParserLanguage language, IParserLogService log, IScannerExtensionConfiguration configuration, ICodeReaderFactory readerFactory) { fLog = log; @@ -162,6 +172,9 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { fMacroDefinitionParser= new MacroDefinitionParser(); fMacroExpander= new MacroExpander(this, fMacroDictionary, fLocationMap, fLexOptions); fCodeReaderFactory= wrapReaderFactory(readerFactory); + if (readerFactory instanceof IAdaptable) { + fIncludeFileResolutionHeuristics= (IIncludeFileResolutionHeuristics) ((IAdaptable) readerFactory).getAdapter(IIncludeFileResolutionHeuristics.class); + } setupMacroDictionary(configuration, info, language); @@ -746,43 +759,50 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { } } - private IncludeFileContent findInclusion(final String filename, final boolean quoteInclude, - final boolean includeNext, final File currentDir) { - return findInclusion(filename, quoteInclude, includeNext, currentDir, createCodeReaderTester); - } - private T findInclusion(final String filename, final boolean quoteInclude, - final boolean includeNext, final File currentDirectory, final IIncludeFileTester tester) { + final boolean includeNext, final String currentFile, final IIncludeFileTester tester) { T reader = null; // filename is an absolute path or it is a Linux absolute path on a windows machine if (new File(filename).isAbsolute() || filename.startsWith("/")) { //$NON-NLS-1$ - return tester.checkFile( EMPTY_STRING, filename ); + return tester.checkFile(EMPTY_STRING, filename, false); } - - if (currentDirectory != null && quoteInclude && !includeNext) { + + if (currentFile != null && quoteInclude && !includeNext) { // Check to see if we find a match in the current directory - String absolutePath = currentDirectory.getAbsolutePath(); - reader = tester.checkFile(absolutePath, filename); - if (reader != null) { - return reader; - } + final File currentDir= new File(currentFile).getParentFile(); + if (currentDir != null) { + String absolutePath = currentDir.getAbsolutePath(); + reader = tester.checkFile(absolutePath, filename, false); + if (reader != null) { + return reader; + } + } } // if we're not include_next, then we are looking for the first occurrence of // the file, otherwise, we ignore all the paths before the current directory final String[] isp= quoteInclude ? fQuoteIncludePaths : fIncludePaths; - if (isp != null ) { + if (isp != null) { int i=0; - if (includeNext && currentDirectory != null) { - i= findIncludePos(isp, currentDirectory) + 1; + if (includeNext && currentFile != null) { + final File currentDir= new File(currentFile).getParentFile(); + if (currentDir != null) { + i= findIncludePos(isp, currentDir) + 1; + } } for (; i < isp.length; ++i) { - reader= tester.checkFile(isp[i], filename); + reader= tester.checkFile(isp[i], filename, false); if (reader != null) { return reader; } } } + if (fIncludeFileResolutionHeuristics != null) { + String location= fIncludeFileResolutionHeuristics.findInclusion(filename, currentFile); + if (location != null) { + return tester.checkFile(null, location, true); + } + } return null; } @@ -1027,20 +1047,21 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { String path= null; boolean reported= false; + boolean isHeuristic= false; if (!active) { // test if the include is inactive just because it was included before (bug 167100) - final File currentDir= userInclude || include_next ? new File(String.valueOf(getCurrentFilename())).getParentFile() : null; - final String resolved= findInclusion(new String(headerName), userInclude, include_next, currentDir, createPathTester); - if (resolved != null && fCodeReaderFactory.hasFileBeenIncludedInCurrentTranslationUnit(resolved)) { - path= resolved; + final IncludeResolution resolved= findInclusion(new String(headerName), userInclude, include_next, getCurrentFilename(), createPathTester); + if (resolved != null && fCodeReaderFactory.hasFileBeenIncludedInCurrentTranslationUnit(resolved.fLocation)) { + path= resolved.fLocation; + isHeuristic= resolved.fHeuristic; } } else { - final File currentDir= userInclude || include_next ? new File(String.valueOf(getCurrentFilename())).getParentFile() : null; - final IncludeFileContent fi= findInclusion(new String(headerName), userInclude, include_next, currentDir); + final IncludeFileContent fi= findInclusion(new String(headerName), userInclude, include_next, getCurrentFilename(), createCodeReaderTester); if (fi != null) { path= fi.getFileLocation(); + isHeuristic= fi.isFoundByHeuristics(); switch(fi.getKind()) { case FOUND_IN_INDEX: processInclusionFromIndex(poundOffset, path, fi); @@ -1050,7 +1071,7 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { if (reader != null && !isCircularInclusion(path)) { reported= true; fAllIncludedFiles.add(path); - ILocationCtx ctx= fLocationMap.pushInclusion(poundOffset, nameOffsets[0], nameOffsets[1], condEndOffset, reader.buffer, path, headerName, userInclude); + ILocationCtx ctx= fLocationMap.pushInclusion(poundOffset, nameOffsets[0], nameOffsets[1], condEndOffset, reader.buffer, path, headerName, userInclude, isHeuristic); ScannerContext fctx= new ScannerContext(ctx, fCurrentContext, new Lexer(reader.buffer, fLexOptions, this, this)); fCurrentContext= fctx; } @@ -1074,7 +1095,7 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { } if (!reported) { - fLocationMap.encounterPoundInclude(poundOffset, nameOffsets[0], nameOffsets[1], condEndOffset, headerName, path, userInclude, active); + fLocationMap.encounterPoundInclude(poundOffset, nameOffsets[0], nameOffsets[1], condEndOffset, headerName, path, userInclude, active, isHeuristic); } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeFileContent.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeFileContent.java index 5d57781cdec..8d104520c2a 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeFileContent.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeFileContent.java @@ -43,6 +43,7 @@ public class IncludeFileContent { private final List fMacroDefinitions; private final List fUsingDirectives; private final String fFileLocation; + private boolean fHeuristic; private List fFiles; /** @@ -144,4 +145,15 @@ public class IncludeFileContent { public List getFilesIncluded() { return fFiles; } + + /** + * Returns whether this inclusion was found by a heuristics. + */ + public boolean isFoundByHeuristics() { + return fHeuristic; + } + + public void setFoundByHeuristics(boolean val) { + fHeuristic= val; + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationMap.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationMap.java index 9cff7875c2c..350642f8700 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationMap.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/LocationMap.java @@ -116,14 +116,14 @@ public class LocationMap implements ILocationResolver { * @param userInclude true when specified with double-quotes. */ public ILocationCtx pushInclusion(int startOffset, int nameOffset, int nameEndOffset, int endOffset, - char[] buffer, String filename, char[] name, boolean userInclude) { + char[] buffer, String filename, char[] name, boolean userInclude, boolean heuristic) { assert fCurrentContext instanceof LocationCtxContainer; int startNumber= getSequenceNumberForOffset(startOffset); int nameNumber= getSequenceNumberForOffset(nameOffset); int nameEndNumber= getSequenceNumberForOffset(nameEndOffset); int endNumber= getSequenceNumberForOffset(endOffset); final ASTInclusionStatement inclusionStatement= - new ASTInclusionStatement(fTranslationUnit, startNumber, nameNumber, nameEndNumber, endNumber, name, filename, userInclude, true); + new ASTInclusionStatement(fTranslationUnit, startNumber, nameNumber, nameEndNumber, endNumber, name, filename, userInclude, true, heuristic); fDirectives.add(inclusionStatement); fCurrentContext= new LocationCtxFile((LocationCtxContainer) fCurrentContext, filename, buffer, startOffset, endOffset, endNumber, inclusionStatement); fLastChildInsertionOffset= 0; @@ -218,12 +218,12 @@ public class LocationMap implements ILocationResolver { * @param active true when include appears in active code. */ public void encounterPoundInclude(int startOffset, int nameOffset, int nameEndOffset, int endOffset, - char[] name, String filename, boolean userInclude, boolean active) { + char[] name, String filename, boolean userInclude, boolean active, boolean heuristic) { startOffset= getSequenceNumberForOffset(startOffset); nameOffset= getSequenceNumberForOffset(nameOffset); nameEndOffset= getSequenceNumberForOffset(nameEndOffset); endOffset= getSequenceNumberForOffset(endOffset); - fDirectives.add(new ASTInclusionStatement(fTranslationUnit, startOffset, nameOffset, nameEndOffset, endOffset, name, filename, userInclude, active)); + fDirectives.add(new ASTInclusionStatement(fTranslationUnit, startOffset, nameOffset, nameEndOffset, endOffset, name, filename, userInclude, active, heuristic)); } public void encounteredComment(int offset, int endOffset, boolean isBlockComment) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/AbstractIndexerTask.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/AbstractIndexerTask.java index 8b6d6acfbe6..85e5dce9ffa 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/AbstractIndexerTask.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/AbstractIndexerTask.java @@ -43,6 +43,7 @@ import org.eclipse.cdt.core.parser.IParserLogService; import org.eclipse.cdt.core.parser.IScannerInfo; import org.eclipse.cdt.core.parser.ParserUtil; import org.eclipse.cdt.core.parser.ScannerInfo; +import org.eclipse.cdt.internal.core.dom.IIncludeFileResolutionHeuristics; import org.eclipse.cdt.internal.core.index.IIndexFragmentFile; import org.eclipse.cdt.internal.core.index.IWritableIndex; import org.eclipse.cdt.internal.core.index.IndexBasedCodeReaderFactory; @@ -134,6 +135,7 @@ public abstract class AbstractIndexerTask extends PDOMWriter { } protected abstract IWritableIndex createIndex(); + protected abstract IIncludeFileResolutionHeuristics createIncludeHeuristics(); protected abstract ICodeReaderFactory createReaderFactory(); protected abstract AbstractLanguage[] getLanguages(String fileName); @@ -178,7 +180,7 @@ public abstract class AbstractIndexerTask extends PDOMWriter { IScannerInfo scanInfo, int options, IProgressMonitor pm) throws CoreException { if (fCodeReaderFactory == null) { if (fIsFastIndexer) { - fCodeReaderFactory= new IndexBasedCodeReaderFactory(fIndex, fResolver, language.getLinkageID(), createReaderFactory(), this); + fCodeReaderFactory= new IndexBasedCodeReaderFactory(fIndex, createIncludeHeuristics(), fResolver, language.getLinkageID(), createReaderFactory(), this); } else { fCodeReaderFactory= createReaderFactory(); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMWriter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMWriter.java index 92f1829508d..1c35383ba69 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMWriter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMWriter.java @@ -89,6 +89,10 @@ abstract public class PDOMWriter { fResolver= resolver; } + protected IndexerInputAdapter getInputAdapter() { + return fResolver; + } + public void setShowActivity(boolean val) { fShowActivity= val; } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMInclude.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMInclude.java index e4cf5d5169e..98dec0f1f34 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMInclude.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMInclude.java @@ -44,6 +44,7 @@ public class PDOMInclude implements IIndexFragmentInclude { private static final int FLAG_SYSTEM_INCLUDE = 1; private static final int FLAG_INACTIVE_INCLUDE = 2; private static final int FLAG_UNRESOLVED_INCLUDE = 4; + private static final int FLAG_RESOLVED_BY_HEURISTICS= 8; private final PDOM pdom; private final int record; @@ -81,6 +82,8 @@ public class PDOMInclude implements IIndexFragmentInclude { } if (unresolved) { flags |= FLAG_UNRESOLVED_INCLUDE; + } else if (include.isResolvedByHeuristics()) { + flags |= FLAG_RESOLVED_BY_HEURISTICS; } return flags; } @@ -224,7 +227,11 @@ public class PDOMInclude implements IIndexFragmentInclude { public boolean isResolved() throws CoreException { return (getFlag() & FLAG_UNRESOLVED_INCLUDE) == 0; } - + + public boolean isResolvedByHeuristics() throws CoreException { + return (getFlag() & FLAG_RESOLVED_BY_HEURISTICS) != 0; + } + public int getNameOffset() throws CoreException { return pdom.getDB().getInt(record + NODE_OFFSET_OFFSET); } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/AbstractPDOMIndexer.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/AbstractPDOMIndexer.java index f9bcc0da5a8..19d26079b25 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/AbstractPDOMIndexer.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/AbstractPDOMIndexer.java @@ -27,6 +27,7 @@ public abstract class AbstractPDOMIndexer implements IPDOMIndexer { public AbstractPDOMIndexer() { fProperties.put(IndexerPreferences.KEY_INDEX_ALL_FILES, String.valueOf(false)); + fProperties.put(IndexerPreferences.KEY_INCLUDE_HEURISTICS, String.valueOf(true)); fProperties.put(IndexerPreferences.KEY_FILES_TO_PARSE_UP_FRONT, ""); //$NON-NLS-1$ fProperties.put(IndexerPreferences.KEY_SKIP_ALL_REFERENCES, String.valueOf(false)); fProperties.put(IndexerPreferences.KEY_SKIP_TYPE_REFERENCES, String.valueOf(false)); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/IndexerPreferences.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/IndexerPreferences.java index 2f8978347ff..639c05b0ef4 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/IndexerPreferences.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/IndexerPreferences.java @@ -45,6 +45,7 @@ public class IndexerPreferences { public static final String KEY_INDEXER_ID= "indexerId"; //$NON-NLS-1$ public static final String KEY_INDEX_ALL_FILES= "indexAllFiles"; //$NON-NLS-1$ + public static final String KEY_INCLUDE_HEURISTICS= "useHeuristicIncludeResolution"; //$NON-NLS-1$ public static final String KEY_FILES_TO_PARSE_UP_FRONT= "filesToParseUpFront"; //$NON-NLS-1$ public static final String KEY_SKIP_ALL_REFERENCES= "skipReferences"; //$NON-NLS-1$ public static final String KEY_SKIP_TYPE_REFERENCES= "skipTypeReferences"; //$NON-NLS-1$ @@ -156,7 +157,7 @@ public class IndexerPreferences { Preferences[] prefs= getPreferences(project, scope); Properties props= new Properties(); for (int i=prefs.length-1; i>=0; i--) { - addProperties(prefs[i], props); + readProperties(prefs[i], props); } return props; } @@ -164,7 +165,7 @@ public class IndexerPreferences { public static Properties getDefaultIndexerProperties() { Preferences prefs= getDefaultPreferences(); Properties props= new Properties(); - addProperties(prefs, props); + readProperties(prefs, props); return props; } @@ -280,7 +281,7 @@ public class IndexerPreferences { return new LocalProjectScope(project).getNode(QUALIFIER).node(INDEXER_NODE); } - private static void addProperties(Preferences preferences, Properties props) { + private static void readProperties(Preferences preferences, Properties props) { try { String[] keys = preferences.keys(); for (int i=0; i < keys.length; i++) { @@ -298,6 +299,7 @@ public class IndexerPreferences { Preferences prefs= defaultPreferences.node(INDEXER_NODE); prefs.put(KEY_INDEXER_ID, IPDOMManager.ID_FAST_INDEXER); prefs.putBoolean(KEY_INDEX_ALL_FILES, false); + prefs.putBoolean(KEY_INCLUDE_HEURISTICS, true); prefs.putBoolean(KEY_SKIP_ALL_REFERENCES, false); prefs.putBoolean(KEY_SKIP_TYPE_REFERENCES, false); prefs.putBoolean(KEY_SKIP_MACRO_REFERENCES, false); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMFastIndexerTask.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMFastIndexerTask.java index 73e1e9afbad..96f45ae1cf6 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMFastIndexerTask.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMFastIndexerTask.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2007 QNX Software Systems and others. + * Copyright (c) 2006, 2008 QNX Software Systems 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 @@ -14,6 +14,7 @@ package org.eclipse.cdt.internal.core.pdom.indexer; import org.eclipse.cdt.core.dom.ICodeReaderFactory; import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.internal.core.dom.IIncludeFileResolutionHeuristics; /** * Configures the abstract indexer to return tasks suitable for fast indexing. @@ -28,4 +29,9 @@ class PDOMFastIndexerTask extends PDOMIndexerTask { protected ICodeReaderFactory createReaderFactory() { return null; } + + @Override + protected IIncludeFileResolutionHeuristics createIncludeHeuristics() { + return new ProjectIndexerIncludeResolutionHeuristics(getCProject().getProject(), getInputAdapter()); + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMFullIndexerTask.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMFullIndexerTask.java index 63b4d8c9301..fedce096a73 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMFullIndexerTask.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMFullIndexerTask.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2007 QNX Software Systems and others. + * Copyright (c) 2006, 2008 QNX Software Systems 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 @@ -14,6 +14,7 @@ package org.eclipse.cdt.internal.core.pdom.indexer; import org.eclipse.cdt.core.dom.ICodeReaderFactory; import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.internal.core.dom.IIncludeFileResolutionHeuristics; import org.eclipse.cdt.internal.core.dom.SavedCodeReaderFactory; @@ -28,6 +29,11 @@ class PDOMFullIndexerTask extends PDOMIndexerTask { @Override protected ICodeReaderFactory createReaderFactory() { - return SavedCodeReaderFactory.getInstance(); + return SavedCodeReaderFactory.createInstance(createIncludeHeuristics()); + } + + @Override + protected IIncludeFileResolutionHeuristics createIncludeHeuristics() { + return new ProjectIndexerIncludeResolutionHeuristics(getCProject().getProject(), getInputAdapter()); } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/ProjectIndexerIncludeResolutionHeuristics.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/ProjectIndexerIncludeResolutionHeuristics.java new file mode 100644 index 00000000000..33a0675a382 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/ProjectIndexerIncludeResolutionHeuristics.java @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Markus Schorn - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.pdom.indexer; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.cdt.core.index.IIndexFileLocation; +import org.eclipse.cdt.internal.core.dom.IIncludeFileResolutionHeuristics; +import org.eclipse.cdt.internal.core.pdom.ASTFilePathResolver; +import org.eclipse.cdt.internal.core.resources.ResourceLookup; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; + +/** + * Heuristics for picking up includes from the project + */ +public class ProjectIndexerIncludeResolutionHeuristics implements IIncludeFileResolutionHeuristics { + private static final String TRUE = "true"; //$NON-NLS-1$ + + @SuppressWarnings("nls") + private static final boolean IGNORE_CASE = new File("a").equals(new File("A")); + + private IProject fProject; + private IProject[] fProjects; + private final ASTFilePathResolver fResolver; + + public ProjectIndexerIncludeResolutionHeuristics(IProject project, ASTFilePathResolver resolver) { + fProject= project; + fResolver= resolver; + } + + public String findInclusion(String include, String currentFile) { + final IIndexFileLocation ifl= fResolver.resolveASTPath(currentFile); + if (ifl == null || ifl.getFullPath() == null) { + return null; + } + + if (fProject == null) + return null; + + + if (fProjects == null) { + if (fProject.isOpen()) { + String val= IndexerPreferences.get(fProject, IndexerPreferences.KEY_INCLUDE_HEURISTICS, TRUE); + if (TRUE.equals(val)) { + fProjects= getOpenReferencedProjects(fProject); + } + } + if (fProjects == null) { + fProject= null; + return null; + } + } + + IFile[] files= ResourceLookup.findFilesByName(new Path(include), fProjects, IGNORE_CASE); + if (files.length == 0) + return null; + + return selectBest(files, ifl.getFullPath().toCharArray()).getLocation().toString(); + } + + + private IResource selectBest(IFile[] files, char[] currentFullPath) { + IFile best= files[0]; + int bestScore= computeScore(best.getFullPath().toString().toCharArray(), currentFullPath); + + for (int i = 1; i < files.length; i++) { + IFile file= files[i]; + int score= computeScore(file.getFullPath().toString().toCharArray(), currentFullPath); + if (score > bestScore) { + bestScore= score; + best= file; + } + } + return best; + } + + private int computeScore(char[] path1, char[] path2) { + final int limit= Math.min(path1.length, path2.length); + int match=0; + for (int i = 0; i < limit; i++) { + if (path1[i] != path2[i]) + break; + if (path1[i] == '/') + match= i; + } + // prefer shortest path with longest matches with + return (match << 16) - path1.length; + } + + private IProject[] getOpenReferencedProjects(IProject prj) { + Set result= new HashSet(); + + if (prj.isOpen()) { + result.add(prj); + + List projectsToSearch= new ArrayList(); + projectsToSearch.add(prj); + for (int i=0; i fExtensions; + Extensions(Set extensions, boolean invert) { + fInvert= invert; + fExtensions= extensions; + } + boolean isRelevant(String filename) { + // accept all files without extension + final int idx= filename.lastIndexOf('.'); + if (idx < 0) + return true; + + return fExtensions.contains(filename.substring(idx+1)) != fInvert; + } + } + + private static class Node { + final Node fParent; + final char[] fResourceName; + final boolean fIsFolder; + + boolean fDeleted; + boolean fHasChildren; + private int fCanonicHash; + + Node(Node parent, char[] name, boolean isFolder) { + fParent= parent; + fResourceName= name; + fIsFolder= isFolder; + if (parent != null) + parent.fHasChildren= true; + } + } + + private final Object fLock= new Object(); + private final Job fUnrefJob; + private SoftReference> fNodeMapRef; + private Map fNodeMap; + private final Map fFileExtensions; + private Extensions fCDTProjectExtensions; + private Extensions fDefaultExtensions; + private Extensions fCurrentExtensions; + private Node fRootNode; + private boolean fNeedCleanup; + private Node fLastFolderNode; + + public ResourceLookupImpl() { + fRootNode= new Node(null, CharArrayUtils.EMPTY, true) {}; + fFileExtensions= new HashMap(); + fUnrefJob= new Job("Timer") { //$NON-NLS-1$ + @Override + protected IStatus run(IProgressMonitor monitor) { + unrefNodeMap(); + return Status.OK_STATUS; + } + }; + fUnrefJob.setSystem(true); + } + + public void startup() { + final IWorkspace workspace = ResourcesPlugin.getWorkspace(); + workspace.addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); + } + + public void shutdown() { + ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); + synchronized (fLock) { + fNodeMap= null; + fNodeMapRef= null; + fFileExtensions.clear(); + } + } + + /** + * Handle resource change notifications. + */ + public void resourceChanged(IResourceChangeEvent event) { + IResourceDelta delta= event.getDelta(); + synchronized (fLock) { + if (fNodeMapRef == null) + return; + boolean unsetMap= false; + if (fNodeMap == null) { + fNodeMap= fNodeMapRef.get(); + if (fNodeMap == null) + return; + unsetMap= true; + } + try { + delta.accept(this); + } catch (CoreException e) { + CCorePlugin.log(e); + } finally { + if (fNeedCleanup) + cleanup(); + fCurrentExtensions= null; + fNeedCleanup= false; + if (unsetMap) + fNodeMap= null; + } + } + } + + /** + * Handles resource change notifications by visiting the delta. + */ + public boolean visit(IResourceDelta delta) throws CoreException { + assert Thread.holdsLock(fLock); + + final IResource res= delta.getResource(); + if (res instanceof IWorkspaceRoot) + return VISIT_CHILDREN; + + if (res instanceof IProject) { + // project not yet handled + final String name = res.getName(); + final Extensions exts= fFileExtensions.get(name); + if (exts == null) + return SKIP_CHILDREN; + + switch (delta.getKind()) { + case IResourceDelta.ADDED: // new projects should not yet be part of the tree + case IResourceDelta.REMOVED: + fFileExtensions.remove(name); + remove(res); + return SKIP_CHILDREN; + + case IResourceDelta.CHANGED: + if ((delta.getFlags() & (TRIGGER_RECALC | IResourceDelta.DESCRIPTION)) != 0) { + fFileExtensions.remove(name); + remove(res); + return SKIP_CHILDREN; + } + break; + } + fCurrentExtensions= exts; + return VISIT_CHILDREN; + } + + // file or folder + switch (delta.getKind()) { + case IResourceDelta.ADDED: + add(res); + return SKIP_CHILDREN; + + case IResourceDelta.CHANGED: + if ((delta.getFlags() & TRIGGER_RECALC) != 0) { + remove(res); + add(res); + return SKIP_CHILDREN; + } + return VISIT_CHILDREN; + + case IResourceDelta.REMOVED: + + remove(res); + return SKIP_CHILDREN; + } + return VISIT_CHILDREN; + } + + + /** + * Add a resource to the tree. + */ + private void add(IResource res) { + assert Thread.holdsLock(fLock); + + if (res instanceof IFile) { + if (fCurrentExtensions.isRelevant(res.getName())) { + createFileNode(res.getFullPath()); + } + } else { + try { + res.accept(this, 0); + } catch (CoreException e) { + CCorePlugin.log(e); + } + } + } + + /** + * Add a resource tree by using a resource proxy visitor. + */ + public boolean visit(IResourceProxy proxy) throws CoreException { + if (proxy.getType() == IResource.FILE) { + if (fCurrentExtensions.isRelevant(proxy.getName())) { + createFileNode(proxy.requestFullPath()); + } + } + return true; + } + + + public void unrefNodeMap() { + synchronized (fLock) { + fNodeMap= null; + } + } + + public void simulateNodeMapCollection() { + synchronized (fLock) { + fNodeMap= null; + fNodeMapRef= new SoftReference>(null); + } + } + + /** + * Initializes nodes for the given projects. Also creates the node map if it was collected. + */ + private void initializeProjects(IProject[] projects) { + assert Thread.holdsLock(fLock); + + if (fNodeMap == null) { + if (fNodeMapRef != null) { + fNodeMap= fNodeMapRef.get(); + } + + if (fNodeMap == null) { + fFileExtensions.clear(); + fNodeMap= new HashMap(); + fNodeMapRef= new SoftReference>(fNodeMap); + } + } + fUnrefJob.cancel(); + fUnrefJob.schedule(UNREF_DELAY); + + for (IProject project : projects) { + if (project.isOpen() && !fFileExtensions.containsKey(project.getName())) { + Extensions ext= fDefaultExtensions; + try { + if (project.hasNature(CProjectNature.C_NATURE_ID)) { + ext= fCDTProjectExtensions; + } + } catch (CoreException e) { + CCorePlugin.log(e); + // treat as non-cdt project + } + fCurrentExtensions= ext; + add(project); + fFileExtensions.put(project.getName(), ext); + fCurrentExtensions= null; + } + } + } + + /** + * Initializes file-extensions and node map + */ + private void initFileExtensions() { + if (fDefaultExtensions == null) { + HashSet select= new HashSet(); + String[] registeredContentTypes= CoreModel.getRegistedContentTypeIds(); + select.addAll(Arrays.asList(registeredContentTypes)); + + final IContentTypeManager ctm= Platform.getContentTypeManager(); + final IContentType[] ctts= ctm.getAllContentTypes(); + Set result= new HashSet(); + outer: for (IContentType ctt : ctts) { + IContentType basedOn= ctt; + while (basedOn != null) { + if (select.contains(basedOn.getId())) + continue outer; + basedOn= basedOn.getBaseType(); + } + // this is a non-cdt content type + String[] fspecs= ctt.getFileSpecs(IContentType.FILE_EXTENSION_SPEC); + result.addAll(Arrays.asList(fspecs)); + } + fCDTProjectExtensions= new Extensions(result, true); + + result= new HashSet(); + select.clear(); + select.add(CCorePlugin.CONTENT_TYPE_CHEADER); + select.add(CCorePlugin.CONTENT_TYPE_CXXHEADER); + for (IContentType ctt : ctts) { + IContentType basedOn= ctt; + boolean selectme= false; + while (basedOn != null) { + if (select.contains(basedOn.getId())) { + selectme= true; + break; + } + basedOn= basedOn.getBaseType(); + } + if (selectme) { + // this is content type for a header file + String[] fspecs= ctt.getFileSpecs(IContentType.FILE_EXTENSION_SPEC); + result.addAll(Arrays.asList(fspecs)); + } + } + fDefaultExtensions= new Extensions(result, false); + } + } + + /** + * Inserts a node for the given path. + */ + private void createFileNode(IPath fullPath) { + final String[] segments= fullPath.segments(); + createNode(toCharArrayArray(segments), segments.length, false); + } + + private char[][] toCharArrayArray(String[] segments) { + final int len= segments.length; + char[][] chsegs= new char[len][]; + for (int i = 0; i < segments.length; i++) { + chsegs[i]= segments[i].toCharArray(); + } + return chsegs; + } + + /** + * Inserts a node for the given path. + */ + private Node createNode(char[][] segments, int segmentCount, boolean folder) { + assert Thread.holdsLock(fLock); + + if (segmentCount == 0) + return fRootNode; + + if (folder && fLastFolderNode != null) { + if (isNodeForSegments(fLastFolderNode, segments, segmentCount)) + return fLastFolderNode; + } + + final char[] name= segments[segmentCount-1]; + final int hash= hashCode(name); + + // search for existing node + Object obj= fNodeMap.get(hash); + + Node[] nodes= null; + int len= 0; + if (obj != null) { + if (obj instanceof Node) { + Node node= (Node) obj; + if (isNodeForSegments(node, segments, segmentCount)) { + if (folder) + fLastFolderNode= node; + return node; + } + nodes= new Node[]{node, null}; + fNodeMap.put(hash, nodes); + len= 1; + } else { + nodes= (Node[]) obj; + for (len=0; len < nodes.length; len++) { + Node node = nodes[len]; + if (node == null) + break; + if (isNodeForSegments(node, segments, segmentCount)) { + if (folder) + fLastFolderNode= node; + return node; + } + } + } + } + final Node parent= createNode(segments, segmentCount-1, true); + Node node= new Node(parent, name, folder); + if (nodes == null) { + fNodeMap.put(hash, node); + } else { + if (len == nodes.length) { + Node[] newNodes= new Node[len+2]; + System.arraycopy(nodes, 0, newNodes, 0, len); + nodes= newNodes; + fNodeMap.put(hash, nodes); + } + nodes[len]= node; + } + + if (folder) + fLastFolderNode= node; + return node; + } + + /** + * Checks whether the given node matches the given segments. + */ + private boolean isNodeForSegments(Node node, char[][] segments, int segmentLength) { + assert Thread.holdsLock(fLock); + + while(segmentLength > 0 && node != null) { + if (!CharArrayUtils.equals(segments[--segmentLength], node.fResourceName)) + return false; + node= node.fParent; + } + return node == fRootNode; + } + + /** + * Remove a resource from the tree + */ + private void remove(IResource res) { + assert Thread.holdsLock(fLock); + + final char[] name= res.getName().toCharArray(); + final int hash= hashCode(name); + + Object obj= fNodeMap.get(hash); + if (obj == null) + return; + + final IPath fullPath= res.getFullPath(); + final int segmentCount= fullPath.segmentCount(); + if (segmentCount == 0) + return; + + final char[][]segments= toCharArrayArray(fullPath.segments()); + if (obj instanceof Node) { + final Node node= (Node) obj; + if (!node.fDeleted && isNodeForSegments(node, segments, segmentCount)) { + node.fDeleted= true; + if (node.fHasChildren) + fNeedCleanup= true; + fNodeMap.remove(hash); + } + } else { + final Node[] nodes= (Node[]) obj; + for (int i= 0; i < nodes.length; i++) { + Node node = nodes[i]; + if (node == null) + return; + if (!node.fDeleted && isNodeForSegments(node, segments, segmentCount)) { + remove(nodes, i); + + if (nodes[0] == null) + fNodeMap.remove(hash); + + node.fDeleted= true; + if (node.fHasChildren) + fNeedCleanup= true; + + return; + } + } + } + } + + private void remove(Node[] nodes, int i) { + int idx= lastValid(nodes, i); + if (idx > 0) { + nodes[i]= nodes[idx]; + nodes[idx]= null; + } + } + + private int lastValid(Node[] nodes, int left) { + int right= nodes.length-1; + while (left < right) { + int mid= (left+right+1)/2; // ==> mid > left + if (nodes[mid] == null) + right= mid-1; + else + left= mid; + } + return right; + } + + private void cleanup() { + assert Thread.holdsLock(fLock); + fLastFolderNode= null; + + for (Iterator iterator = fNodeMap.values().iterator(); iterator.hasNext();) { + Object obj= iterator.next(); + if (obj instanceof Node) { + if (isDeleted((Node) obj)) { + iterator.remove(); + } + } else { + Node[] nodes= (Node[]) obj; + int j= 0; + for (int i = 0; i < nodes.length; i++) { + final Node node = nodes[i]; + if (node == null) { + if (j==0) { + iterator.remove(); + } + break; + } + if (!isDeleted(node)) { + if (i != j) { + nodes[j]= node; + nodes[i]= null; + } + j++; + } else { + nodes[i]= null; + } + } + } + } + } + + private boolean isDeleted(Node node) { + while(node != null) { + if (node.fDeleted) + return true; + node= node.fParent; + } + return false; + } + + /** + * Computes a case insensitive hash-code for file names. + */ + private int hashCode(char[] name) { + int h= 0; + final int len = name.length; + for (int i = 0; i < len; i++) { + h = 31*h + Character.toUpperCase(name[i]); + } + return h; + } + + /** + * Searches for all files with the given location. + */ + public IFile[] findFilesForLocation(URI location, IProject[] projects) { + initFileExtensions(); + String name= extractName(location); + Node[] candidates; + synchronized (fLock) { + initializeProjects(projects); + Object obj= fNodeMap.get(hashCode(name.toCharArray())); + if (obj == null) { + return NO_FILES; + } + candidates= convert(obj); + } + return extractMatchesForLocation(candidates, location); + } + + private Node[] convert(Object obj) { + if (obj instanceof Node) + return new Node[] {(Node) obj}; + + final Node[] nodes= (Node[]) obj; + final int len= lastValid(nodes, -1)+1; + final Node[] result= new Node[len]; + System.arraycopy(nodes, 0, result, 0, len); + return result; + } + + /** + * Returns an array of files for the given name. Search is limited to the supplied projects. + */ + public IFile[] findFilesByName(IPath relativeLocation, IProject[] projects, boolean ignoreCase) { + final int segCount= relativeLocation.segmentCount(); + if (segCount < 1) + return NO_FILES; + + final String name= relativeLocation.lastSegment(); + Node[] candidates; + + initFileExtensions(); + synchronized (fLock) { + initializeProjects(projects); + Object obj= fNodeMap.get(hashCode(name.toCharArray())); + if (obj == null) { + return NO_FILES; + } + candidates= convert(obj); + } + String suffix= relativeLocation.toString(); + while(suffix.startsWith("../")) { //$NON-NLS-1$ + suffix= suffix.substring(3); + } + return extractMatchesForName(candidates, name, suffix, ignoreCase); + } + + private String extractName(URI location) { + String path= location.getPath(); + int idx= path.lastIndexOf('/'); + return path.substring(idx+1); + } + + /** + * Selects the actual matches for the list of candidate nodes. + */ + private IFile[] extractMatchesForName(Node[] candidates, String name, String suffix, boolean ignoreCase) { + final char[] n1= name.toCharArray(); + final int namelen = n1.length; + int resultIdx= 0; + + if (ignoreCase) { + for (int j = 0; j < namelen; j++) { + n1[j]= Character.toUpperCase(n1[j]); + } + } + final int suffixLen= suffix.length(); + final IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot(); + IFile[] result= null; + outer: for (int i = 0; i < candidates.length; i++) { + final Node node = candidates[i]; + if (!node.fIsFolder) { + final char[] n2= node.fResourceName; + if (namelen == n2.length) { + for (int j = 0; j < n2.length; j++) { + final char c= ignoreCase ? Character.toUpperCase(n2[j]) : n2[j]; + if (c != n1[j]) + continue outer; + } + final IFile file= root.getFile(createPath(node)); + final URI loc= file.getLocationURI(); + if (loc != null) { + String path= loc.getPath(); + final int len= path.length(); + if (len >= suffixLen && + suffix.regionMatches(ignoreCase, 0, path, len-suffixLen, suffixLen)) { + if (result == null) + result= new IFile[candidates.length-i]; + result[resultIdx++]= root.getFile(createPath(node)); + } + } + } + } + } + if (result==null) + return NO_FILES; + + if (resultIdx < result.length) { + IFile[] copy= new IFile[resultIdx]; + System.arraycopy(result, 0, copy, 0, resultIdx); + return copy; + } + return result; + } + + private IPath createPath(Node node) { + if (node == fRootNode) + return Path.ROOT; + + return createPath(node.fParent).append(new String(node.fResourceName)); + } + + /** + * Selects the actual matches from the list of candidates + */ + private IFile[] extractMatchesForLocation(Node[] candidates, URI location) { + final IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot(); + final String searchPath= getCanonicalPath(location); + IFile[] result= null; + int resultIdx= 0; + for (int i = 0; i < candidates.length; i++) { + final Node node = candidates[i]; + if (!node.fIsFolder) { + final IFile file= root.getFile(createPath(node)); + final URI loc= file.getLocationURI(); + if (loc != null) { + if (!loc.equals(location)) { + if (searchPath == null) + continue; + + if (node.fCanonicHash != 0 && node.fCanonicHash != searchPath.hashCode()) + continue; + + final String candPath= getCanonicalPath(loc); + if (candPath == null) + continue; + + node.fCanonicHash= candPath.hashCode(); + if (!candPath.equals(searchPath)) + continue; + } + if (result == null) + result= new IFile[candidates.length-i]; + result[resultIdx++]= root.getFile(createPath(node)); + } + } + } + if (result==null) + return NO_FILES; + + if (resultIdx < result.length) { + IFile[] copy= new IFile[resultIdx]; + System.arraycopy(result, 0, copy, 0, resultIdx); + return copy; + } + return result; + } + + private String getCanonicalPath(URI location) { + if (!"file".equals(location.getScheme())) //$NON-NLS-1$ + return null; + + String path= location.getPath(); + try { + path= new File(path).getCanonicalPath(); + } catch (IOException e) { + // use non-canonical version + } + return path; + } + + + + @SuppressWarnings("nls") + public void dump() { + List lines= new ArrayList(); + synchronized (fLock) { + for (Iterator iterator = fNodeMap.values().iterator(); iterator.hasNext();) { + Node[] nodes= convert(iterator.next()); + for (int i = 0; i < nodes.length; i++) { + final Node node = nodes[i]; + if (node == null) { + break; + } + lines.add(toString(node)); + } + } + } + Collections.sort(lines); + System.out.println("Dumping files:"); + for (Iterator iterator = lines.iterator(); iterator.hasNext();) { + String line = iterator.next(); + System.out.println(line); + } + System.out.flush(); + } + + @SuppressWarnings("nls") + private String toString(Node node) { + if (node == fRootNode) + return ""; + + return toString(node.fParent) + "/" + new String(node.fResourceName); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/dialogs/AbstractIndexerPage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/dialogs/AbstractIndexerPage.java index 5cb379cfecb..dcf6c537346 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/dialogs/AbstractIndexerPage.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/dialogs/AbstractIndexerPage.java @@ -36,6 +36,7 @@ public abstract class AbstractIndexerPage extends AbstractCOptionPage { protected static final String TRUE = String.valueOf(true); private Button fAllFiles; + private Button fIncludeHeuristics; private Text fFilesToParseUpFront; private Button fSkipReferences; private Button fSkipTypeReferences; @@ -57,6 +58,7 @@ public abstract class AbstractIndexerPage extends AbstractCOptionPage { public void createControl(Composite parent) { Composite page = ControlFactory.createComposite(parent, 1); fAllFiles= createAllFilesButton(page); + fIncludeHeuristics= createIncludeHeuristicsButton(page); fSkipReferences= createSkipReferencesButton(page); fSkipTypeReferences= createSkipTypeReferencesButton(page); fSkipMacroReferences= createSkipMacroReferencesButton(page); @@ -81,6 +83,11 @@ public abstract class AbstractIndexerPage extends AbstractCOptionPage { boolean indexAllFiles= TRUE.equals(properties.get(IndexerPreferences.KEY_INDEX_ALL_FILES)); fAllFiles.setSelection(indexAllFiles); } + if (fIncludeHeuristics != null) { + Object prop= properties.get(IndexerPreferences.KEY_INCLUDE_HEURISTICS); + boolean use= prop == null || TRUE.equals(prop); + fIncludeHeuristics.setSelection(use); + } if (fSkipReferences != null) { boolean skipReferences= TRUE.equals(properties.get(IndexerPreferences.KEY_SKIP_ALL_REFERENCES)); fSkipReferences.setSelection(skipReferences); @@ -109,6 +116,9 @@ public abstract class AbstractIndexerPage extends AbstractCOptionPage { if (fAllFiles != null) { props.put(IndexerPreferences.KEY_INDEX_ALL_FILES, String.valueOf(fAllFiles.getSelection())); } + if (fIncludeHeuristics != null) { + props.put(IndexerPreferences.KEY_INCLUDE_HEURISTICS, String.valueOf(fIncludeHeuristics.getSelection())); + } if (fFilesToParseUpFront != null) { props.put(IndexerPreferences.KEY_FILES_TO_PARSE_UP_FRONT, fFilesToParseUpFront.getText()); } @@ -168,7 +178,11 @@ public abstract class AbstractIndexerPage extends AbstractCOptionPage { private Button createAllFilesButton(Composite page) { return ControlFactory.createCheckBox(page, INDEX_ALL_FILES); } - + + private Button createIncludeHeuristicsButton(Composite page) { + return ControlFactory.createCheckBox(page, DialogsMessages.AbstractIndexerPage_heuristicIncludes); + } + private Button createSkipReferencesButton(Composite page) { return ControlFactory.createCheckBox(page, DialogsMessages.AbstractIndexerPage_skipAllReferences); } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/dialogs/DialogsMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/dialogs/DialogsMessages.java index a7791950b94..ec81607abee 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/dialogs/DialogsMessages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/dialogs/DialogsMessages.java @@ -16,6 +16,10 @@ import org.eclipse.osgi.util.NLS; public class DialogsMessages extends NLS { private static final String BUNDLE_NAME = "org.eclipse.cdt.ui.dialogs.DialogsMessages"; //$NON-NLS-1$ + /** + * @since 5.1 + */ + public static String AbstractIndexerPage_heuristicIncludes; public static String AbstractIndexerPage_indexAllFiles; public static String AbstractIndexerPage_indexUpFront; public static String AbstractIndexerPage_skipAllReferences; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/dialogs/DialogsMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/dialogs/DialogsMessages.properties index 162629498fc..0f67f298e51 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/dialogs/DialogsMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/dialogs/DialogsMessages.properties @@ -13,6 +13,7 @@ PreferenceScopeBlock_enableProjectSettings=Enable project specific settings PreferenceScopeBlock_storeWithProject=Store settings with project PreferenceScopeBlock_preferenceLink=Configure Workspace Settings... +AbstractIndexerPage_heuristicIncludes=Allow heuristic resolution of includes AbstractIndexerPage_indexAllFiles=Index all files (files neither built nor included, also) AbstractIndexerPage_skipAllReferences=Skip all references (Call Hierarchy and Search will not work) AbstractIndexerPage_skipTypeReferences=Skip type references (Search for type references will not work)