    001    // Copyright 2006, 2007, 2008, 2010, 2011 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.tapestry5.ioc.services;
    016
    017    import java.io.File;
    018    import java.lang.reflect.Method;
    019    import java.net.URISyntaxException;
    020    import java.net.URL;
    021    import java.util.Map;
    022    import java.util.concurrent.atomic.AtomicLong;
    023
    024    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    025
    026    /**
    027     * Handy method useful when creating new classes using {@link org.apache.tapestry5.ioc.services.ClassFab}.
    028     *
    029     * @deprecated Deprecated in Tapestry 5.3, to be removed in 5.4 with no replacement
    030     */
    031    @SuppressWarnings("all")
    032    public final class ClassFabUtils
    033    {
    034        private static final AtomicLong UID_GENERATOR = new AtomicLong(System.currentTimeMillis());
    035
    036        public static String nextUID()
    037        {
    038            return Long.toHexString(UID_GENERATOR.getAndIncrement());
    039        }
    040
    041        /**
    042         * Generates a unique class name, which will be in the default package.
    043         */
    044        public static synchronized String generateClassName(String baseName)
    045        {
    046            return "$" + baseName + "_" + nextUID();
    047        }
    048
    049        /**
    050         * Returns a class name derived from the provided interfaceClass. The package part of the interface name is stripped
    051         * out, and the result passed to {@link #generateClassName(String)}.
    052         */
    053        public static String generateClassName(Class interfaceClass)
    054        {
    055            return generateClassName(interfaceClass.getSimpleName());
    056        }
    057
    058        /**
    059         * Javassist needs the class name to be as it appears in source code, even for arrays. Invoking getName() on a Class
    060         * instance representing an array returns the internal format (i.e, "[...;" or something). This returns it as it
    061         * would appear in Java code.
    062         */
    063        public static String toJavaClassName(Class inputClass)
    064        {
    065            if (inputClass.isArray())
    066                return toJavaClassName(inputClass.getComponentType()) + "[]";
    067
    068            return inputClass.getName();
    069        }
    070
    071        /**
    072         * Returns true if the method is the standard toString() method. Very few interfaces will ever include this method
    073         * as part of the interface, but we have to be sure.
    074         */
    075        public static boolean isToString(Method method)
    076        {
    077            if (!method.getName().equals("toString"))
    078                return false;
    079
    080            if (method.getParameterTypes().length > 0)
    081                return false;
    082
    083            return method.getReturnType().equals(String.class);
    084        }
    085
    086        public static Class getPrimitiveType(String primitiveTypeName)
    087        {
    088            return PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.get(primitiveTypeName).primitiveType;
    089        }
    090
    091        private static class PrimitiveInfo
    092        {
    093            private final Class primitiveType;
    094
    095            private final String typeCode;
    096
    097            private final Class wrapperType;
    098
    099            private final String unwrapMethod;
    100
    101            public PrimitiveInfo(Class primitiveType, String typeCode, Class wrapperType, String unwrapMethod)
    102            {
    103                this.primitiveType = primitiveType;
    104                this.typeCode = typeCode;
    105                this.wrapperType = wrapperType;
    106                this.unwrapMethod = unwrapMethod;
    107            }
    108        }
    109
    110        private static final Map<String, PrimitiveInfo> PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO = CollectionFactory.newMap();
    111        private static final Map<Class, PrimitiveInfo> WRAPPER_TYPE_TO_PRIMITIVE_INFO = CollectionFactory.newMap();
    112
    113        static
    114        {
    115            add(boolean.class, "Z", Boolean.class, "booleanValue");
    116            add(short.class, "S", Short.class, "shortValue");
    117            add(int.class, "I", Integer.class, "intValue");
    118            add(long.class, "J", Long.class, "longValue");
    119            add(float.class, "F", Float.class, "floatValue");
    120            add(double.class, "D", Double.class, "doubleValue");
    121            add(char.class, "C", Character.class, "charValue");
    122            add(byte.class, "B", Byte.class, "byteValue");
    123        }
    124
    125        private static void add(Class primitiveType, String typeCode, Class wrapperType, String unwrapMethod)
    126        {
    127            PrimitiveInfo info = new PrimitiveInfo(primitiveType, typeCode, wrapperType, unwrapMethod);
    128
    129            PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.put(primitiveType.getName(), info);
    130
    131            WRAPPER_TYPE_TO_PRIMITIVE_INFO.put(wrapperType, info);
    132        }
    133
    134        /**
    135         * Translates types from standard Java format to Java VM format. For example, java.util.Locale remains
    136         * java.util.Locale, but int[][] is translated to [[I and java.lang.Object[] to [Ljava.lang.Object;
    137         */
    138        public static String toJVMBinaryName(String type)
    139        {
    140            // if it is not an array, just return the type itself
    141            if (!type.endsWith("[]"))
    142                return type;
    143
    144            // if it is an array, convert it to JavaVM-style format
    145            StringBuilder buffer = new StringBuilder();
    146
    147            while (type.endsWith("[]"))
    148            {
    149                buffer.append("[");
    150                type = type.substring(0, type.length() - 2);
    151            }
    152
    153            PrimitiveInfo pi = PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.get(type);
    154
    155            if (pi != null)
    156            {
    157                buffer.append(pi.typeCode);
    158            }
    159            else
    160            {
    161                buffer.append("L");
    162                buffer.append(type);
    163                buffer.append(";");
    164            }
    165
    166            return buffer.toString();
    167        }
    168
    169        /**
    170         * Given a wrapper type, determines the corresponding primitive type.
    171         */
    172        public static Class getPrimitiveType(Class wrapperType)
    173        {
    174            return WRAPPER_TYPE_TO_PRIMITIVE_INFO.get(wrapperType).primitiveType;
    175        }
    176
    177        /**
    178         * Returns the wrapper type for an input type; for most types, this is the type. For primitive types, it is the
    179         * corresponding wrapper type.
    180         *
    181         * @param type
    182         *            type to check
    183         * @return type or corresponding wrapper type
    184         */
    185        public static Class getWrapperType(Class type)
    186        {
    187            PrimitiveInfo info = PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.get(type.getName());
    188
    189            return info == null ? type : info.wrapperType;
    190        }
    191
    192        /**
    193         * Takes a reference and casts it to the desired type. If the desired type is a primitive type, then the reference
    194         * is cast to the correct wrapper type and a call to the correct unwrapper method is added. The end result is code
    195         * that can be assigned to a field or parameter of the desired type (even if desired type is a primitive).
    196         *
    197         * @param reference
    198         *            to be cast
    199         * @param desiredType
    200         *            desired object or primitive type
    201         * @return Javassist code to peform the cast
    202         */
    203        public static String castReference(String reference, String desiredType)
    204        {
    205            if (isPrimitiveType(desiredType))
    206            {
    207                PrimitiveInfo info = PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.get(desiredType);
    208
    209                return String.format("((%s)%s).%s()", info.wrapperType.getName(), reference, info.unwrapMethod);
    210            }
    211
    212            return String.format("(%s)%s", desiredType, reference);
    213        }
    214
    215        /**
    216         * Given a primitive type, finds the unwrap method of the corresponding wrapper type.
    217         *
    218         * @param primitiveType
    219         * @return method name
    220         */
    221        public static String getUnwrapMethodName(Class primitiveType)
    222        {
    223            return PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.get(primitiveType.getName()).unwrapMethod;
    224        }
    225
    226        /**
    227         * Given a type name, determines if that is the name of a primitive type.
    228         */
    229        public static boolean isPrimitiveType(String typeName)
    230        {
    231            return PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.containsKey(typeName);
    232        }
    233
    234        /**
    235         * Converts a Class to a JVM type code (the way class information is expressed in a class file).
    236         */
    237        public static String getTypeCode(Class type)
    238        {
    239            if (type.equals(void.class))
    240                return "V";
    241
    242            if (type.isPrimitive())
    243                return PRIMITIVE_TYPE_NAME_TO_PRIMITIVE_INFO.get(type.getName()).typeCode;
    244
    245            if (type.isArray())
    246                return "[" + getTypeCode(type.getComponentType());
    247
    248            return "L" + type.getName().replace('.', '/') + ";";
    249        }
    250
    251        /**
    252         * Given a Class instance, convert the name into a path that can be used to locate
    253         * the underlying class file on the classpath.
    254         *
    255         * @since 5.2.0
    256         */
    257        public static String getPathForClass(Class clazz)
    258        {
    259            assert clazz != null;
    260
    261            return getPathForClassNamed(clazz.getName());
    262        }
    263
    264        /**
    265         * Given a fully qualified class name, converts to a path on the classpath.
    266         *
    267         * @since 5.2.0
    268         */
    269        public static String getPathForClassNamed(String className)
    270        {
    271            return className.replace('.', '/') + ".class";
    272        }
    273
    274        /**
    275         * Converts a URL with protocol "file" to a File instance.
    276         *
    277         * @since 5.2.0
    278         */
    279        public static File toFileFromFileProtocolURL(URL url)
    280        {
    281            assert url != null;
    282
    283            if (!url.getProtocol().equals("file"))
    284                throw new IllegalArgumentException(String.format("URL %s does not use the 'file' protocol.", url));
    285
    286            // http://weblogs.java.net/blog/kohsuke/archive/2007/04/how_to_convert.html
    287
    288            try
    289            {
    290                return new File(url.toURI());
    291            }
    292            catch (URISyntaxException ex)
    293            {
    294                return new File(url.getPath());
    295            }
    296        }
    297    }