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