/******************************************************************************* | |
* Copyright (c) 2009 Mia-Software. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v2.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v20.html | |
* | |
* Contributors: | |
* Sebastien Minguet (Mia-Software) - initial API and implementation | |
* Frederic Madiot (Mia-Software) - initial API and implementation | |
* Fabien Giquel (Mia-Software) - initial API and implementation | |
* Gabriel Barbier (Mia-Software) - initial API and implementation | |
* Erwan Breton (Sodifrance) - initial API and implementation | |
* Romain Dervaux (Mia-Software) - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.modisco.java.discoverer.internal.io.java.binding; | |
import org.eclipse.jdt.core.dom.ASTParser; | |
import org.eclipse.jdt.core.dom.ArrayType; | |
import org.eclipse.jdt.core.dom.ClassInstanceCreation; | |
import org.eclipse.jdt.core.dom.ConstructorInvocation; | |
import org.eclipse.jdt.core.dom.IBinding; | |
import org.eclipse.jdt.core.dom.IMethodBinding; | |
import org.eclipse.jdt.core.dom.IPackageBinding; | |
import org.eclipse.jdt.core.dom.ITypeBinding; | |
import org.eclipse.jdt.core.dom.IVariableBinding; | |
import org.eclipse.jdt.core.dom.Name; | |
import org.eclipse.jdt.core.dom.ParameterizedType; | |
import org.eclipse.jdt.core.dom.PrimitiveType; | |
import org.eclipse.jdt.core.dom.SuperConstructorInvocation; | |
import org.eclipse.jdt.core.dom.WildcardType; | |
import org.eclipse.modisco.infra.common.core.logging.MoDiscoLogger; | |
import org.eclipse.modisco.java.discoverer.internal.JavaActivator; | |
import org.eclipse.modisco.java.discoverer.internal.Messages; | |
/** | |
* A factory which uses the JDT Binding API to build MoDisco {@link Binding}s. | |
* | |
* @see IBinding | |
* @see ASTParser#setResolveBindings(boolean) | |
*/ | |
public final class JDTDelegateBindingFactory implements IBindingFactory { | |
private boolean logJDTBindingsIssues = false; | |
/** | |
* The unique instance of this factory. | |
*/ | |
private static IBindingFactory instance = new JDTDelegateBindingFactory(); | |
/** | |
* Singleton pattern. | |
*/ | |
private JDTDelegateBindingFactory() { | |
super(); | |
} | |
/** | |
* Returns the unique instance of this factory. | |
* | |
* @return the instance. | |
*/ | |
public static IBindingFactory getInstance() { | |
return JDTDelegateBindingFactory.instance; | |
} | |
public void setLogJDTBindingsIssues(final boolean newValue) { | |
this.logJDTBindingsIssues = newValue; | |
} | |
public Binding getBindingForName(final Name name) { | |
Binding result = getBinding(name.resolveBinding()); | |
if (result == null) { | |
// System.out.println("*** WARNING : binding '" + | |
// name.getFullyQualifiedName() + "' unresolved."); | |
result = new UnresolvedBinding(name.getFullyQualifiedName()); | |
} | |
return result; | |
} | |
public Binding getBindingForPrimitiveType(final PrimitiveType type) { | |
Binding result = new Binding(type.getPrimitiveTypeCode().toString()); | |
return result; | |
} | |
public Binding getBindingForParameterizedType(final ParameterizedType type) { | |
Binding result = null; | |
ITypeBinding binding = type.resolveBinding(); | |
if (binding == null) { | |
if (this.logJDTBindingsIssues) { | |
MoDiscoLogger.logWarning("*** WARNING : binding '" //$NON-NLS-1$ | |
+ type.toString() + "' unresolved.", JavaActivator //$NON-NLS-1$ | |
.getDefault()); | |
} | |
result = new UnresolvedBinding(type.toString()); | |
} else { | |
String bindingId = binding.getQualifiedName(); | |
// bindingId is of kind 'PP.XX<AA, BB, ..>' | |
if (bindingId.indexOf('<') > 0) { | |
result = new Binding(bindingId); | |
} else { | |
// wrong jdt 'PP.XX' return in some misc java construct with | |
// compile errors (bugzilla 324761) | |
result = new UnresolvedBinding(type.toString()); | |
} | |
} | |
return result; | |
} | |
public Binding getBindingForWildCardType(final WildcardType type) { | |
Binding result = null; | |
ITypeBinding binding = type.resolveBinding(); | |
if (binding == null) { | |
if (this.logJDTBindingsIssues) { | |
MoDiscoLogger.logWarning("*** WARNING : binding '" //$NON-NLS-1$ | |
+ type.toString() + "' unresolved.", JavaActivator //$NON-NLS-1$ | |
.getDefault()); | |
} | |
result = new UnresolvedBinding(type.toString()); | |
} else { | |
result = new Binding(binding.getQualifiedName()); | |
} | |
return result; | |
} | |
public Binding getBindingForArrayType(final ArrayType type) { | |
Binding result = null; | |
ITypeBinding binding = type.resolveBinding(); | |
if (binding == null) { | |
if (this.logJDTBindingsIssues) { | |
MoDiscoLogger.logWarning("*** WARNING : binding '" //$NON-NLS-1$ | |
+ type.toString() + "' unresolved.", JavaActivator //$NON-NLS-1$ | |
.getDefault()); | |
} | |
result = new UnresolvedBinding(type.toString()); | |
} else { | |
result = new ClassBinding(); | |
result.setName(binding.getQualifiedName()); | |
} | |
return result; | |
} | |
public Binding getBindingForClassInstanceCreation(final ClassInstanceCreation constructorCall) { | |
Binding result = null; | |
IMethodBinding binding = constructorCall.resolveConstructorBinding(); | |
if (binding == null || binding.getDeclaringClass() == null) { | |
if (this.logJDTBindingsIssues) { | |
MoDiscoLogger.logWarning("*** WARNING : binding '" //$NON-NLS-1$ | |
+ constructorCall.toString() + Messages.JDTDelegateBindingFactory_10, | |
JavaActivator.getDefault()); | |
} | |
result = new UnresolvedBinding(constructorCall.toString()); | |
} else { | |
result = getMethodBinding(binding); | |
} | |
return result; | |
} | |
public Binding getBindingForConstructorInvocation(final ConstructorInvocation constructorCall) { | |
Binding result = null; | |
IMethodBinding binding = constructorCall.resolveConstructorBinding(); | |
if (binding == null || binding.getDeclaringClass() == null) { | |
if (this.logJDTBindingsIssues) { | |
MoDiscoLogger.logWarning("*** WARNING : binding '" //$NON-NLS-1$ | |
+ constructorCall.toString() + "' unresolved.", //$NON-NLS-1$ | |
JavaActivator.getDefault()); | |
} | |
result = new UnresolvedBinding(constructorCall.toString()); | |
} else { | |
result = getMethodBinding(binding); | |
} | |
return result; | |
} | |
public Binding getBindingForSuperConstructorInvocation( | |
final SuperConstructorInvocation constructorCall) { | |
Binding result = null; | |
IMethodBinding binding = constructorCall.resolveConstructorBinding(); | |
if (binding == null || binding.getDeclaringClass() == null) { | |
// managing misc binding.getName() NPE | |
if (this.logJDTBindingsIssues) { | |
MoDiscoLogger.logWarning("*** WARNING : binding '" //$NON-NLS-1$ | |
+ constructorCall.toString() + "' unresolved.", //$NON-NLS-1$ | |
JavaActivator.getDefault()); | |
} | |
result = new UnresolvedBinding(constructorCall.toString()); | |
} else { | |
result = getMethodBinding(binding); | |
} | |
return result; | |
} | |
private Binding getBinding(final IBinding binding) { | |
Binding result = null; | |
if (binding instanceof IMethodBinding) { | |
result = getMethodBinding((IMethodBinding) binding); | |
} else if (binding instanceof ITypeBinding) { | |
result = getClassBinding((ITypeBinding) binding, false); | |
} else if (binding instanceof IPackageBinding) { | |
result = getPackageBinding((IPackageBinding) binding); | |
} else if (binding instanceof IVariableBinding && ((IVariableBinding) binding).isField()) { | |
result = getFieldBinding((IVariableBinding) binding); | |
} else if (binding instanceof IVariableBinding && !((IVariableBinding) binding).isField()) { | |
result = getVariableBinding((IVariableBinding) binding); | |
} | |
return result; | |
} | |
private MethodBinding getMethodBinding(final IMethodBinding methodBinding) { | |
/* | |
* in case of generic method, a parameterized method binding | |
* encapsulates real method in a second binding. | |
*/ | |
IMethodBinding binding = methodBinding.getMethodDeclaration(); | |
MethodBinding result = new MethodBinding(); | |
result.setName(binding.getName()); | |
result.setDeclaringClass(getClassBinding(binding.getDeclaringClass(), false)); | |
result.setConstructor(binding.isConstructor()); | |
result.setAnnotationMember(binding.isAnnotationMember()); | |
for (int i = 0; i < binding.getParameterTypes().length; i++) { | |
result.getParameters().add(getParameterBinding(binding.getParameterTypes()[i])); | |
} | |
return result; | |
} | |
private ParameterBinding getParameterBinding(final ITypeBinding binding) { | |
ParameterBinding result = new ParameterBinding(); | |
result.setDimensions(binding.getDimensions()); | |
if (binding.isArray()) { | |
result.setElementType(getClassBinding(binding.getElementType(), true)); | |
} else { | |
result.setElementType(getClassBinding(binding, true)); | |
} | |
return result; | |
} | |
private static PackageBinding getPackageBinding(final IPackageBinding binding) { | |
PackageBinding result = new PackageBinding(); | |
result.setName(binding.getName()); | |
return result; | |
} | |
private ClassBinding getClassBinding(final ITypeBinding bindingParameter, | |
final boolean isParameterBinding) { | |
/* | |
* in case of generic type, a parameterized type binding encapsulates | |
* real type in a second binding. | |
*/ | |
ITypeBinding binding = bindingParameter.getTypeDeclaration(); | |
ClassBinding result = new ClassBinding(); | |
if (binding.isTypeVariable() && isParameterBinding) { | |
/* | |
* we don't want type variables or generic types in parameter | |
* bindings, so we replace it by the erasure | |
* | |
* @see org.eclipse.jdt.core.dom.ITypeBinding#getErasure() | |
*/ | |
if (binding.getErasure() != null) { | |
result = getClassBinding(binding.getErasure(), isParameterBinding); | |
} else { | |
// in some case, it could be null (see bug 328143) | |
result.setName(binding.getName()); | |
result.setTypeVariable(binding.isTypeVariable()); | |
} | |
} else if (binding.isAnonymous()) { | |
/* | |
* as anonymous types don't have names, we cannot have a qualified | |
* name hence they suppose a local context, so we rely on the | |
* binding key | |
*/ | |
result.setName(binding.getKey()); | |
} else { | |
result.setName(binding.getName()); | |
result.setTypeVariable(binding.isTypeVariable()); | |
result.setInterface(binding.isInterface()); | |
result.setEnum(binding.isEnum()); | |
result.setAnnotation(binding.isAnnotation()); | |
if (binding.getPackage() != null) { | |
result.setOwnerPackage(getPackageBinding(binding.getPackage())); | |
} | |
if (binding.getDeclaringClass() != null) { | |
result.setDeclaringClass(getClassBinding(binding.getDeclaringClass(), false)); | |
} | |
ITypeBinding superClass = binding.getSuperclass(); | |
if (binding.isClass() && superClass != null | |
&& !superClass.getQualifiedName().equals("java.lang.Object")) { //$NON-NLS-1$ | |
result.setSuperClass(getClassBinding(superClass, false)); | |
} | |
/* | |
* annotations can't have explicit superinterfaces we don't save | |
* this information because an annotation don't have superinterfaces | |
* in the model | |
*/ | |
if (!binding.isAnnotation() && binding.getInterfaces() != null) { | |
for (ITypeBinding anInterface : binding.getInterfaces()) { | |
if (!anInterface.getQualifiedName().equals("java.lang.Object")) { //$NON-NLS-1$ | |
result.getSuperInterfaces().add(getClassBinding(anInterface, false)); | |
} | |
} | |
} | |
if (binding.getTypeParameters() != null) { | |
for (ITypeBinding typeParameter : binding.getTypeParameters()) { | |
result.addTypeParameters(typeParameter.getName()); | |
} | |
} | |
} | |
return result; | |
} | |
private FieldBinding getFieldBinding(final IVariableBinding binding) { | |
FieldBinding result = new FieldBinding(); | |
result.setName(binding.getName()); | |
result.setEnumConstant(binding.isEnumConstant()); | |
if (binding.getDeclaringClass() != null) { | |
result.setDeclaringClass(getClassBinding(binding.getDeclaringClass(), false)); | |
} | |
return result; | |
} | |
private static VariableBinding getVariableBinding(final IVariableBinding binding) { | |
VariableBinding result = new VariableBinding(); | |
result.setName(String.valueOf(binding.getVariableId())); | |
return result; | |
} | |
public boolean isLocal(final Name name) { | |
return isLocalVariable(name) || isLocalMethod(name); | |
} | |
private static boolean isLocalVariable(final Name name) { | |
boolean result = false; | |
IBinding binding = name.resolveBinding(); | |
if (binding != null) { | |
result = (binding instanceof IVariableBinding && !((IVariableBinding) binding) | |
.isField()); | |
} | |
return result; | |
} | |
private static boolean isLocalMethod(final Name name) { | |
boolean result = false; | |
IBinding binding = name.resolveBinding(); | |
if (binding != null) { | |
if (binding instanceof IMethodBinding) { | |
ITypeBinding declaringClass = ((IMethodBinding) binding).getDeclaringClass(); | |
if (declaringClass != null) { | |
result = declaringClass.isAnonymous(); | |
} | |
} | |
} | |
return result; | |
} | |
} |