blob: b6a848cb264eb71d4dae242b915e7b4e6d213f59 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 BEA Systems Inc.
* 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:
* tyeung@bea.com - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.apt.core.internal.env;
import java.io.File;
import java.util.*;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.apt.core.env.Phase;
import org.eclipse.jdt.apt.core.internal.AptPlugin;
import org.eclipse.jdt.apt.core.internal.AptProject;
import org.eclipse.jdt.apt.core.internal.declaration.EclipseDeclarationImpl;
import org.eclipse.jdt.apt.core.internal.declaration.PackageDeclarationImpl;
import org.eclipse.jdt.apt.core.internal.declaration.PackageDeclarationImplNoBinding;
import org.eclipse.jdt.apt.core.internal.declaration.TypeDeclarationImpl;
import org.eclipse.jdt.apt.core.internal.type.PrimitiveTypeImpl;
import org.eclipse.jdt.apt.core.internal.type.VoidTypeImpl;
import org.eclipse.jdt.apt.core.internal.util.DeclarationsUtil;
import org.eclipse.jdt.apt.core.internal.util.Factory;
import org.eclipse.jdt.apt.core.internal.util.PackageUtil;
import org.eclipse.jdt.apt.core.internal.util.TypesUtil;
import org.eclipse.jdt.apt.core.internal.util.Visitors.AnnotatedNodeVisitor;
import org.eclipse.jdt.apt.core.util.AptConfig;
import org.eclipse.jdt.core.BindingKey;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTRequestor;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.apt.AnnotationProcessorListener;
import com.sun.mirror.apt.Filer;
import com.sun.mirror.apt.Messager;
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
import com.sun.mirror.declaration.Declaration;
import com.sun.mirror.declaration.PackageDeclaration;
import com.sun.mirror.declaration.TypeDeclaration;
import com.sun.mirror.util.Declarations;
import com.sun.mirror.util.Types;
/**
* Base annotation processor environment that supports type system navigation.
* No support for problem registration as well as type generation.
*
* @author tyeung
*/
public class BaseProcessorEnv implements AnnotationProcessorEnvironment
{
static{
final AST ast = AST.newAST(AST.JLS3);
EMPTY_AST_UNIT = ast.newCompilationUnit();
}
public static final CompilationUnit EMPTY_AST_UNIT;
public static final ICompilationUnit[] NO_UNIT = new ICompilationUnit[0];
public static final CompilationUnit[] NO_AST_UNITs = new CompilationUnit[0];
public static final String[] NO_KEYS = new String[0];
private static final int BOOLEAN_INDEX = 0;
private static final int BYTE_INDEX = 1;
private static final int CHAR_INDEX = 2;
private static final int DOUBLE_INDEX = 3;
private static final int FLOAT_INDEX = 4;
private static final int INT_INDEX = 5;
private static final int LONG_INDEX = 6;
private static final int SHORT_INDEX = 7;
private static final String DOT_JAVA = ".java"; //$NON-NLS-1$
protected CompilationUnit _astRoot;
protected final Phase _phase;
protected IFile _file;
protected final IJavaProject _javaProject;
protected final AptProject _aptProject;
/**
* Unmodifiable map of processor options, including -A options.
* Set in ctor and then not changed.
*/
protected final Map<String, String> _options;
/**
* Mapping model compilation unit to dom compilation unit.
* The assumption here is that once the client examine some binding from some file,
* it will continue to examine other bindings from came from that same file.
*/
protected final Map<ICompilationUnit, CompilationUnit> _modelCompUnit2astCompUnit;
/**
* Mapping (source) top-level type binding to the compilation unit that defines it.
*/
protected final Map<ITypeBinding, ICompilationUnit> _typeBinding2ModelCompUnit;
// void type and the primitive types will be null if the '_file'
// is outside of the workspace.
private VoidTypeImpl _voidType;
private PrimitiveTypeImpl[] _primitives;
// This type cache exists for the duration of a single round.
// We store positive as well as negative hits. Negative hits are
// stored with a value of null
protected final Map<String,TypeDeclaration> _typeCache = new HashMap<String,TypeDeclaration>();
protected IPackageFragmentRoot[] _packageRootsCache;
public BaseProcessorEnv(CompilationUnit astCompilationUnit,
IFile file,
IJavaProject javaProj,
Phase phase )
{
_astRoot = astCompilationUnit;
_file = file;
_javaProject = javaProj;
_phase = phase;
_options = initOptions(javaProj);
_modelCompUnit2astCompUnit = new HashMap<ICompilationUnit, CompilationUnit>();
_typeBinding2ModelCompUnit = new HashMap<ITypeBinding, ICompilationUnit>();
_aptProject = AptPlugin.getAptProject(javaProj);
}
/**
* Set the _options map based on the current project/workspace settings.
* There is a bug in Sun's apt implementation: it parses the command line
* incorrectly, such that -Akey=value gets added to the options map as
* key "-Akey=value" and value "". In order to support processors written
* to run on Sun's apt as well as processors written without this bug
* in mind, we populate the map with two copies of every option, one the
* expected way ("key" / "value") and the other the Sun way
* ("-Akey=value" / ""). We make exceptions for the non-dash-A options
* that we set automatically, such as -classpath, -target, and so forth;
* since these wouldn't have come from a -A option we don't construct a
* -Akey=value variant.
*
* Called from constructor. A new Env is constructed for each build pass,
* so this will always be up to date with the latest settings.
*/
private Map<String, String> initOptions(IJavaProject jproj) {
Map<String, String> procOptions = AptConfig.getProcessorOptions(jproj);
// options is large enough to include the translated -A options
Map<String, String> options = new HashMap<String, String>(procOptions.size() * 2);
// Add configured options
for (Map.Entry<String, String> entry : procOptions.entrySet()) {
String value = entry.getValue();
String key = entry.getKey();
options.put(key, value);
if (!AptConfig.isAutomaticProcessorOption(key)) {
String sunStyle;
if (value != null) {
sunStyle = "-A" + entry.getKey() + "=" + value; //$NON-NLS-1$ //$NON-NLS-2$
}
else {
sunStyle = "-A" + entry.getKey(); //$NON-NLS-1$
}
options.put(sunStyle, ""); //$NON-NLS-1$
}
}
return Collections.unmodifiableMap(options);
}
public Types getTypeUtils()
{
return new TypesUtil(this);
}
public Declarations getDeclarationUtils()
{
return new DeclarationsUtil();
}
public void addListener(AnnotationProcessorListener listener) {
throw new UnsupportedOperationException("Not supported!"); //$NON-NLS-1$
}
public void removeListener(AnnotationProcessorListener listener) {
throw new UnsupportedOperationException("Not supported!"); //$NON-NLS-1$
}
/**
* @return the list of all named type declarations in the compilation units associated with
* this environment - usually just one compilation unit, except in batch mode
* where it will be all compilation units in the build.
* This implementation is different from the API specification in that it does not return
* all included source types in the universe.
*/
public Collection<TypeDeclaration> getTypeDeclarations()
{
final List<ITypeBinding> bindings = getTypeBindings();
if( bindings.isEmpty() )
return Collections.emptyList();
final List<TypeDeclaration> mirrorDecls = new ArrayList<TypeDeclaration>(bindings.size());
for( ITypeBinding binding : bindings ){
final TypeDeclaration mirrorDecl = Factory.createReferenceType(binding, this);
if( mirrorDecl != null )
mirrorDecls.add(mirrorDecl);
}
return mirrorDecls;
}
@SuppressWarnings("unchecked")
protected List<AbstractTypeDeclaration> searchLocallyForTypeDeclarations()
{
return _astRoot.types();
}
private List<ITypeBinding> getTypeBindings()
{
final List<AbstractTypeDeclaration> declTypes = searchLocallyForTypeDeclarations();
if( declTypes == null || declTypes.isEmpty() )
return Collections.emptyList();
final List<ITypeBinding> typeBindings = new ArrayList<ITypeBinding>(declTypes.size());
for( AbstractTypeDeclaration decl : declTypes ){
getTypeBindings(decl.resolveBinding(), typeBindings);
}
return typeBindings;
}
/**
* Add <code>type</code> and all its declared nested type(s) to <code>types</code>
* @param type the container type
* @param typeBindings upon return, contains all the nested types within <code>type</code>
* and the type itself.
*/
protected void getTypeBindings(final ITypeBinding type, final List<ITypeBinding> typeBindings)
{
if( type == null ) return;
typeBindings.add(type);
for( ITypeBinding nestedType : type.getDeclaredTypes() ) {
//typeBindings.add(nestedType);
getTypeBindings(nestedType, typeBindings);
}
}
public Collection<TypeDeclaration> getSpecifiedTypeDeclarations()
{
return getTypeDeclarations();
}
public Collection<Declaration> getDeclarationsAnnotatedWith(AnnotationTypeDeclaration a)
{
final ITypeBinding annotationType = TypesUtil.getTypeBinding(a);
if( annotationType == null || !annotationType.isAnnotation()) return Collections.emptyList();
return getDeclarationsAnnotatedWith(annotationType);
}
/**
* Go through the current compilation unit and look for ast nodes that has annotations.
* @return the map between ast node and
*/
protected Map<ASTNode, List<Annotation>> getASTNodesWithAnnotations()
{
final Map<ASTNode, List<Annotation>> astNode2Anno = new HashMap<ASTNode, List<Annotation>>();
final AnnotatedNodeVisitor visitor = new AnnotatedNodeVisitor(astNode2Anno);
_astRoot.accept(visitor);
return astNode2Anno;
}
private List<Declaration> getDeclarationsAnnotatedWith(final ITypeBinding annotationType)
{
final Map<ASTNode, List<Annotation>> astNode2Anno = getASTNodesWithAnnotations();
if( astNode2Anno.isEmpty() )
return Collections.emptyList();
final List<Declaration> decls = new ArrayList<Declaration>();
for(Map.Entry<ASTNode, List<Annotation>> entry : astNode2Anno.entrySet() ){
final ASTNode node = entry.getKey();
for( Annotation anno : entry.getValue() ){
final IBinding resolvedTypeBinding = anno.resolveTypeBinding();
if( annotationType.isEqualTo(resolvedTypeBinding) )
getDeclarations(node, decls);
}
}
return decls;
}
protected IFile getFileForNode(final ASTNode node)
{
if( node.getRoot() == _astRoot )
return _file;
throw new IllegalStateException(); // should never get here.
}
/**
* @param node the ast node in question
* @param decls the list to be populated.
* adding the declaration(s) corresponding to the ast node to this list.
*/
@SuppressWarnings("unchecked")
protected void getDeclarations(ASTNode node, List<Declaration>decls)
{
if( node == null ) return;
IBinding binding = null;
switch( node.getNodeType() )
{
case ASTNode.FIELD_DECLARATION:
final List<VariableDeclarationFragment> fragments =
((org.eclipse.jdt.core.dom.FieldDeclaration)node).fragments();
for( VariableDeclarationFragment frag : fragments ){
final IBinding fieldBinding = frag.resolveBinding();
final EclipseDeclarationImpl decl;
if( fieldBinding != null )
decl = Factory.createDeclaration(fieldBinding, this);
else{
decl = Factory.createDeclaration(frag, getFileForNode(frag), this);
}
if( decl != null )
decls.add(decl);
}
return;
case ASTNode.ENUM_CONSTANT_DECLARATION:
binding = ((org.eclipse.jdt.core.dom.EnumConstantDeclaration)node).resolveVariable();
break;
case ASTNode.METHOD_DECLARATION:
binding = ((org.eclipse.jdt.core.dom.MethodDeclaration)node).resolveBinding();
break;
case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION:
binding = ((AnnotationTypeMemberDeclaration)node).resolveBinding();
break;
case ASTNode.TYPE_DECLARATION:
case ASTNode.ANNOTATION_TYPE_DECLARATION:
case ASTNode.ENUM_DECLARATION:
binding = ((AbstractTypeDeclaration)node).resolveBinding();
break;
case ASTNode.SINGLE_VARIABLE_DECLARATION:
// Need to create the declaration with the ast node, not the binding
break;
case ASTNode.PACKAGE_DECLARATION:
binding = ((org.eclipse.jdt.core.dom.PackageDeclaration)node).resolveBinding();
break;
default:
throw new UnsupportedOperationException("unknown node type: " + node.getNodeType()); //$NON-NLS-1$
}
final EclipseDeclarationImpl decl;
if( binding != null )
decl = Factory.createDeclaration(binding, this);
else{
decl = Factory.createDeclaration(node, getFileForNode(node), this);
}
if( decl != null )
decls.add( decl );
return;
}
/**
* @param binding must be correspond to a type, method or field declaration.
* @return the ast node the corresponds to the declaration of the given binding.
* Return null if none is found.
*/
public ASTNode getASTNodeForBinding(final IBinding binding)
{
final CompilationUnit astUnit = getCompilationUnitForBinding(binding);
if( astUnit == null )
return null;
return astUnit.findDeclaringNode(binding.getKey());
}
public Map<String, String> getOptions(){ return _options; }
// does not generate dependencies
public TypeDeclaration getTypeDeclaration(String name)
{
if( name == null || name.length() == 0 ) return null;
// get rid of the generics parts.
final int index = name.indexOf('<');
if( index != -1 )
name = name.substring(0, index);
ITypeBinding typeBinding = null;
try {
typeBinding = getTypeDefinitionBindingFromName(name);
}
catch (ArrayIndexOutOfBoundsException e) {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=133947
// if the name is invalid, JDT can throw an ArrayIndexOutOfBoundsException
// We'll ignore this and return null to the user
AptPlugin.log(e, "Unable to get type definition binding for: " + name); //$NON-NLS-1$
}
return Factory.createReferenceType(typeBinding, this);
}
/**
* @param fullyQualifiedName the fully qualified name of a type.
* The name cannot contain type argument or array signature.
* The name *must* also be correct wrt $ for inner-class separators.
* e.g. java.util.Map$Entry, NOT java.util.Map.Entry
* @return the type binding corresponding to the parameter.
*/
protected ITypeBinding getTypeDefinitionBindingFromCorrectName(
final String fullyQualifiedName ){
final int dollarIndex = fullyQualifiedName.indexOf('$');
final String toplevelTypeName;
if( dollarIndex < 0 )
toplevelTypeName = fullyQualifiedName;
else
toplevelTypeName = fullyQualifiedName.substring(0, dollarIndex);
// locate the compilation unit for the type of interest.
// we need this information so that when we request the binding for 'fullyQualifiedName'
// we can get the dom pipeline to return back to us the ast compilation unit
// which we will need to correctly compute the number of methods, fields and constructors.
// see CR259011 -theodora
ICompilationUnit unit = getICompilationUnitForTopLevelType(toplevelTypeName);
final String key = BindingKey.createTypeBindingKey(fullyQualifiedName);
return getTypeBindingFromKey(key, unit);
}
private ITypeBinding getTypeDefinitionBindingFromName(String fullyQualifiedName) {
// We don't know for sure that the name we have represents a top-level type,
// so we need to loop backwards until we find one, in case we have something
// like "java.util.Map.Entry", converting it to "java.util.Map$Entry". --jgarms
ITypeBinding binding = getTypeDefinitionBindingFromCorrectName(fullyQualifiedName);
while (binding == null) {
int dotIndex = fullyQualifiedName.lastIndexOf('.');
if (dotIndex == -1) {
break;
}
fullyQualifiedName = fullyQualifiedName.substring(0, dotIndex) +
"$" + //$NON-NLS-1$
fullyQualifiedName.substring(dotIndex + 1);
binding = getTypeDefinitionBindingFromCorrectName(fullyQualifiedName);
}
return binding;
}
/**
* @param key
* @param unit the unit that contains the definition of type whose type key is <code>key</code>
* if <code>key</code> is a wild card, primitive, array type or parameterized type, this should be null.
* @return return the type binding for the given key or null if none is found.
*/
protected ITypeBinding getTypeBindingFromKey(final String key, final ICompilationUnit unit){
class BindingRequestor extends ASTRequestor
{
private ITypeBinding _result = null;
public void acceptAST(ICompilationUnit source, CompilationUnit ast) {
if( source == unit ){
_modelCompUnit2astCompUnit.put(source, ast);
}
}
public void acceptBinding(String bindingKey, IBinding binding)
{
if( binding != null && binding.getKind() == IBinding.TYPE )
_result = (ITypeBinding)binding;
}
}
final BindingRequestor requestor = new BindingRequestor();
final ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setResolveBindings(true);
parser.setProject(_javaProject);
ICompilationUnit[] units = unit == null ? NO_UNIT : new ICompilationUnit[]{unit};
parser.createASTs(units, new String[]{key}, requestor, null);
final ITypeBinding result = requestor._result;
if(result != null && unit != null){
final CompilationUnit astUnit = _modelCompUnit2astCompUnit.get(unit);
// make sure everything is lining up properly.
if( astUnit.findDeclaringNode(result) != null ){
ITypeBinding declaringClass = getDeclaringClass(result);
_typeBinding2ModelCompUnit.put(declaringClass, unit);
}
}
return result;
}
/**
* @param key the key to a type binding, could be reference type, array or primitive.
* @return the binding corresponding to the given key or null if none is found.
*/
public ITypeBinding getTypeBindingFromKey(final String key)
{
return getTypeBindingFromKey(key, null);
}
public TypeDeclaration getTypeDeclaration(final IType type) {
if (type == null) return null;
String name = type.getFullyQualifiedName();
return getTypeDeclaration(name);
}
public PackageDeclaration getPackage(String name)
{
if (name == null)
throw new IllegalArgumentException("name cannot be null"); //$NON-NLS-1$
IPackageFragment[] pkgFrags = PackageUtil.getPackageFragments(name, this);
// No packages found, null expected
if (pkgFrags.length == 0)
return null;
// If there are no source or class files, we'll need to return
// a special implementation of the package decl that expects
// no declarations inside it
boolean containsNoJavaResources = true;
for (IPackageFragment pkg : pkgFrags) {
try {
if (pkg.containsJavaResources()) {
containsNoJavaResources = false;
break;
}
}
catch (JavaModelException e) {}
}
if (containsNoJavaResources)
return new PackageDeclarationImplNoBinding(pkgFrags);
// We should be able to create a class or
// source file from one of the packages.
ICompilationUnit compUnit = null;
IClassFile classFile = null;
OUTER:
for (IPackageFragment pkg : pkgFrags) {
try {
ICompilationUnit[] compUnits = pkg.getCompilationUnits();
if (compUnits.length > 0) {
compUnit = compUnits[0];
break;
}
IClassFile[] classFiles = pkg.getClassFiles();
if (classFiles.length > 0) {
// Need to grab the first one that's not an inner class,
// as eclipse has trouble parsing inner class files
for (IClassFile tempClassFile : classFiles) {
if (tempClassFile.getElementName().indexOf("$") < 0) { //$NON-NLS-1$
classFile = tempClassFile;
break OUTER;
}
}
}
}
catch (JavaModelException e) {}
}
IType type = null;
if (compUnit != null) {
try {
IType[] types = compUnit.getAllTypes();
if (types.length > 0) {
type = types[0];
}
}
catch (JavaModelException e) {}
}
else if (classFile != null) {
type = classFile.getType();
}
// Given a type, we can construct a package declaration impl from it,
// but we must hide the fact that it came from a real declaration,
// as the client requested it without that context
if (type != null) {
TypeDeclarationImpl typeDecl = (TypeDeclarationImpl)getTypeDeclaration(type);
ITypeBinding binding = typeDecl.getDeclarationBinding();
return new PackageDeclarationImpl(binding.getPackage(), typeDecl, this, true, pkgFrags);
}
// No classes or source files found
return new PackageDeclarationImplNoBinding(pkgFrags);
}
protected CompilationUnit searchLocallyForBinding(final IBinding binding)
{
if (_astRoot == null) {
throw new IllegalStateException("_astRoot is null. Check that types or environments are not being cached between builds or reconciles by user code"); //$NON-NLS-1$
}
final ASTNode node = _astRoot.findDeclaringNode(binding);
if( node != null )
return _astRoot;
return null;
}
/**
* Retrieve the <code>ICompilationUnit</code> whose top-level type has
* <code>topTypeQName</code> as its fully qualified name.
* @param topTypeQName
* @return the <code>ICompilationUnit</code> matching <code>topTypeQName</code> or
* <code>null</code> if one doesn't exist.
*/
private ICompilationUnit getICompilationUnitForTopLevelType(final String topTypeQName ){
final String pathname = topTypeQName.replace('.', File.separatorChar) + DOT_JAVA;
final IPath path = Path.fromOSString(pathname);
try{
final IJavaElement element = _javaProject.findElement(path);
if( element instanceof ICompilationUnit )
return (ICompilationUnit)element;
else // dropping class files.
return null;
}
catch(JavaModelException e){
return null;
}
}
/**
* @param binding must be correspond to a type, method or field declaration.
* @return the compilation unit that contains the declaration of the given binding.
*/
public CompilationUnit getCompilationUnitForBinding(final IBinding binding)
{
assert binding.getKind() == IBinding.TYPE ||
binding.getKind() == IBinding.METHOD ||
binding.getKind() == IBinding.VARIABLE ;
CompilationUnit domUnit = searchLocallyForBinding(binding);
if( domUnit != null )
return domUnit;
else{
final IMember member = (IMember)binding.getJavaElement();
final ICompilationUnit unit;
if( member != null ){
unit = member.getCompilationUnit();
}
else{
final ITypeBinding typeBinding = getDeclaringClass(binding);
// binary type don't have compilation unit.
if( !typeBinding.isFromSource() )
return null;
if( _typeBinding2ModelCompUnit.get(typeBinding) != null )
unit = _typeBinding2ModelCompUnit.get(typeBinding);
else{
final String qname = typeBinding.getQualifiedName();
unit = getICompilationUnitForTopLevelType(qname);
}
}
if( unit == null ) return null;
final CompilationUnit astUnit = _modelCompUnit2astCompUnit.get(unit);
if( astUnit != null ) return astUnit;
else{
// Note: very expensive operation. we are re-compiling a file with binding information.
final ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setResolveBindings(true);
parser.setSource(unit);
parser.setFocalPosition(0);
CompilationUnit resultUnit = (CompilationUnit)parser.createAST(null);
_modelCompUnit2astCompUnit.put(unit, resultUnit);
return resultUnit;
}
}
}
public Filer getFiler(){
throw new UnsupportedOperationException("Not supported: the EnvironmentFactory API is for type system navigation only"); //$NON-NLS-1$
}
public Messager getMessager(){
throw new UnsupportedOperationException("Not supported: the EnvironmentFactory API is for type system navigation only"); //$NON-NLS-1$
}
/**
* @param binding a type, method or field binding.
* @return the top-level type binding that declares <code>binding</code>
* or itself if it is already one.
*/
protected static ITypeBinding getDeclaringClass(final IBinding binding)
{
assert binding != null : "binding cannot be null"; //$NON-NLS-1$
ITypeBinding aTypeBinding = null;
switch( binding.getKind() )
{
case IBinding.TYPE:
aTypeBinding = (ITypeBinding)binding;
break;
case IBinding.METHOD:
aTypeBinding = ((IMethodBinding)binding).getDeclaringClass();
break;
case IBinding.VARIABLE:
aTypeBinding = ((IVariableBinding)binding).getDeclaringClass();
break;
default:
throw new IllegalStateException("unrecognized binding type " + binding.getKind()); //$NON-NLS-1$
}
if(aTypeBinding == null ) return null;
while( !aTypeBinding.isTopLevel() ){
aTypeBinding = aTypeBinding.getDeclaringClass();
}
return aTypeBinding;
}
/**
* The environment caches the package fragment roots, as
* they are expensive to compute
*/
public IPackageFragmentRoot[] getAllPackageFragmentRoots() throws JavaModelException {
if (_packageRootsCache == null) {
_packageRootsCache = getJavaProject().getAllPackageFragmentRoots();
}
return _packageRootsCache;
}
protected IFile searchLocallyForIFile(final IBinding binding)
{
if (_astRoot == null) {
return null;
}
ASTNode node = _astRoot.findDeclaringNode(binding);
if( node != null )
return _file;
return null;
}
/**
* @param binding must be correspond to a type, method or field declaration
* @return the file that contains the declaration of given binding.
*/
public IFile getDeclaringFileForBinding(final IBinding binding)
{
assert binding.getKind() == IBinding.TYPE ||
binding.getKind() == IBinding.METHOD ||
binding.getKind() == IBinding.VARIABLE ;
// check to see whether it is in the current file.
IFile file = searchLocallyForIFile(binding);
if( file != null )
return file;
final IMember member = (IMember)binding.getJavaElement();
if( member != null ){
final ICompilationUnit unit = member.getCompilationUnit();
return (IFile)unit.getResource();
}
else{
final ITypeBinding type = getDeclaringClass(binding);
assert type.isTopLevel() : "type must be top-level type"; //$NON-NLS-1$
ICompilationUnit unit = _typeBinding2ModelCompUnit.get(type);
if( unit != null )
return (IFile)unit.getResource();
final String qname = type.getQualifiedName();
unit = getICompilationUnitForTopLevelType(qname);
if( unit == null )
return null;
return (IFile)unit.getResource();
}
}
static class BaseRequestor extends ASTRequestor
{
ICompilationUnit[] parseUnits;
CompilationUnit[] asts;
BaseRequestor(ICompilationUnit[] parseUnits)
{
asts = new CompilationUnit[parseUnits.length];
// Init all units to empty to prevent any NPEs
Arrays.fill(asts, EMPTY_AST_UNIT);
this.parseUnits = parseUnits;
}
public void acceptAST(ICompilationUnit source, CompilationUnit ast) {
for( int i=0, len = asts.length; i<len; i++ ){
if( source == parseUnits[i] ){
asts[i] = ast;
break;
}
}
}
}
/**
* Parse and fully resolve all files.
* @param javaProject
* @param parseUnits the files to be parsed and resolved.
*/
static void createASTs(
final IJavaProject javaProject,
final ICompilationUnit[] parseUnits,
ASTRequestor requestor)
{
// Construct exactly 1 binding key. When acceptBinding is called we know that
// All ASTs have been returned. This also means that a pipeline is opened when
// there are no asts. This is needed by the batch processors.
String bogusKey = BindingKey.createTypeBindingKey("java.lang.Object"); //$NON-NLS-1$
String[] keys = new String[] {bogusKey};
ASTParser p = ASTParser.newParser( AST.JLS3 );
p.setResolveBindings( true );
p.setProject( javaProject );
p.setKind( ASTParser.K_COMPILATION_UNIT );
p.createASTs( parseUnits, keys, requestor, null);
}
/**
* This should create an AST without imports or method-body statements
*/
public static CompilationUnit createAST(
IJavaProject javaProject,
final ICompilationUnit compilationUnit)
{
if(compilationUnit == null)
return null;
class CompilationUnitRequestor extends ASTRequestor
{
CompilationUnit domUnit = EMPTY_AST_UNIT;
public void acceptAST(ICompilationUnit source, CompilationUnit ast) {
if( source == compilationUnit )
domUnit = ast;
}
}
CompilationUnitRequestor requestor = new CompilationUnitRequestor();
ASTParser p = ASTParser.newParser( AST.JLS3 );
p.setResolveBindings( true );
p.setProject( javaProject );
p.setKind( ASTParser.K_COMPILATION_UNIT );
p.createASTs( new ICompilationUnit[]{compilationUnit}, NO_KEYS, requestor, null);
if( AptPlugin.DEBUG ){
AptPlugin.trace("created DOM AST for " + compilationUnit.getElementName() ); //$NON-NLS-1$
}
return requestor.domUnit;
}
/**
* @return the ast current being processed
*/
protected AST getCurrentDietAST(){
return _astRoot.getAST();
}
private void initPrimitives()
{
if(_primitives != null ) return;
AST ast = getCurrentDietAST();
_primitives = new PrimitiveTypeImpl[8];
// boolean
ITypeBinding binding = ast.resolveWellKnownType(ITypeConstants.BOOLEAN);
if( binding == null )
throw new IllegalStateException("fail to locate " + ITypeConstants.BOOLEAN); //$NON-NLS-1$
_primitives[BOOLEAN_INDEX] = new PrimitiveTypeImpl(binding);
// byte
binding = ast.resolveWellKnownType(ITypeConstants.BYTE);
if( binding == null )
throw new IllegalStateException("fail to locate " + ITypeConstants.BYTE); //$NON-NLS-1$
_primitives[BYTE_INDEX] = new PrimitiveTypeImpl(binding);
// char
binding = ast.resolveWellKnownType(ITypeConstants.CHAR);
if( binding == null )
throw new IllegalStateException("fail to locate " + ITypeConstants.BYTE); //$NON-NLS-1$
_primitives[CHAR_INDEX] = new PrimitiveTypeImpl(binding);
// double
binding = ast.resolveWellKnownType(ITypeConstants.DOUBLE);
if( binding == null )
throw new IllegalStateException("fail to locate " + ITypeConstants.BYTE); //$NON-NLS-1$
_primitives[DOUBLE_INDEX] = new PrimitiveTypeImpl(binding);
// float
binding = ast.resolveWellKnownType(ITypeConstants.FLOAT);
if( binding == null )
throw new IllegalStateException("fail to locate " + ITypeConstants.BYTE); //$NON-NLS-1$
_primitives[FLOAT_INDEX] = new PrimitiveTypeImpl(binding);
// int
binding = ast.resolveWellKnownType(ITypeConstants.INT);
if( binding == null )
throw new IllegalStateException("fail to locate " + ITypeConstants.BYTE); //$NON-NLS-1$
_primitives[INT_INDEX] = new PrimitiveTypeImpl(binding);
// long
binding = ast.resolveWellKnownType(ITypeConstants.LONG);
if( binding == null )
throw new IllegalStateException("fail to locate " + ITypeConstants.BYTE); //$NON-NLS-1$
_primitives[LONG_INDEX] = new PrimitiveTypeImpl(binding);
// short
binding = ast.resolveWellKnownType(ITypeConstants.SHORT);
if( binding == null )
throw new IllegalStateException("fail to locate " + ITypeConstants.BYTE); //$NON-NLS-1$
_primitives[SHORT_INDEX] = new PrimitiveTypeImpl(binding);
// void
binding = ast.resolveWellKnownType(ITypeConstants.VOID);
if( binding == null )
throw new IllegalStateException("fail to locate " + ITypeConstants.BYTE); //$NON-NLS-1$
_voidType = new VoidTypeImpl(binding);
}
public PrimitiveTypeImpl getBooleanType(){
initPrimitives();
return _primitives[BOOLEAN_INDEX];
}
public PrimitiveTypeImpl getByteType(){
initPrimitives();
return _primitives[BYTE_INDEX];
}
public PrimitiveTypeImpl getCharType(){
initPrimitives();
return _primitives[CHAR_INDEX];
}
public PrimitiveTypeImpl getDoubleType(){
initPrimitives();
return _primitives[DOUBLE_INDEX];
}
public PrimitiveTypeImpl getFloatType(){
initPrimitives();
return _primitives[FLOAT_INDEX];
}
public PrimitiveTypeImpl getIntType(){
initPrimitives();
return _primitives[INT_INDEX];
}
public PrimitiveTypeImpl getLongType(){
initPrimitives();
return _primitives[LONG_INDEX];
}
public PrimitiveTypeImpl getShortType(){
initPrimitives();
return _primitives[SHORT_INDEX];
}
public VoidTypeImpl getVoidType(){
initPrimitives();
return _voidType;
}
public CompilationUnit getAstCompilationUnit(){ return _astRoot; }
public IFile getFile() { return _file; }
public Phase getPhase(){ return _phase; }
public IProject getProject(){ return _javaProject.getProject(); }
public IJavaProject getJavaProject(){ return _javaProject; }
public AptProject getAptProject(){ return _aptProject; }
}