geronimo-xbean-scm mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From d...@apache.org
Subject svn commit: r630653 - in /geronimo/xbean/trunk/xbean-reflect: ./ src/main/java/org/apache/xbean/parameter/ src/test/java/org/apache/xbean/parameter/
Date Sun, 24 Feb 2008 18:27:51 GMT
Author: dain
Date: Sun Feb 24 10:27:50 2008
New Revision: 630653

URL: http://svn.apache.org/viewvc?rev=630653&view=rev
Log:
Added code to read constructor and method parameter names from debug table in byte code

Added:
    geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/
    geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/AsmParameterNames.java
    geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/ParameterNames.java
    geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/parameter/
    geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/parameter/ParameterNamesTest.java
Modified:
    geronimo/xbean/trunk/xbean-reflect/pom.xml

Modified: geronimo/xbean/trunk/xbean-reflect/pom.xml
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/pom.xml?rev=630653&r1=630652&r2=630653&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/pom.xml (original)
+++ geronimo/xbean/trunk/xbean-reflect/pom.xml Sun Feb 24 10:27:50 2008
@@ -31,6 +31,21 @@
   <packaging>bundle</packaging>
   <name>XBean :: Reflect</name>
 
+  <dependencies>
+    <dependency>
+      <groupId>asm</groupId>
+      <artifactId>asm</artifactId>
+      <version>2.2.3</version>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>asm</groupId>
+      <artifactId>asm-commons</artifactId>
+      <version>2.2.3</version>
+      <optional>true</optional>
+    </dependency>
+  </dependencies>
+
   <profiles>
     <profile>
       <activation>

