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.17 2006/12/03 15:33:33 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: import java.util.ArrayList;
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(
116: final String classLoaderSource) {
117: ObjectUtilities.classLoaderSource = classLoaderSource;
118: }
119:
120: /**
121: * Returns <code>true</code> if the two objects are equal OR both
122: * <code>null</code>.
123: *
124: * @param o1 object 1 (<code>null</code> permitted).
125: * @param o2 object 2 (<code>null</code> permitted).
126: * @return <code>true</code> or <code>false</code>.
127: */
128: public static boolean equal(final Object o1, final Object o2) {
129: if (o1 == o2) {
130: return true;
131: }
132: if (o1 != null) {
133: return o1.equals(o2);
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(
167: "Null 'object' argument.");
168: }
169: if (object instanceof PublicCloneable) {
170: final PublicCloneable pc = (PublicCloneable) object;
171: return pc.clone();
172: } else {
173: try {
174: final Method method = object.getClass().getMethod(
175: "clone", (Class[]) null);
176: if (Modifier.isPublic(method.getModifiers())) {
177: return method.invoke(object, (Object[]) null);
178: }
179: } catch (NoSuchMethodException e) {
180: Log
181: .warn("Object without clone() method is impossible.");
182: } catch (IllegalAccessException e) {
183: Log.warn("Object.clone(): unable to call method.");
184: } catch (InvocationTargetException e) {
185: Log
186: .warn("Object without clone() method is impossible.");
187: }
188: }
189: throw new CloneNotSupportedException("Failed to clone.");
190: }
191:
192: /**
193: * Returns a new collection containing clones of all the items in the
194: * specified collection.
195: *
196: * @param collection the collection (<code>null</code> not permitted).
197: * @return A new collection containing clones of all the items in the
198: * specified collection.
199: * @throws CloneNotSupportedException if any of the items in the collection
200: * cannot be cloned.
201: */
202: public static Collection deepClone(final Collection collection)
203: throws CloneNotSupportedException {
204:
205: if (collection == null) {
206: throw new IllegalArgumentException(
207: "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 = (Collection) ObjectUtilities
213: .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: } else {
221: result.add(null);
222: }
223: }
224: return result;
225: }
226:
227: /**
228: * Redefines the custom classloader.
229: *
230: * @param classLoader the new classloader or null to use the default.
231: */
232: public synchronized static void setClassLoader(
233: final ClassLoader classLoader) {
234: ObjectUtilities.classLoader = classLoader;
235: }
236:
237: /**
238: * Returns the custom classloader or null, if no custom classloader is defined.
239: *
240: * @return the custom classloader or null to use the default.
241: */
242: public static ClassLoader getClassLoader() {
243: return classLoader;
244: }
245:
246: /**
247: * Returns the classloader, which was responsible for loading the given
248: * class.
249: *
250: * @param c the classloader, either an application class loader or the
251: * boot loader.
252: * @return the classloader, never null.
253: * @throws SecurityException if the SecurityManager does not allow to grab
254: * the context classloader.
255: */
256: public synchronized static ClassLoader getClassLoader(final Class c) {
257: if (classLoader != null) {
258: return classLoader;
259: }
260: if ("ThreadContext".equals(classLoaderSource)) {
261: final ClassLoader threadLoader = Thread.currentThread()
262: .getContextClassLoader();
263: if (threadLoader != null) {
264: return threadLoader;
265: }
266: }
267:
268: // Context classloader - do not cache ..
269: final ClassLoader applicationCL = c.getClassLoader();
270: if (applicationCL == null) {
271: return ClassLoader.getSystemClassLoader();
272: } else {
273: return applicationCL;
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: if (cl == null) {
287: return null;
288: }
289: return cl.getResource(name);
290: }
291:
292: /**
293: * Returns the resource specified by the <strong>relative</strong> name.
294: *
295: * @param name the name of the resource relative to the given class
296: * @param c the source class
297: * @return the url of the resource or null, if not found.
298: */
299: public static URL getResourceRelative(final String name,
300: final Class c) {
301: final ClassLoader cl = getClassLoader(c);
302: final String cname = convertName(name, c);
303: if (cl == null) {
304: return null;
305: }
306: return cl.getResource(cname);
307: }
308:
309: /**
310: * Transform the class-relative resource name into a global name by
311: * appending it to the classes package name. If the name is already a
312: * global name (the name starts with a "/"), then the name is returned
313: * unchanged.
314: *
315: * @param name the resource name
316: * @param c the class which the resource is relative to
317: * @return the tranformed name.
318: */
319: private static String convertName(final String name, Class c) {
320: if (name.startsWith("/")) {
321: // strip leading slash..
322: return name.substring(1);
323: }
324:
325: // we cant work on arrays, so remove them ...
326: while (c.isArray()) {
327: c = c.getComponentType();
328: }
329: // extract the package ...
330: final String baseName = c.getName();
331: final int index = baseName.lastIndexOf('.');
332: if (index == -1) {
333: return name;
334: }
335:
336: final String pkgName = baseName.substring(0, index);
337: return pkgName.replace('.', '/') + "/" + name;
338: }
339:
340: /**
341: * Returns the inputstream for the resource specified by the
342: * <strong>absolute</strong> name.
343: *
344: * @param name the name of the resource
345: * @param context the source class
346: * @return the url of the resource or null, if not found.
347: */
348: public static InputStream getResourceAsStream(final String name,
349: final Class context) {
350: final URL url = getResource(name, context);
351: if (url == null) {
352: return null;
353: }
354:
355: try {
356: return url.openStream();
357: } catch (IOException e) {
358: return null;
359: }
360: }
361:
362: /**
363: * Returns the inputstream for the resource specified by the
364: * <strong>relative</strong> name.
365: *
366: * @param name the name of the resource relative to the given class
367: * @param context the source class
368: * @return the url of the resource or null, if not found.
369: */
370: public static InputStream getResourceRelativeAsStream(
371: final String name, final Class context) {
372: final URL url = getResourceRelative(name, context);
373: if (url == null) {
374: return null;
375: }
376:
377: try {
378: return url.openStream();
379: } catch (IOException e) {
380: return null;
381: }
382: }
383:
384: /**
385: * Tries to create a new instance of the given class. This is a short cut
386: * for the common bean instantiation code.
387: *
388: * @param className the class name as String, never null.
389: * @param source the source class, from where to get the classloader.
390: * @return the instantiated object or null, if an error occured.
391: */
392: public static Object loadAndInstantiate(final String className,
393: final Class source) {
394: try {
395: final ClassLoader loader = getClassLoader(source);
396: final Class c = loader.loadClass(className);
397: return c.newInstance();
398: } catch (Exception e) {
399: return null;
400: }
401: }
402:
403: /**
404: * Tries to create a new instance of the given class. This is a short cut
405: * for the common bean instantiation code. This method is a type-safe method
406: * and will not instantiate the class unless it is an instance of the given
407: * type.
408: *
409: * @param className the class name as String, never null.
410: * @param source the source class, from where to get the classloader.
411: * @return the instantiated object or null, if an error occured.
412: */
413: public static Object loadAndInstantiate(final String className,
414: final Class source, final Class type) {
415: try {
416: final ClassLoader loader = getClassLoader(source);
417: final Class c = loader.loadClass(className);
418: if (type.isAssignableFrom(c)) {
419: return c.newInstance();
420: }
421: } catch (Exception e) {
422: return null;
423: }
424: return null;
425: }
426:
427: public static boolean isJDK14() {
428: final ClassLoader loader = getClassLoader(ObjectUtilities.class);
429: if (loader != null) {
430: try {
431: loader.loadClass("java.util.RandomAccess");
432: return true;
433: } catch (ClassNotFoundException e) {
434: return false;
435: } catch (Exception e) {
436: // do nothing, but do not crash ...
437: }
438: }
439: // OK, the quick and dirty, but secure way failed. Lets try it
440: // using the standard way.
441: try {
442: final String version = System
443: .getProperty("java.vm.specification.version");
444: // parse the beast...
445: if (version == null) {
446: return false;
447: }
448:
449: String[] versions = parseVersions(version);
450: String[] target = new String[] { "1", "4" };
451: return (ArrayUtilities.compareVersionArrays(versions,
452: target) >= 0);
453: } catch (Exception e) {
454: return false;
455: }
456: }
457:
458: private static String[] parseVersions(String version) {
459: if (version == null) {
460: return new String[0];
461: }
462:
463: final ArrayList versions = new ArrayList();
464: StringTokenizer strtok = new StringTokenizer(version, ".");
465: while (strtok.hasMoreTokens()) {
466: versions.add(strtok.nextToken());
467: }
468: return (String[]) versions.toArray(new String[versions.size()]);
469: }
470: }
|