| /******************************************************************************* |
| * Copyright (c) 2005, 2007 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.declaration; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.jdt.apt.core.internal.env.BaseProcessorEnv; |
| import org.eclipse.jdt.apt.core.internal.util.Factory; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; |
| import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration; |
| import org.eclipse.jdt.core.dom.BodyDeclaration; |
| 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.declaration.Declaration; |
| import com.sun.mirror.declaration.FieldDeclaration; |
| import com.sun.mirror.declaration.MethodDeclaration; |
| import com.sun.mirror.declaration.PackageDeclaration; |
| import com.sun.mirror.declaration.TypeDeclaration; |
| import com.sun.mirror.declaration.TypeParameterDeclaration; |
| import com.sun.mirror.type.DeclaredType; |
| import com.sun.mirror.type.InterfaceType; |
| import com.sun.mirror.type.ReferenceType; |
| import com.sun.mirror.type.TypeMirror; |
| import com.sun.mirror.util.DeclarationVisitor; |
| |
| public abstract class TypeDeclarationImpl extends MemberDeclarationImpl |
| implements TypeDeclaration, DeclaredType, ReferenceType, EclipseMirrorType |
| { |
| // jdt core compiler add a field to a type with the following name when there is a hierachy problem with the type. |
| private static final String HAS_INCONSISTENT_TYPE_HIERACHY = "has inconsistent hierarchy"; //$NON-NLS-1$ |
| public TypeDeclarationImpl(final ITypeBinding binding, |
| final BaseProcessorEnv env) |
| { |
| super(binding, env); |
| } |
| |
| public String getQualifiedName() |
| { |
| ITypeBinding type = getTypeBinding(); |
| return type.getQualifiedName(); |
| } |
| |
| public String getSimpleName() |
| { |
| ITypeBinding type = getTypeBinding(); |
| return type.getName(); |
| } |
| |
| public PackageDeclaration getPackage() |
| { |
| ITypeBinding binding = getDeclarationBinding(); |
| return new PackageDeclarationImpl(binding.getPackage(), this, _env, false); |
| } |
| |
| public void accept(DeclarationVisitor visitor) |
| { |
| visitor.visitTypeDeclaration(this); |
| } |
| |
| public ITypeBinding getTypeBinding(){ return (ITypeBinding)_binding; } |
| |
| @SuppressWarnings("unchecked") |
| private void getASTFields( |
| final AbstractTypeDeclaration typeDecl, |
| final List<FieldDeclaration> results){ |
| final List bodyDecls = typeDecl.bodyDeclarations(); |
| for( int i=0, len=bodyDecls.size(); i<len; i++ ){ |
| final BodyDeclaration bodyDecl = (BodyDeclaration)bodyDecls.get(i); |
| IFile file = null; |
| if( bodyDecl.getNodeType() == ASTNode.FIELD_DECLARATION ){ |
| final List<VariableDeclarationFragment> fragments = |
| ((org.eclipse.jdt.core.dom.FieldDeclaration)bodyDecl).fragments(); |
| for( VariableDeclarationFragment frag : fragments ){ |
| final IBinding fieldBinding = frag.resolveBinding(); |
| if( fieldBinding == null ){ |
| if( file == null ) |
| file = getResource(); |
| final EclipseDeclarationImpl decl = Factory.createDeclaration(frag, file, _env); |
| if( decl != null ) |
| results.add((FieldDeclaration)decl); |
| } |
| } |
| } |
| } |
| } |
| |
| public Collection<FieldDeclaration> getFields() |
| { |
| final List<FieldDeclaration> results = new ArrayList<FieldDeclaration>(); |
| final ITypeBinding typeBinding = getDeclarationBinding(); |
| if( isFromSource() ){ |
| final ASTNode node = |
| _env.getASTNodeForBinding(typeBinding); |
| if( node != null ){ |
| switch( node.getNodeType() ) |
| { |
| case ASTNode.TYPE_DECLARATION: |
| case ASTNode.ANNOTATION_TYPE_DECLARATION: |
| case ASTNode.ENUM_DECLARATION: |
| AbstractTypeDeclaration typeDecl = |
| (AbstractTypeDeclaration)node; |
| // built the ast based methods first. |
| getASTFields(typeDecl, results); |
| break; |
| default: |
| // the ast node for a type binding should be a AbstractTypeDeclaration. |
| throw new IllegalStateException("expecting a AbstractTypeDeclaration but got " //$NON-NLS-1$ |
| + node.getClass().getName() ); |
| } |
| } |
| } |
| // either type is binary or |
| // constructing the binding based fields for source type. |
| final IVariableBinding[] fields = typeBinding.getDeclaredFields(); |
| for( IVariableBinding field : fields ){ |
| // note that the name HAS_INCONSISTENT_TYPE_HIERACHY is not a legal java identifier |
| // so there is no chance that we are filtering out actual declared fields. |
| if( field.isSynthetic() || HAS_INCONSISTENT_TYPE_HIERACHY.equals(field.getName())) |
| continue; |
| Declaration mirrorDecl = Factory.createDeclaration(field, _env); |
| if( mirrorDecl != null) |
| results.add( (FieldDeclaration)mirrorDecl); |
| } |
| return results; |
| } |
| |
| public Collection<TypeDeclaration> getNestedTypes() |
| { |
| final ITypeBinding[] memberTypes = getDeclarationBinding().getDeclaredTypes(); |
| final List<TypeDeclaration> results = new ArrayList<TypeDeclaration>(memberTypes.length); |
| for( ITypeBinding type : memberTypes ){ |
| Declaration mirrorDecl = Factory.createReferenceType(type, _env); |
| if( mirrorDecl != null ) |
| results.add((TypeDeclaration)mirrorDecl); |
| } |
| return results; |
| } |
| |
| public Collection<TypeParameterDeclaration> getFormalTypeParameters() |
| { |
| final ITypeBinding[] typeParams = getDeclarationBinding().getTypeParameters(); |
| final List<TypeParameterDeclaration> results = new ArrayList<TypeParameterDeclaration>(typeParams.length); |
| for( ITypeBinding typeParam : typeParams ){ |
| Declaration mirrorDecl = Factory.createDeclaration(typeParam, _env); |
| if( mirrorDecl != null ) |
| results.add( (TypeParameterDeclaration)mirrorDecl ); |
| } |
| return results; |
| } |
| |
| public TypeDeclaration getDeclaringType() |
| { |
| final ITypeBinding decl = getDeclarationBinding(); |
| if( decl.isMember() ) |
| return Factory.createReferenceType(decl.getDeclaringClass(), _env); |
| return null; |
| } |
| |
| // Start of implementation of DeclaredType API |
| public Collection<TypeMirror> getActualTypeArguments() |
| { |
| final ITypeBinding type = getTypeBinding(); |
| final ITypeBinding[] typeArgs = type.getTypeArguments(); |
| if( typeArgs == null || typeArgs.length == 0 ) |
| return Collections.emptyList(); |
| |
| final Collection<TypeMirror> result = new ArrayList<TypeMirror>(typeArgs.length); |
| for( ITypeBinding arg : typeArgs ){ |
| final TypeMirror mirror = Factory.createTypeMirror(arg, _env); |
| if (mirror == null) |
| result.add(Factory.createErrorClassType(arg)); |
| else |
| result.add(mirror); |
| } |
| |
| return result; |
| } |
| |
| public DeclaredType getContainingType() |
| { |
| final ITypeBinding outer = getTypeBinding().getDeclaringClass(); |
| return Factory.createReferenceType(outer, _env); |
| } |
| |
| public TypeDeclaration getDeclaration() |
| { |
| final ITypeBinding declBinding = getDeclarationBinding(); |
| if( declBinding == _binding ) return this; |
| else return Factory.createReferenceType(declBinding, _env); |
| } |
| |
| public Collection<InterfaceType> getSuperinterfaces() |
| { |
| final ITypeBinding[] superInterfaceBindings = getDeclarationBinding().getInterfaces(); |
| if( superInterfaceBindings == null || superInterfaceBindings.length == 0 ) |
| return Collections.emptyList(); |
| final List<InterfaceType> results = new ArrayList<InterfaceType>(superInterfaceBindings.length); |
| for( ITypeBinding binding : superInterfaceBindings ){ |
| if( binding.isInterface() ){ |
| final TypeDeclarationImpl mirrorDecl = Factory.createReferenceType(binding, _env); |
| if( mirrorDecl.kind() == MirrorKind.TYPE_INTERFACE ){ |
| results.add((InterfaceType)mirrorDecl); |
| } |
| } |
| else results.add(Factory.createErrorInterfaceType(binding)); |
| } |
| return results; |
| } |
| |
| |
| // End of implementation of DeclaredType API |
| |
| public ITypeBinding getDeclarationBinding() |
| { |
| final ITypeBinding type = getTypeBinding(); |
| return type.getTypeDeclaration(); |
| } |
| |
| /** |
| * create mirror methods that does not have a binding represention. |
| */ |
| @SuppressWarnings("unchecked") |
| protected void getASTMethods( |
| final AbstractTypeDeclaration typeDecl, |
| final List<MethodDeclaration> results){ |
| final List bodyDecls = typeDecl.bodyDeclarations(); |
| IFile file = null; |
| for( int i=0, len=bodyDecls.size(); i<len; i++ ){ |
| final BodyDeclaration bodyDecl = (BodyDeclaration)bodyDecls.get(i); |
| switch(bodyDecl.getNodeType()){ |
| case ASTNode.METHOD_DECLARATION: |
| final org.eclipse.jdt.core.dom.MethodDeclaration methodDecl = |
| (org.eclipse.jdt.core.dom.MethodDeclaration)bodyDecl; |
| |
| if( !methodDecl.isConstructor() ){ |
| final IMethodBinding methodBinding = methodDecl.resolveBinding(); |
| // built an ast based representation. |
| if( methodBinding == null ){ |
| if( file == null ) |
| file = getResource(); |
| MethodDeclaration mirrorDecl = |
| (MethodDeclaration)Factory.createDeclaration(methodDecl, file, _env); |
| if( mirrorDecl != null ) |
| results.add(mirrorDecl); |
| } |
| } |
| break; |
| case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: |
| final AnnotationTypeMemberDeclaration memberDecl = |
| (AnnotationTypeMemberDeclaration)bodyDecl; |
| final IMethodBinding methodBinding = memberDecl.resolveBinding(); |
| // built an ast based representation. |
| if( methodBinding == null ){ |
| if( file == null ) |
| file = getResource(); |
| MethodDeclaration mirrorDecl = |
| (MethodDeclaration)Factory.createDeclaration(memberDecl, file, _env); |
| if( mirrorDecl != null ) |
| results.add(mirrorDecl); |
| } |
| break; |
| } |
| } |
| } |
| |
| protected List<? extends MethodDeclaration> _getMethods() |
| { |
| final List<MethodDeclaration> results = new ArrayList<MethodDeclaration>(); |
| if( isFromSource() ){ |
| // need to consult the ast since methods with broken signature |
| // do not appear in bindings. |
| final ITypeBinding typeBinding = getDeclarationBinding(); |
| final ASTNode node = |
| _env.getASTNodeForBinding(typeBinding); |
| if( node != null ){ |
| switch( node.getNodeType() ) |
| { |
| case ASTNode.TYPE_DECLARATION: |
| case ASTNode.ANNOTATION_TYPE_DECLARATION: |
| case ASTNode.ENUM_DECLARATION: |
| AbstractTypeDeclaration typeDecl = |
| (AbstractTypeDeclaration)node; |
| // built the ast based methods first. |
| getASTMethods(typeDecl, results); |
| break; |
| default: |
| // the ast node for a type binding should be a AbstractTypeDeclaration. |
| throw new IllegalStateException("expecting a AbstractTypeDeclaration but got " //$NON-NLS-1$ |
| + node.getClass().getName() ); |
| } |
| } |
| } |
| // build methods for binding type or |
| // build the binding based method for source type. |
| final IMethodBinding[] methods = getDeclarationBinding().getDeclaredMethods(); |
| for( IMethodBinding method : methods ){ |
| if( method.isConstructor() || method.isSynthetic() ) continue; |
| Declaration mirrorDecl = Factory.createDeclaration(method, _env); |
| if( mirrorDecl != null) |
| results.add((MethodDeclaration)mirrorDecl); |
| } |
| return results; |
| } |
| |
| public String toString() |
| { |
| return getQualifiedName(); |
| } |
| |
| public boolean isFromSource(){ return getDeclarationBinding().isFromSource(); } |
| |
| public boolean isAssignmentCompatible(EclipseMirrorType left) { |
| return isSubTypeCompatible(left); |
| } |
| |
| public boolean isSubTypeCompatible(EclipseMirrorType type) { |
| // Operate on erasures - ignore generics for now |
| // Also ignore boxing for now |
| ITypeBinding thisErased = getTypeBinding().getErasure(); |
| ITypeBinding typeErased = type.getTypeBinding().getErasure(); |
| |
| if (kind() == MirrorKind.TYPE_CLASS) { |
| if (type.kind() == MirrorKind.TYPE_CLASS) |
| return isSubClassOf(thisErased, typeErased); |
| if (type.kind() == MirrorKind.TYPE_INTERFACE) |
| return isImplementorOf(thisErased, typeErased); |
| return false; |
| } |
| else { //kind() == MirrorKind.TYPE_INTERFACE |
| if (type.kind() == MirrorKind.TYPE_INTERFACE) |
| return isImplementorOf(thisErased, typeErased); |
| if (type.kind() == MirrorKind.TYPE_CLASS) |
| return "java.lang.Object".equals(getQualifiedName()); //$NON-NLS-1$ |
| return false; |
| } |
| } |
| |
| private static boolean isImplementorOf(ITypeBinding t1, ITypeBinding t2) { |
| if (eq(t1,t2)) return true; |
| ITypeBinding[] intfs = t1.getInterfaces(); |
| |
| for (ITypeBinding intf : intfs) { |
| if (isImplementorOf(intf.getErasure(), t2)) |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean isSubClassOf(ITypeBinding t1, ITypeBinding t2) { |
| while(t1 != null) { |
| if (eq(t1, t2)) return true; |
| t1 = t1.getSuperclass(); |
| } |
| return false; |
| } |
| |
| private static boolean eq(ITypeBinding t1, ITypeBinding t2) { |
| return t1.getQualifiedName().equals(t2.getQualifiedName()); |
| } |
| } |