blob: a39baaef70c7ed053c7dc5a5663daf7641aeb827 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v0.5
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v05.html
*
* Contributors:
* IBM Corp. - Rational Software - initial implementation
******************************************************************************/
/*
* Created on Jun 13, 2003
*/
package org.eclipse.cdt.internal.core.search.matching;
import java.io.CharArrayReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.parser.DefaultProblemHandler;
import org.eclipse.cdt.core.parser.IParser;
import org.eclipse.cdt.core.parser.IProblem;
import org.eclipse.cdt.core.parser.IScanner;
import org.eclipse.cdt.core.parser.IScannerInfo;
import org.eclipse.cdt.core.parser.IScannerInfoProvider;
import org.eclipse.cdt.core.parser.ISourceElementCallbackDelegate;
import org.eclipse.cdt.core.parser.ISourceElementRequestor;
import org.eclipse.cdt.core.parser.ParserFactory;
import org.eclipse.cdt.core.parser.ParserFactoryException;
import org.eclipse.cdt.core.parser.ParserLanguage;
import org.eclipse.cdt.core.parser.ParserMode;
import org.eclipse.cdt.core.parser.ParserUtil;
import org.eclipse.cdt.core.parser.ScannerInfo;
import org.eclipse.cdt.core.parser.ast.IASTASMDefinition;
import org.eclipse.cdt.core.parser.ast.IASTAbstractTypeSpecifierDeclaration;
import org.eclipse.cdt.core.parser.ast.IASTClassReference;
import org.eclipse.cdt.core.parser.ast.IASTClassSpecifier;
import org.eclipse.cdt.core.parser.ast.IASTCodeScope;
import org.eclipse.cdt.core.parser.ast.IASTCompilationUnit;
import org.eclipse.cdt.core.parser.ast.IASTElaboratedTypeSpecifier;
import org.eclipse.cdt.core.parser.ast.IASTEnumerationReference;
import org.eclipse.cdt.core.parser.ast.IASTEnumerationSpecifier;
import org.eclipse.cdt.core.parser.ast.IASTEnumerator;
import org.eclipse.cdt.core.parser.ast.IASTEnumeratorReference;
import org.eclipse.cdt.core.parser.ast.IASTField;
import org.eclipse.cdt.core.parser.ast.IASTFieldReference;
import org.eclipse.cdt.core.parser.ast.IASTFunction;
import org.eclipse.cdt.core.parser.ast.IASTFunctionReference;
import org.eclipse.cdt.core.parser.ast.IASTInclusion;
import org.eclipse.cdt.core.parser.ast.IASTLinkageSpecification;
import org.eclipse.cdt.core.parser.ast.IASTMacro;
import org.eclipse.cdt.core.parser.ast.IASTMethod;
import org.eclipse.cdt.core.parser.ast.IASTMethodReference;
import org.eclipse.cdt.core.parser.ast.IASTNamespaceDefinition;
import org.eclipse.cdt.core.parser.ast.IASTNamespaceReference;
import org.eclipse.cdt.core.parser.ast.IASTOffsetableNamedElement;
import org.eclipse.cdt.core.parser.ast.IASTParameterReference;
import org.eclipse.cdt.core.parser.ast.IASTReference;
import org.eclipse.cdt.core.parser.ast.IASTScope;
import org.eclipse.cdt.core.parser.ast.IASTTemplateDeclaration;
import org.eclipse.cdt.core.parser.ast.IASTTemplateInstantiation;
import org.eclipse.cdt.core.parser.ast.IASTTemplateSpecialization;
import org.eclipse.cdt.core.parser.ast.IASTTypedefDeclaration;
import org.eclipse.cdt.core.parser.ast.IASTTypedefReference;
import org.eclipse.cdt.core.parser.ast.IASTUsingDeclaration;
import org.eclipse.cdt.core.parser.ast.IASTUsingDirective;
import org.eclipse.cdt.core.parser.ast.IASTVariable;
import org.eclipse.cdt.core.parser.ast.IASTVariableReference;
import org.eclipse.cdt.core.search.ICSearchConstants;
import org.eclipse.cdt.core.search.ICSearchPattern;
import org.eclipse.cdt.core.search.ICSearchResultCollector;
import org.eclipse.cdt.core.search.ICSearchScope;
import org.eclipse.cdt.core.search.IMatch;
import org.eclipse.cdt.internal.core.model.IWorkingCopy;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
/**
* @author aniefer
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class MatchLocator implements ISourceElementRequestor, ICSearchConstants {
ArrayList matchStorage;
public static boolean VERBOSE = false;
/**
*
*/
public MatchLocator( ICSearchPattern pattern, ICSearchResultCollector collector, ICSearchScope scope, IProgressMonitor monitor) {
super();
searchPattern = pattern;
resultCollector = collector;
searchScope = scope;
progressMonitor = monitor;
}
public boolean acceptProblem(IProblem problem) { return DefaultProblemHandler.ruleOnProblem(problem, ParserMode.COMPLETE_PARSE ); }
public void acceptUsingDirective(IASTUsingDirective usageDirective) { }
public void acceptUsingDeclaration(IASTUsingDeclaration usageDeclaration) { }
public void acceptASMDefinition(IASTASMDefinition asmDefinition) { }
public void acceptAbstractTypeSpecDeclaration(IASTAbstractTypeSpecifierDeclaration abstractDeclaration) {}
public void enterTemplateDeclaration(IASTTemplateDeclaration declaration) { }
public void enterTemplateSpecialization(IASTTemplateSpecialization specialization) { }
public void enterTemplateInstantiation(IASTTemplateInstantiation instantiation) { }
public void exitTemplateDeclaration(IASTTemplateDeclaration declaration) {}
public void exitTemplateSpecialization(IASTTemplateSpecialization specialization) { }
public void exitTemplateExplicitInstantiation(IASTTemplateInstantiation instantiation) { }
public void enterCodeBlock(IASTCodeScope scope) { }
public void exitCodeBlock(IASTCodeScope scope) { }
public void enterLinkageSpecification(IASTLinkageSpecification linkageSpec){
pushScope( linkageSpec );
}
public void exitLinkageSpecification(IASTLinkageSpecification linkageSpec){
popScope();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.parser.ISourceElementRequestor#acceptParameterReference(org.eclipse.cdt.internal.core.parser.ast.complete.ASTParameterReference)
*/
public void acceptParameterReference(IASTParameterReference reference)
{
check( REFERENCES, reference );
}
public void acceptTypedefDeclaration(IASTTypedefDeclaration typedef){
lastDeclaration = typedef;
check( DECLARATIONS, typedef );
}
public void acceptTypedefReference( IASTTypedefReference reference ){
check( REFERENCES, reference );
}
public void acceptEnumeratorReference(IASTEnumeratorReference reference){
check( REFERENCES, reference );
}
public void acceptMacro(IASTMacro macro){
check( DECLARATIONS, macro );
}
public void acceptVariable(IASTVariable variable){
lastDeclaration = variable;
check( DECLARATIONS, variable );
//A declaration is a definition unless...:
//it contains the extern specifier or a linkage-spec and no initializer
if( variable.getInitializerClause() != null ||
( !variable.isExtern() && !(currentScope instanceof IASTLinkageSpecification) ) ){
check( DEFINITIONS, variable );
}
}
public void acceptField(IASTField field){
lastDeclaration = field;
if( currentScope instanceof IASTClassSpecifier ){
check( DECLARATIONS, field );
if( !field.isStatic() ){
check( DEFINITIONS, field );
}
} else {
check( DEFINITIONS, field );
}
}
public void acceptEnumerationSpecifier(IASTEnumerationSpecifier enumeration){
lastDeclaration = enumeration;
check( DECLARATIONS, enumeration );
Iterator iter = enumeration.getEnumerators();
while( iter.hasNext() ){
IASTEnumerator enumerator = (IASTEnumerator) iter.next();
lastDeclaration = enumerator;
check ( DECLARATIONS, enumerator );
check ( DEFINITIONS, enumerator );
}
}
public void acceptFunctionDeclaration(IASTFunction function){
lastDeclaration = function;
check( DECLARATIONS, function );
}
public void acceptMethodDeclaration(IASTMethod method){
lastDeclaration = method;
check( DECLARATIONS, method );
}
public void acceptClassReference(IASTClassReference reference) {
check( REFERENCES, reference );
}
public void acceptNamespaceReference( IASTNamespaceReference reference ){
check( REFERENCES, reference );
}
public void acceptVariableReference( IASTVariableReference reference ){
check( REFERENCES, reference );
}
public void acceptFieldReference( IASTFieldReference reference ){
check( REFERENCES, reference );
}
public void acceptEnumerationReference( IASTEnumerationReference reference ){
check( REFERENCES, reference );
}
public void acceptFunctionReference( IASTFunctionReference reference ){
check( REFERENCES, reference );
}
public void acceptMethodReference( IASTMethodReference reference ){
check( REFERENCES, reference );
}
public void enterFunctionBody(IASTFunction function){
lastDeclaration = function;
if( !function.previouslyDeclared() )
check( DECLARATIONS, function );
check( DEFINITIONS, function );
pushScope( function );
}
public void enterMethodBody(IASTMethod method) {
lastDeclaration = method;
if( !method.previouslyDeclared() )
check( DECLARATIONS, method );
check( DEFINITIONS, method );
pushScope( method );
}
public void enterCompilationUnit(IASTCompilationUnit compilationUnit) {
pushScope( compilationUnit );
}
public void enterNamespaceDefinition(IASTNamespaceDefinition namespaceDefinition) {
lastDeclaration = namespaceDefinition;
check( DECLARATIONS, namespaceDefinition );
check( DEFINITIONS, namespaceDefinition );
pushScope( namespaceDefinition );
}
public void enterClassSpecifier(IASTClassSpecifier classSpecification) {
lastDeclaration = classSpecification;
check( DECLARATIONS, classSpecification );
pushScope( classSpecification );
}
public void exitFunctionBody(IASTFunction function) {
popScope();
}
public void exitMethodBody(IASTMethod method) {
popScope();
}
public void exitClassSpecifier(IASTClassSpecifier classSpecification) {
popScope();
}
public void exitNamespaceDefinition(IASTNamespaceDefinition namespaceDefinition) {
popScope();
}
public void exitCompilationUnit(IASTCompilationUnit compilationUnit){
popScope();
}
public void enterInclusion(IASTInclusion inclusion) {
String includePath = inclusion.getFullFileName();
IPath path = new Path( includePath );
IResource resource = null;
if( workspaceRoot != null ){
resource = workspaceRoot.getFileForLocation( path );
// if( resource == null ){
// //TODO:What to do if the file is not in the workspace?
// IFile file = currentResource.getProject().getFile( inclusion.getName() );
// try{
// file.createLink( path, 0, null );
// } catch ( CoreException e ){
// file = null;
// }
// resource = file;
// }
}
resourceStack.addFirst( ( currentResource != null ) ? (Object)currentResource : (Object)currentPath );
currentResource = resource;
currentPath = ( resource == null ) ? path : null;
}
public void exitInclusion(IASTInclusion inclusion) {
Object obj = resourceStack.removeFirst();
if( obj instanceof IResource ){
currentResource = (IResource)obj;
currentPath = null;
} else {
currentPath = (IPath) obj;
currentResource = null;
}
}
public void locateMatches( String [] paths, IWorkspace workspace, IWorkingCopy[] workingCopies,ArrayList matches ){
matchStorage = matches;
workspaceRoot = (workspace != null) ? workspace.getRoot() : null;
HashMap wcPaths = new HashMap();
int wcLength = (workingCopies == null) ? 0 : workingCopies.length;
if( wcLength > 0 ){
String [] newPaths = new String[ wcLength ];
for( int i = 0; i < wcLength; i++ ){
IWorkingCopy workingCopy = workingCopies[ i ];
String path = workingCopy.getOriginalElement().getPath().toString();
wcPaths.put( path, workingCopy );
newPaths[ i ] = path;
}
int len = paths.length;
String [] tempArray = new String[ len + wcLength ];
System.arraycopy( paths, 0, tempArray, 0, len );
System.arraycopy( newPaths, 0, tempArray, len, wcLength );
paths = tempArray;
}
Arrays.sort( paths );
int length = paths.length;
if( progressMonitor != null ){
progressMonitor.beginTask( "", length );
}
for( int i = 0; i < length; i++ ){
if( progressMonitor != null ) {
if( progressMonitor.isCanceled() ){
throw new OperationCanceledException();
} else {
progressMonitor.worked( 1 );
}
}
String pathString = paths[ i ];
//skip duplicates
if( i > 0 && pathString.equals( paths[ i - 1 ] ) ) continue;
if (!searchScope.encloses(pathString)) continue;
Reader reader = null;
IPath realPath = null;
IProject project = null;
if( workspaceRoot != null ){
IWorkingCopy workingCopy = (IWorkingCopy)wcPaths.get( pathString );
if( workingCopy != null ){
reader = new CharArrayReader( workingCopy.getContents() );
currentResource = workingCopy.getResource();
realPath = currentResource.getLocation();
project = currentResource.getProject();
} else {
currentResource = workspaceRoot.findMember( pathString, true );
try{
if( currentResource != null && currentResource instanceof IFile ){
IFile file = (IFile) currentResource;
reader = new InputStreamReader( file.getContents() );
realPath = currentResource.getLocation();
project = file.getProject();
}
} catch ( CoreException e ){
continue;
}
}
}
if( currentResource == null ) {
IPath path = new Path( pathString );
try {
currentPath = path;
reader = new FileReader( path.toFile() );
realPath = currentPath;
} catch (FileNotFoundException e) {
continue;
}
}
//Get the scanner info
IScannerInfo scanInfo = new ScannerInfo();
IScannerInfoProvider provider = CCorePlugin.getDefault().getScannerInfoProvider(project);
if (provider != null){
IScannerInfo buildScanInfo = provider.getScannerInformation(project);
if( buildScanInfo != null )
scanInfo = new ScannerInfo(buildScanInfo.getDefinedSymbols(), buildScanInfo.getIncludePaths());
}
ParserLanguage language = null;
if( project != null ){
language = CoreModel.getDefault().hasCCNature( project ) ? ParserLanguage.CPP : ParserLanguage.C;
} else {
//TODO no project, what language do we use?
language = ParserLanguage.CPP;
}
IParser parser = null;
try
{
IScanner scanner = ParserFactory.createScanner( reader, realPath.toOSString(), scanInfo, ParserMode.COMPLETE_PARSE, language, this, ParserUtil.getParserLogService() );
parser = ParserFactory.createParser( scanner, this, ParserMode.COMPLETE_PARSE, language, ParserUtil.getParserLogService() );
}
catch( ParserFactoryException pfe )
{
}
if (VERBOSE)
MatchLocator.verbose("*** New Search for path: " + pathString);
parser.parse();
}
}
protected void report( ISourceElementCallbackDelegate node, int accuracyLevel ){
try {
if( currentResource != null && !searchScope.encloses(currentResource.getFullPath().toOSString() ) ){
return;
}
int offset = 0;
int end = 0;
if( node instanceof IASTReference ){
IASTReference reference = (IASTReference) node;
offset = reference.getOffset();
end = offset + reference.getName().length();;
if (VERBOSE)
MatchLocator.verbose("Report Match: " + reference.getName());
} else if( node instanceof IASTOffsetableNamedElement ){
IASTOffsetableNamedElement offsetableElement = (IASTOffsetableNamedElement) node;
offset = offsetableElement.getNameOffset() != 0 ? offsetableElement.getNameOffset()
: offsetableElement.getStartingOffset();
end = offsetableElement.getNameEndOffset();
if( end == 0 ){
end = offset + offsetableElement.getName().length();;
}
if (VERBOSE)
MatchLocator.verbose("Report Match: " + offsetableElement.getName());
}
IMatch match = null;
ISourceElementCallbackDelegate object = null;
if( node instanceof IASTReference ){
if( currentScope instanceof IASTFunction || currentScope instanceof IASTMethod ){
object = (ISourceElementCallbackDelegate) currentScope;
} else {
object = lastDeclaration;
}
} else {
if( currentScope instanceof IASTFunction || currentScope instanceof IASTMethod ){
//local declaration, only report if not being filtered
if( shouldExcludeLocalDeclarations ){
return;
}
object = (ISourceElementCallbackDelegate) currentScope;
} else {
object = node;
}
}
if( currentResource != null ){
match = resultCollector.createMatch( currentResource, offset, end, object );
} else if( currentPath != null ){
match = resultCollector.createMatch( currentPath, offset, end, object );
}
if( match != null ){
//Save till later
//resultCollector.acceptMatch( match );
matchStorage.add(match);
}
} catch (CoreException e) {
}
}
private void check( LimitTo limit, ISourceElementCallbackDelegate node ){
if( !searchPattern.canAccept( limit ) )
return;
int level = ICSearchPattern.IMPOSSIBLE_MATCH;
if( node instanceof IASTReference ){
level = searchPattern.matchLevel( ((IASTReference)node).getReferencedElement(), limit );
} else {
level = searchPattern.matchLevel( node, limit );
}
if( level != ICSearchPattern.IMPOSSIBLE_MATCH )
{
report( node, level );
}
}
private void pushScope( IASTScope scope ){
scopeStack.addFirst( currentScope );
currentScope = scope;
}
private IASTScope popScope(){
IASTScope oldScope = currentScope;
currentScope = (scopeStack.size() > 0 ) ? (IASTScope) scopeStack.removeFirst() : null;
return oldScope;
}
public void setShouldExcludeLocalDeclarations( boolean exclude ){
shouldExcludeLocalDeclarations = exclude;
}
private boolean shouldExcludeLocalDeclarations = false;
private ISourceElementCallbackDelegate lastDeclaration;
private ICSearchPattern searchPattern;
private ICSearchResultCollector resultCollector;
private IProgressMonitor progressMonitor;
private IPath currentPath = null;
private ICSearchScope searchScope;
private IWorkspaceRoot workspaceRoot;
private IResource currentResource = null;
private LinkedList resourceStack = new LinkedList();
private IASTScope currentScope = null;
private LinkedList scopeStack = new LinkedList();
/* (non-Javadoc)
* @see org.eclipse.cdt.core.parser.ISourceElementRequestor#acceptElaboratedForewardDeclaration(org.eclipse.cdt.core.parser.ast.IASTElaboratedTypeSpecifier)
*/
public void acceptElaboratedForewardDeclaration(IASTElaboratedTypeSpecifier elaboratedType){
check( DECLARATIONS, elaboratedType );
}
public static void verbose(String log) {
System.out.println("(" + Thread.currentThread() + ") " + log);
}
}