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.12 2005/11/06 22:16:38 taqua 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.Collection;
059    import java.util.Iterator;
060    
061    /**
062     * A collection of useful static utility methods for handling classes and object
063     * instantiation.
064     *
065     * @author Thomas Morgner
066     */
067    public final class ObjectUtilities {
068    
069        /**
070         * A constant for using the TheadContext as source for the classloader.
071         */
072        public static final String THREAD_CONTEXT = "ThreadContext";
073        /**
074         * A constant for using the ClassContext as source for the classloader.
075         */
076        public static final String CLASS_CONTEXT = "ClassContext";
077    
078        /**
079         * By default use the thread context.
080         */
081        private static String classLoaderSource = THREAD_CONTEXT;
082        /**
083         * The custom classloader to be used (if not null).
084         */
085        private static ClassLoader classLoader;
086    
087        /**
088         * Default constructor - private.
089         */
090        private ObjectUtilities() {
091        }
092    
093        /**
094         * Returns the internal configuration entry, whether the classloader of
095         * the thread context or the context classloader should be used.
096         *
097         * @return the classloader source, either THREAD_CONTEXT or CLASS_CONTEXT.
098         */
099        public static String getClassLoaderSource() {
100            return classLoaderSource;
101        }
102    
103        /**
104         * Defines the internal configuration entry, whether the classloader of
105         * the thread context or the context classloader should be used.
106         * <p/>
107         * This setting can only be defined using the API, there is no safe way
108         * to put this into an external configuration file.
109         *
110         * @param classLoaderSource the classloader source,
111         *                          either THREAD_CONTEXT or CLASS_CONTEXT.
112         */
113        public static void setClassLoaderSource(final String classLoaderSource) {
114            ObjectUtilities.classLoaderSource = classLoaderSource;
115        }
116    
117        /**
118         * Returns <code>true</code> if the two objects are equal OR both 
119         * <code>null</code>.
120         *
121         * @param o1 object 1 (<code>null</code> permitted).
122         * @param o2 object 2 (<code>null</code> permitted).
123         * @return <code>true</code> or <code>false</code>.
124         */
125        public static boolean equal(final Object o1, final Object o2) {
126            if (o1 == o2) {
127                return true;
128            }
129            if (o1 != null) {
130                return o1.equals(o2);
131            }
132            else {
133                return false;
134            }
135        }
136    
137        /**
138         * Returns a hash code for an object, or zero if the object is 
139         * <code>null</code>.
140         *
141         * @param object the object (<code>null</code> permitted).
142         * @return The object's hash code (or zero if the object is
143         *         <code>null</code>).
144         */
145        public static int hashCode(final Object object) {
146            int result = 0;
147            if (object != null) {
148                result = object.hashCode();
149            }
150            return result;
151        }
152    
153        /**
154         * Returns a clone of the specified object, if it can be cloned, otherwise
155         * throws a CloneNotSupportedException.
156         *
157         * @param object the object to clone (<code>null</code> not permitted).
158         * @return A clone of the specified object.
159         * @throws CloneNotSupportedException if the object cannot be cloned.
160         */
161        public static Object clone(final Object object) 
162            throws CloneNotSupportedException {
163            if (object == null) {
164                throw new IllegalArgumentException("Null 'object' argument.");
165            }
166            if (object instanceof PublicCloneable) {
167                final PublicCloneable pc = (PublicCloneable) object;
168                return pc.clone();
169            }
170            else {
171                try {
172                    final Method method = object.getClass().getMethod("clone", 
173                            (Class[]) null);
174                    if (Modifier.isPublic(method.getModifiers())) {
175                        return method.invoke(object, (Object[]) null);
176                    }
177                }
178                catch (NoSuchMethodException e) {
179                    Log.warn("Object without clone() method is impossible.");
180                }
181                catch (IllegalAccessException e) {
182                    Log.warn("Object.clone(): unable to call method.");
183                }
184                catch (InvocationTargetException e) {
185                    Log.warn("Object without clone() method is impossible.");
186                }
187            }
188            throw new CloneNotSupportedException("Failed to clone.");
189        }
190    
191        /**
192         * Returns a new collection containing clones of all the items in the
193         * specified collection.
194         *
195         * @param collection the collection (<code>null</code> not permitted).
196         * @return A new collection containing clones of all the items in the 
197         *         specified collection.
198         * @throws CloneNotSupportedException if any of the items in the collection
199         *                                    cannot be cloned.
200         */
201        public static Collection deepClone(final Collection collection)
202            throws CloneNotSupportedException {
203    
204            if (collection == null) {
205                throw new IllegalArgumentException("Null 'collection' argument.");
206            }
207            // all JDK-Collections are cloneable ...
208            // and if the collection is not clonable, then we should throw
209            // a CloneNotSupportedException anyway ...
210            final Collection result 
211                = (Collection) ObjectUtilities.clone(collection);
212            result.clear();
213            final Iterator iterator = collection.iterator();
214            while (iterator.hasNext()) {
215                final Object item = iterator.next();
216                if (item != null) {
217                    result.add(clone(item));
218                }
219                else {
220                    result.add(null);
221                }
222            }
223            return result;
224        }
225    
226        /**
227         * Redefines the custom classloader.
228         *
229         * @param classLoader the new classloader or null to use the default.
230         */
231        public synchronized static void setClassLoader(
232                final ClassLoader classLoader) {
233            ObjectUtilities.classLoader = classLoader;
234        }
235    
236        /**
237         * Returns the custom classloader or null, if no custom classloader is defined.
238         *
239         * @return the custom classloader or null to use the default.
240         */
241        public static ClassLoader getClassLoader() {
242          return classLoader;
243        }
244    
245        /**
246         * Returns the classloader, which was responsible for loading the given
247         * class.
248         *
249         * @param c the classloader, either an application class loader or the
250         *          boot loader.
251         * @return the classloader, never null.
252         * @throws SecurityException if the SecurityManager does not allow to grab
253         *                           the context classloader.
254         */
255        public synchronized static ClassLoader getClassLoader(final Class c) {
256            if (classLoader != null) {
257                return classLoader;
258            }
259            if ("ThreadContext".equals(classLoaderSource)) {
260                final ClassLoader threadLoader 
261                    = Thread.currentThread().getContextClassLoader();
262                return threadLoader;
263            }
264            else {
265                // Context classloader - do not cache ..
266                final ClassLoader applicationCL = c.getClassLoader();
267                if (applicationCL == null) {
268                    return ClassLoader.getSystemClassLoader();
269                }
270                else {
271                    return applicationCL;
272                }
273            }
274        }
275    
276    
277        /**
278         * Returns the resource specified by the <strong>absolute</strong> name.
279         *
280         * @param name the name of the resource
281         * @param c    the source class
282         * @return the url of the resource or null, if not found.
283         */
284        public static URL getResource(final String name, final Class c) {
285            final ClassLoader cl = getClassLoader(c);
286            return cl.getResource(name);
287        }
288    
289        /**
290         * Returns the resource specified by the <strong>relative</strong> name.
291         *
292         * @param name the name of the resource relative to the given class
293         * @param c    the source class
294         * @return the url of the resource or null, if not found.
295         */
296        public static URL getResourceRelative(final String name, final Class c) {
297            final ClassLoader cl = getClassLoader(c);
298            final String cname = convertName(name, c);
299            return cl.getResource(cname);
300        }
301    
302        /**
303         * Transform the class-relative resource name into a global name by 
304         * appending it to the classes package name. If the name is already a 
305         * global name (the name starts with a "/"), then the name is returned 
306         * unchanged.
307         *
308         * @param name the resource name
309         * @param c    the class which the resource is relative to
310         * @return the tranformed name.
311         */
312        private static String convertName(final String name, Class c) {
313            if (name.startsWith("/")) {
314                // strip leading slash..
315                return name.substring(1);
316            }
317    
318            // we cant work on arrays, so remove them ...
319            while (c.isArray()) {
320                c = c.getComponentType();
321            }
322            // extract the package ...
323            final String baseName = c.getName();
324            final int index = baseName.lastIndexOf('.');
325            if (index == -1) {
326                return name;
327            }
328    
329            final String pkgName = baseName.substring(0, index);
330            return pkgName.replace('.', '/') + "/" + name;
331        }
332    
333        /**
334         * Returns the inputstream for the resource specified by the
335         * <strong>absolute</strong> name.
336         *
337         * @param name the name of the resource
338         * @param context the source class
339         * @return the url of the resource or null, if not found.
340         */
341        public static InputStream getResourceAsStream(final String name,
342                                                      final Class context) {
343            final URL url = getResource(name, context);
344            if (url == null) {
345                return null;
346            }
347    
348            try {
349                return url.openStream();
350            }
351            catch (IOException e) {
352                return null;
353            }
354        }
355    
356        /**
357         * Returns the inputstream for the resource specified by the
358         * <strong>relative</strong> name.
359         *
360         * @param name the name of the resource relative to the given class
361         * @param context the source class
362         * @return the url of the resource or null, if not found.
363         */
364        public static InputStream getResourceRelativeAsStream
365            (final String name, final Class context) {
366            final URL url = getResourceRelative(name, context);
367            if (url == null) {
368                return null;
369            }
370    
371            try {
372                return url.openStream();
373            }
374            catch (IOException e) {
375                return null;
376            }
377        }
378    
379        /**
380         * Tries to create a new instance of the given class. This is a short cut
381         * for the common bean instantiation code.
382         *
383         * @param className the class name as String, never null.
384         * @param source    the source class, from where to get the classloader.
385         * @return the instantiated object or null, if an error occured.
386         */
387        public static Object loadAndInstantiate(final String className, 
388                                                final Class source) {
389            try {
390                final ClassLoader loader = getClassLoader(source);
391                final Class c = loader.loadClass(className);
392                return c.newInstance();
393            }
394            catch (Exception e) {
395                return null;
396            }
397        }
398    
399    }