    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
    050    package org.jfree.util;
    051
    052    import java.io.IOException;
    053    import java.io.InputStream;
    054    import java.lang.reflect.InvocationTargetException;
    055    import java.lang.reflect.Method;
    056    import java.lang.reflect.Modifier;
    057    import java.net.URL;
    058    import java.util.ArrayList;
    059    import java.util.Collection;
    060    import java.util.Iterator;
    061    import 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     */
    069    public 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    }