/* * Copyright 2014 Lukas Krejci * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package org.revapi.java.checks.methods; import java.util.Collections; import java.util.EnumSet; import java.util.List; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.util.SimpleElementVisitor7; import org.revapi.Difference; import org.revapi.java.spi.CheckBase; import org.revapi.java.spi.Code; import org.revapi.java.spi.JavaMethodElement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Lukas Krejci * @since 0.1 */ public final class Added extends CheckBase { private static final Logger LOG = LoggerFactory.getLogger(Added.class); private final SimpleElementVisitor7<TypeElement, Void> enclosingClassExtractor = new SimpleElementVisitor7<TypeElement, Void>() { @Override protected TypeElement defaultAction(Element e, Void ignored) { return null; } @Override public TypeElement visitType(TypeElement e, Void ignored) { return e; } }; @Override public EnumSet<Type> getInterest() { return EnumSet.of(Type.METHOD); } @Override protected void doVisitMethod(JavaMethodElement oldMethod, JavaMethodElement newMethod) { if (oldMethod == null && newMethod != null && isAccessible(newMethod)) { pushActive(null, newMethod); } } @Override protected List<Difference> doEnd() { ActiveElements<JavaMethodElement> methods = popIfActive(); if (methods == null) { return null; } // we need to consider several cases here: // 1) method added to a interface // 2) method added to a final class // 3) concrete method added to a non-final class // 4) abstract method added to a non-final class // 5) final method added to a non-final class // 5) previously inherited method is now declared in class ExecutableElement method = methods.newElement.getDeclaringElement(); if (methods.newElement.getParent() == null) { LOG.warn("Could not find an enclosing class of method " + method + ". That's weird."); return null; } TypeElement enclosingClass = (TypeElement) methods.newElement.getParent().getDeclaringElement(); Difference difference; if (enclosingClass.getKind() == ElementKind.INTERFACE) { if (method.isDefault()) { difference = createDifference(Code.METHOD_DEFAULT_METHOD_ADDED_TO_INTERFACE, Code.attachmentsFor(methods.oldElement, methods.newElement)); } else if (method.getModifiers().contains(Modifier.STATIC)) { //statics on interface can only be called using the interface they are declared on, even if a method //with a same signature was declared on some of the super types in the old version, the users would //not have been able to call those methods using the current type. So we don't need to specialize here //based on the presence of a previously inherited method. difference = createDifference(Code.METHOD_STATIC_METHOD_ADDED_TO_INTERFACE, Code.attachmentsFor(methods.oldElement, methods.newElement)); } else { difference = createDifference(Code.METHOD_ADDED_TO_INTERFACE, Code.attachmentsFor(methods.oldElement, methods.newElement)); } } else if (method.getModifiers().contains(Modifier.ABSTRACT)) { difference = createDifference(Code.METHOD_ABSTRACT_METHOD_ADDED, Code.attachmentsFor(methods.oldElement, methods.newElement)); } else if (method.getModifiers().contains(Modifier.FINAL) && !enclosingClass.getModifiers().contains(Modifier.FINAL)) { difference = createDifference(Code.METHOD_FINAL_METHOD_ADDED_TO_NON_FINAL_CLASS, Code.attachmentsFor(methods.oldElement, methods.newElement)); } else { difference = createDifference(Code.METHOD_ADDED, Code.attachmentsFor(methods.oldElement, methods.newElement)); } return Collections.singletonList(difference); } }