blob: faab68d28e808b5aa60b2cba61586f0e5c407aeb [file] [log] [blame]
/*******************************************************************************
* 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());
}
}