An utility class to ease up using property-file resource bundles. : Properties « Collections « Java Tutorial






/* 
 * 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()]);
     }
 }








9.34.Properties
9.34.1.Setting and Getting Elements
9.34.2.using properties
9.34.3.Getting property by String key value
9.34.4.Getting a key List from Properties
9.34.5.Loading and Saving properties
9.34.6.Use store() to save the properties
9.34.7.List Properties to a print stream or print writer
9.34.8.Using Enumeration to loop through Properties
9.34.9.Put value to a Property list.
9.34.10.Sort Properties when saving
9.34.11.Sorts a property list and turns the sorted list into a string.
9.34.12.Sorts property list and print out each key=value pair prepended with specific indentation.
9.34.13.Load a properties file in the classpath
9.34.14.A Properties file stored in a JAR can be loaded this way
9.34.15.Load a properties file in the startup directory
9.34.16.Have a multi-line value in a properties file
9.34.17.Use XML with Properties
9.34.18.Store properties as XML file
9.34.19.Getting and Setting Properties
9.34.20.Convert a Properties list into a map.
9.34.21.To read a Properties file via an Applet
9.34.22.Read system property as an integer
9.34.23.Read a set of properties from the received input stream, strip off any excess white space that exists in those property values,
9.34.24.Property access utility methods
9.34.25.An utility class to ease up using property-file resource bundles.
9.34.26.Copy a set of properties from one Property to another.
9.34.27.Create Properties from String array
9.34.28.Gets strong-type-value property from a standard Properties
9.34.29.Merge Properties Into Map
9.34.30.Property Loader
9.34.31.Returns a Properties object matching the given node
9.34.32.The properties iterator iterates over a set of enumerated properties.
9.34.33.Use a default property list.