1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

Implementation of an heuristics to resolve include files, bug 213562

This commit is contained in:
Markus Schorn 2008-10-01 09:59:44 +00:00
parent ddd10d64c3
commit a359eb6090
38 changed files with 1560 additions and 120 deletions

View file

@ -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() {

View file

@ -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);
}
}
}

View file

@ -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() {

View file

@ -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);

View file

@ -1350,4 +1350,53 @@ public class IndexBugsTests extends BaseTestCase {
fIndex.releaseReadLock();
}
}
// #include <header.h>
// #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();
}
}
}

View file

@ -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());

View file

@ -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,

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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.
* <p>
* This interface is not intended to be implemented by clients.
* </p>
* <p>
* <strong>EXPERIMENTAL</strong>. 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.
* </p>
*
* @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;
}

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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 {

View file

@ -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> {
T checkFile(String path, String fileName);
T checkFile(String path, String fileName, boolean isHeuristicMatch);
}
final private IIncludeFileTester<IncludeFileContent> createCodeReaderTester= new IIncludeFileTester<IncludeFileContent>() {
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<String> createPathTester= new IIncludeFileTester<String>() {
public String checkFile(String path, String fileName) {
private static class IncludeResolution {String fLocation; boolean fHeuristic;}
final private IIncludeFileTester<IncludeResolution> createPathTester= new IIncludeFileTester<IncludeResolution>() {
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> T findInclusion(final String filename, final boolean quoteInclude,
final boolean includeNext, final File currentDirectory, final IIncludeFileTester<T> tester) {
final boolean includeNext, final String currentFile, final IIncludeFileTester<T> 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);
}
}

View file

@ -43,6 +43,7 @@ public class IncludeFileContent {
private final List<IIndexMacro> fMacroDefinitions;
private final List<ICPPUsingDirective> fUsingDirectives;
private final String fFileLocation;
private boolean fHeuristic;
private List<IIndexFile> fFiles;
/**
@ -144,4 +145,15 @@ public class IncludeFileContent {
public List<IIndexFile> getFilesIncluded() {
return fFiles;
}
/**
* Returns whether this inclusion was found by a heuristics.
*/
public boolean isFoundByHeuristics() {
return fHeuristic;
}
public void setFoundByHeuristics(boolean val) {
fHeuristic= val;
}
}

View file

@ -116,14 +116,14 @@ public class LocationMap implements ILocationResolver {
* @param userInclude <code>true</code> 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 <code>true</code> 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) {

View file

@ -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();

View file

@ -89,6 +89,10 @@ abstract public class PDOMWriter {
fResolver= resolver;
}
protected IndexerInputAdapter getInputAdapter() {
return fResolver;
}
public void setShowActivity(boolean val) {
fShowActivity= val;
}

View file

@ -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);
}

View file

@ -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));

View file

@ -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);

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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<IProject> result= new HashSet<IProject>();
if (prj.isOpen()) {
result.add(prj);
List<IProject> projectsToSearch= new ArrayList<IProject>();
projectsToSearch.add(prj);
for (int i=0; i<projectsToSearch.size(); i++) {
IProject project= projectsToSearch.get(i);
IProject[] nextLevel;
try {
nextLevel= project.getReferencedProjects();
for (IProject prjNextLevel : nextLevel) {
if (prjNextLevel.isOpen() && result.add(prjNextLevel)) {
projectsToSearch.add(prjNextLevel);
}
}
} catch (CoreException e) {
}
}
}
return result.toArray(new IProject[result.size()]);
}
}

View file