Added: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/AsmParameterNames.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/AsmParameterNames.java?rev=630653&view=auto
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/AsmParameterNames.java
(added)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/AsmParameterNames.java
Sun Feb 24 10:27:50 2008
@@ -0,0 +1,300 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.xbean.parameter;
+
+import java.io.IOException;
+import java.io.InputStream;
+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.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+/**
+ * Implementation of ParameterNames that uses ASM to read the parameter names from the local
variable table in the
+ * class byte code.
+ *
+ * This wonderful piece of code was taken from org.springframework.core.LocalVariableTableParameterNameDiscover
+ */
+public class AsmParameterNames implements ParameterNames {
+    /**
+     * Weak map from Constructor to List&lt;String&gt;.
+     */
+    private final WeakHashMap<Constructor,List<String>> constructorCache = new
WeakHashMap<Constructor,List<String>>();
+
+    /**
+     * Weak map from Method to List&lt;String&gt;.
+     */
+    private final WeakHashMap<Method,List<String>> methodCache = new WeakHashMap<Method,List<String>>();
+
+    /**
+     * Gets the parameter names of the specified method or null if the class was compiled
without debug symbols on.
+     * @param method the method for which the parameter names should be retrieved
+     * @return the parameter names or null if the class was compilesd without debug symbols
on
+     */
+    public List<String> get(Method method) {
+        // check the cache
+        if (methodCache.containsKey(method)) {
+            return methodCache.get(method);
+        }
+
+        Map<Method,List<String>> allMethodParameters = getAllMethodParameters(method.getDeclaringClass(),
method.getName());
+        return allMethodParameters.get(method);
+    }
+
+    /**
+     * Gets the parameter names of the specified constructor or null if the class was compiled
without debug symbols on.
+     * @param constructor the constructor for which the parameters should be retrieved
+     * @return the parameter names or null if the class was compiled without debug symbols
on
+     */
+    public List<String> get(Constructor constructor) {
+        // check the cache
+        if (constructorCache.containsKey(constructor)) {
+            return constructorCache.get(constructor);
+        }
+
+        Map<Constructor,List<String>> allConstructorParameters = getAllConstructorParameters(constructor.getDeclaringClass());
+        return allConstructorParameters.get(constructor);
+    }
+
+    /**
+     * Gets the parameter names of all constructoror null if the class was compiled without
debug symbols on.
+     * @param clazz the class for which the constructor parameter names should be retrieved
+     * @return a map from Constructor object to the parameter names or null if the class
was compiled without debug symbols on
+     */
+    public Map<Constructor,List<String>> getAllConstructorParameters(Class clazz)
{
+        // Determine the constructors?
+        Constructor[] constructors = clazz.getConstructors();
+        if (constructors.length == 0) {
+            return Collections.emptyMap();
+        }
+
+        // Check the cache
+        if (constructorCache.containsKey(constructors[0])) {
+            Map<Constructor,List<String>> constructorParameters = new HashMap<Constructor,List<String>>();
+            for (Constructor constructor : constructors) {
+                constructorParameters.put(constructor, constructorCache.get(constructor));
+            }
+            return constructorParameters;
+        }
+
+        // Load the parameter names using ASM
+        Map<Constructor,List<String>> constructorParameters = new HashMap<Constructor,List<String>>
();
+        try {
+            ClassReader reader = AsmParameterNames.createClassReader(clazz);
+
+            AsmParameterNames.AllParameterNamesDiscoveringVisitor visitor = new AsmParameterNames.AllParameterNamesDiscoveringVisitor(clazz);
+            reader.accept(visitor, false);
+
+            Map exceptions = visitor.getExceptions();
+            if (exceptions.size() == 1) {
+                throw new RuntimeException((Exception)exceptions.values().iterator().next());
+            }
+            if (!exceptions.isEmpty()) {
+                throw new RuntimeException(exceptions.toString());
+            }
+
+            constructorParameters = visitor.getConstructorParameters();
+        } catch (IOException ex) {
+        }
+
+        // Cache the names
+        for (Constructor constructor : constructors) {
+            constructorCache.put(constructor, constructorParameters.get(constructor));
+        }
+        return constructorParameters;
+    }
+
+    /**
+     * Gets the parameter names of all methods with the specified name or null if the class
was compiled without debug symbols on.
+     * @param clazz the class for which the method parameter names should be retrieved
+     * @param methodName the of the method for which the parameters should be retrieved
+     * @return a map from Method object to the parameter names or null if the class was compiled
without debug symbols on
+     */
+    public Map<Method,List<String>> getAllMethodParameters(Class clazz, String
methodName) {
+        // Determine the constructors?
+        Method[] methods = getMethods(clazz, methodName);
+        if (methods.length == 0) {
+            return Collections.emptyMap();
+        }
+
+        // Check the cache
+        if (methodCache.containsKey(methods[0])) {
+            Map<Method,List<String>> methodParameters = new HashMap<Method,List<String>>();
+            for (Method method : methods) {
+                methodParameters.put(method, methodCache.get(method));
+            }
+            return methodParameters;
+        }
+
+        // Load the parameter names using ASM
+        Map<Method,List<String>>  methodParameters = new HashMap<Method,List<String>>();
+        try {
+            ClassReader reader = AsmParameterNames.createClassReader(clazz);
+
+            AsmParameterNames.AllParameterNamesDiscoveringVisitor visitor = new AsmParameterNames.AllParameterNamesDiscoveringVisitor(clazz,
methodName);
+            reader.accept(visitor, false);
+
+            Map exceptions = visitor.getExceptions();
+            if (exceptions.size() == 1) {
+                throw new RuntimeException((Exception)exceptions.values().iterator().next());
+            }
+            if (!exceptions.isEmpty()) {
+                throw new RuntimeException(exceptions.toString());
+            }
+
+            methodParameters = visitor.getMethodParameters();
+        } catch (IOException ex) {
+        }
+
+        // Cache the names
+        for (Method method : methods) {
+            methodCache.put(method, methodParameters.get(method));
+        }
+        return methodParameters;
+    }
+
+    private Method[] getMethods(Class clazz, String methodName) {
+        Method[] methods = clazz.getMethods();
+        List<Method> matchingMethod = new ArrayList<Method>(methods.length);
+        for (Method method : methods) {
+            if (method.getName().equals(methodName)) {
+                matchingMethod.add(method);
+            }
+        }
+        return matchingMethod.toArray(new Method[matchingMethod.size()]);
+    }
+
+    private static ClassReader createClassReader(Class declaringClass) throws IOException
{
+        InputStream in = null;
+        try {
+            ClassLoader classLoader = declaringClass.getClassLoader();
+            in = classLoader.getResourceAsStream(declaringClass.getName().replace('.', '/')
+ ".class");
+            ClassReader reader = new ClassReader(in);
+            return reader;
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException ignored) {
+                }
+            }
+        }
+    }
+
+    private static class AllParameterNamesDiscoveringVisitor extends EmptyVisitor {
+        private final Map<Constructor,List<String>> constructorParameters = new
HashMap<Constructor,List<String>>();
+        private final Map<Method,List<String>> methodParameters = new HashMap<Method,List<String>>();
+        private final Map<String,Exception> exceptions = new HashMap<String,Exception>();
+        private final String methodName;
+        private final Map<String,Method> methodMap = new HashMap<String,Method>();
+        private final Map<String,Constructor> constructorMap = new HashMap<String,Constructor>();
+
+        public AllParameterNamesDiscoveringVisitor(Class type, String methodName) {
+            this.methodName = methodName;
+
+            Method[] methods = type.getMethods();
+            for (Method method : methods) {
+                if (method.getName().equals(methodName)) {
+                    methodMap.put(Type.getMethodDescriptor(method), method);
+                }
+            }
+        }
+
+        public AllParameterNamesDiscoveringVisitor(Class type) {
+            this.methodName = "<init>";
+
+            Constructor[] constructors = type.getConstructors();
+            for (Constructor constructor : constructors) {
+                Type[] types = new Type[constructor.getParameterTypes().length];
+                for (int j = 0; j < types.length; j++) {
+                    types[j] = Type.getType(constructor.getParameterTypes()[j]);
+                }
+                constructorMap.put(Type.getMethodDescriptor(Type.VOID_TYPE, types), constructor);
+            }
+        }
+
+        public Map<Constructor, List<String>> getConstructorParameters() {
+            return constructorParameters;
+        }
+
+        public Map<Method, List<String>> getMethodParameters() {
+            return methodParameters;
+        }
+
+        public Map<String,Exception> getExceptions() {
+            return exceptions;
+        }
+
+        public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions) {
+            if (!name.equals(this.methodName)) {
+                return null;
+            }
+
+            try {
+                final List<String> parameterNames;
+                final boolean isStaticMethod;
+
+                if (methodName.equals("<init>")) {
+                    Constructor constructor = constructorMap.get(desc);
+                    if (constructor == null) {
+                        return null;
+                    }
+                    parameterNames = new ArrayList<String>(constructor.getParameterTypes().length);
+                    parameterNames.addAll(Collections.<String>nCopies(constructor.getParameterTypes().length,
null));
+                    constructorParameters.put(constructor, parameterNames);
+                    isStaticMethod = false;
+                } else {
+                    Method method = methodMap.get(desc);
+                    if (method == null) {
+                        return null;
+                    }
+                    parameterNames = new ArrayList<String>(method.getParameterTypes().length);
+                    parameterNames.addAll(Collections.<String>nCopies(method.getParameterTypes().length,
null));
+                    methodParameters.put(method, parameterNames);
+                    isStaticMethod = Modifier.isStatic(method.getModifiers());
+                }
+
+                return new EmptyVisitor() {
+                    // assume static method until we get a first parameter name
+                    public void visitLocalVariable(String name, String description, String
signature, Label start, Label end, int index) {
+                        if (isStaticMethod) {
+                            parameterNames.set(index, name);
+                        } else if (index > 0) {
+                            // for non-static the 0th arg is "this" so we need to offset
by -1
+                            parameterNames.set(index - 1, name);
+                        }
+                    }
+                };
+            } catch (Exception e) {
+                this.exceptions.put(signature, e);
+            }
+            return null;
+        }
+    }
+}

