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's class
111         * isn't compatible (externally generated, for instance), then 
112         * <code>interfaceClass</code> is returned instead.
113         * 
114         * @param instance
115         *            the object instance to obtain a class from
116         * @param interfaceClass
117         *            the interface class to return if the instance is not compatible
118         */
119        public static Class getInstanceClass(ClassFab classFab, Object instance, Class interfaceClass)
120        {
121            Class instanceClass = instance.getClass();
122    
123            if (!classFab.canConvert(instanceClass))
124                return interfaceClass;
125    
126            return instanceClass;
127        }
128    
129        /**
130         * Returns the class of an instance. However, if the instance is, in fact, a JDK proxy, returns
131         * the interfaceClass (because JDK proxies do not work with Javassist).
132         * 
133         * @param instance
134         *            the object instance to obtain a class from
135         * @param interfaceClass
136         *            the interface class to return if the instance is a JDK proxy.
137         * @deprecated Please use version which takes a ClassFab object.
138         */
139        public static Class getInstanceClass(Object instance, Class interfaceClass)
140        {
141            Class instanceClass = instance.getClass();
142    
143            if (Proxy.isProxyClass(instanceClass))
144                return interfaceClass;
145    
146            return instanceClass;
147        }
148    
149        /**
150         * Adds a method that does nothing. If the method returns a value, it will return null, 0 or
151         * false (depending on the type).
152         * 
153         * @since 1.1
154         */
155    
156        public static void addNoOpMethod(ClassFab cf, MethodSignature m)
157        {
158            StringBuffer body = new StringBuffer("{ ");
159    
160            Class returnType = m.getReturnType();
161    
162            if (returnType != void.class)
163            {
164                body.append("return");
165    
166                if (returnType.isPrimitive())
167                {
168                    if (returnType == boolean.class)
169                        body.append(" false");
170                    else if (returnType == long.class)
171                        body.append(" 0L");
172                    else if (returnType == float.class)
173                        body.append(" 0.0f");
174                    else if (returnType == double.class)
175                        body.append(" 0.0d");
176                    else
177                        body.append(" 0");
178                }
179                else
180                {
181                    body.append(" null");
182                }
183    
184                body.append(";");
185            }
186    
187            body.append(" }");
188    
189            cf.addMethod(Modifier.PUBLIC, m, body.toString());
190        }
191    }