From 89a0050ceaf2a50517056582d1c188052f994fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Mu=C3=B1iz?= Date: Tue, 18 Aug 2015 12:05:01 +0200 Subject: [PATCH] [JENKINS-30002] Improve Util.isOverridden --- core/src/main/java/hudson/Util.java | 38 +++++++-- .../java/hudson/util/IsOverriddenTest.java | 80 +++++++++++++++++++ 2 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 core/src/test/java/hudson/util/IsOverriddenTest.java diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java index 24d2ffd268..e9297a83eb 100644 --- a/core/src/main/java/hudson/Util.java +++ b/core/src/main/java/hudson/Util.java @@ -26,6 +26,7 @@ package hudson; import com.sun.jna.Memory; import com.sun.jna.Native; import com.sun.jna.NativeLong; + import edu.umd.cs.findbugs.annotations.SuppressWarnings; import hudson.Proc.LocalProc; import hudson.model.TaskListener; @@ -33,6 +34,7 @@ import hudson.os.PosixAPI; import hudson.util.QuotedStringTokenizer; import hudson.util.VariableResolver; import hudson.util.jna.WinIOException; + import org.apache.commons.io.IOUtils; import org.apache.commons.lang.time.FastDateFormat; import org.apache.tools.ant.BuildException; @@ -40,14 +42,18 @@ import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Chmod; import org.apache.tools.ant.taskdefs.Copy; import org.apache.tools.ant.types.FileSet; + import jnr.posix.FileStat; import jnr.posix.POSIX; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; + import java.io.*; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; @@ -70,12 +76,14 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import hudson.util.jna.Kernel32Utils; - import static hudson.util.jna.GNUCLibrary.LIBC; + import java.security.DigestInputStream; + import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import javax.annotation.Nullable; + import org.apache.commons.codec.digest.DigestUtils; /** @@ -1415,16 +1423,36 @@ public class Util { } /** - * Checks if the public method defined on the base type with the given arguments - * are overridden in the given derived type. + * Checks if the method defined on the base type with the given arguments + * is overridden in the given derived type. */ public static boolean isOverridden(@Nonnull Class base, @Nonnull Class derived, @Nonnull String methodName, @Nonnull Class... types) { + return !getMethod(base, methodName, types).equals(getMethod(derived, methodName, types)); + } + + private static Method getMethod(@Nonnull Class clazz, @Nonnull String methodName, @Nonnull Class... types) { + Method res = null; try { - return !base.getMethod(methodName, types).equals( - derived.getMethod(methodName,types)); + res = clazz.getDeclaredMethod(methodName, types); + // private, static or final methods can not be overridden + if (res != null && (Modifier.isPrivate(res.getModifiers()) || Modifier.isFinal(res.getModifiers()) + || Modifier.isStatic(res.getModifiers()))) { + res = null; + } } catch (NoSuchMethodException e) { + // Method not found in clazz, let's search in superclasses + Class superclass = clazz.getSuperclass(); + if (superclass != null) { + res = getMethod(superclass, methodName, types); + } + } catch (SecurityException e) { throw new AssertionError(e); } + if (res == null) { + throw new IllegalArgumentException( + String.format("Method %s not found in %s (or it is private, final or static)", methodName, clazz.getName())); + } + return res; } /** diff --git a/core/src/test/java/hudson/util/IsOverriddenTest.java b/core/src/test/java/hudson/util/IsOverriddenTest.java new file mode 100644 index 0000000000..8877d6ee41 --- /dev/null +++ b/core/src/test/java/hudson/util/IsOverriddenTest.java @@ -0,0 +1,80 @@ +/* + * The MIT License + * + * Copyright (c) 2015, CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package hudson.util; + +import org.junit.Test; +import static org.junit.Assert.*; + +import hudson.Util; + +/** + * Test for {@link Util.isOverridden} method. + */ +public class IsOverriddenTest { + + /** + * Test that a method is found by isOverridden even when it is inherited from an intermediate class. + */ + @Test + public void isOverriddenTest() { + assertTrue(Util.isOverridden(Base.class, Derived.class, "method")); + assertTrue(Util.isOverridden(Base.class, Intermediate.class, "method")); + assertFalse(Util.isOverridden(Base.class, Base.class, "method")); + assertTrue(Util.isOverridden(Base.class, Intermediate.class, "setX", Object.class)); + assertTrue(Util.isOverridden(Base.class, Intermediate.class, "getX")); + } + + /** + * Negative test. + * Trying to check for a method which does not exist in the hierarchy, + */ + @Test(expected = IllegalArgumentException.class) + public void isOverriddenNegativeTest() { + Util.isOverridden(Base.class, Derived.class, "method2"); + } + + /** + * Do not inspect private methods. + */ + @Test(expected = IllegalArgumentException.class) + public void avoidPrivateMethodsInspection() { + Util.isOverridden(Base.class, Intermediate.class, "aPrivateMethod"); + } + + public abstract class Base { + protected abstract void method(); + private void aPrivateMethod() {} + public void setX(T t) {} + public T getX() { return null; } + } + public abstract class Intermediate extends Base { + protected void method() {} + private void aPrivateMethod() {} + public void setX(Integer i) {} + public Integer getX() { return 0; } + } + public class Derived extends Intermediate {} + +} + -- GitLab