Added: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/ParameterNames.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/ParameterNames.java?rev=630653&view=auto
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/ParameterNames.java
(added)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/ParameterNames.java
Sun Feb 24 10:27:50 2008
@@ -0,0 +1,57 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.xbean.parameter;
+
+import java.util.List;
+import java.util.Map;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+
+/**
+ * Determines the parameter names of Constructors or Methods.
+ */
+public interface ParameterNames {
+    /**
+     * Gets the parameter names of the specified method or null if the class was compiled
without debug symbols on.
+     * @param method the method for which the parameter names should be retrieved
+     * @return the parameter names or null if the class was compilesd without debug symbols
on
+     */
+    List<String> get(Method method);
+
+    /**
+     * Gets the parameter names of the specified constructor or null if the class was compiled
without debug symbols on.
+     * @param constructor the constructor for which the parameters should be retrieved
+     * @return the parameter names or null if the class was compiled without debug symbols
on
+     */
+    List<String> get(Constructor constructor);
+
+    /**
+     * Gets the parameter names of all constructoror null if the class was compiled without
debug symbols on.
+     * @param clazz the class for which the constructor parameter names should be retrieved
+     * @return a map from Constructor object to the parameter names or null if the class
was compiled without debug symbols on
+     */
+    Map<Constructor,List<String>> getAllConstructorParameters(Class clazz);
+
+    /**
+     * Gets the parameter names of all methods with the specified name or null if the class
was compiled without debug symbols on.
+     * @param clazz the class for which the method parameter names should be retrieved
+     * @param methodName the of the method for which the parameters should be retrieved
+     * @return a map from Method object to the parameter names or null if the class was compiled
without debug symbols on
+     */
+    Map<Method,List<String>> getAllMethodParameters(Class clazz, String methodName);
+}

