    001/* ========================================================================
    002 * JCommon : a free general purpose class library for the Java(tm) platform
    003 * ========================================================================
    004 *
    005 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
    006 *
    007 * Project Info:  http://www.jfree.org/jcommon/index.html
    008 *
    009 * This library is free software; you can redistribute it and/or modify it
    010 * under the terms of the GNU Lesser General Public License as published by
    011 * the Free Software Foundation; either version 2.1 of the License, or
    012 * (at your option) any later version.
    013 *
    014 * This library is distributed in the hope that it will be useful, but
    015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017 * License for more details.
    018 *
    019 * You should have received a copy of the GNU Lesser General Public
    020 * License along with this library; if not, write to the Free Software
    021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022 * USA.
    023 *
    024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025 * in the United States and other countries.]
    026 *
    027 * ---------------------
    028 * ObjectUtilitiess.java
    029 * ---------------------
    030 * (C) Copyright 2003-2005, by Object Refinery Limited.
    031 *
    032 * Original Author:  David Gilbert (for Object Refinery Limited);
    033 * Contributor(s):   -;
    034 *
    035 * $Id: ObjectUtilities.java,v 1.21 2008/09/10 09:24:41 mungady Exp $
    036 *
    037 * Changes
    038 * -------
    039 * 25-Mar-2003 : Version 1 (DG);
    040 * 15-Sep-2003 : Fixed bug in clone(List) method (DG);
    041 * 25-Nov-2004 : Modified clone(Object) method to fail with objects that
    042 *               cannot be cloned, added new deepClone(Collection) method.
    043 *               Renamed ObjectUtils --> ObjectUtilities (DG);
    044 * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
    045 * 18-Aug-2005 : Added casts to suppress compiler warnings, as suggested in
    046 *               patch 1260622 (DG);
    047 *
    048 */
    049
    050package org.jfree.util;
    051
    052import java.io.IOException;
    053import java.io.InputStream;
    054import java.lang.reflect.InvocationTargetException;
    055import java.lang.reflect.Method;
    056import java.lang.reflect.Modifier;
    057import java.net.URL;
    058import java.util.ArrayList;
    059import java.util.Collection;
    060import java.util.Iterator;
    061import java.util.StringTokenizer;
    062
    063/**
    064 * A collection of useful static utility methods for handling classes and object
    065 * instantiation.
    066 *
    067 * @author Thomas Morgner
    068 */
    069public final class ObjectUtilities {
    070
    071    /**
    072     * A constant for using the TheadContext as source for the classloader.
    073     */
    074    public static final String THREAD_CONTEXT = "ThreadContext";
    075    /**
    076     * A constant for using the ClassContext as source for the classloader.
    077     */
    078    public static final String CLASS_CONTEXT = "ClassContext";
    079
    080    /**
    081     * By default use the thread context.
    082     */
    083    private static String classLoaderSource = THREAD_CONTEXT;
    084    /**
    085     * The custom classloader to be used (if not null).
    086     */
    087    private static ClassLoader classLoader;
    088
    089    /**
    090     * Default constructor - private.
    091     */
    092    private ObjectUtilities() {
    093    }
    094
    095    /**
    096     * Returns the internal configuration entry, whether the classloader of
    097     * the thread context or the context classloader should be used.
    098     *
    099     * @return the classloader source, either THREAD_CONTEXT or CLASS_CONTEXT.
    100     */
    101    public static String getClassLoaderSource() {
    102        return classLoaderSource;
    103    }
    104
    105    /**
    106     * Defines the internal configuration entry, whether the classloader of
    107     * the thread context or the context classloader should be used.
    108     * <p/>
    109     * This setting can only be defined using the API, there is no safe way
    110     * to put this into an external configuration file.
    111     *
    112     * @param classLoaderSource the classloader source,
    113     *                          either THREAD_CONTEXT or CLASS_CONTEXT.
    114     */
    115    public static void setClassLoaderSource(final String classLoaderSource) {
    116        ObjectUtilities.classLoaderSource = classLoaderSource;
    117    }
    118
    119    /**
    120     * Returns <code>true</code> if the two objects are equal OR both
    121     * <code>null</code>.
    122     *
    123     * @param o1 object 1 (<code>null</code> permitted).
    124     * @param o2 object 2 (<code>null</code> permitted).
    125     * @return <code>true</code> or <code>false</code>.
    126     */
    127    public static boolean equal(final Object o1, final Object o2) {
    128        if (o1 == o2) {
    129            return true;
    130        }
    131        if (o1 != null) {
    132            return o1.equals(o2);
    133        }
    134        else {
    135            return false;
    136        }
    137    }
    138
    139    /**
    140     * Returns a hash code for an object, or zero if the object is
    141     * <code>null</code>.
    142     *
    143     * @param object the object (<code>null</code> permitted).
    144     * @return The object's hash code (or zero if the object is
    145     *         <code>null</code>).
    146     */
    147    public static int hashCode(final Object object) {
    148        int result = 0;
    149        if (object != null) {
    150            result = object.hashCode();
    151        }
    152        return result;
    153    }
    154
    155    /**
    156     * Returns a clone of the specified object, if it can be cloned, otherwise
    157     * throws a CloneNotSupportedException.
    158     *
    159     * @param object the object to clone (<code>null</code> not permitted).
    160     * @return A clone of the specified object.
    161     * @throws CloneNotSupportedException if the object cannot be cloned.
    162     */
    163    public static Object clone(final Object object)
    164        throws CloneNotSupportedException {
    165        if (object == null) {
    166            throw new IllegalArgumentException("Null 'object' argument.");
    167        }
    168        if (object instanceof PublicCloneable) {
    169            final PublicCloneable pc = (PublicCloneable) object;
    170            return pc.clone();
    171        }
    172        else {
    173            try {
    174                final Method method = object.getClass().getMethod("clone",
    175                        (Class[]) null);
    176                if (Modifier.isPublic(method.getModifiers())) {
    177                    return method.invoke(object, (Object[]) null);
    178                }
    179            }
    180            catch (NoSuchMethodException e) {
    181                Log.warn("Object without clone() method is impossible.");
    182            }
    183            catch (IllegalAccessException e) {
    184                Log.warn("Object.clone(): unable to call method.");
    185            }
    186            catch (InvocationTargetException e) {
    187                Log.warn("Object without clone() method is impossible.");
    188            }
    189        }
    190        throw new CloneNotSupportedException("Failed to clone.");
    191    }
    192
    193    /**
    194     * Returns a new collection containing clones of all the items in the
    195     * specified collection.
    196     *
    197     * @param collection the collection (<code>null</code> not permitted).
    198     * @return A new collection containing clones of all the items in the
    199     *         specified collection.
    200     * @throws CloneNotSupportedException if any of the items in the collection
    201     *                                    cannot be cloned.
    202     */
    203    public static Collection deepClone(final Collection collection)
    204        throws CloneNotSupportedException {
    205
    206        if (collection == null) {
    207            throw new IllegalArgumentException("Null 'collection' argument.");
    208        }
    209        // all JDK-Collections are cloneable ...
    210        // and if the collection is not clonable, then we should throw
    211        // a CloneNotSupportedException anyway ...
    212        final Collection result
    213            = (Collection) ObjectUtilities.clone(collection);
    214        result.clear();
    215        final Iterator iterator = collection.iterator();
    216        while (iterator.hasNext()) {
    217            final Object item = iterator.next();
    218            if (item != null) {
    219                result.add(clone(item));
    220            }
    221            else {
    222                result.add(null);
    223            }
    224        }
    225        return result;
    226    }
    227
    228    /**
    229     * Redefines the custom classloader.
    230     *
    231     * @param classLoader the new classloader or null to use the default.
    232     */
    233    public static synchronized void setClassLoader(
    234            final ClassLoader classLoader) {
    235        ObjectUtilities.classLoader = classLoader;
    236    }
    237
    238    /**
    239     * Returns the custom classloader or null, if no custom classloader is defined.
    240     *
    241     * @return the custom classloader or null to use the default.
    242     */
    243    public static ClassLoader getClassLoader() {
    244      return classLoader;
    245    }
    246
    247    /**
    248     * Returns the classloader, which was responsible for loading the given
    249     * class.
    250     *
    251     * @param c the classloader, either an application class loader or the
    252     *          boot loader.
    253     * @return the classloader, never null.
    254     * @throws SecurityException if the SecurityManager does not allow to grab
    255     *                           the context classloader.
    256     */
    257    public static ClassLoader getClassLoader(final Class c) {
    258        final String localClassLoaderSource;
    259        synchronized(ObjectUtilities.class)
    260        {
    261          if (classLoader != null) {
    262              return classLoader;
    263          }
    264          localClassLoaderSource = classLoaderSource;
    265        }
    266
    267        if ("ThreadContext".equals(localClassLoaderSource)) {
    268            final ClassLoader threadLoader = Thread.currentThread().getContextClassLoader();
    269            if (threadLoader != null) {
    270                return threadLoader;
    271            }
    272        }
    273
    274        // Context classloader - do not cache ..
    275        final ClassLoader applicationCL = c.getClassLoader();
    276        if (applicationCL == null) {
    277            return ClassLoader.getSystemClassLoader();
    278        }
    279        else {
    280            return applicationCL;
    281        }
    282    }
    283
    284
    285    /**
    286     * Returns the resource specified by the <strong>absolute</strong> name.
    287     *
    288     * @param name the name of the resource
    289     * @param c    the source class
    290     * @return the url of the resource or null, if not found.
    291     */
    292    public static URL getResource(final String name, final Class c) {
    293        final ClassLoader cl = getClassLoader(c);
    294        if (cl == null) {
    295            return null;
    296        }
    297        return cl.getResource(name);
    298    }
    299
    300    /**
    301     * Returns the resource specified by the <strong>relative</strong> name.
    302     *
    303     * @param name the name of the resource relative to the given class
    304     * @param c    the source class
    305     * @return the url of the resource or null, if not found.
    306     */
    307    public static URL getResourceRelative(final String name, final Class c) {
    308        final ClassLoader cl = getClassLoader(c);
    309        final String cname = convertName(name, c);
    310        if (cl == null) {
    311            return null;
    312        }
    313        return cl.getResource(cname);
    314    }
    315
    316    /**
    317     * Transform the class-relative resource name into a global name by
    318     * appending it to the classes package name. If the name is already a
    319     * global name (the name starts with a "/"), then the name is returned
    320     * unchanged.
    321     *
    322     * @param name the resource name
    323     * @param c    the class which the resource is relative to
    324     * @return the tranformed name.
    325     */
    326    private static String convertName(final String name, Class c) {
    327        if (name.startsWith("/")) {
    328            // strip leading slash..
    329            return name.substring(1);
    330        }
    331
    332        // we cant work on arrays, so remove them ...
    333        while (c.isArray()) {
    334            c = c.getComponentType();
    335        }
    336        // extract the package ...
    337        final String baseName = c.getName();
    338        final int index = baseName.lastIndexOf('.');
    339        if (index == -1) {
    340            return name;
    341        }
    342
    343        final String pkgName = baseName.substring(0, index);
    344        return pkgName.replace('.', '/') + "/" + name;
    345    }
    346
    347    /**
    348     * Returns the inputstream for the resource specified by the
    349     * <strong>absolute</strong> name.
    350     *
    351     * @param name the name of the resource
    352     * @param context the source class
    353     * @return the url of the resource or null, if not found.
    354     */
    355    public static InputStream getResourceAsStream(final String name,
    356                                                  final Class context) {
    357        final URL url = getResource(name, context);
    358        if (url == null) {
    359            return null;
    360        }
    361
    362        try {
    363            return url.openStream();
    364        }
    365        catch (IOException e) {
    366            return null;
    367        }
    368    }
    369
    370    /**
    371     * Returns the inputstream for the resource specified by the
    372     * <strong>relative</strong> name.
    373     *
    374     * @param name the name of the resource relative to the given class
    375     * @param context the source class
    376     * @return the url of the resource or null, if not found.
    377     */
    378    public static InputStream getResourceRelativeAsStream
    379        (final String name, final Class context) {
    380        final URL url = getResourceRelative(name, context);
    381        if (url == null) {
    382            return null;
    383        }
    384
    385        try {
    386            return url.openStream();
    387        }
    388        catch (IOException e) {
    389            return null;
    390        }
    391    }
    392
    393    /**
    394     * Tries to create a new instance of the given class. This is a short cut
    395     * for the common bean instantiation code.
    396     *
    397     * @param className the class name as String, never null.
    398     * @param source    the source class, from where to get the classloader.
    399     * @return the instantiated object or null, if an error occured.
    400     */
    401    public static Object loadAndInstantiate(final String className,
    402                                            final Class source) {
    403        try {
    404            final ClassLoader loader = getClassLoader(source);
    405            final Class c = loader.loadClass(className);
    406            return c.newInstance();
    407        }
    408        catch (Exception e) {
    409            return null;
    410        }
    411    }
    412
    413    /**
    414     * Tries to create a new instance of the given class. This is a short cut
    415     * for the common bean instantiation code. This method is a type-safe method
    416     * and will not instantiate the class unless it is an instance of the given
    417     * type.
    418     *
    419     * @param className the class name as String, never null.
    420     * @param source    the source class, from where to get the classloader.
    421     * @param type  the type.
    422     * @return the instantiated object or null, if an error occurred.
    423     */
    424    public static Object loadAndInstantiate(final String className,
    425                                            final Class source,
    426                                            final Class type) {
    427        try {
    428            final ClassLoader loader = getClassLoader(source);
    429            final Class c = loader.loadClass(className);
    430            if (type.isAssignableFrom(c)) {
    431                return c.newInstance();
    432            }
    433        }
    434        catch (Exception e) {
    435            return null;
    436        }
    437        return null;
    438    }
    439
    440    /**
    441     * Returns <code>true</code> if this is version 1.4 or later of the
    442     * Java runtime.
    443     *
    444     * @return A boolean.
    445     */
    446    public static boolean isJDK14() {
    447        try {
    448          final ClassLoader loader = getClassLoader(ObjectUtilities.class);
    449          if (loader != null) {
    450              try {
    451                loader.loadClass("java.util.RandomAccess");
    452                return true;
    453              }
    454              catch (ClassNotFoundException e) {
    455                return false;
    456              }
    457              catch(Exception e) {
    458                // do nothing, but do not crash ...
    459              }
    460          }
    461        }
    462        catch (Exception e) {
    463          // cant do anything about it, we have to accept and ignore it ..
    464        }
    465
    466        // OK, the quick and dirty, but secure way failed. Lets try it
    467        // using the standard way.
    468        try {
    469            final String version = System.getProperty
    470                    ("java.vm.specification.version");
    471            // parse the beast...
    472            if (version == null) {
    473                return false;
    474            }
    475
    476            String[] versions = parseVersions(version);
    477            String[] target = new String[]{ "1", "4" };
    478            return (ArrayUtilities.compareVersionArrays(versions, target) >= 0);
    479        }
    480        catch(Exception e) {
    481            return false;
    482        }
    483    }
    484
    485    private static String[] parseVersions (String version)
    486    {
    487      if (version == null)
    488      {
    489        return new String[0];
    490      }
    491
    492      final ArrayList versions = new ArrayList();
    493      final StringTokenizer strtok = new StringTokenizer(version, ".");
    494      while (strtok.hasMoreTokens())
    495      {
    496        versions.add (strtok.nextToken());
    497      }
    498      return (String[]) versions.toArray(new String[versions.size()]);
    499    }
    500}