001package org.andromda.core.common;
002
003import java.lang.reflect.Field;
004import java.lang.reflect.Method;
005import java.lang.reflect.Modifier;
006import java.net.URL;
007import java.util.ArrayList;
008import java.util.Arrays;
009import java.util.Collection;
010import java.util.LinkedHashSet;
011import java.util.List;
012import java.util.Set;
013import org.apache.commons.collections.CollectionUtils;
014import org.apache.commons.lang.StringUtils;
015
016// TODO: FindBugs: This class has a simple name that is identical to that of its superclass. This can be exceptionally confusing.
017/**
018 * Contains utilities for dealing with classes.
019 *
020 * @author Chad Brandon
021 * @author Bob Fields
022 */
023public class ClassUtils
024    extends org.apache.commons.lang.ClassUtils
025{
026    /**
027     * Creates a new instance of the class having the given <code>className</code>.
028     *
029     * @param className the name of the class to instantiate.
030     * @return Object the new instance
031     */
032    public static Object newInstance(final String className)
033    {
034        try
035        {
036            return loadClass(className).newInstance();
037        }
038        catch (final Throwable throwable)
039        {
040            throw new ClassUtilsException(throwable);
041        }
042    }
043
044    /**
045     * Creates a new instance of the class given the <code>type</code>.
046     *
047     * @param type the type from which to instantiate the new instance.
048     * @return Object the new instance
049     */
050    public static Object newInstance(final Class type)
051    {
052        try
053        {
054            return type != null ? type.newInstance() : null;
055        }
056        catch (final Throwable throwable)
057        {
058            throw new ClassUtilsException(throwable);
059        }
060    }
061
062    /**
063     * Loads and returns the class having the className. Will load but normal classes and the classes representing
064     * primitives.
065     *
066     * @param className the name of the class to load.
067     * @return Class the loaded class
068     */
069    public static Class loadClass(String className)
070    {
071        ExceptionUtils.checkEmpty(
072            "className",
073            className);
074        className = StringUtils.trimToNull(className);
075
076        // get rid of any array notation
077        className = StringUtils.replace(
078                className,
079                "[]",
080                "");
081
082        final ClassLoader loader = getClassLoader();
083        Class loadedClass;
084        try
085        {
086            // check and see if it's a primitive and if so convert it
087            if (ClassUtils.isPrimitiveType(className))
088            {
089                loadedClass = getPrimitiveClass(
090                        className,
091                        loader);
092            }
093            else
094            {
095                loadedClass = loader.loadClass(className);
096            }
097        }
098        catch (final Throwable throwable)
099        {
100            throw new ClassUtilsException(throwable);
101        }
102        return loadedClass;
103    }
104
105    /**
106     * Gets the appropriate class loader instance.
107     *
108     * @return the class loader.
109     */
110    public static ClassLoader getClassLoader()
111    {
112        ClassLoader loader = Thread.currentThread().getContextClassLoader();
113        if (loader == null)
114        {
115            loader = ClassUtils.class.getClassLoader();
116            Thread.currentThread().setContextClassLoader(loader);
117        }
118        return loader;
119    }
120
121    /**
122     * <p> Returns the type class name for a Java primitive.
123     * </p>
124     *
125     * @param name a <code>String</code> with the name of the type
126     * @param loader the loader to use.
127     * @return a <code>String</code> with the name of the corresponding
128     *         java.lang wrapper class if <code>name</code> is a Java
129     *         primitive type; <code>false</code> if not
130     */
131    protected static Class getPrimitiveClass(
132        final String name,
133        final ClassLoader loader)
134    {
135        ExceptionUtils.checkEmpty(
136            "name",
137            name);
138        ExceptionUtils.checkNull(
139            "loader",
140            loader);
141
142        Class primitiveClass = null;
143        if (isPrimitiveType(name) && !"void".equals(name))
144        {
145            final String className;
146            if ("char".equals(name))
147            {
148                className = "Character";
149            }
150            else if ("int".equals(name))
151            {
152                className = "Integer";
153            }
154            else
155            {
156                className = StringUtils.capitalize(name);
157            }
158
159            try
160            {
161                if (StringUtils.isNotBlank(className))
162                {
163                    Field field = loader.loadClass(className).getField("TYPE");
164                    primitiveClass = (Class)field.get(null);
165                }
166            }
167            catch (final Exception exception)
168            {
169                throw new ClassUtilsException(exception);
170            }
171        }
172        return primitiveClass;
173    }
174
175    /**
176     * Returns a collection of all static fields values for the given
177     * <code>clazz</code> and <code>type</code> of field.
178     *
179     * @param clazz  the Class from which to retrieve the static fields
180     * @param type the type of static fields to retrieve, if null all are retrieved
181     * @return Collection the collection of static field values.
182     * @throws IllegalAccessException - if some aspect of this static field prevents it from being added to this collection.
183     */
184    public static Collection<Object> getStaticFieldValues(
185        final Class clazz,
186        final Class type)
187        throws IllegalAccessException
188    {
189        ExceptionUtils.checkNull(
190            "clazz",
191            clazz);
192        final Field[] fields = clazz.getFields();
193        int fieldsNum = fields.length;
194
195        final List<Object> values = new ArrayList<Object>();
196        Field field;
197        int modifiers;
198        for (int ctr = 0; ctr < fieldsNum; ctr++)
199        {
200            field = fields[ctr];
201            modifiers = field.getModifiers();
202            if (Modifier.isStatic(modifiers))
203            {
204                if (type != null)
205                {
206                    if (type == field.getType())
207                    {
208                        values.add(fields[ctr].get(null));
209                    }
210                }
211                else
212                {
213                    values.add(fields[ctr].get(null));
214                }
215            }
216        }
217        return values;
218    }
219
220    /**
221     * Retrieves all interfaces for the given <code>className</code> (including <code>className</code>
222     * itself, assuming it's an interface as well).
223     *
224     * @param className the root interface className
225     * @return a list containing all interfaces ordered from the root down.
226     */
227    public static List<Class> getInterfaces(final String className)
228    {
229        final List<Class> interfaces = new ArrayList<Class>();
230        if (StringUtils.isNotBlank(className))
231        {
232            interfaces.addAll(getInterfaces(ClassUtils.loadClass(className.trim())));
233        }
234        return interfaces;
235    }
236
237    /**
238     * Retrieves all interfaces for the given <code>clazz</code> (including <code>clazz</code>
239     * itself, assuming it's an interface as well).
240     *
241     * @param clazz the root interface class
242     * @return a list containing all interfaces ordered from the root down.
243     */
244    public static List<Class> getInterfaces(final Class clazz)
245    {
246        final List<Class> interfaces = new ArrayList<Class>();
247        if (clazz != null)
248        {
249            interfaces.addAll(ClassUtils.getAllInterfaces(clazz));
250            if (clazz.isInterface())
251            {
252                interfaces.add(
253                    0,
254                    clazz);
255            }
256        }
257        return interfaces;
258    }
259
260    /**
261     * Gets the interfaces for the given <code>className</code> in reverse order.
262     *
263     * @param className the name of the class for which to retrieve the interfaces
264     * @return the array containing the reversed interfaces.
265     */
266    public static Class[] getInterfacesReversed(final String className)
267    {
268        final List<Class> interfacesList = getInterfaces(className);
269        Class[] interfaces = interfacesList.toArray(new Class[interfacesList.size()]);
270        if (interfaces.length > 0)
271        {
272            CollectionUtils.reverseArray(interfaces);
273        }
274        return interfaces;
275    }
276
277    /**
278     * <p>
279     * Checks if a given type name is a Java primitive type. </p>
280     *
281     * @param name a <code>String</code> with the name of the type
282     * @return <code>true</code> if <code>name</code> is a Java primitive type; <code>false</code> if not
283     */
284    protected static boolean isPrimitiveType(final String name)
285    {
286        return "void".equals(name) || "char".equals(name) || "byte".equals(name) || "short".equals(name) ||
287        "int".equals(name) || "long".equals(name) || "float".equals(name) || "double".equals(name) ||
288        "boolean".equals(name);
289    }
290
291    /**
292     * The suffix for class files.
293     */
294    public static final String CLASS_EXTENSION = ".class";
295
296    /**
297     * Searches the contents of the <code>directoryUri</code> and returns the first
298     * Class found that is of the given <code>type</code>.
299     *
300     * @param directoryUris the URIs to search, ie. directories or archives.
301     * @param type the type to find.
302     * @return the class or null if not found.
303     */
304    public static Class findClassOfType(
305        final URL[] directoryUris,
306        final Class type)
307    {
308        Class found = null;
309        if (directoryUris != null && directoryUris.length > 0)
310        {
311            for (URL directoryUri : directoryUris)
312            {
313                final List<String> contents = ResourceUtils.getDirectoryContents(
314                        directoryUri,
315                        false,
316                        null);
317                for (final String path : contents)
318                {
319                    if (path.endsWith(CLASS_EXTENSION))
320                    {
321                        final String typeName =
322                                StringUtils.replace(
323                                        ResourceUtils.normalizePath(path).replace('/', '.'),
324                                        CLASS_EXTENSION,
325                                        "");
326                        try
327                        {
328                            final Class loadedClass = getClassLoader().loadClass(typeName);
329                            if (type.isAssignableFrom(loadedClass))
330                            {
331                                found = loadedClass;
332                                break;
333                            }
334                        }
335                        catch (final ClassNotFoundException exception)
336                        {
337                            // - ignore, means the file wasn't a class
338                        }
339                    }
340                }
341            }
342        }
343        return found;
344    }
345
346    /**
347     * Loads all methods from the given <code>clazz</code> (this includes
348     * all super class methods, public, private and protected).
349     *
350     * @param clazz the class to retrieve the methods.
351     * @return the loaded methods.
352     */
353    public static List<Method> getAllMethods(final Class clazz)
354    {
355        final Set<Method> methods = new LinkedHashSet<Method>();
356        loadMethods(clazz, methods);
357        return new ArrayList<Method>(methods);
358    }
359
360    /**
361     * Loads all methods from the given <code>clazz</code> (this includes
362     * all super class methods).
363     *
364     * @param methods the list to load full of methods.
365     * @param clazz the class to retrieve the methods.
366     */
367    private static void loadMethods(
368        final Class clazz,
369        final Set<Method> methods)
370    {
371        methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
372        if (clazz.getSuperclass() != null)
373        {
374            loadMethods(
375                    clazz.getSuperclass(),
376                    methods);
377        }
378    }
379
380    /**
381     * Indicates whether or not a class of the given <code>type</code>
382     * is present in one of the given <code>directoryUris</code>.
383     *
384     * @param directoryUris the URIs to search, ie. directories or archives.
385     * @param type the type to check.
386     * @return true/false.
387     */
388    public static boolean isClassOfTypePresent(
389        final URL[] directoryUris,
390        final Class type)
391    {
392        return ClassUtils.findClassOfType(directoryUris, type) != null;
393    }
394}