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    }