You are viewing a plain text version of this content. The canonical link for it is here.
Posted to xbean-scm@geronimo.apache.org by da...@apache.org on 2008/02/24 19:27:51 UTC
svn commit: r630653 - in /geronimo/xbean/trunk/xbean-reflect: ./
src/main/java/org/apache/xbean/parameter/
src/test/java/org/apache/xbean/parameter/
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<String>.
+ */
+ private final WeakHashMap<Constructor,List<String>> constructorCache = new WeakHashMap<Constructor,List<String>>();
+
+ /**
+ * Weak map from Method to List<String>.
+ */
+ 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;
+ }
+}