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
015package org.apache.hivemind.service;
016
017import java.lang.reflect.Method;
018import java.lang.reflect.Modifier;
019import java.lang.reflect.Proxy;
020
021/**
022 * Static class containing utility methods.
023 * 
024 * @author Howard Lewis Ship
025 */
026public 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}