1ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski/*
2ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Copyright (C) 2010 The Android Open Source Project
3ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
4ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
5ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * you may not use this file except in compliance with the License.
6ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * You may obtain a copy of the License at
7ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
8ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
9ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
10ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Unless required by applicable law or agreed to in writing, software
11ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
12ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * See the License for the specific language governing permissions and
14ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * limitations under the License.
15ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski */
16ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
17ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskipackage com.android.layoutlib.bridge;
18ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
19ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport com.android.tools.layoutlib.annotations.LayoutlibDelegate;
20ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport com.android.tools.layoutlib.create.CreateInfo;
21ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
22ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport java.lang.reflect.Method;
23ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport java.lang.reflect.Modifier;
24ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport java.util.ArrayList;
25ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport java.util.List;
26ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
27ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskiimport junit.framework.TestCase;
28ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
29ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski/**
30ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Tests that native delegate classes implement all the required methods.
31ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
32ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * This looks at {@link CreateInfo#DELEGATE_CLASS_NATIVES} to get the list of classes that
33ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * have their native methods reimplemented through a delegate.
34ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
35ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * Since the reimplemented methods are not native anymore, we look for the annotation
36ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * {@link LayoutlibDelegate}, and look for a matching method in the delegate (named the same
37ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * as the modified class with _Delegate added as a suffix).
38ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * If the original native method is not static, then we make sure the delegate method also
39ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski * include the original class as first parameter (to access "this").
40ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski *
41ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski */
42ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinskipublic class TestDelegates extends TestCase {
43ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
44d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta    private List<String> mErrors = new ArrayList<String>();
45d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta
46ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    public void testNativeDelegates() {
47ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
48ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        final String[] classes = CreateInfo.DELEGATE_CLASS_NATIVES;
49d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta        mErrors.clear();
50d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta        for (String clazz : classes) {
51d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta            loadAndCompareClasses(clazz, clazz + "_Delegate");
52ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        }
53d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta        assertTrue(getErrors(), mErrors.isEmpty());
54ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    }
55ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
56ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    public void testMethodDelegates() {
57ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        final String[] methods = CreateInfo.DELEGATE_METHODS;
58d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta        mErrors.clear();
59d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta        for (String methodName : methods) {
60ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            // extract the class name
61ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            String className = methodName.substring(0, methodName.indexOf('#'));
62ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            String targetClassName = className.replace('$', '_') + "_Delegate";
63ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
64ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            loadAndCompareClasses(className, targetClassName);
65ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        }
66d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta        assertTrue(getErrors(), mErrors.isEmpty());
67ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    }
68ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
69ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    private void loadAndCompareClasses(String originalClassName, String delegateClassName) {
70ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        // load the classes
71ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        try {
72ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            ClassLoader classLoader = TestDelegates.class.getClassLoader();
73ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            Class<?> originalClass = classLoader.loadClass(originalClassName);
74ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            Class<?> delegateClass = classLoader.loadClass(delegateClassName);
75ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
76ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            compare(originalClass, delegateClass);
77ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        } catch (ClassNotFoundException e) {
78d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta            mErrors.add("Failed to load class: " + e.getMessage());
79ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        } catch (SecurityException e) {
80d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta            mErrors.add("Failed to load class: " + e.getMessage());
81ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        }
82ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    }
83ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
84ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    private void compare(Class<?> originalClass, Class<?> delegateClass) throws SecurityException {
85ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        List<Method> checkedDelegateMethods = new ArrayList<Method>();
86ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
87ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        // loop on the methods of the original class, and for the ones that are annotated
88ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        // with @LayoutlibDelegate, look for a matching method in the delegate class.
89ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        // The annotation is automatically added by layoutlib_create when it replace a method
90ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        // by a call to a delegate
91ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        Method[] originalMethods = originalClass.getDeclaredMethods();
92ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        for (Method originalMethod : originalMethods) {
93ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            // look for methods that are delegated: they have the LayoutlibDelegate annotation
94ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            if (originalMethod.getAnnotation(LayoutlibDelegate.class) == null) {
95ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                continue;
96ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            }
97ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
98ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            // get the signature.
99ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            Class<?>[] parameters = originalMethod.getParameterTypes();
100ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
101ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            // if the method is not static, then the class is added as the first parameter
102ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            // (for "this")
103ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            if ((originalMethod.getModifiers() & Modifier.STATIC) == 0) {
104ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
105ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                Class<?>[] newParameters = new Class<?>[parameters.length + 1];
106ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                newParameters[0] = originalClass;
107ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
108ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                parameters = newParameters;
109ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            }
110ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
111ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            // if the original class is an inner class that's not static, then
112ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            // we add this on the enclosing class at the beginning
113ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            if (originalClass.getEnclosingClass() != null &&
114ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                    (originalClass.getModifiers() & Modifier.STATIC) == 0) {
115ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                Class<?>[] newParameters = new Class<?>[parameters.length + 1];
116ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                newParameters[0] = originalClass.getEnclosingClass();
117ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
118ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                parameters = newParameters;
119ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            }
120ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
121ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            try {
122ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                // try to load the method with the given parameter types.
123ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                Method delegateMethod = delegateClass.getDeclaredMethod(originalMethod.getName(),
124ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                        parameters);
125ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
126f9a823540ce0e23e3bbdd7c1fb8bf2639e20a8bcDeepanshu Gupta                // check the return type of the methods match.
127d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                if (delegateMethod.getReturnType() != originalMethod.getReturnType()) {
128d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                    mErrors.add(
129d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                            String.format("Delegate method %1$s.%2$s does not match the " +
130d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                                    "corresponding framework method which returns %3$s",
131d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                            delegateClass.getName(),
132d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                            getMethodName(delegateMethod),
133d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                            originalMethod.getReturnType().getName()));
134d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                }
135f9a823540ce0e23e3bbdd7c1fb8bf2639e20a8bcDeepanshu Gupta
136ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                // check that the method has the annotation
137d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                if (delegateMethod.getAnnotation(LayoutlibDelegate.class) == null) {
138d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                    mErrors.add(
139d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                            String.format("Delegate method %1$s for class %2$s does not have the " +
140d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                                            "@LayoutlibDelegate annotation",
141d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                                    delegateMethod.getName(),
142d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                                    originalClass.getName()));
143d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                }
144ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
145ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                // check that the method is static
146d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                if ((delegateMethod.getModifiers() & Modifier.STATIC) != Modifier.STATIC) {
147d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                    mErrors.add(
148d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                            String.format(
149d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                                    "Delegate method %1$s for class %2$s is not static",
150d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                                    delegateMethod.getName(),
151d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                                    originalClass.getName())
152d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                    );
153d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                }
154ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
155ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                // add the method as checked.
156ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                checkedDelegateMethods.add(delegateMethod);
157ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            } catch (NoSuchMethodException e) {
158ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                String name = getMethodName(originalMethod, parameters);
159d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                mErrors.add(String.format("Missing %1$s.%2$s", delegateClass.getName(), name));
160ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            }
161ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        }
162ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
163ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        // look for dead (delegate) code.
164ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        // This looks for all methods in the delegate class, and if they have the
165ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        // @LayoutlibDelegate annotation, make sure they have been previously found as a
166ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        // match for a method in the original class.
167ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        // If not, this means the method is a delegate for a method that either doesn't exist
168ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        // anymore or is not delegated anymore.
169ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        Method[] delegateMethods = delegateClass.getDeclaredMethods();
170ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        for (Method delegateMethod : delegateMethods) {
171ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            // look for methods that are delegates: they have the LayoutlibDelegate annotation
172ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            if (delegateMethod.getAnnotation(LayoutlibDelegate.class) == null) {
173ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                continue;
174ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            }
175ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
176b1bd12381b7b0b57eff6d9bb1a5ae23309ab2b5cJerome Gaillard            // constructor_after methods are called by the constructor of the original class
177b1bd12381b7b0b57eff6d9bb1a5ae23309ab2b5cJerome Gaillard            if ("constructor_after".equals(delegateMethod.getName())) {
178b1bd12381b7b0b57eff6d9bb1a5ae23309ab2b5cJerome Gaillard                continue;
179b1bd12381b7b0b57eff6d9bb1a5ae23309ab2b5cJerome Gaillard            }
180b1bd12381b7b0b57eff6d9bb1a5ae23309ab2b5cJerome Gaillard
181d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta            if (!checkedDelegateMethods.contains(delegateMethod)) {
182d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                mErrors.add(String.format(
183d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                        "Delegate method %1$s.%2$s is not used anymore and must be removed",
184d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                        delegateClass.getName(),
185d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta                        getMethodName(delegateMethod)));
186d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta            }
187ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        }
188ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
189ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    }
190ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
191ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    private String getMethodName(Method method) {
192ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        return getMethodName(method, method.getParameterTypes());
193ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    }
194ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
195ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    private String getMethodName(Method method, Class<?>[] parameters) {
196ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        // compute a full class name that's long but not too long.
197ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        StringBuilder sb = new StringBuilder(method.getName() + "(");
198ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        for (int j = 0; j < parameters.length; j++) {
199ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            Class<?> theClass = parameters[j];
200ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            int dimensions = 0;
201ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            while (theClass.isArray()) {
202ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                dimensions++;
203ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                theClass = theClass.getComponentType();
204ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            }
2059fe7fca9bcdceade9c654c6a8dcf0c48be16d78dDeepanshu Gupta            sb.append(theClass.getName());
206ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            for (int i = 0; i < dimensions; i++) {
207ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                sb.append("[]");
208ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            }
209ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            if (j < (parameters.length - 1)) {
210ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski                sb.append(",");
211ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski            }
212ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        }
213ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        sb.append(")");
214ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski
215ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski        return sb.toString();
216ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski    }
217d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta
218d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta    private String getErrors() {
219d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta        StringBuilder s = new StringBuilder();
220d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta        for (String error : mErrors) {
221d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta            s.append(error).append('\n');
222d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta        }
223d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta        return s.toString();
224d6b67539db5d0b4338cc1478999e20ff9f8728d2Deepanshu Gupta    }
225ab775ecdd189b32e35b0d3f4a821502f88b03a4bAdam Lesinski}
226