Added: geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/parameter/ParameterNamesTest.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/parameter/ParameterNamesTest.java?rev=630653&view=auto
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/parameter/ParameterNamesTest.java
(added)
+++ geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/parameter/ParameterNamesTest.java
Sun Feb 24 10:27:50 2008
@@ -0,0 +1,186 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.xbean.parameter;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+import org.apache.xbean.recipe.RecipeHelper;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class ParameterNamesTest extends TestCase {
+    private final ParameterNames parameterNames = new AsmParameterNames();
+    public void testConstructor() throws Exception {
+        Constructor constructor = TestClass.class.getConstructor(int.class, Object.class,
Long.class);
+        assertParameterNames(Arrays.asList("one", "two", "three"), constructor);
+    }
+
+    public void testMethod() throws Exception {
+        Method method = TestClass.class.getMethod("instanceMethod", int.class, Object.class,
Long.class);
+        assertParameterNames(Arrays.asList("x", "y", "z"), method);
+    }
+
+    public void testStaticMethod() throws Exception {
+        Method method = TestClass.class.getMethod("factoryMethod", int.class, Object.class,
Long.class);
+        assertParameterNames(Arrays.asList("a", "b", "c"), method);
+    }
+
+    public void testInheritedMethod() throws Exception {
+        Method method = TestClass.class.getMethod("inheritedMethod", Map.class);
+        assertParameterNames(Arrays.asList("nothing"), method);
+    }
+
+    public void testPrivateConstructor() throws Exception {
+        Constructor constructor = findPrivateConstructor(TestClass.class, new Class[]{Double.class});
+        assertNull(parameterNames.get(constructor));
+    }
+
+    public void testPrivateMethod() throws Exception {
+        Method method = findPrivateMethod(TestClass.class, "factoryMethod", new Class[] {Double.class});
+        assertNull(parameterNames.get(method));
+    }
+
+    public void testAllConstructors() throws Exception {
+        Map<Constructor,List<String>> expectedMap = new HashMap<Constructor,List<String>>();
+        expectedMap.put(TestClass.class.getConstructor(int.class, Object.class, Long.class),Arrays.asList("one",
"two", "three"));
+        expectedMap.put(TestClass.class.getConstructor(int.class),Arrays.asList("foo"));
+        expectedMap.put(TestClass.class.getConstructor(Object.class),Arrays.asList("bar"));
+        expectedMap.put(TestClass.class.getConstructor(Object[].class),Arrays.asList("objectArray"));
+
+        Map<Constructor, List<String>> actualMap = parameterNames.getAllConstructorParameters(TestClass.class);
+        assertEquals(expectedMap, actualMap);
+    }
+
+    public void testAllInstanceMethods() throws Exception {
+        Map<Method,List<String>> expectedMap = new HashMap<Method,List<String>>();
+        expectedMap.put(TestClass.class.getMethod("instanceMethod", int.class, Object.class,
Long.class), Arrays.asList("x", "y", "z"));
+        expectedMap.put(TestClass.class.getMethod("instanceMethod", int.class), Arrays.asList("apple"));
+        expectedMap.put(TestClass.class.getMethod("instanceMethod", Object.class), Arrays.asList("ipod"));
+
+        Map<Method, List<String>> actualMap = parameterNames.getAllMethodParameters(TestClass.class,
"instanceMethod");
+        assertEquals(expectedMap, actualMap);
+    }
+
+    public void testAllStataicMethods() throws Exception {
+        Map<Method,List<String>> expectedMap = new HashMap<Method,List<String>>();
+        expectedMap.put(TestClass.class.getMethod("factoryMethod", int.class, Object.class,
Long.class), Arrays.asList("a", "b", "c"));
+        expectedMap.put(TestClass.class.getMethod("factoryMethod", int.class), Arrays.asList("beer"));
+        expectedMap.put(TestClass.class.getMethod("factoryMethod", Object.class), Arrays.asList("pizza"));
+
+        Map<Method, List<String>> actualMap = parameterNames.getAllMethodParameters(TestClass.class,
"factoryMethod");
+        assertEquals(expectedMap, actualMap);
+    }
+
+    public void testAllMixedMethods() throws Exception {
+        Map<Method,List<String>> expectedMap = new HashMap<Method,List<String>>();
+        expectedMap.put(TestClass.class.getMethod("mixedMethods", Double.class), Arrays.asList("gin"));
+        expectedMap.put(TestClass.class.getMethod("mixedMethods", Short.class), Arrays.asList("tonic"));
+
+        Map<Method, List<String>> actualMap = parameterNames.getAllMethodParameters(TestClass.class,
"mixedMethods");
+        assertEquals(expectedMap, actualMap);
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    private static class ParentTestClass {
+        public void inheritedMethod(Map nothing) {}
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    private static abstract class TestClass extends ParentTestClass {
+        public TestClass(int one, Object two, Long three) {}
+        public TestClass(int foo) {}
+        public TestClass(Object bar) {}
+        public TestClass(Object[] objectArray) {}
+        private TestClass(Double scotch) {}
+
+        public static void factoryMethod(int a, Object b, Long c) {}
+        public static void factoryMethod(int beer) {}
+        public static void factoryMethod(Object pizza) {}
+        private static void factoryMethod(Double shot) {}
+
+        public void instanceMethod(int x, Object y, Long z) {}
+        public void instanceMethod(int apple) {}
+        public void instanceMethod(Object ipod) {}
+        private void instanceMethod(Double psp) {}
+
+        public static void mixedMethods(Double gin) {}
+        public void mixedMethods(Short tonic) {}
+
+        public abstract void abstractMethod(Byte ear);
+    }
+
+    private void assertParameterNames(List<String> expectedNames, Constructor constructor)
{
+        List<String> actualNames = parameterNames.get(constructor);
+        assertEquals(expectedNames, actualNames);
+    }
+
+    private void assertParameterNames(List<String> expectedNames, Method method) {
+        List<String> actualNames = parameterNames.get(method);
+        assertEquals(expectedNames, actualNames);
+    }
+
+    private static void assertEquals(List<String> expectedNames, List<String>
actualNames) {
+        assertNotNull(expectedNames);
+        assertNotNull(actualNames);
+        assertEquals(Arrays.asList(expectedNames), Arrays.asList(actualNames));
+    }
+
+    @SuppressWarnings({"SuspiciousMethodCalls"})
+    private void assertEquals(Map<?,List<String>> expectedMap, Map<?,List<String>>
actualMap) {
+        for (Map.Entry<?, List<String>> entry : actualMap.entrySet()) {
+            Object key = entry.getKey();
+            List<String> expectedNames = expectedMap.get(key);
+            List<String> actualNames = entry.getValue();
+            assertEquals(expectedNames, actualNames);
+        }
+    }
+
+    private Constructor findPrivateConstructor(Class clazz, Class[] argTypes) {
+        Constructor[] constructors = clazz.getDeclaredConstructors();
+        for (Constructor constructor : constructors) {
+            if (RecipeHelper.isAssignableFrom(argTypes, constructor.getParameterTypes()))
{
+                if (!Modifier.isPublic(constructor.getModifiers())) {
+                    return constructor;
+                }
+            }
+        }
+        fail("Private constructor not found");
+        return null;
+    }
+
+    private Method findPrivateMethod(Class clazz, String methodName, Class[] argTypes) {
+        Method[] methods = clazz.getDeclaredMethods();
+        for (Method method : methods) {
+            if (method.getName().equals(methodName) && RecipeHelper.isAssignableFrom(argTypes,
method.getParameterTypes())) {
+                if (!Modifier.isPublic(method.getModifiers())) {
+                    return method;
+                }
+            }
+        }
+        fail("Private method not found");
+        return null;
+    }
+}



Mime
View raw message