Mega Code Archive

 
Categories / Java / Development Class
 

An utility class to ease up using property-file resource bundles

/*   * JCommon : a free general purpose class library for the Java(tm) platform  *   *  * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.  *  * Project Info:  http://www.jfree.org/jcommon/index.html  *  * This library is free software; you can redistribute it and/or modify it  * under the terms of the GNU Lesser General Public License as published by  * the Free Software Foundation; either version 2.1 of the License, or  * (at your option) any later version.  *  * This library is distributed in the hope that it will be useful, but  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public  * License for more details.  *  * You should have received a copy of the GNU Lesser General Public  * License along with this library; if not, write to the Free Software  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,  * USA.  *  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.  * in the United States and other countries.]  *  * ---------------------  * ReadOnlyIterator.java  * ---------------------  * (C)opyright 2003-2008, by Thomas Morgner and Contributors.  *  * Original Author:  Thomas Morgner;  * Contributor(s):   David Gilbert (for Object Refinery Limited);  *  * $Id: ResourceBundleSupport.java,v 1.12 2008/12/18 09:57:32 mungady Exp $  *  * Changes  * -------  * 18-Dec-2008 : Use ResourceBundleWrapper - see JFreeChart patch 1607918 by  *               Jess Thrysoee (DG);  *  */ import java.awt.Image; import java.awt.Toolkit; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; import java.net.URLClassLoader; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.StringTokenizer; import java.util.TreeMap; import java.util.TreeSet; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JMenu; import javax.swing.KeyStroke; import sun.rmi.runtime.Log; /**  * An utility class to ease up using property-file resource bundles.  * <p/>  * The class support references within the resource bundle set to minimize the  * occurence of duplicate keys. References are given in the format:  * <pre>  * a.key.name=@referenced.key  * </pre>  * <p/>  * A lookup to a key in an other resource bundle should be written by  * <pre>  * a.key.name=@@resourcebundle_name@referenced.key  * </pre>  *  * @author Thomas Morgner  */ public class ResourceBundleSupport {   /**    * The resource bundle that will be used for local lookups.    */   private ResourceBundle resources;   /**    * A cache for string values, as looking up the cache is faster than looking    * up the value in the bundle.    */   private TreeMap cache;   /**    * The current lookup path when performing non local lookups. This prevents    * infinite loops during such lookups.    */   private TreeSet lookupPath;   /**    * The name of the local resource bundle.    */   private String resourceBase;   /**    * The locale for this bundle.    */   private Locale locale;   /**    * Creates a new instance.    *    * @param locale  the locale.    * @param baseName the base name of the resource bundle, a fully qualified    *                 class name    */   public ResourceBundleSupport(final Locale locale, final String baseName)   {     this(locale, ResourceBundleWrapper.getBundle(baseName, locale), baseName);   }   /**    * Creates a new instance.    *    * @param locale         the locale for which this resource bundle is    *                       created.    * @param resourceBundle the resourcebundle    * @param baseName       the base name of the resource bundle, a fully    *                       qualified class name    */   protected ResourceBundleSupport(final Locale locale,                                   final ResourceBundle resourceBundle,                                   final String baseName)   {     if (locale == null)     {       throw new NullPointerException("Locale must not be null");     }     if (resourceBundle == null)     {       throw new NullPointerException("Resources must not be null");     }     if (baseName == null)     {       throw new NullPointerException("BaseName must not be null");     }     this.locale = locale;     this.resources = resourceBundle;     this.resourceBase = baseName;     this.cache = new TreeMap();     this.lookupPath = new TreeSet();   }   /**    * Creates a new instance.    *    * @param locale         the locale for which the resource bundle is    *                       created.    * @param resourceBundle the resourcebundle    */   public ResourceBundleSupport(final Locale locale,                                final ResourceBundle resourceBundle)   {     this(locale, resourceBundle, resourceBundle.toString());   }   /**    * Creates a new instance.    *    * @param baseName the base name of the resource bundle, a fully qualified    *                 class name    */   public ResourceBundleSupport(final String baseName)   {     this(Locale.getDefault(), ResourceBundleWrapper.getBundle(baseName),             baseName);   }   /**    * Creates a new instance.    *    * @param resourceBundle the resourcebundle    * @param baseName       the base name of the resource bundle, a fully    *                       qualified class name    */   protected ResourceBundleSupport(final ResourceBundle resourceBundle,                                   final String baseName)   {     this(Locale.getDefault(), resourceBundle, baseName);   }   /**    * Creates a new instance.    *    * @param resourceBundle the resourcebundle    */   public ResourceBundleSupport(final ResourceBundle resourceBundle)   {     this(Locale.getDefault(), resourceBundle, resourceBundle.toString());   }   /**    * The base name of the resource bundle.    *    * @return the resource bundle's name.    */   protected final String getResourceBase()   {     return this.resourceBase;   }   /**    * Gets a string for the given key from this resource bundle or one of its    * parents. If the key is a link, the link is resolved and the referenced    * string is returned instead.    *    * @param key the key for the desired string    * @return the string for the given key    * @throws NullPointerException     if <code>key</code> is <code>null</code>    * @throws MissingResourceException if no object for the given key can be    *                                  found    * @throws ClassCastException       if the object found for the given key is    *                                  not a string    */   public synchronized String getString(final String key)   {     final String retval = (String) this.cache.get(key);     if (retval != null)     {       return retval;     }     this.lookupPath.clear();     return internalGetString(key);   }   /**    * Performs the lookup for the given key. If the key points to a link the    * link is resolved and that key is looked up instead.    *    * @param key the key for the string    * @return the string for the given key    */   protected String internalGetString(final String key)   {     if (this.lookupPath.contains(key))     {       throw new MissingResourceException           ("InfiniteLoop in resource lookup",               getResourceBase(), this.lookupPath.toString());     }     final String fromResBundle = this.resources.getString(key);     if (fromResBundle.startsWith("@@"))     {       // global forward ...       final int idx = fromResBundle.indexOf('@', 2);       if (idx == -1)       {         throw new MissingResourceException             ("Invalid format for global lookup key.", getResourceBase(), key);       }       try       {         final ResourceBundle res = ResourceBundleWrapper.getBundle             (fromResBundle.substring(2, idx));         return res.getString(fromResBundle.substring(idx + 1));       }       catch (Exception e)       {         System.out.println("Error during global lookup:"+ e);         throw new MissingResourceException             ("Error during global lookup", getResourceBase(), key);       }     }     else if (fromResBundle.startsWith("@"))     {       // local forward ...       final String newKey = fromResBundle.substring(1);       this.lookupPath.add(key);       final String retval = internalGetString(newKey);       this.cache.put(key, retval);       return retval;     }     else     {       this.cache.put(key, fromResBundle);       return fromResBundle;     }   }   /**    * Returns an scaled icon suitable for buttons or menus.    *    * @param key   the name of the resource bundle key    * @param large true, if the image should be scaled to 24x24, or false for    *              16x16    * @return the icon.    */   public Icon getIcon(final String key, final boolean large)   {     final String name = getString(key);     return createIcon(name, true, large);   }   /**    * Returns an unscaled icon.    *    * @param key the name of the resource bundle key    * @return the icon.    */   public Icon getIcon(final String key)   {     final String name = getString(key);     return createIcon(name, false, false);   }   /**    * Returns the mnemonic stored at the given resourcebundle key. The mnemonic    * should be either the symbolic name of one of the KeyEvent.VK_* constants    * (without the 'VK_') or the character for that key.    * <p/>    * For the enter key, the resource bundle would therefore either contain    * "ENTER" or "\n".    * <pre>    * a.resourcebundle.key=ENTER    * an.other.resourcebundle.key=\n    * </pre>    *    * @param key the resourcebundle key    * @return the mnemonic    */   public Integer getMnemonic(final String key)   {     final String name = getString(key);     return createMnemonic(name);   }   /**    * Returns an optional mnemonic.    *    * @param key  the key.    *    * @return The mnemonic.    */   public Integer getOptionalMnemonic(final String key)   {     final String name = getString(key);     if (name != null && name.length() > 0)     {       return createMnemonic(name);     }     return null;   }   /**    * Returns the keystroke stored at the given resourcebundle key.    * <p/>    * The keystroke will be composed of a simple key press and the plattform's    * MenuKeyMask.    * <p/>    * The keystrokes character key should be either the symbolic name of one of    * the KeyEvent.VK_* constants or the character for that key.    * <p/>    * For the 'A' key, the resource bundle would therefore either contain    * "VK_A" or "a".    * <pre>    * a.resourcebundle.key=VK_A    * an.other.resourcebundle.key=a    * </pre>    *    * @param key the resourcebundle key    * @return the mnemonic    * @see Toolkit#getMenuShortcutKeyMask()    */   public KeyStroke getKeyStroke(final String key)   {     return getKeyStroke(key, getMenuKeyMask());   }   /**    * Returns an optional key stroke.    *    * @param key  the key.    *    * @return The key stroke.    */   public KeyStroke getOptionalKeyStroke(final String key)   {     return getOptionalKeyStroke(key, getMenuKeyMask());   }   /**    * Returns the keystroke stored at the given resourcebundle key.    * <p/>    * The keystroke will be composed of a simple key press and the given    * KeyMask. If the KeyMask is zero, a plain Keystroke is returned.    * <p/>    * The keystrokes character key should be either the symbolic name of one of    * the KeyEvent.VK_* constants or the character for that key.    * <p/>    * For the 'A' key, the resource bundle would therefore either contain    * "VK_A" or "a".    * <pre>    * a.resourcebundle.key=VK_A    * an.other.resourcebundle.key=a    * </pre>    *    * @param key the resourcebundle key.    * @param mask  the mask.    *    * @return the mnemonic    * @see Toolkit#getMenuShortcutKeyMask()    */   public KeyStroke getKeyStroke(final String key, final int mask)   {     final String name = getString(key);     return KeyStroke.getKeyStroke(createMnemonic(name).intValue(), mask);   }   /**    * Returns an optional key stroke.    *    * @param key  the key.    * @param mask  the mask.    *    * @return The key stroke.    */   public KeyStroke getOptionalKeyStroke(final String key, final int mask)   {     final String name = getString(key);     if (name != null && name.length() > 0)     {       return KeyStroke.getKeyStroke(createMnemonic(name).intValue(), mask);     }     return null;   }   /**    * Returns a JMenu created from a resource bundle definition.    * <p/>    * The menu definition consists of two keys, the name of the menu and the    * mnemonic for that menu. Both keys share a common prefix, which is    * extended by ".name" for the name of the menu and ".mnemonic" for the    * mnemonic.    * <p/>    * <pre>    * # define the file menu    * menu.file.name=File    * menu.file.mnemonic=F    * </pre>    * The menu definition above can be used to create the menu by calling    * <code>createMenu ("menu.file")</code>.    *    * @param keyPrefix the common prefix for that menu    * @return the created menu    */   public JMenu createMenu(final String keyPrefix)   {     final JMenu retval = new JMenu();     retval.setText(getString(keyPrefix + ".name"));     retval.setMnemonic(getMnemonic(keyPrefix + ".mnemonic").intValue());     return retval;   }   /**    * Returns a URL pointing to a resource located in the classpath. The    * resource is looked up using the given key.    * <p/>    * Example: The load a file named 'logo.gif' which is stored in a java    * package named 'org.jfree.resources':    * <pre>    * mainmenu.logo=org/jfree/resources/logo.gif    * </pre>    * The URL for that file can be queried with: <code>getResource("mainmenu.logo");</code>.    *    * @param key the key for the resource    * @return the resource URL    */   public URL getResourceURL(final String key)   {     final String name = getString(key);     final URL in = ObjectUtilities.getResource(name, ResourceBundleSupport.class);     if (in == null)     {       System.out.println("Unable to find file in the class path: " + name + "; key=" + key);     }     return in;   }   /**    * Attempts to load an image from classpath. If this fails, an empty image    * icon is returned.    *    * @param resourceName the name of the image. The name should be a global    *                     resource name.    * @param scale        true, if the image should be scaled, false otherwise    * @param large        true, if the image should be scaled to 24x24, or    *                     false for 16x16    * @return the image icon.    */   private ImageIcon createIcon(final String resourceName, final boolean scale,                                final boolean large)   {     final URL in = ObjectUtilities.getResource(resourceName, ResourceBundleSupport.class);     ;     if (in == null)     {       System.out.println("Unable to find file in the class path: " + resourceName);       return new ImageIcon(createTransparentImage(1, 1));     }     final Image img = Toolkit.getDefaultToolkit().createImage(in);     if (img == null)     {       System.out.println("Unable to instantiate the image: " + resourceName);       return new ImageIcon(createTransparentImage(1, 1));     }     if (scale)     {       if (large)       {         return new ImageIcon(img.getScaledInstance(24, 24, Image.SCALE_SMOOTH));       }       return new ImageIcon(img.getScaledInstance(16, 16, Image.SCALE_SMOOTH));     }     return new ImageIcon(img);   }   /**    * Creates the Mnemonic from the given String. The String consists of the    * name of the VK constants of the class KeyEvent without VK_*.    *    * @param keyString the string    * @return the mnemonic as integer    */   private Integer createMnemonic(final String keyString)   {     if (keyString == null)     {       throw new NullPointerException("Key is null.");     }     if (keyString.length() == 0)     {       throw new IllegalArgumentException("Key is empty.");     }     int character = keyString.charAt(0);     if (keyString.startsWith("VK_"))     {       try       {         final Field f = KeyEvent.class.getField(keyString);         final Integer keyCode = (Integer) f.get(null);         character = keyCode.intValue();       }       catch (Exception nsfe)       {         // ignore the exception ...       }     }     return new Integer(character);   }   /**    * Returns the plattforms default menu shortcut keymask.    *    * @return the default key mask.    */   private int getMenuKeyMask()   {     try     {       return Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();     }     catch (UnsupportedOperationException he)     {       // headless exception extends UnsupportedOperation exception,       // but the HeadlessException is not defined in older JDKs...       return InputEvent.CTRL_MASK;     }   }   /**    * Creates a transparent image.  These can be used for aligning menu items.    *    * @param width  the width.    * @param height the height.    * @return the created transparent image.    */   private BufferedImage createTransparentImage(final int width,                                                final int height)   {     final BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);     final int[] data = img.getRGB(0, 0, width, height, null, 0, width);     Arrays.fill(data, 0x00000000);     img.setRGB(0, 0, width, height, data, 0, width);     return img;   }   /**    * Creates a transparent icon. The Icon can be used for aligning menu    * items.    *    * @param width  the width of the new icon    * @param height the height of the new icon    * @return the created transparent icon.    */   public Icon createTransparentIcon(final int width, final int height)   {     return new ImageIcon(createTransparentImage(width, height));   }   /**    * Formats the message stored in the resource bundle (using a    * MessageFormat).    *    * @param key       the resourcebundle key    * @param parameter the parameter for the message    * @return the formated string    */   public String formatMessage(final String key, final Object parameter)   {     return formatMessage(key, new Object[]{parameter});   }   /**    * Formats the message stored in the resource bundle (using a    * MessageFormat).    *    * @param key  the resourcebundle key    * @param par1 the first parameter for the message    * @param par2 the second parameter for the message    * @return the formated string    */   public String formatMessage(final String key,                               final Object par1,                               final Object par2)   {     return formatMessage(key, new Object[]{par1, par2});   }   /**    * Formats the message stored in the resource bundle (using a    * MessageFormat).    *    * @param key        the resourcebundle key    * @param parameters the parameter collection for the message    * @return the formated string    */   public String formatMessage(final String key, final Object[] parameters)   {     final MessageFormat format = new MessageFormat(getString(key));     format.setLocale(getLocale());     return format.format(parameters);   }   /**    * Returns the current locale for this resource bundle.    *    * @return the locale.    */   public Locale getLocale()   {     return this.locale;   } } /*   * JCommon : a free general purpose class library for the Java(tm) platform  *   *  * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.  *  * Project Info:  http://www.jfree.org/jcommon/index.html  *  * This library is free software; you can redistribute it and/or modify it  * under the terms of the GNU Lesser General Public License as published by  * the Free Software Foundation; either version 2.1 of the License, or  * (at your option) any later version.  *  * This library is distributed in the hope that it will be useful, but  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public  * License for more details.  *  * You should have received a copy of the GNU Lesser General Public  * License along with this library; if not, write to the Free Software  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,  * USA.  *  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.  * in the United States and other countries.]  *  * --------------------------  * ResourceBundleWrapper.java  * --------------------------  * (C)opyright 2008, by Jess Thrysoee and Contributors.  *  * Original Author:  Jess Thrysoee;  * Contributor(s):   David Gilbert (for Object Refinery Limited);  *  * Changes  * -------  * 18-Dec-2008 : Version 1 (JT);  *  */ /**  * Wrapper of ResourceBundle.getBundle() methods. This wrapper is introduced to  * avoid a dramatic performance penalty by superfluous resource (and classes  * loaded by Class.forName) lookups on web server in applets.  *  * <pre>  * public class AppletC extends javax.swing.JApplet {  *    public void init() {  *       ResourceBundleWrapper.removeCodeBase(getCodeBase(),  *               (URLClassLoader) getClass().getClassLoader());  *    ...  * </pre>  *  * @see <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4243379">  *               Bug ID: 4243379</a>  * @see <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4668479">  *               Bug ID: 4668479</a>  *  * @since 1.0.15  */  class ResourceBundleWrapper {     /**      * A special class loader with no code base lookup.  This field may be      * <code>null</code> (the field is only initialised if removeCodeBase() is      * called from an applet.      */     private static URLClassLoader noCodeBaseClassLoader;     /**      * Private constructor.      */     private ResourceBundleWrapper() {         // all methods are static, no need to instantiate     }     /**      * Instantiate a {@link URLClassLoader} for resource lookups where the      * codeBase URL is removed.  This method is typically called from an      * applet's init() method.  If this method is never called, the      * <code>getBundle()</code> methods map to the standard      * {@link ResourceBundle} lookup methods.      *      * @param codeBase  the codeBase URL.      * @param urlClassLoader  the class loader.      */     public static void removeCodeBase(URL codeBase,             URLClassLoader urlClassLoader) {         List urlsNoBase = new ArrayList();         URL[] urls = urlClassLoader.getURLs();         for (int i = 0; i < urls.length; i++) {             if (! urls[i].sameFile(codeBase)) {                 urlsNoBase.add(urls[i]);             }         }         // substitute the filtered URL list         URL[] urlsNoBaseArray = (URL[]) urlsNoBase.toArray(new URL[0]);         noCodeBaseClassLoader = URLClassLoader.newInstance(urlsNoBaseArray);     }     /**      * Finds and returns the specified resource bundle.      *      * @param baseName  the base name.      *      * @return The resource bundle.      */     public static final ResourceBundle getBundle(String baseName) {         // the noCodeBaseClassLoader is configured by a call to the         // removeCodeBase() method, typically in the init() method of an         // applet...         if (noCodeBaseClassLoader != null) {             return ResourceBundle.getBundle(baseName, Locale.getDefault(),                     noCodeBaseClassLoader);         }         else {             // standard ResourceBundle behaviour             return ResourceBundle.getBundle(baseName);         }     }     /**      * Finds and returns the specified resource bundle.      *      * @param baseName  the base name.      * @param locale  the locale.      *      * @return The resource bundle.      */     public static final ResourceBundle getBundle(String baseName,             Locale locale) {         // the noCodeBaseClassLoader is configured by a call to the         // removeCodeBase() method, typically in the init() method of an         // applet...         if (noCodeBaseClassLoader != null) {             return ResourceBundle.getBundle(baseName, locale,                     noCodeBaseClassLoader);         }         else {             // standard ResourceBundle behaviour             return ResourceBundle.getBundle(baseName, locale);         }     }     /**      * Maps directly to <code>ResourceBundle.getBundle(baseName, locale,      * loader)</code>.      *      * @param baseName  the base name.      * @param locale  the locale.      * @param loader  the class loader.      *      * @return The resource bundle.      */     public static ResourceBundle getBundle(String baseName, Locale locale,             ClassLoader loader) {         return ResourceBundle.getBundle(baseName, locale, loader);     } }  /*    * JCommon : a free general purpose class library for the Java(tm) platform   *    *   * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.   *   * Project Info:  http://www.jfree.org/jcommon/index.html   *   * This library is free software; you can redistribute it and/or modify it   * under the terms of the GNU Lesser General Public License as published by   * the Free Software Foundation; either version 2.1 of the License, or   * (at your option) any later version.   *   * This library is distributed in the hope that it will be useful, but   * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY   * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public   * License for more details.   *   * You should have received a copy of the GNU Lesser General Public   * License along with this library; if not, write to the Free Software   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,   * USA.   *   * [Java is a trademark or registered trademark of Sun Microsystems, Inc.   * in the United States and other countries.]   *   * ---------------------   * ObjectUtilitiess.java   * ---------------------   * (C) Copyright 2003-2005, by Object Refinery Limited.   *   * Original Author:  David Gilbert (for Object Refinery Limited);   * Contributor(s):   -;   *   * $Id: ObjectUtilities.java,v 1.21 2008/09/10 09:24:41 mungady Exp $   *   * Changes   * -------   * 25-Mar-2003 : Version 1 (DG);   * 15-Sep-2003 : Fixed bug in clone(List) method (DG);   * 25-Nov-2004 : Modified clone(Object) method to fail with objects that   *               cannot be cloned, added new deepClone(Collection) method.   *               Renamed ObjectUtils --> ObjectUtilities (DG);   * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);   * 18-Aug-2005 : Added casts to suppress compiler warnings, as suggested in   *               patch 1260622 (DG);   *   */  /**   * A collection of useful static utility methods for handling classes and object   * instantiation.   *   * @author Thomas Morgner   */  final class ObjectUtilities {      /**       * A constant for using the TheadContext as source for the classloader.       */      public static final String THREAD_CONTEXT = "ThreadContext";      /**       * A constant for using the ClassContext as source for the classloader.       */      public static final String CLASS_CONTEXT = "ClassContext";      /**       * By default use the thread context.       */      private static String classLoaderSource = THREAD_CONTEXT;      /**       * The custom classloader to be used (if not null).       */      private static ClassLoader classLoader;      /**       * Default constructor - private.       */      private ObjectUtilities() {      }      /**       * Returns the internal configuration entry, whether the classloader of       * the thread context or the context classloader should be used.       *       * @return the classloader source, either THREAD_CONTEXT or CLASS_CONTEXT.       */      public static String getClassLoaderSource() {          return classLoaderSource;      }      /**       * Defines the internal configuration entry, whether the classloader of       * the thread context or the context classloader should be used.       * <p/>       * This setting can only be defined using the API, there is no safe way       * to put this into an external configuration file.       *       * @param classLoaderSource the classloader source,       *                          either THREAD_CONTEXT or CLASS_CONTEXT.       */      public static void setClassLoaderSource(final String classLoaderSource) {          ObjectUtilities.classLoaderSource = classLoaderSource;      }      /**       * Returns <code>true</code> if the two objects are equal OR both       * <code>null</code>.       *       * @param o1 object 1 (<code>null</code> permitted).       * @param o2 object 2 (<code>null</code> permitted).       * @return <code>true</code> or <code>false</code>.       */      public static boolean equal(final Object o1, final Object o2) {          if (o1 == o2) {              return true;          }          if (o1 != null) {              return o1.equals(o2);          }          else {              return false;          }      }      /**       * Returns a hash code for an object, or zero if the object is       * <code>null</code>.       *       * @param object the object (<code>null</code> permitted).       * @return The object's hash code (or zero if the object is       *         <code>null</code>).       */      public static int hashCode(final Object object) {          int result = 0;          if (object != null) {              result = object.hashCode();          }          return result;      }      /**       * Returns a clone of the specified object, if it can be cloned, otherwise       * throws a CloneNotSupportedException.       *       * @param object the object to clone (<code>null</code> not permitted).       * @return A clone of the specified object.       * @throws CloneNotSupportedException if the object cannot be cloned.       */      public static Object clone(final Object object)          throws CloneNotSupportedException {          if (object == null) {              throw new IllegalArgumentException("Null 'object' argument.");          }              try {                  final Method method = object.getClass().getMethod("clone",                          (Class[]) null);                  if (Modifier.isPublic(method.getModifiers())) {                      return method.invoke(object, (Object[]) null);                  }              }              catch (NoSuchMethodException e) {                System.out.println("Object without clone() method is impossible.");              }              catch (IllegalAccessException e) {                System.out.println("Object.clone(): unable to call method.");              }              catch (InvocationTargetException e) {                  System.out.println("Object without clone() method is impossible.");              }          throw new CloneNotSupportedException("Failed to clone.");      }      /**       * Returns a new collection containing clones of all the items in the       * specified collection.       *       * @param collection the collection (<code>null</code> not permitted).       * @return A new collection containing clones of all the items in the       *         specified collection.       * @throws CloneNotSupportedException if any of the items in the collection       *                                    cannot be cloned.       */      public static Collection deepClone(final Collection collection)          throws CloneNotSupportedException {          if (collection == null) {              throw new IllegalArgumentException("Null 'collection' argument.");          }          // all JDK-Collections are cloneable ...          // and if the collection is not clonable, then we should throw          // a CloneNotSupportedException anyway ...          final Collection result              = (Collection) ObjectUtilities.clone(collection);          result.clear();          final Iterator iterator = collection.iterator();          while (iterator.hasNext()) {              final Object item = iterator.next();              if (item != null) {                  result.add(clone(item));              }              else {                  result.add(null);              }          }          return result;      }      /**       * Returns the classloader, which was responsible for loading the given       * class.       *       * @param c the classloader, either an application class loader or the       *          boot loader.       * @return the classloader, never null.       * @throws SecurityException if the SecurityManager does not allow to grab       *                           the context classloader.       */      public static ClassLoader getClassLoader(final Class c) {          final String localClassLoaderSource;          synchronized(ObjectUtilities.class)          {            if (classLoader != null) {                return classLoader;            }            localClassLoaderSource = classLoaderSource;          }          if ("ThreadContext".equals(localClassLoaderSource)) {              final ClassLoader threadLoader = Thread.currentThread().getContextClassLoader();              if (threadLoader != null) {                  return threadLoader;              }          }          // Context classloader - do not cache ..          final ClassLoader applicationCL = c.getClassLoader();          if (applicationCL == null) {              return ClassLoader.getSystemClassLoader();          }          else {              return applicationCL;          }      }      /**       * Returns the resource specified by the <strong>absolute</strong> name.       *       * @param name the name of the resource       * @param c    the source class       * @return the url of the resource or null, if not found.       */      public static URL getResource(final String name, final Class c) {          final ClassLoader cl = getClassLoader(c);          if (cl == null) {              return null;          }          return cl.getResource(name);      }      /**       * Returns the resource specified by the <strong>relative</strong> name.       *       * @param name the name of the resource relative to the given class       * @param c    the source class       * @return the url of the resource or null, if not found.       */      public static URL getResourceRelative(final String name, final Class c) {          final ClassLoader cl = getClassLoader(c);          final String cname = convertName(name, c);          if (cl == null) {              return null;          }          return cl.getResource(cname);      }      /**       * Transform the class-relative resource name into a global name by       * appending it to the classes package name. If the name is already a       * global name (the name starts with a "/"), then the name is returned       * unchanged.       *       * @param name the resource name       * @param c    the class which the resource is relative to       * @return the tranformed name.       */      private static String convertName(final String name, Class c) {          if (name.startsWith("/")) {              // strip leading slash..              return name.substring(1);          }          // we cant work on arrays, so remove them ...          while (c.isArray()) {              c = c.getComponentType();          }          // extract the package ...          final String baseName = c.getName();          final int index = baseName.lastIndexOf('.');          if (index == -1) {              return name;          }          final String pkgName = baseName.substring(0, index);          return pkgName.replace('.', '/') + "/" + name;      }      /**       * Returns the inputstream for the resource specified by the       * <strong>absolute</strong> name.       *       * @param name the name of the resource       * @param context the source class       * @return the url of the resource or null, if not found.       */      public static InputStream getResourceAsStream(final String name,                                                    final Class context) {          final URL url = getResource(name, context);          if (url == null) {              return null;          }          try {              return url.openStream();          }          catch (IOException e) {              return null;          }      }      /**       * Returns the inputstream for the resource specified by the       * <strong>relative</strong> name.       *       * @param name the name of the resource relative to the given class       * @param context the source class       * @return the url of the resource or null, if not found.       */      public static InputStream getResourceRelativeAsStream          (final String name, final Class context) {          final URL url = getResourceRelative(name, context);          if (url == null) {              return null;          }          try {              return url.openStream();          }          catch (IOException e) {              return null;          }      }      /**       * Tries to create a new instance of the given class. This is a short cut       * for the common bean instantiation code.       *       * @param className the class name as String, never null.       * @param source    the source class, from where to get the classloader.       * @return the instantiated object or null, if an error occured.       */      public static Object loadAndInstantiate(final String className,                                              final Class source) {          try {              final ClassLoader loader = getClassLoader(source);              final Class c = loader.loadClass(className);              return c.newInstance();          }          catch (Exception e) {              return null;          }      }      /**       * Tries to create a new instance of the given class. This is a short cut       * for the common bean instantiation code. This method is a type-safe method       * and will not instantiate the class unless it is an instance of the given       * type.       *       * @param className the class name as String, never null.       * @param source    the source class, from where to get the classloader.       * @param type  the type.       * @return the instantiated object or null, if an error occurred.       */      public static Object loadAndInstantiate(final String className,                                              final Class source,                                              final Class type) {          try {              final ClassLoader loader = getClassLoader(source);              final Class c = loader.loadClass(className);              if (type.isAssignableFrom(c)) {                  return c.newInstance();              }          }          catch (Exception e) {              return null;          }          return null;      }      /**       * Returns <code>true</code> if this is version 1.4 or later of the       * Java runtime.       *       * @return A boolean.       */      public static boolean isJDK14() {          return false;      }      private static String[] parseVersions (String version)      {        if (version == null)        {          return new String[0];        }        final ArrayList versions = new ArrayList();        final StringTokenizer strtok = new StringTokenizer(version, ".");        while (strtok.hasMoreTokens())        {          versions.add (strtok.nextToken());        }        return (String[]) versions.toArray(new String[versions.size()]);      }  }