blob: bbedb9bd5ebb68831c461113cc0282d6ad1fca5d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2014 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
* Jesper Steen Moller - Bug 412150 [1.8] [compiler] Enable reflected parameter names during annotation processing
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.apt.model;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ElementVisitor;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationHolder;
import org.eclipse.jdt.internal.compiler.lookup.AptBinaryLocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodVerifier;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
public class ExecutableElementImpl extends ElementImpl implements
ExecutableElement {
private Name _name = null;
/* package */ ExecutableElementImpl(BaseProcessingEnvImpl env, MethodBinding binding) {
super(env, binding);
}
@Override
public <R, P> R accept(ElementVisitor<R, P> v, P p)
{
return v.visitExecutable(this, p);
}
@Override
protected AnnotationBinding[] getAnnotationBindings()
{
return ((MethodBinding)_binding).getAnnotations();
}
@Override
public AnnotationValue getDefaultValue() {
MethodBinding binding = (MethodBinding)_binding;
Object defaultValue = binding.getDefaultValue();
if (defaultValue != null) return new AnnotationMemberValue(_env, defaultValue, binding);
return null;
}
@Override
public List<? extends Element> getEnclosedElements() {
return Collections.emptyList();
}
@Override
public Element getEnclosingElement() {
MethodBinding binding = (MethodBinding)_binding;
if (null == binding.declaringClass) {
return null;
}
return _env.getFactory().newElement(binding.declaringClass);
}
@Override
public String getFileName() {
ReferenceBinding dc = ((MethodBinding)_binding).declaringClass;
char[] name = dc.getFileName();
if (name == null)
return null;
return new String(name);
}
@Override
public ElementKind getKind() {
MethodBinding binding = (MethodBinding)_binding;
if (binding.isConstructor()) {
return ElementKind.CONSTRUCTOR;
}
else if (CharOperation.equals(binding.selector, TypeConstants.CLINIT)) {
return ElementKind.STATIC_INIT;
}
else if (CharOperation.equals(binding.selector, TypeConstants.INIT)) {
return ElementKind.INSTANCE_INIT;
}
else {
return ElementKind.METHOD;
}
}
@Override
public Set<Modifier> getModifiers() {
MethodBinding binding = (MethodBinding)_binding;
return Factory.getModifiers(binding.modifiers, getKind());
}
@Override
PackageElement getPackage()
{
MethodBinding binding = (MethodBinding)_binding;
if (null == binding.declaringClass) {
return null;
}
return _env.getFactory().newPackageElement(binding.declaringClass.fPackage);
}
@Override
public List<? extends VariableElement> getParameters() {
MethodBinding binding = (MethodBinding)_binding;
int length = binding.parameters == null ? 0 : binding.parameters.length;
if (0 != length) {
AbstractMethodDeclaration methodDeclaration = binding.sourceMethod();
List<VariableElement> params = new ArrayList<VariableElement>(length);
if (methodDeclaration != null) {
for (Argument argument : methodDeclaration.arguments) {
VariableElement param = new VariableElementImpl(_env, argument.binding);
params.add(param);
}
} else {
// binary method
AnnotationBinding[][] parameterAnnotationBindings = null;
AnnotationHolder annotationHolder = binding.declaringClass.retrieveAnnotationHolder(binding, false);
if (annotationHolder != null) {
parameterAnnotationBindings = annotationHolder.getParameterAnnotations();
}
// we need to filter the synthetic arguments
int i = 0;
for (TypeBinding typeBinding : binding.parameters) {
char name[] = binding.parameterNames.length > i ? binding.parameterNames[i] : null;
if (name == null) {
StringBuilder builder = new StringBuilder("arg");//$NON-NLS-1$
builder.append(i);
name = String.valueOf(builder).toCharArray();
}
VariableElement param = new VariableElementImpl(_env,
new AptBinaryLocalVariableBinding(
name,
typeBinding,
0,
parameterAnnotationBindings != null ? parameterAnnotationBindings[i] : null,
binding));
params.add(param);
i++;
}
}
return Collections.unmodifiableList(params);
}
return Collections.emptyList();
}
@Override
public TypeMirror getReturnType() {
MethodBinding binding = (MethodBinding)_binding;
if (binding.returnType == null) {
return null;
}
else return _env.getFactory().newTypeMirror(binding.returnType);
}
@Override
public Name getSimpleName() {
MethodBinding binding = (MethodBinding)_binding;
if (_name == null) {
_name = new NameImpl(binding.selector);
}
return _name;
}
@Override
public List<? extends TypeMirror> getThrownTypes() {
MethodBinding binding = (MethodBinding)_binding;
if (binding.thrownExceptions.length == 0) {
return Collections.emptyList();
}
List<TypeMirror> list = new ArrayList<TypeMirror>(binding.thrownExceptions.length);
for (ReferenceBinding exception : binding.thrownExceptions) {
list.add(_env.getFactory().newTypeMirror(exception));
}
return list;
}
@Override
public List<? extends TypeParameterElement> getTypeParameters() {
MethodBinding binding = (MethodBinding)_binding;
TypeVariableBinding[] variables = binding.typeVariables();
if (variables.length == 0) {
return Collections.emptyList();
}
List<TypeParameterElement> params = new ArrayList<TypeParameterElement>(variables.length);
for (TypeVariableBinding variable : variables) {
params.add(_env.getFactory().newTypeParameterElement(variable, this));
}
return Collections.unmodifiableList(params);
}
@Override
public boolean hides(Element hidden)
{
if (!(hidden instanceof ExecutableElementImpl)) {
return false;
}
MethodBinding hiderBinding = (MethodBinding)_binding;
MethodBinding hiddenBinding = (MethodBinding)((ExecutableElementImpl)hidden)._binding;
if (hiderBinding == hiddenBinding) {
return false;
}
if (hiddenBinding.isPrivate()) {
return false;
}
// See JLS 8.4.8: hiding only applies to static methods
if (!hiderBinding.isStatic() || !hiddenBinding.isStatic()) {
return false;
}
// check names
if (!CharOperation.equals(hiddenBinding.selector, hiderBinding.selector)) {
return false;
}
// check parameters
if (!_env.getLookupEnvironment().methodVerifier().isMethodSubsignature(hiderBinding, hiddenBinding)) {
return false;
}
return null != hiderBinding.declaringClass.findSuperTypeOriginatingFrom(hiddenBinding.declaringClass);
}
@Override
public boolean isVarArgs() {
return ((MethodBinding) _binding).isVarargs();
}
/**
* Return true if this method overrides {@code overridden} in the context of {@code type}. For
* instance, consider
* <pre>
* interface A { void f(); }
* class B { void f() {} }
* class C extends B implements I { }
* </pre>
* In the context of B, B.f() does not override A.f(); they are unrelated. But in the context of
* C, B.f() does override A.f(). That is, the copy of B.f() that C inherits overrides A.f().
* This is equivalent to considering two questions: first, does C inherit B.f(); if so, does
* the inherited C.f() override A.f(). If B.f() were private, for instance, then in the context
* of C it would still not override A.f().
*
* @see javax.lang.model.util.Elements#overrides(ExecutableElement, ExecutableElement, TypeElement)
* @jls3 8.4.8 Inheritance, Overriding, and Hiding
* @jls3 9.4.1 Inheritance and Overriding
*/
public boolean overrides(ExecutableElement overridden, TypeElement type)
{
MethodBinding overriddenBinding = (MethodBinding)((ExecutableElementImpl) overridden)._binding;
ReferenceBinding overriderContext = (ReferenceBinding)((TypeElementImpl)type)._binding;
if ((MethodBinding)_binding == overriddenBinding
|| overriddenBinding.isStatic()
|| overriddenBinding.isPrivate()
|| ((MethodBinding)_binding).isStatic()) {
return false;
}
char[] selector = ((MethodBinding)_binding).selector;
if (!CharOperation.equals(selector, overriddenBinding.selector))
return false;
// Construct a binding to the equivalent of this (the overrider) as it would be inherited by 'type'.
// Can only do this if 'type' is descended from the overrider.
// Second clause of the AND is required to match a peculiar javac behavior.
if (null == overriderContext.findSuperTypeOriginatingFrom(((MethodBinding)_binding).declaringClass) &&
null == ((MethodBinding)_binding).declaringClass.findSuperTypeOriginatingFrom(overriderContext)) {
return false;
}
MethodBinding overriderBinding = new MethodBinding((MethodBinding)_binding, overriderContext);
if (overriderBinding.isPrivate()) {
// a private method can never override another method. The other method would either be
// private itself, in which case it would not be visible; or this would be a restriction
// of access, which is a compile-time error.
return false;
}
TypeBinding match = overriderBinding.declaringClass.findSuperTypeOriginatingFrom(overriddenBinding.declaringClass);
if (!(match instanceof ReferenceBinding)) return false;
org.eclipse.jdt.internal.compiler.lookup.MethodBinding[] superMethods = ((ReferenceBinding)match).getMethods(selector);
LookupEnvironment lookupEnvironment = _env.getLookupEnvironment();
if (lookupEnvironment == null) return false;
MethodVerifier methodVerifier = lookupEnvironment.methodVerifier();
for (int i = 0, length = superMethods.length; i < length; i++) {
if (superMethods[i].original() == overriddenBinding) {
return methodVerifier.doesMethodOverride(overriderBinding, superMethods[i]);
}
}
return false;
}
public TypeMirror getReceiverType() {
return _env.getFactory().getReceiverType((MethodBinding) _binding);
}
public boolean isDefault() {
if (_binding != null) {
return ((MethodBinding)_binding).isDefaultMethod();
}
return false;
}
}