@ -55,6 +55,7 @@ import org.eclipse.cdt.internal.core.model.IBufferFactory;
import org.eclipse.cdt.internal.core.model.Util;
import org.eclipse.cdt.internal.core.model.WorkingCopy;
import org.eclipse.cdt.internal.core.pdom.PDOMManager;
import org.eclipse.cdt.internal.core.resources.ResourceLookup;
import org.eclipse.cdt.internal.core.settings.model.CProjectDescriptionManager;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
@ -322,6 +323,7 @@ public class CCorePlugin extends Plugin {
}
fNewCProjectDescriptionManager.shutdown();
ResourceLookup.shutdown();
savePluginPreferences();
} finally {
@ -340,7 +342,8 @@ public class CCorePlugin extends Plugin {
cdtLog = new CDTLogWriter(CCorePlugin.getDefault().getStateLocation().append(".log").toFile()); //$NON-NLS-1$
configurePluginDebugOptions();
PositionTrackerManager.getInstance().install();
ResourceLookup.startup();
// new project model needs to register the resource listener first.
fNewCProjectDescriptionManager= CProjectDescriptionManager.getInstance();
final Job post1= fNewCProjectDescriptionManager.startup();

View file

@ -1,12 +1,12 @@
/*******************************************************************************
* 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
* IBM - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.core.dom;
import org.eclipse.cdt.core.dom.ast.IASTCompletionNode;
@ -105,9 +105,9 @@ public class CDOM implements IASTServiceProvider {
case PARSE_SAVED_RESOURCES:
return SavedCodeReaderFactory.getInstance();
case PARSE_WORKING_COPY_WITH_SAVED_INCLUSIONS:
return new PartialWorkingCopyCodeReaderFactory( provider );
return new PartialWorkingCopyCodeReaderFactory(provider, null);
case PARSE_WORKING_COPY_WHENEVER_POSSIBLE:
return new WorkingCopyCodeReaderFactory( provider );
return new WorkingCopyCodeReaderFactory( provider, null );
}
return null;
}

View file

@ -0,0 +1,34 @@
/*******************************************************************************
* 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.dom;
import org.eclipse.cdt.core.dom.ICodeReaderFactory;
import org.eclipse.core.runtime.IAdaptable;
/**
* Base implementation for all code reader factories.
*/
public abstract class AbstractCodeReaderFactory implements ICodeReaderFactory, IAdaptable {
private final IIncludeFileResolutionHeuristics fHeuristics;
public AbstractCodeReaderFactory(IIncludeFileResolutionHeuristics heuristics) {
fHeuristics= heuristics;
}
@SuppressWarnings("unchecked")
public Object getAdapter(Class adapter) {
if (adapter.isAssignableFrom(IIncludeFileResolutionHeuristics.class)) {
return fHeuristics;
}
return null;
}
}

View file

@ -0,0 +1,26 @@
/*******************************************************************************
* 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.dom;
/**
* Abstract base class for heuristic include file resolution.
*/
public interface IIncludeFileResolutionHeuristics {
/**
* Attempt to find a file for the given include without using an include search path.
* @param include the include as provided in the directive
* @param currentFile the file the inclusion belongs to.
* @return a location for the inclusion or null.
*/
String findInclusion(String include, String currentFile);
}

View file

@ -19,7 +19,7 @@ import org.eclipse.cdt.core.parser.ICodeReaderCache;
*
* @since 4.0
*/
public class NullCodeReaderFactory implements ICodeReaderFactory {
public class NullCodeReaderFactory extends AbstractCodeReaderFactory {
private static final char[] EMPTY_CHARS = new char[0];
private static final ICodeReaderFactory INSTANCE= new NullCodeReaderFactory();
@ -29,7 +29,7 @@ public class NullCodeReaderFactory implements ICodeReaderFactory {
}
private NullCodeReaderFactory() {
// singleton
super(null);
}
/*

View file

@ -14,7 +14,6 @@ import java.util.Arrays;
import java.util.Iterator;
import org.eclipse.cdt.core.dom.CDOM;
import org.eclipse.cdt.core.dom.ICodeReaderFactory;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.model.IWorkingCopy;
import org.eclipse.cdt.core.model.IWorkingCopyProvider;
@ -26,8 +25,7 @@ import org.eclipse.cdt.internal.core.parser.EmptyIterator;
/**
* @author jcamelon
*/
public class PartialWorkingCopyCodeReaderFactory
implements ICodeReaderFactory {
public class PartialWorkingCopyCodeReaderFactory extends AbstractCodeReaderFactory {
private final IWorkingCopyProvider provider;
private ICodeReaderCache cache = null;
@ -35,7 +33,8 @@ public class PartialWorkingCopyCodeReaderFactory
/**
* @param provider
*/
public PartialWorkingCopyCodeReaderFactory(IWorkingCopyProvider provider) {
public PartialWorkingCopyCodeReaderFactory(IWorkingCopyProvider provider, IIncludeFileResolutionHeuristics heuristics) {
super(heuristics);
this.provider = provider;
cache = SavedCodeReaderFactory.getInstance().getCodeReaderCache();
}

View file

@ -12,7 +12,6 @@ package org.eclipse.cdt.internal.core.dom;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.CDOM;
import org.eclipse.cdt.core.dom.ICodeReaderFactory;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.parser.CodeReader;
import org.eclipse.cdt.core.parser.CodeReaderCache;
@ -22,43 +21,49 @@ import org.eclipse.core.runtime.Preferences;
/**
* @author jcamelon
*/
public class SavedCodeReaderFactory implements ICodeReaderFactory {
public class SavedCodeReaderFactory extends AbstractCodeReaderFactory {
private ICodeReaderCache cache = null;
private static ICodeReaderCache cache;
private static SavedCodeReaderFactory instance = new SavedCodeReaderFactory(null);
public static SavedCodeReaderFactory getInstance()
{
public static SavedCodeReaderFactory getInstance() {
return instance;
}
private static SavedCodeReaderFactory instance = new SavedCodeReaderFactory();
public static SavedCodeReaderFactory createInstance(IIncludeFileResolutionHeuristics heuristics) {
return new SavedCodeReaderFactory(heuristics);
}
private SavedCodeReaderFactory()
{
int size= CodeReaderCache.DEFAULT_CACHE_SIZE_IN_MB;
final CCorePlugin corePlugin = CCorePlugin.getDefault();
if (corePlugin != null) {
Preferences pluginPreferences = corePlugin.getPluginPreferences();
if (pluginPreferences != null) {
size = pluginPreferences.getInt(CodeReaderCache.CODE_READER_BUFFER);
if (size == 0) {
String [] properties = pluginPreferences.propertyNames();
boolean found = false;
for (int j = 0; j < properties.length; ++j) {
if (properties[j].equals( CodeReaderCache.CODE_READER_BUFFER)) {
found = true;
break;
private SavedCodeReaderFactory(IIncludeFileResolutionHeuristics heuristics) {
super(heuristics);
if (cache == null) {
int size= CodeReaderCache.DEFAULT_CACHE_SIZE_IN_MB;
final CCorePlugin corePlugin = CCorePlugin.getDefault();
if (corePlugin != null) {
Preferences pluginPreferences = corePlugin.getPluginPreferences();
if (pluginPreferences != null) {
size = pluginPreferences.getInt(CodeReaderCache.CODE_READER_BUFFER);
if (size == 0) {
String [] properties = pluginPreferences.propertyNames();
boolean found = false;
for (int j = 0; j < properties.length; ++j) {
if (properties[j].equals( CodeReaderCache.CODE_READER_BUFFER)) {
found = true;
break;
}
}
}
if (!found) {
if (!found) {
size= CodeReaderCache.DEFAULT_CACHE_SIZE_IN_MB;
}
} else if (size < 0) {
size= CodeReaderCache.DEFAULT_CACHE_SIZE_IN_MB;
}
} else if (size < 0) {
size= CodeReaderCache.DEFAULT_CACHE_SIZE_IN_MB;
}
}
cache= new CodeReaderCache(size);
}
cache = new CodeReaderCache(size);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.dom.ICodeReaderFactory#getUniqueIdentifier()

View file

@ -6,7 +6,7 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
* IBM - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom;
@ -23,8 +23,8 @@ public class WorkingCopyCodeReaderFactory extends PartialWorkingCopyCodeReaderFa
/**
* @param provider
*/
public WorkingCopyCodeReaderFactory(IWorkingCopyProvider provider) {
super(provider);
public WorkingCopyCodeReaderFactory(IWorkingCopyProvider provider, IIncludeFileResolutionHeuristics heuristics) {
super(provider, heuristics);
}
/* (non-Javadoc)

View file

@ -0,0 +1,65 @@
/*******************************************************************************
* 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.resources;
import java.net.URI;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
/**
* Allows for looking up resources by location or name.
*/
public class ResourceLookup {
private static ResourceLookupImpl sInstance= new ResourceLookupImpl();
public static void startup() {
sInstance.startup();
}
public static void shutdown() {
sInstance.shutdown();
}
public static IFile[] findFilesForLocation(URI location, IProject[] projects) {
return sInstance.findFilesForLocation(location, projects);
}
/**
* Searches for files with the given location suffix.
* @param locationSuffix the suffix to match, always used as relative path.
* @param projects the projects to search
* @param ignoreCase whether or not to ignore case when comparing the suffix.
*/
public static IFile[] findFilesByName(IPath locationSuffix, IProject[] projects, boolean ignoreCase) {
return sInstance.findFilesByName(locationSuffix, projects, ignoreCase);
}
/**
* For testing, only.
*/
public static void dump() {
sInstance.dump();
}
/**
* For testing, only.
*/
public static void unrefNodeMap() {
sInstance.unrefNodeMap();
}
/**
* For testing, only.
*/
public static void simulateNodeMapCollection() {
sInstance.simulateNodeMapCollection();
}
}

View file

@ -0,0 +1,809 @@
/*******************************************************************************
* 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.resources;
import java.io.File;
import java.io.IOException;
import java.lang.ref.SoftReference;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.CProjectNature;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.content.IContentTypeManager;
import org.eclipse.core.runtime.jobs.Job;
/**
* Allows for looking up resources by location or name. When using this class 100 bytes per resource
* are needed. Therefore the support is limited to header-files int non-cdt projects and all files
* except non-cdt-files in CDT projects.
*
* The information for a project is initialized when first requested and then it is kept up to date
* using a resource change listener. No memory is used, as long as the class is not used.
* When information is not used for more than 10 minutes, the data-structures will be held via a weak
* reference, only and are subject to garbage collection.
*
* The node map stores a map from hash-code of file-names to nodes.
* A node contains the name of a file plus a link to the parent resource. From that we can compute
* the resource path and obtain further information via the resource.
*/
class ResourceLookupImpl implements IResourceChangeListener, IResourceDeltaVisitor, IResourceProxyVisitor {
private static final int UNREF_DELAY = 10 * 60000; // 10 min
private static final boolean VISIT_CHILDREN = true;
private static final boolean SKIP_CHILDREN = false;
private static final IFile[] NO_FILES = new IFile[0];
private static final int TRIGGER_RECALC=
IResourceDelta.TYPE | IResourceDelta.REPLACED |
IResourceDelta.LOCAL_CHANGED | IResourceDelta.OPEN;
private static class Extensions {
private final boolean fInvert;
private final Set<String> fExtensions;
Extensions(Set<String> 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<Map<Integer, Object>> fNodeMapRef;
private Map<Integer, Object> fNodeMap;
private final Map<String, Extensions> 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<String, Extensions>();
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<Map<Integer, Object>>(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<Integer, Object>();
fNodeMapRef= new SoftReference<Map<Integer, Object>>(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<String> select= new HashSet<String>();
String[] registeredContentTypes= CoreModel.getRegistedContentTypeIds();
select.addAll(Arrays.asList(registeredContentTypes));
final IContentTypeManager ctm= Platform.getContentTypeManager();
final IContentType[] ctts= ctm.getAllContentTypes();
Set<String> result= new HashSet<String>();
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<String>();
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<Object> 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<String> lines= new ArrayList<String>();
synchronized (fLock) {
for (Iterator<Object> 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<String> 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);
}
}

View file

@ -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);
}

View file

@ -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;

View file

@ -13,6 +13,7 @@
PreferenceScopeBlock_enableProjectSettings=Enable project specific settings
PreferenceScopeBlock_storeWithProject=Store settings with project
PreferenceScopeBlock_preferenceLink=<a>Configure Workspace Settings...</a>
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)