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    }