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
015package org.apache.tapestry5.ioc.services;
016
017import java.io.File;
018import java.lang.reflect.Method;
019import java.net.URISyntaxException;
020import java.net.URL;
021import java.util.Map;
022import java.util.concurrent.atomic.AtomicLong;
023
024import 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")
032public 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}