/** * Copyright (C) 2006-2017 INRIA and contributors * Spoon - http://spoon.gforge.inria.fr/ * * This software is governed by the CeCILL-C License under French law and * abiding by the rules of distribution of free software. You can use, modify * and/or redistribute the software under the terms of the CeCILL-C license as * circulated by CEA, CNRS and INRIA at http://www.cecill.info. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL-C license and that you accept its terms. */ package spoon.support.reflect.reference; import spoon.Launcher; import spoon.reflect.code.CtLambda; import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtConstructor; import spoon.reflect.declaration.CtExecutable; import spoon.reflect.declaration.CtMethod; import spoon.reflect.declaration.CtType; import spoon.reflect.declaration.ModifierKind; import spoon.reflect.reference.CtActualTypeContainer; import spoon.reflect.reference.CtExecutableReference; import spoon.reflect.reference.CtTypeReference; import spoon.reflect.visitor.CtVisitor; import spoon.reflect.visitor.filter.NameFilter; import spoon.support.reflect.declaration.CtElementImpl; import spoon.support.util.RtHelper; import spoon.support.visitor.MethodTypingContext; import spoon.support.visitor.SignaturePrinter; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import static spoon.reflect.ModelElementContainerDefaultCapacities.METHOD_TYPE_PARAMETERS_CONTAINER_DEFAULT_CAPACITY; public class CtExecutableReferenceImpl<T> extends CtReferenceImpl implements CtExecutableReference<T> { private static final long serialVersionUID = 1L; boolean stat = false; List<CtTypeReference<?>> actualTypeArguments = CtElementImpl.emptyList(); CtTypeReference<?> declaringType; /** * For methods, stores the return type of the method. (not pretty-printed). * For constructors, stores the type of the target constructor (pretty-printed). */ CtTypeReference<T> type; List<CtTypeReference<?>> parameters = CtElementImpl.emptyList(); public CtExecutableReferenceImpl() { super(); } @Override public void accept(CtVisitor visitor) { visitor.visitCtExecutableReference(this); } @Override public List<CtTypeReference<?>> getActualTypeArguments() { return actualTypeArguments; } @Override public boolean isConstructor() { return getSimpleName().equals(CONSTRUCTOR_NAME); } // @Override // public <A extends Annotation> A getAnnotation(Class<A> annotationType) { // A annotation = super.getAnnotation(annotationType); // if (annotation != null) { // return annotation; // } // // use reflection // Class<?> c = getDeclaringType().getActualClass(); // for (Method m : RtHelper.getAllMethods(c)) { // if (!getSimpleName().equals(m.getName())) { // continue; // } // if (getParameterTypes().size() != m.getParameterTypes().length) { // continue; // } // int i = 0; // for (Class<?> t : m.getParameterTypes()) { // if (t != getParameterTypes().get(i).getActualClass()) { // break; // } // i++; // } // if (i == getParameterTypes().size()) { // m.setAccessible(true); // return m.getAnnotation(annotationType); // } // } // return null; // } // @Override // public Annotation[] getAnnotations() { // Annotation[] annotations = super.getAnnotations(); // if (annotations != null) { // return annotations; // } // // use reflection // Class<?> c = getDeclaringType().getActualClass(); // for (Method m : RtHelper.getAllMethods(c)) { // if (!getSimpleName().equals(m.getName())) { // continue; // } // if (getParameterTypes().size() != m.getParameterTypes().length) { // continue; // } // int i = 0; // for (Class<?> t : m.getParameterTypes()) { // if (t != getParameterTypes().get(i).getActualClass()) { // break; // } // i++; // } // if (i == getParameterTypes().size()) { // m.setAccessible(true); // return m.getAnnotations(); // } // } // return null; // } @Override @SuppressWarnings("unchecked") public CtExecutable<T> getDeclaration() { final CtTypeReference<?> typeRef = getDeclaringType(); return typeRef == null ? null : getCtExecutable(typeRef.getDeclaration()); } @Override public CtExecutable<T> getExecutableDeclaration() { return getCtExecutable(getDeclaringType().getTypeDeclaration()); } private CtExecutable<T> getCtExecutable(CtType<?> typeDecl) { if (typeDecl == null) { return null; } CtExecutable<T> method = typeDecl.getMethod(getSimpleName(), parameters.toArray(new CtTypeReferenceImpl<?>[parameters.size()])); if ((method == null) && (typeDecl instanceof CtClass) && (getSimpleName().equals("<init>"))) { try { return (CtExecutable<T>) ((CtClass<?>) typeDecl).getConstructor(parameters.toArray(new CtTypeReferenceImpl<?>[parameters.size()])); } catch (ClassCastException e) { Launcher.LOGGER.error(e.getMessage(), e); } } else if (method == null && getSimpleName().startsWith("lambda$")) { final List<CtLambda<T>> elements = (List<CtLambda<T>>) typeDecl.getElements(new NameFilter<CtLambda<T>>(getSimpleName())); if (elements.size() == 0) { return null; } return elements.get(0); } return method; } @Override public CtTypeReference<?> getDeclaringType() { return declaringType; } @Override public CtTypeReference<T> getType() { return type; } @Override public List<CtTypeReference<?>> getParameters() { return unmodifiableList(parameters); } @Override public <C extends CtExecutableReference<T>> C setParameters(List<CtTypeReference<?>> parameters) { if (parameters == null || parameters.isEmpty()) { this.parameters = CtElementImpl.emptyList(); return (C) this; } if (this.parameters == CtElementImpl.<CtTypeReference<?>>emptyList()) { this.parameters = new ArrayList<>(); } this.parameters.clear(); for (CtTypeReference<?> parameter : parameters) { if (parameter == null) { continue; } parameter.setParent(this); this.parameters.add(parameter); } return (C) this; } @Override @SuppressWarnings("unchecked") public <S extends T> CtExecutableReference<S> getOverridingExecutable( CtTypeReference<?> subType) { if ((subType == null) || subType.equals(getDeclaringType())) { return null; } CtType<?> t = subType.getDeclaration(); if (t == null) { return null; } if (!(t instanceof CtClass)) { return null; } CtClass<?> c = (CtClass<?>) t; for (CtMethod<?> m : c.getMethods()) { if (m.getReference().isOverriding(this)) { return (CtExecutableReference<S>) m.getReference(); } } return getOverridingExecutable(c.getSuperclass()); } @Override public boolean isOverriding(CtExecutableReference<?> executable) { CtExecutable<?> exec = executable.getExecutableDeclaration(); CtExecutable<?> thisExec = getExecutableDeclaration(); if (exec == null || thisExec == null) { //the declaration of this executable is not in spoon model //use light detection algorithm, which ignores generic types final boolean isSame = getSimpleName().equals(executable.getSimpleName()) && getParameters().equals(executable.getParameters()) && getActualTypeArguments().equals(executable.getActualTypeArguments()); if (!isSame) { return false; } if (!getDeclaringType().isSubtypeOf(executable.getDeclaringType())) { return false; } return true; } if (exec instanceof CtMethod<?>) { CtMethod<?> method = (CtMethod<?>) exec; return new MethodTypingContext().setExecutableReference(this).isOverriding(method); } //it is not a method. So we can return true only if it is reference to the this executable return exec == getDeclaration(); } @Override public <C extends CtActualTypeContainer> C setActualTypeArguments(List<? extends CtTypeReference<?>> actualTypeArguments) { if (actualTypeArguments == null || actualTypeArguments.isEmpty()) { this.actualTypeArguments = CtElementImpl.emptyList(); return (C) this; } if (this.actualTypeArguments == CtElementImpl.<CtTypeReference<?>>emptyList()) { this.actualTypeArguments = new ArrayList<>(); } this.actualTypeArguments.clear(); for (CtTypeReference<?> actualTypeArgument : actualTypeArguments) { addActualTypeArgument(actualTypeArgument); } return (C) this; } @Override public <C extends CtExecutableReference<T>> C setDeclaringType(CtTypeReference<?> declaringType) { if (declaringType != null) { declaringType.setParent(this); } this.declaringType = declaringType; return (C) this; } @Override public <C extends CtExecutableReference<T>> C setType(CtTypeReference<T> type) { if (type != null) { type.setParent(this); } this.type = type; return (C) this; } @Override protected AnnotatedElement getActualAnnotatedElement() { if (isConstructor()) { return getActualConstructor(); } else { return getActualMethod(); } } @Override public Method getActualMethod() { List<CtTypeReference<?>> parameters = this.getParameters(); method_loop: for (Method m : getDeclaringType().getActualClass().getDeclaredMethods()) { if (!m.getDeclaringClass().isSynthetic() && m.isSynthetic()) { continue; } if (!m.getName().equals(getSimpleName())) { continue; } if (m.getParameterTypes().length != parameters.size()) { continue; } for (int i = 0; i < parameters.size(); i++) { if (m.getParameterTypes()[i] != parameters.get(i).getActualClass()) { continue method_loop; } } return m; } return null; } public Constructor<?> getActualConstructor() { List<CtTypeReference<?>> parameters = this.getParameters(); constructor_loop: for (Constructor<?> c : getDeclaringType().getActualClass().getDeclaredConstructors()) { if (c.getParameterTypes().length != parameters.size()) { continue; } for (int i = 0; i < parameters.size(); i++) { if (c.getParameterTypes()[i] != parameters.get(i).getActualClass()) { continue constructor_loop; } } return c; } return null; } public boolean isStatic() { return stat; } @Override public <C extends CtExecutableReference<T>> C setStatic(boolean b) { this.stat = b; return (C) this; } @Override public boolean isFinal() { CtExecutable<T> e = getDeclaration(); if (e != null) { if (e instanceof CtMethod) { return ((CtMethod<T>) e).hasModifier(ModifierKind.FINAL); } else if (e instanceof CtConstructor) { return ((CtConstructor<T>) e).hasModifier(ModifierKind.FINAL); } return false; } Method m = getActualMethod(); return m != null && Modifier.isFinal(m.getModifiers()); } @Override public void replace(CtExecutableReference<?> reference) { super.replace(reference); } public Set<ModifierKind> getModifiers() { CtExecutable<T> e = getDeclaration(); if (e != null) { if (e instanceof CtMethod) { return ((CtMethod<T>) e).getModifiers(); } else if (e instanceof CtConstructor) { return ((CtConstructor<T>) e).getModifiers(); } return CtElementImpl.emptySet(); } Method m = getActualMethod(); if (m != null) { return RtHelper.getModifiers(m.getModifiers()); } Constructor<?> c = getActualConstructor(); if (c != null) { return RtHelper.getModifiers(c.getModifiers()); } return Collections.emptySet(); } @Override public CtExecutableReference<?> getOverridingExecutable() { CtTypeReference<?> st = getDeclaringType().getSuperclass(); CtTypeReference<Object> objectType = getFactory().Type().OBJECT; if (st == null) { return getOverloadedExecutable(objectType, objectType); } return getOverloadedExecutable(st, objectType); } private CtExecutableReference<?> getOverloadedExecutable( CtTypeReference<?> t, CtTypeReference<Object> objectType) { if (t == null) { return null; } for (CtExecutableReference<?> e : t.getDeclaredExecutables()) { if (this.isOverriding(e)) { return e; } } if (t.equals(objectType)) { return null; } CtTypeReference<?> st = t.getSuperclass(); if (st == null) { return getOverloadedExecutable(objectType, objectType); } return getOverloadedExecutable(t.getSuperclass(), objectType); } @Override public <C extends CtActualTypeContainer> C addActualTypeArgument(CtTypeReference<?> actualTypeArgument) { if (actualTypeArgument == null) { return (C) this; } if (actualTypeArguments == CtElementImpl.<CtTypeReference<?>>emptyList()) { actualTypeArguments = new ArrayList<>(METHOD_TYPE_PARAMETERS_CONTAINER_DEFAULT_CAPACITY); } actualTypeArgument.setParent(this); actualTypeArguments.add(actualTypeArgument); return (C) this; } @Override public boolean removeActualTypeArgument( CtTypeReference<?> actualTypeArgument) { return actualTypeArguments != CtElementImpl.<CtTypeReference<?>>emptyList() && actualTypeArguments.remove(actualTypeArgument); } @Override public String getSignature() { final SignaturePrinter pr = new SignaturePrinter(); pr.scan(this); return pr.getSignature(); } @Override public CtExecutableReference<T> clone() { return (CtExecutableReference<T>) super.clone(); } }