001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.lang3.reflect;
018
019import static org.junit.Assert.assertEquals;
020import static org.junit.Assert.assertNotNull;
021import static org.junit.Assert.assertNotSame;
022import static org.junit.Assert.assertNull;
023import static org.junit.Assert.assertSame;
024import static org.junit.Assert.assertTrue;
025import static org.junit.Assert.fail;
026
027import java.lang.reflect.Method;
028import java.util.Arrays;
029import java.util.HashMap;
030import java.util.Map;
031
032import org.apache.commons.lang3.ArrayUtils;
033import org.apache.commons.lang3.math.NumberUtils;
034import org.apache.commons.lang3.mutable.Mutable;
035import org.apache.commons.lang3.mutable.MutableObject;
036import org.junit.Before;
037import org.junit.Test;
038
039/**
040 * Unit tests MethodUtils
041 * @version $Id: MethodUtilsTest.java 1436770 2013-01-22 07:09:45Z ggregory $
042 */
043public class MethodUtilsTest {
044  
045    private static interface PrivateInterface {}
046    
047    static class TestBeanWithInterfaces implements PrivateInterface {
048        public String foo() {
049            return "foo()";
050        }
051    }
052    
053    public static class TestBean {
054
055        public static String bar() {
056            return "bar()";
057        }
058
059        public static String bar(final int i) {
060            return "bar(int)";
061        }
062
063        public static String bar(final Integer i) {
064            return "bar(Integer)";
065        }
066
067        public static String bar(final double d) {
068            return "bar(double)";
069        }
070
071        public static String bar(final String s) {
072            return "bar(String)";
073        }
074
075        public static String bar(final Object o) {
076            return "bar(Object)";
077        }
078        
079        public static void oneParameterStatic(final String s) {
080            // empty
081        }
082
083        @SuppressWarnings("unused")
084        private void privateStuff() {
085        }
086
087
088        public String foo() {
089            return "foo()";
090        }
091
092        public String foo(final int i) {
093            return "foo(int)";
094        }
095
096        public String foo(final Integer i) {
097            return "foo(Integer)";
098        }
099
100        public String foo(final double d) {
101            return "foo(double)";
102        }
103
104        public String foo(final String s) {
105            return "foo(String)";
106        }
107
108        public String foo(final Object o) {
109            return "foo(Object)";
110        }
111        
112        public void oneParameter(final String s) {
113            // empty
114        }
115    }
116
117    private static class TestMutable implements Mutable<Object> {
118        @Override
119        public Object getValue() {
120            return null;
121        }
122
123        @Override
124        public void setValue(final Object value) {
125        }
126    }
127
128    private TestBean testBean;
129    private final Map<Class<?>, Class<?>[]> classCache = new HashMap<Class<?>, Class<?>[]>();
130
131    @Before
132    public void setUp() throws Exception {
133        testBean = new TestBean();
134        classCache.clear();
135    }
136
137    @Test
138    public void testConstructor() throws Exception {
139        assertNotNull(MethodUtils.class.newInstance());
140    }
141
142    @Test
143    public void testInvokeMethod() throws Exception {
144        assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo",
145                (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY));
146        assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo",
147                (Object[]) null));
148        assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo", 
149                (Object[]) null, (Class<?>[]) null));
150        assertEquals("foo(String)", MethodUtils.invokeMethod(testBean, "foo",
151                ""));
152        assertEquals("foo(Object)", MethodUtils.invokeMethod(testBean, "foo",
153                new Object()));
154        assertEquals("foo(Object)", MethodUtils.invokeMethod(testBean, "foo",
155                Boolean.TRUE));
156        assertEquals("foo(Integer)", MethodUtils.invokeMethod(testBean, "foo",
157                NumberUtils.INTEGER_ONE));
158        assertEquals("foo(int)", MethodUtils.invokeMethod(testBean, "foo",
159                NumberUtils.BYTE_ONE));
160        assertEquals("foo(double)", MethodUtils.invokeMethod(testBean, "foo",
161                NumberUtils.LONG_ONE));
162        assertEquals("foo(double)", MethodUtils.invokeMethod(testBean, "foo",
163                NumberUtils.DOUBLE_ONE));
164    }
165
166    @Test
167    public void testInvokeExactMethod() throws Exception {
168        assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo",
169                (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY));
170        assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo",
171                (Object[]) null));
172        assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo", 
173                (Object[]) null, (Class<?>[]) null));
174        assertEquals("foo(String)", MethodUtils.invokeExactMethod(testBean,
175                "foo", ""));
176        assertEquals("foo(Object)", MethodUtils.invokeExactMethod(testBean,
177                "foo", new Object()));
178        assertEquals("foo(Integer)", MethodUtils.invokeExactMethod(testBean,
179                "foo", NumberUtils.INTEGER_ONE));
180        assertEquals("foo(double)", MethodUtils.invokeExactMethod(testBean,
181                "foo", new Object[] { NumberUtils.DOUBLE_ONE },
182                new Class[] { Double.TYPE }));
183
184        try {
185            MethodUtils
186                    .invokeExactMethod(testBean, "foo", NumberUtils.BYTE_ONE);
187            fail("should throw NoSuchMethodException");
188        } catch (final NoSuchMethodException e) {
189        }
190        try {
191            MethodUtils
192                    .invokeExactMethod(testBean, "foo", NumberUtils.LONG_ONE);
193            fail("should throw NoSuchMethodException");
194        } catch (final NoSuchMethodException e) {
195        }
196        try {
197            MethodUtils.invokeExactMethod(testBean, "foo", Boolean.TRUE);
198            fail("should throw NoSuchMethodException");
199        } catch (final NoSuchMethodException e) {
200        }
201    }
202
203    @Test
204    public void testInvokeStaticMethod() throws Exception {
205        assertEquals("bar()", MethodUtils.invokeStaticMethod(TestBean.class,
206                "bar", (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY));
207        assertEquals("bar()", MethodUtils.invokeStaticMethod(TestBean.class,
208                "bar", (Object[]) null));
209        assertEquals("bar()", MethodUtils.invokeStaticMethod(TestBean.class,
210                "bar", (Object[]) null, (Class<?>[]) null));
211        assertEquals("bar(String)", MethodUtils.invokeStaticMethod(
212                TestBean.class, "bar", ""));
213        assertEquals("bar(Object)", MethodUtils.invokeStaticMethod(
214                TestBean.class, "bar", new Object()));
215        assertEquals("bar(Object)", MethodUtils.invokeStaticMethod(
216                TestBean.class, "bar", Boolean.TRUE));
217        assertEquals("bar(Integer)", MethodUtils.invokeStaticMethod(
218                TestBean.class, "bar", NumberUtils.INTEGER_ONE));
219        assertEquals("bar(int)", MethodUtils.invokeStaticMethod(TestBean.class,
220                "bar", NumberUtils.BYTE_ONE));
221        assertEquals("bar(double)", MethodUtils.invokeStaticMethod(
222                TestBean.class, "bar", NumberUtils.LONG_ONE));
223        assertEquals("bar(double)", MethodUtils.invokeStaticMethod(
224                TestBean.class, "bar", NumberUtils.DOUBLE_ONE));
225        
226        try {
227            MethodUtils.invokeStaticMethod(TestBean.class, "does_not_exist");
228            fail("should throw NoSuchMethodException");
229        } catch (final NoSuchMethodException e) {
230        }
231    }
232
233    @Test
234    public void testInvokeExactStaticMethod() throws Exception {
235        assertEquals("bar()", MethodUtils.invokeExactStaticMethod(TestBean.class,
236                "bar", (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY));
237        assertEquals("bar()", MethodUtils.invokeExactStaticMethod(TestBean.class,
238                "bar", (Object[]) null));
239        assertEquals("bar()", MethodUtils.invokeExactStaticMethod(TestBean.class,
240                "bar", (Object[]) null, (Class<?>[]) null));
241        assertEquals("bar(String)", MethodUtils.invokeExactStaticMethod(
242                TestBean.class, "bar", ""));
243        assertEquals("bar(Object)", MethodUtils.invokeExactStaticMethod(
244                TestBean.class, "bar", new Object()));
245        assertEquals("bar(Integer)", MethodUtils.invokeExactStaticMethod(
246                TestBean.class, "bar", NumberUtils.INTEGER_ONE));
247        assertEquals("bar(double)", MethodUtils.invokeExactStaticMethod(
248                TestBean.class, "bar", new Object[] { NumberUtils.DOUBLE_ONE },
249                new Class[] { Double.TYPE }));
250
251        try {
252            MethodUtils.invokeExactStaticMethod(TestBean.class, "bar",
253                    NumberUtils.BYTE_ONE);
254            fail("should throw NoSuchMethodException");
255        } catch (final NoSuchMethodException e) {
256        }
257        try {
258            MethodUtils.invokeExactStaticMethod(TestBean.class, "bar",
259                    NumberUtils.LONG_ONE);
260            fail("should throw NoSuchMethodException");
261        } catch (final NoSuchMethodException e) {
262        }
263        try {
264            MethodUtils.invokeExactStaticMethod(TestBean.class, "bar",
265                    Boolean.TRUE);
266            fail("should throw NoSuchMethodException");
267        } catch (final NoSuchMethodException e) {
268        }
269    }
270
271    @Test
272    public void testGetAccessibleInterfaceMethod() throws Exception {
273        final Class<?>[][] p = { ArrayUtils.EMPTY_CLASS_ARRAY, null };
274        for (final Class<?>[] element : p) {
275            final Method method = TestMutable.class.getMethod("getValue", element);
276            final Method accessibleMethod = MethodUtils.getAccessibleMethod(method);
277            assertNotSame(accessibleMethod, method);
278            assertSame(Mutable.class, accessibleMethod.getDeclaringClass());
279        }
280    }
281    
282    @Test
283    public void testGetAccessibleMethodPrivateInterface() throws Exception {
284        final Method expected = TestBeanWithInterfaces.class.getMethod("foo");
285        assertNotNull(expected);
286        final Method actual = MethodUtils.getAccessibleMethod(TestBeanWithInterfaces.class, "foo");
287        assertNull(actual);
288    }
289
290    @Test
291    public void testGetAccessibleInterfaceMethodFromDescription()
292            throws Exception {
293        final Class<?>[][] p = { ArrayUtils.EMPTY_CLASS_ARRAY, null };
294        for (final Class<?>[] element : p) {
295            final Method accessibleMethod = MethodUtils.getAccessibleMethod(
296                    TestMutable.class, "getValue", element);
297            assertSame(Mutable.class, accessibleMethod.getDeclaringClass());
298        }
299    }
300
301    @Test
302    public void testGetAccessiblePublicMethod() throws Exception {
303        assertSame(MutableObject.class, MethodUtils.getAccessibleMethod(
304                MutableObject.class.getMethod("getValue",
305                        ArrayUtils.EMPTY_CLASS_ARRAY)).getDeclaringClass());
306    }
307
308    @Test
309    public void testGetAccessiblePublicMethodFromDescription() throws Exception {
310        assertSame(MutableObject.class, MethodUtils.getAccessibleMethod(
311                MutableObject.class, "getValue", ArrayUtils.EMPTY_CLASS_ARRAY)
312                .getDeclaringClass());
313    }
314    
315    @Test
316   public void testGetAccessibleMethodInaccessible() throws Exception {
317        final Method expected = TestBean.class.getDeclaredMethod("privateStuff");
318        final Method actual = MethodUtils.getAccessibleMethod(expected);
319        assertNull(actual);
320    }
321
322    @Test
323   public void testGetMatchingAccessibleMethod() throws Exception {
324        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
325                ArrayUtils.EMPTY_CLASS_ARRAY, ArrayUtils.EMPTY_CLASS_ARRAY);
326        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
327                null, ArrayUtils.EMPTY_CLASS_ARRAY);
328        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
329                singletonArray(String.class), singletonArray(String.class));
330        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
331                singletonArray(Object.class), singletonArray(Object.class));
332        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
333                singletonArray(Boolean.class), singletonArray(Object.class));
334        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
335                singletonArray(Byte.class), singletonArray(Integer.TYPE));
336        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
337                singletonArray(Byte.TYPE), singletonArray(Integer.TYPE));
338        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
339                singletonArray(Short.class), singletonArray(Integer.TYPE));
340        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
341                singletonArray(Short.TYPE), singletonArray(Integer.TYPE));
342        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
343                singletonArray(Character.class), singletonArray(Integer.TYPE));
344        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
345                singletonArray(Character.TYPE), singletonArray(Integer.TYPE));
346        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
347                singletonArray(Integer.class), singletonArray(Integer.class));
348        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
349                singletonArray(Integer.TYPE), singletonArray(Integer.TYPE));
350        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
351                singletonArray(Long.class), singletonArray(Double.TYPE));
352        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
353                singletonArray(Long.TYPE), singletonArray(Double.TYPE));
354        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
355                singletonArray(Float.class), singletonArray(Double.TYPE));
356        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
357                singletonArray(Float.TYPE), singletonArray(Double.TYPE));
358        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
359                singletonArray(Double.class), singletonArray(Double.TYPE));
360        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
361                singletonArray(Double.TYPE), singletonArray(Double.TYPE));
362        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo",
363                singletonArray(Double.TYPE), singletonArray(Double.TYPE));
364        expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testOne",
365                singletonArray(ParentObject.class), singletonArray(ParentObject.class));
366        expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testOne",
367                singletonArray(ChildObject.class), singletonArray(ParentObject.class));
368        expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testTwo",
369                singletonArray(ParentObject.class), singletonArray(GrandParentObject.class));
370        expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testTwo",
371                singletonArray(ChildObject.class), singletonArray(ChildInterface.class));
372    }
373
374    @Test
375    public void testNullArgument() {
376        expectMatchingAccessibleMethodParameterTypes(TestBean.class, "oneParameter",
377                singletonArray(null), singletonArray(String.class));
378    }
379
380    private void expectMatchingAccessibleMethodParameterTypes(final Class<?> cls,
381            final String methodName, final Class<?>[] requestTypes, final Class<?>[] actualTypes) {
382        final Method m = MethodUtils.getMatchingAccessibleMethod(cls, methodName,
383                requestTypes);
384        assertTrue(toString(m.getParameterTypes()) + " not equals "
385                + toString(actualTypes), Arrays.equals(actualTypes, m
386                .getParameterTypes()));
387    }
388
389    private String toString(final Class<?>[] c) {
390        return Arrays.asList(c).toString();
391    }
392
393    private Class<?>[] singletonArray(final Class<?> c) {
394        Class<?>[] result = classCache.get(c);
395        if (result == null) {
396            result = new Class[] { c };
397            classCache.put(c, result);
398        }
399        return result;
400    }
401
402    public static class InheritanceBean {
403        public void testOne(final Object obj) {}
404        public void testOne(final GrandParentObject obj) {}
405        public void testOne(final ParentObject obj) {}
406        public void testTwo(final Object obj) {}
407        public void testTwo(final GrandParentObject obj) {}
408        public void testTwo(final ChildInterface obj) {}
409    }
410    
411    interface ChildInterface {}    
412    public static class GrandParentObject {}
413    public static class ParentObject extends GrandParentObject {}
414    public static class ChildObject extends ParentObject implements ChildInterface {}
415    
416}