/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core.search.indexing;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ExtraFlags;
import org.eclipse.jdt.internal.compiler.ISourceElementRequestor;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.core.search.processing.JobManager;
/**
* This class is used by the JavaParserIndexer. When parsing the java file, the requestor recognizes
* the java elements (methods, fields, ...) and add them to an index.
*/
public class SourceIndexerRequestor implements ISourceElementRequestor, IIndexConstants {
SourceIndexer indexer;
char[] packageName= CharOperation.NO_CHAR;
char[][] enclosingTypeNames= new char[5][];
int depth= 0;
int methodDepth= 0;
public SourceIndexerRequestor(SourceIndexer indexer) {
this.indexer= indexer;
}
/**
* @see ISourceElementRequestor#acceptAnnotationTypeReference(char[][], int, int)
*/
public void acceptAnnotationTypeReference(char[][] typeName, int sourceStart, int sourceEnd) {
int length= typeName.length;
for (int i= 0; i < length - 1; i++)
acceptUnknownReference(typeName[i], 0);
acceptAnnotationTypeReference(typeName[length - 1], 0);
}
/**
* @see ISourceElementRequestor#acceptAnnotationTypeReference(char[], int)
*/
public void acceptAnnotationTypeReference(char[] simpleTypeName, int sourcePosition) {
this.indexer.addAnnotationTypeReference(simpleTypeName);
}
/**
* @see ISourceElementRequestor#acceptConstructorReference(char[], int, int)
*/
public void acceptConstructorReference(char[] typeName, int argCount, int sourcePosition) {
if (CharOperation.indexOf(Signature.C_GENERIC_START, typeName) > 0) {
typeName= Signature.toCharArray(Signature.getTypeErasure(Signature.createTypeSignature(typeName, false)).toCharArray());
}
this.indexer.addConstructorReference(typeName, argCount);
int lastDot= CharOperation.lastIndexOf('.', typeName);
if (lastDot != -1) {
char[][] qualification= CharOperation.splitOn('.', CharOperation.subarray(typeName, 0, lastDot));
for (int i= 0, length= qualification.length; i < length; i++) {
this.indexer.addNameReference(qualification[i]);
}
}
}
/**
* @see ISourceElementRequestor#acceptFieldReference(char[], int)
*/
public void acceptFieldReference(char[] fieldName, int sourcePosition) {
this.indexer.addFieldReference(fieldName);
}
/**
* @see ISourceElementRequestor#acceptImport(int, int, char[][], boolean, int)
*/
public void acceptImport(int declarationStart, int declarationEnd, char[][] tokens, boolean onDemand, int modifiers) {
// imports have already been reported while creating the ImportRef node (see SourceElementParser#comsume*ImportDeclarationName() methods)
}
/**
* @see ISourceElementRequestor#acceptLineSeparatorPositions(int[])
*/
public void acceptLineSeparatorPositions(int[] positions) {
// implements interface method
}
/**
* @see ISourceElementRequestor#acceptMethodReference(char[], int, int)
*/
public void acceptMethodReference(char[] methodName, int argCount, int sourcePosition) {
this.indexer.addMethodReference(methodName, argCount);
}
/**
* @see ISourceElementRequestor#acceptPackage(ImportReference)
*/
public void acceptPackage(ImportReference importReference) {
this.packageName= CharOperation.concatWith(importReference.getImportName(), '.');
}
/**
* @see ISourceElementRequestor#acceptProblem(CategorizedProblem)
*/
public void acceptProblem(CategorizedProblem problem) {
// implements interface method
}
/**
* @see ISourceElementRequestor#acceptTypeReference(char[][], int, int)
*/
public void acceptTypeReference(char[][] typeName, int sourceStart, int sourceEnd) {
int length= typeName.length;
for (int i= 0; i < length - 1; i++)
acceptUnknownReference(typeName[i], 0); // ?
acceptTypeReference(typeName[length - 1], 0);
}
/**
* @see ISourceElementRequestor#acceptTypeReference(char[], int)
*/
public void acceptTypeReference(char[] simpleTypeName, int sourcePosition) {
this.indexer.addTypeReference(simpleTypeName);
}
/**
* @see ISourceElementRequestor#acceptUnknownReference(char[][], int, int)
*/
public void acceptUnknownReference(char[][] name, int sourceStart, int sourceEnd) {
for (int i= 0; i < name.length; i++) {
acceptUnknownReference(name[i], 0);
}
}
/**
* @see ISourceElementRequestor#acceptUnknownReference(char[], int)
*/
public void acceptUnknownReference(char[] name, int sourcePosition) {
this.indexer.addNameReference(name);
}
private void addDefaultConstructorIfNecessary(TypeInfo typeInfo) {
boolean hasConstructor= false;
TypeDeclaration typeDeclaration= typeInfo.node;
AbstractMethodDeclaration[] methods= typeDeclaration.methods;
int methodCounter= methods == null ? 0 : methods.length;
done: for (int i= 0; i < methodCounter; i++) {
AbstractMethodDeclaration method= methods[i];
if (method.isConstructor() && !method.isDefaultConstructor()) {
hasConstructor= true;
break done;
}
}
if (!hasConstructor) {
this.indexer.addDefaultConstructorDeclaration(
typeInfo.name,
this.packageName == null ? CharOperation.NO_CHAR : this.packageName,
typeInfo.modifiers,
getMoreExtraFlags(typeInfo.extraFlags));
}
}
/*
* Rebuild the proper qualification for the current source type:
*
* java.lang.Object ---> null
* java.util.Hashtable$Entry --> [Hashtable]
* x.y.A$B$C --> [A, B]
*/
public char[][] enclosingTypeNames() {
if (this.depth == 0)
return null;
char[][] qualification= new char[this.depth][];
System.arraycopy(this.enclosingTypeNames, 0, qualification, 0, this.depth);
return qualification;
}
private void enterAnnotationType(TypeInfo typeInfo) {
char[][] typeNames;
if (this.methodDepth > 0) {
typeNames= ONE_ZERO_CHAR;
} else {
typeNames= enclosingTypeNames();
}
this.indexer.addAnnotationTypeDeclaration(typeInfo.modifiers, this.packageName, typeInfo.name, typeNames, typeInfo.secondary);
addDefaultConstructorIfNecessary(typeInfo);
pushTypeName(typeInfo.name);
}
private void enterClass(TypeInfo typeInfo) {
// eliminate possible qualifications, given they need to be fully resolved again
if (typeInfo.superclass != null) {
typeInfo.superclass= getSimpleName(typeInfo.superclass);
// add implicit constructor reference to default constructor
this.indexer.addConstructorReference(typeInfo.superclass, 0);
}
if (typeInfo.superinterfaces != null) {
for (int i= 0, length= typeInfo.superinterfaces.length; i < length; i++) {
typeInfo.superinterfaces[i]= getSimpleName(typeInfo.superinterfaces[i]);
}
}
char[][] typeNames;
if (this.methodDepth > 0) {
// set specific ['0'] value for local and anonymous to be able to filter them
typeNames= ONE_ZERO_CHAR;
} else {
typeNames= enclosingTypeNames();
}
char[][] typeParameterSignatures= null;
if (typeInfo.typeParameters != null) {
int typeParametersLength= typeInfo.typeParameters.length;
typeParameterSignatures= new char[typeParametersLength][];
for (int i= 0; i < typeParametersLength; i++) {
ISourceElementRequestor.TypeParameterInfo typeParameterInfo= typeInfo.typeParameters[i];
typeParameterSignatures[i]= Signature.createTypeParameterSignature(typeParameterInfo.name, typeParameterInfo.bounds == null ? CharOperation.NO_CHAR_CHAR : typeParameterInfo.bounds);
}
}
this.indexer.addClassDeclaration(typeInfo.modifiers, this.packageName, typeInfo.name, typeNames, typeInfo.superclass, typeInfo.superinterfaces, typeParameterSignatures, typeInfo.secondary);
addDefaultConstructorIfNecessary(typeInfo);
pushTypeName(typeInfo.name);
}
/**
* @see ISourceElementRequestor#enterCompilationUnit()
*/
public void enterCompilationUnit() {
// implements interface method
}
/**
* @see ISourceElementRequestor#enterConstructor(ISourceElementRequestor.MethodInfo)
*/
public void enterConstructor(MethodInfo methodInfo) {
int argCount= methodInfo.parameterTypes == null ? 0 : methodInfo.parameterTypes.length;
this.indexer.addConstructorDeclaration(
methodInfo.name,
argCount,
null,
methodInfo.parameterTypes,
methodInfo.parameterNames,
methodInfo.modifiers,
methodInfo.declaringPackageName,
methodInfo.declaringTypeModifiers,
methodInfo.exceptionTypes,
getMoreExtraFlags(methodInfo.extraFlags));
this.methodDepth++;
}
private void enterEnum(TypeInfo typeInfo) {
// eliminate possible qualifications, given they need to be fully resolved again
if (typeInfo.superinterfaces != null) {
for (int i= 0, length= typeInfo.superinterfaces.length; i < length; i++) {
typeInfo.superinterfaces[i]= getSimpleName(typeInfo.superinterfaces[i]);
}
}
char[][] typeNames;
if (this.methodDepth > 0) {
typeNames= ONE_ZERO_CHAR;
} else {
typeNames= enclosingTypeNames();
}
char[] superclass= typeInfo.superclass == null ? CharOperation.concatWith(TypeConstants.JAVA_LANG_ENUM, '.') : typeInfo.superclass;
this.indexer.addEnumDeclaration(typeInfo.modifiers, this.packageName, typeInfo.name, typeNames, superclass, typeInfo.superinterfaces, typeInfo.secondary);
addDefaultConstructorIfNecessary(typeInfo);
pushTypeName(typeInfo.name);
}
/**
* @see ISourceElementRequestor#enterField(ISourceElementRequestor.FieldInfo)
*/
public void enterField(FieldInfo fieldInfo) {
this.indexer.addFieldDeclaration(fieldInfo.type, fieldInfo.name);
this.methodDepth++;
}
/**
* @see ISourceElementRequestor#enterInitializer(int, int)
*/
public void enterInitializer(int declarationSourceStart, int modifiers) {
this.methodDepth++;
}
private void enterInterface(TypeInfo typeInfo) {
// eliminate possible qualifications, given they need to be fully resolved again
if (typeInfo.superinterfaces != null) {
for (int i= 0, length= typeInfo.superinterfaces.length; i < length; i++) {
typeInfo.superinterfaces[i]= getSimpleName(typeInfo.superinterfaces[i]);
}
}
char[][] typeNames;
if (this.methodDepth > 0) {
typeNames= ONE_ZERO_CHAR;
} else {
typeNames= enclosingTypeNames();
}
char[][] typeParameterSignatures= null;
if (typeInfo.typeParameters != null) {
int typeParametersLength= typeInfo.typeParameters.length;
typeParameterSignatures= new char[typeParametersLength][];
for (int i= 0; i < typeParametersLength; i++) {
ISourceElementRequestor.TypeParameterInfo typeParameterInfo= typeInfo.typeParameters[i];
typeParameterSignatures[i]= Signature.createTypeParameterSignature(typeParameterInfo.name, typeParameterInfo.bounds);
}
}
this.indexer.addInterfaceDeclaration(typeInfo.modifiers, this.packageName, typeInfo.name, typeNames, typeInfo.superinterfaces, typeParameterSignatures, typeInfo.secondary);
addDefaultConstructorIfNecessary(typeInfo);
pushTypeName(typeInfo.name);
}
/**
* @see ISourceElementRequestor#enterMethod(ISourceElementRequestor.MethodInfo)
*/
public void enterMethod(MethodInfo methodInfo) {
this.indexer.addMethodDeclaration(methodInfo.name, methodInfo.parameterTypes, methodInfo.returnType, methodInfo.exceptionTypes);
this.methodDepth++;
}
/**
* @see ISourceElementRequestor#enterType(ISourceElementRequestor.TypeInfo)
*/
public void enterType(TypeInfo typeInfo) {
// TODO (jerome) might want to merge the 4 methods
switch (TypeDeclaration.kind(typeInfo.modifiers)) {
case TypeDeclaration.CLASS_DECL:
enterClass(typeInfo);
break;
case TypeDeclaration.ANNOTATION_TYPE_DECL:
enterAnnotationType(typeInfo);
break;
case TypeDeclaration.INTERFACE_DECL:
enterInterface(typeInfo);
break;
case TypeDeclaration.ENUM_DECL:
enterEnum(typeInfo);
break;
}
}
/**
* @see ISourceElementRequestor#exitCompilationUnit(int)
*/
public void exitCompilationUnit(int declarationEnd) {
// implements interface method
}
/**
* @see ISourceElementRequestor#exitConstructor(int)
*/
public void exitConstructor(int declarationEnd) {
this.methodDepth--;
}
/**
* @see ISourceElementRequestor#exitField(int, int, int)
*/
public void exitField(int initializationStart, int declarationEnd, int declarationSourceEnd) {
this.methodDepth--;
}
/**
* @see ISourceElementRequestor#exitInitializer(int)
*/
public void exitInitializer(int declarationEnd) {
this.methodDepth--;
}
/**
* @see ISourceElementRequestor#exitMethod(int, Expression)
*/
public void exitMethod(int declarationEnd, Expression defaultValue) {
this.methodDepth--;
}
/**
* @see ISourceElementRequestor#exitType(int)
*/
public void exitType(int declarationEnd) {
popTypeName();
}
/*
* Returns the unqualified name without parameters from the given type name.
*/
private char[] getSimpleName(char[] typeName) {
int lastDot= -1, lastGenericStart= -1;
int depthCount= 0;
int length= typeName.length;
lastDotLookup: for (int i= length - 1; i >= 0; i--) {
switch (typeName[i]) {
case '.':
if (depthCount == 0) {
lastDot= i;
break lastDotLookup;
}
break;
case '<':
depthCount--;
if (depthCount == 0)
lastGenericStart= i;
break;
case '>':
depthCount++;
break;
}
}
if (lastGenericStart < 0) {
if (lastDot < 0) {
return typeName;
}
return CharOperation.subarray(typeName, lastDot + 1, length);
}
return CharOperation.subarray(typeName, lastDot + 1, lastGenericStart);
}
private int getMoreExtraFlags(int extraFlags) {
if (this.methodDepth > 0) {
extraFlags|= ExtraFlags.IsLocalType;
}
return extraFlags;
}
public void popTypeName() {
if (this.depth > 0) {
this.enclosingTypeNames[--this.depth]= null;
} else if (JobManager.VERBOSE) {
// dump a trace so it can be tracked down
try {
this.enclosingTypeNames[-1]= null;
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
}
}
public void pushTypeName(char[] typeName) {
if (this.depth == this.enclosingTypeNames.length)
System.arraycopy(this.enclosingTypeNames, 0, this.enclosingTypeNames= new char[this.depth * 2][], 0, this.depth);
this.enclosingTypeNames[this.depth++]= typeName;
}
}