001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.hivemind.service;
016    
017    import java.lang.reflect.Method;
018    import java.lang.reflect.Modifier;
019    import java.lang.reflect.Proxy;
020    
021    /**
022     * Static class containing utility methods.
023     * 
024     * @author Howard Lewis Ship
025     */
026    public class ClassFabUtils
027    {
028        private static long _uid = System.currentTimeMillis();
029    
030        private static final char QUOTE = '"';
031    
032        private ClassFabUtils()
033        {
034        }
035    
036        /**
037         * Generates a unique class name, which will be in the default package.
038         */
039    
040        public static synchronized String generateClassName(String baseName)
041        {
042            return "$" + baseName + "_" + Long.toHexString(_uid++);
043        }
044    
045        /**
046         * Returns a class name derived from the provided interfaceClass. The package part of the
047         * interface name is stripped out, and the result passed to {@link #generateClassName(String)}.
048         * 
049         * @since 1.1
050         */
051    
052        public static synchronized String generateClassName(Class interfaceClass)
053        {
054            String name = interfaceClass.getName();
055    
056            int dotx = name.lastIndexOf('.');
057    
058            return generateClassName(name.substring(dotx + 1));
059        }
060    
061        /**
062         * Javassist needs the class name to be as it appears in source code, even for arrays. Invoking
063         * getName() on a Class instance representing an array returns the internal format (i.e, "[...;"
064         * or something). This returns it as it would appear in Java code.
065         */
066        public static String getJavaClassName(Class inputClass)
067        {
068            if (inputClass.isArray())
069                return getJavaClassName(inputClass.getComponentType()) + "[]";
070    
071            return inputClass.getName();
072        }
073    
074        /**
075         * Returns true if the method is the standard toString() method. Very few interfaces will ever
076         * include this method as part of the interface, but we have to be sure.
077         */
078        public static boolean isToString(Method method)
079        {
080            if (!method.getName().equals("toString"))
081                return false;
082    
083            if (method.getParameterTypes().length > 0)
084                return false;
085    
086            return method.getReturnType().equals(String.class);
087        }
088    
089        /**
090         * Adds a <code>toString()</code> method to a class that returns a fixed, pre-computed value.
091         * 
092         * @param classFab
093         *            ClassFab used to construct the new class.
094         * @param toStringResult
095         *            fixed result to be returned by the method.
096         */
097        public static void addToStringMethod(ClassFab classFab, String toStringResult)
098        {
099            StringBuffer buffer = new StringBuffer("return ");
100            buffer.append(QUOTE);
101            buffer.append(toStringResult);
102            buffer.append(QUOTE);
103            buffer.append(";");
104    
105            classFab.addMethod(Modifier.PUBLIC, new MethodSignature(String.class, "toString", null,
106                    null), buffer.toString());
107        }
108    
109        /**
110         * Returns the class of an instance. However, if the instance is, in fact, a JDK proxy, returns
111         * the interfaceClass (because JDK proxies do not work with Javassist).
112         * 
113         * @param instance
114         *            the object instance to obtain a class from
115         * @param interfaceClass
116         *            the interface class to return if the instance is a JDK proxy.
117         */
118        public static Class getInstanceClass(Object instance, Class interfaceClass)
119        {
120            Class instanceClass = instance.getClass();
121    
122            if (Proxy.isProxyClass(instanceClass))
123                return interfaceClass;
124    
125            return instanceClass;
126        }
127    
128        /**
129         * Adds a method that does nothing. If the method returns a value, it will return null, 0 or
130         * false (depending on the type).
131         * 
132         * @since 1.1
133         */
134    
135        public static void addNoOpMethod(ClassFab cf, MethodSignature m)
136        {
137            StringBuffer body = new StringBuffer("{ ");
138    
139            Class returnType = m.getReturnType();
140    
141            if (returnType != void.class)
142            {
143                body.append("return");
144    
145                if (returnType.isPrimitive())
146                {
147                    if (returnType == boolean.class)
148                        body.append(" false");
149                    else if (returnType == long.class)
150                        body.append(" 0L");
151                    else if (returnType == float.class)
152                        body.append(" 0.0f");
153                    else if (returnType == double.class)
154                        body.append(" 0.0d");
155                    else
156                        body.append(" 0");
157                }
158                else
159                {
160                    body.append(" null");
161                }
162    
163                body.append(";");
164            }
165    
166            body.append(" }");
167    
168            cf.addMethod(Modifier.PUBLIC, m, body.toString());
169        }
170    }