| /******************************************************************************* |
| * 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; } |
| } |