You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@avalon.apache.org by ne...@apache.org on 2001/10/30 00:10:25 UTC

cvs commit: jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n ResourceBundleSelector.java ResourceBundle.java PrefixedMapper.java LocaleToUriMapper.java FlatXMLResourceBundle.java DirectoryMapper.java DefaultMapper.java AbstractResourceBundle.java XMLResourceBundle.java XMLResourceBundleFactory.java

neeme       01/10/29 15:10:25

  Modified:    src/scratchpad/org/apache/avalon/excalibur/i18n
                        XMLResourceBundle.java
  Added:       src/scratchpad/org/apache/avalon/excalibur/i18n
                        ResourceBundleSelector.java ResourceBundle.java
                        PrefixedMapper.java LocaleToUriMapper.java
                        FlatXMLResourceBundle.java DirectoryMapper.java
                        DefaultMapper.java AbstractResourceBundle.java
  Removed:     src/scratchpad/org/apache/avalon/excalibur/i18n
                        XMLResourceBundleFactory.java
  Log:
  Made some major updates, short summary of the changes:
  ----------
  * added org.apache.avalon.excalibur.i18n.ResourceBundle "behavioral" interface
  * moved all non XMLResourceBundle specific code to AbstractResourceBundle
  * made XMLResourceBundle methods more "extendable-friendly" (private vs. protected methods)
  * added FlatXMLResourceBundle class (extends XMLResourceBundle) that provides support for bundles with the "flat" XML schema that Cocoon i18n transformer currently uses. this should also work as an example of how to extend the base implementations.
  * added getLastModified() method that Torsten mentioned.
  ---
  * added org.apache.avalon.excalibur.i18n.LocaleToUriMapper "behavioral" interface, to make it easy to implement different mappings
  * added abstract base-class PrefixedMapper that provides base for different "prefixed" implementations (prefix = root directory under current implementations, but not always).
  * moved the default mapping implementation code from XMLResourceFactory to DefaultMapper class (that extends PrefixedMapped)
  * added one more mapping implementation that I had in one corner of by disk, DirectoryMapper
  ---
  * renamed XMLResourceBundleFactory into ResourceBundleSelector as all the XMLResourceBundle specific code was moved out from there and it is generic enough now.
  ----------
  
  Revision  Changes    Path
  1.7       +107 -239  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/XMLResourceBundle.java
  
  Index: XMLResourceBundle.java
  ===================================================================
  RCS file: /home/cvs/jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/XMLResourceBundle.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- XMLResourceBundle.java	2001/10/25 16:09:03	1.6
  +++ XMLResourceBundle.java	2001/10/29 23:10:24	1.7
  @@ -8,97 +8,91 @@
   package org.apache.avalon.excalibur.i18n;
   
   /** JDK classes **/
  -import java.io.IOException;
  -import java.util.Map;
  -import java.util.HashMap;
  -import java.util.Hashtable;
  -import java.util.Locale;
  -import java.util.Enumeration;
  -import java.util.ResourceBundle;
  -import java.util.MissingResourceException;
  -
  -/** W3C DOM classes **/
  +import org.apache.avalon.framework.activity.Disposable;
  +import org.apache.avalon.framework.component.Composable;
  +import org.apache.avalon.framework.component.Component;
  +import org.apache.avalon.framework.component.ComponentManager;
  +import org.apache.avalon.excalibur.xml.xpath.XPathProcessor;
   import org.w3c.dom.Document;
  +import org.w3c.dom.NamedNodeMap;
   import org.w3c.dom.Node;
   import org.w3c.dom.NodeList;
  -import org.w3c.dom.NamedNodeMap;
  -
  -/** SAX classes **/
   import org.xml.sax.SAXException;
   
  -/** Parser classes **/
   import javax.xml.parsers.DocumentBuilder;
   import javax.xml.parsers.DocumentBuilderFactory;
   import javax.xml.parsers.ParserConfigurationException;
  -
  -/** XPath classes **/
  -import org.apache.xpath.XPathAPI;
  -
  -/** TRaX classes **/
  -import javax.xml.transform.TransformerException;
  -
  -/** Avalon classes **/
  -import org.apache.avalon.framework.component.Component;
  -import org.apache.avalon.framework.logger.Loggable;
  -import org.apache.log.Logger;
  +import java.io.IOException;
  +import java.util.HashMap;
  +import java.util.Locale;
  +import java.util.Map;
  +import java.util.MissingResourceException;
   
   /**
    * @author <a href="mailto:mengelhart@earthtrip.com">Mike Engelhart</a>
    * @author <a href="mailto:neeme@one.lv">Neeme Praks</a>
    * @author <a href="mailto:oleg@one.lv">Oleg Podolsky</a>
  - * @version $Id: XMLResourceBundle.java,v 1.6 2001/10/25 16:09:03 bloritsch Exp $
  + * @version $Id: XMLResourceBundle.java,v 1.7 2001/10/29 23:10:24 neeme Exp $
    */
  -public class XMLResourceBundle
  -    extends ResourceBundle
  -    implements Loggable, Component
  -{
  +public class XMLResourceBundle extends AbstractResourceBundle
  +implements Disposable, Composable {
  +
       /** DOM factory */
  -    protected static DocumentBuilderFactory docfactory =
  -        DocumentBuilderFactory.newInstance();
  +    private static DocumentBuilderFactory docfactory =
  +    	DocumentBuilderFactory.newInstance();
   
       /** Cache for storing string values for existing XPaths */
  -    private Hashtable cache = new Hashtable();
  +    private Map cache = new HashMap();
   
       /** Cache for storing non-existing XPaths */
       private Map cacheNotFound = new HashMap();
   
  -    /** Bundle name */
  -    private String name = "";
  -
       /** DOM-tree containing the bundle content */
       private Document doc;
   
  -    /** Locale of the bundle */
  -    private Locale locale;
  +    /** Component Manager */
  +    protected ComponentManager manager = null;
   
  -    /** Parent of the current bundle */
  -    protected XMLResourceBundle parent = null;
  +    /** XPath Processor */
  +    private XPathProcessor processor = null;
   
  -    /** Logger */
  -    protected Logger logger;
  +    public void compose(ComponentManager manager) {
  +        this.manager = manager;
  +        try {
  +            this.processor = (XPathProcessor)this.manager.lookup(XPathProcessor.ROLE);
  +        } catch (Exception e) {
  +            logger.error("cannot obtain XPathProcessor", e);
  +        }
  +    }
   
       /**
        * Initalize the bundle
        *
  -     * @param name              name of the bundle
        * @param fileName          name of the XML source file
  -     * @param locale            locale
  -     * @param parent            parent bundle of this bundle
        * @param cacheAtStartup    cache all the keys when constructing?
        * @exception IOException   if an IO error occurs while reading the file
        * @exception ParserConfigurationException if no parser is configured
        * @exception SAXException  if an error occurs while parsing the file
  +     */
  +    public void init(String fileName, boolean cacheAtStartup)
  +    throws Exception {
  +        if (logger.isInfoEnabled()) logger.info("Constructing XMLResourceBundle: " + getName() + ", locale: " + getLocale());
  +        this.doc = loadResourceBundle(fileName);
  +        if (cacheAtStartup) cacheAll(doc.getDocumentElement(), "");
  +    }
  +
  +    /**
  +     * Convert the &quot;user view&quot; of the lookup key to the
  +     * &quot;system view&quot;.
  +     * Current implementation coverts dots into slashes, so common Java property
  +     * names become XPath paths.
  +     * E.g: this.is.java.property.key --> /this/is/java/property/key
  +     *
  +     * @param key           user key
  +     * @return              system key
        */
  -    public void init(String name, String fileName, Locale locale, XMLResourceBundle parent, boolean cacheAtStartup)
  -        throws IOException, ParserConfigurationException, SAXException
  -    {
  -        if (logger.isInfoEnabled()) logger.info("Constructing XMLResourceBundle: " + name + ", locale: " + locale);
  -        this.name = name;
  -        this.doc = loadResourceBundle(fileName, logger);
  -        this.locale = locale;
  -        this.parent = parent;
  -        if (cacheAtStartup)
  -            cacheAll(doc.getDocumentElement(), "");
  +    public String convertKey(String userKey) {
  +        return '/' + userKey.replace('.', '/');
       }
   
       /**
  @@ -110,31 +104,11 @@
        * @exception ParserConfigurationException if no parser is configured
        * @exception SAXException  if an error occurs while parsing the file
        */
  -    protected static Document loadResourceBundle(String fileName, Logger logger)
  -        throws IOException, ParserConfigurationException, SAXException
  -    {
  +    protected static Document loadResourceBundle(String fileName)
  +    throws IOException, ParserConfigurationException, SAXException {
           DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
           DocumentBuilder builder = factory.newDocumentBuilder();
  -
  -        try
  -        {
  -            return builder.parse(fileName);
  -        }
  -        catch (SAXException se)
  -        {
  -            logger.warn("Could not use the file '" + fileName + "' returning a new Document object instead.", se);
  -            return builder.newDocument();
  -        }
  -    }
  -
  -    /**
  -     * Gets the name of the bundle.
  -     *
  -     * @return the name
  -     */
  -    public String getName()
  -    {
  -        return this.name;
  +        return builder.parse(fileName);
       }
   
       /**
  @@ -142,30 +116,18 @@
        *
        * @return the DOM tree
        */
  -    public Document getDocument()
  -    {
  +    public Document getDocument() {
           return this.doc;
       }
   
       /**
  -     * Gets the locale of the bundle.
  -     *
  -     * @return the locale
  -     */
  -    public Locale getLocale()
  -    {
  -        return locale;
  -    }
  -
  -    /**
        * Does the &quot;key-cache&quot; contain the value with such key?
        *
        * @param   key     the key to the value to be returned
        * @return  true if contains, false otherwise
        */
  -    private boolean cacheContains(String key)
  -    {
  -        if (logger.isDebugEnabled()) logger.debug(name + ": cache contains: " + key);
  +    protected boolean cacheContains(String key) {
  +        if (logger.isDebugEnabled()) logger.debug(getName() + ": cache contains: " + key);
           return cache.containsKey(key);
       }
   
  @@ -175,9 +137,8 @@
        * @param   key     the key to the value to be returned
        * @return  true if contains, false otherwise
        */
  -    private boolean cacheNotFoundContains(String key)
  -    {
  -        if (logger.isDebugEnabled()) logger.debug(name + ": cache_not_found contains: " + key);
  +    protected boolean cacheNotFoundContains(String key) {
  +        if (logger.isDebugEnabled()) logger.debug(getName() + ": cache_not_found contains: " + key);
           return cacheNotFound.containsKey(key);
       }
   
  @@ -187,9 +148,8 @@
        * @param   key     the key
        * @param   value   the value
        */
  -    private void cacheKey(String key, String value)
  -    {
  -        if (logger.isDebugEnabled()) logger.debug(name + ": caching: " + key + " = " + value);
  +    protected void cacheKey(String key, String value) {
  +        if (logger.isDebugEnabled()) logger.debug(getName() + ": caching: " + key + " = " + value);
           cache.put(key, value);
       }
   
  @@ -198,9 +158,8 @@
        *
        * @param   key     the key
        */
  -    private void cacheNotFoundKey(String key)
  -    {
  -        if (logger.isDebugEnabled()) logger.debug(name + ": caching not_found: " + key);
  +    protected void cacheNotFoundKey(String key) {
  +        if (logger.isDebugEnabled()) logger.debug(getName() + ": caching not_found: " + key);
           cacheNotFound.put(key, "");
       }
   
  @@ -210,9 +169,8 @@
        * @param   key     the key
        * @return          the value
        */
  -    private String getFromCache(String key)
  -    {
  -        if (logger.isDebugEnabled()) logger.debug(name + ": returning from cache: " + key);
  +    protected String getFromCache(String key) {
  +        if (logger.isDebugEnabled()) logger.debug(getName() + ": returning from cache: " + key);
           return (String) cache.get(key);
       }
   
  @@ -223,27 +181,22 @@
        * @param   parent          parent node, must be an element
        * @param   pathToParent    XPath to the parent node
        */
  -    private void cacheAll(Node parent, String pathToParent)
  -    {
  +    protected void cacheAll(Node parent, String pathToParent) {
           NodeList children = parent.getChildNodes();
           int childnum = children.getLength();
   
  -        for(int i = 0; i < childnum; i++)
  -        {
  +        for(int i = 0; i < childnum; i++) {
               Node child = children.item(i);
   
  -            if(child.getNodeType() == Node.ELEMENT_NODE)
  -            {
  +            if(child.getNodeType() == Node.ELEMENT_NODE) {
                   StringBuffer pathToChild = new StringBuffer(pathToParent).append('/').append(child.getNodeName());
   
                   NamedNodeMap attrs = child.getAttributes();
  -                if(attrs != null)
  -                {
  +                if(attrs != null) {
                       Node temp = null;
                       String pathToAttr = null;
                       int attrnum = attrs.getLength();
  -                    for(int j = 0; j < attrnum; j++)
  -                    {
  +                    for(int j = 0; j < attrnum; j++) {
                           temp = attrs.item(j);
                           if (!temp.getNodeName().equalsIgnoreCase("xml:lang"))
                               pathToChild.append("[@").append(temp.getNodeName())
  @@ -262,24 +215,21 @@
       }
   
       /**
  -     * Get value by key and substitute variables.
  +     * Get value by key.
        *
        * @param key           key
  -     * @param dictionary    map with variable values
  -     * @return              value with variable values substituted
  -     * @exception MissingResourceException if resource was not found
  -     */
  -    public String getString(String key, Map dictionary)
  -        throws MissingResourceException
  -    {
  -        String value = _getString(key);
  +     * @return              value
  +     * @exception MissingResourceException if value was not found
  +     */
  +    public String getString(String key) throws MissingResourceException {
  +        String value = _getString(convertKey(key));
           if (value == null)
               throw new MissingResourceException(
                       "Unable to locate resource: " + key,
                       XMLResourceBundle.class.getName(),
                       key);
           else
  -            return substitute(value, dictionary);
  +            return value;
       }
   
       /**
  @@ -288,19 +238,21 @@
        * @param   key     the key
        * @return          the value
        */
  -    private String _getString(String key)
  -    {
  +    protected String _getString(String key) {
           if (key == null) return null;
           String value = getFromCache(key);
   
           if (value == null && !cacheNotFoundContains(key))
           {
  -            value = _getString(this.doc.getDocumentElement(), key);
  +	    if (doc != null)
  +                value = _getString(this.doc.getDocumentElement(), key);
  +
               if (value == null)
               {
  -                if (this.parent != null)
  -                    value = this.parent._getString(key);
  +                if (getParent() != null)
  +                    value = getParent().getString(key);
               }
  +
               if (value != null)
                   cacheKey(key, value);
               else
  @@ -316,16 +268,13 @@
        * @param   key     the key
        * @return          the value
        */
  -    private String _getString(Node node, String key)
  -    {
  +    protected String _getString(Node node, String key) {
           String value = null;
  -        try
  -        {
  +        try {
               value = getTextValue(_getNode(node, key));
           }
  -        catch (Exception e)
  -        {
  -            logger.error(name + ": error while locating resource: " + key, e);
  +        catch (Exception e) {
  +            logger.error(getName() + ": error while locating resource: " + key, e);
           }
           return value;
       }
  @@ -336,8 +285,7 @@
        * @param   node    the node
        * @return          the value
        */
  -    private static String getTextValue(Node element)
  -    {
  +    protected static String getTextValue(Node element) {
           if (element == null) return null;
           NodeList list = element.getChildNodes();
           int listsize = list.getLength();
  @@ -345,11 +293,19 @@
           Node item = null;
           String itemValue = null;
   
  -        for(int i = 0; i < listsize; i++)
  -        {
  -            item = list.item(i); if(item.getNodeType() != Node.TEXT_NODE) return null;
  -            itemValue = item.getNodeValue(); if(itemValue == null) return null;
  -            itemValue = itemValue.trim(); if(itemValue.length() == 0) return null;
  +        for(int i = 0; i < listsize; i++) {
  +            item = list.item(i);
  +	    if(item.getNodeType() != Node.TEXT_NODE)
  +	        return null;
  +
  +            itemValue = item.getNodeValue();
  +	    if(itemValue == null)
  +	        return null;
  +
  +            itemValue = itemValue.trim();
  +	    if(itemValue.length() == 0)
  +	        return null;
  +
               return itemValue;
           }
           return null;
  @@ -361,8 +317,7 @@
        * @param   key     the key
        * @return          the node
        */
  -    private Node _getNode(String key)
  -    {
  +    protected Node _getNode(String key) {
           return _getNode(this.doc.getDocumentElement(), key);
       }
   
  @@ -374,105 +329,18 @@
        * @param   key         the key
        * @return              the node
        */
  -    private Node _getNode(Node rootNode, String key)
  -    {
  +    protected Node _getNode(Node rootNode, String key) {
           Node node = null;
  -        try
  -        {
  -            node = XPathAPI.selectSingleNode(rootNode, key);
  +        try {
  +            node = this.processor.selectSingleNode(rootNode, key);
           }
  -        catch (Exception e)
  -        {
  +        catch (Exception e) {
               logger.error("Error while locating resource with key: " + key, e);
           }
           return node;
       }
   
  -    /**
  -     * Substitute the &quot;variables&quot; in the string with the values
  -     * provided in the map.
  -     *
  -     * @param value         value where to search for variables
  -     * @param dictionary    map with variable values
  -     * @return              value with variable values substituted
  -     */
  -    public String substitute(String value, Map dictionary)
  -    {
  -        if (value == null || dictionary == null) return value;
  -
  -        StringBuffer result = new StringBuffer(value.length());
  -        int startPos = 0;
  -        int endPos = 0;
  -        int lastPos = value.length();
  -        Object varValue = "";
  -        String varKey = "", oldKey = "";
  -        while (endPos < lastPos)
  -        {
  -            startPos = endPos;
  -            endPos = value.indexOf('{', startPos);
  -            if (endPos == -1)
  -                endPos = lastPos;
  -            result.append(value.substring(startPos, endPos));
  -            if (endPos < lastPos)
  -                endPos++;
  -            if (endPos < lastPos)
  -            {
  -                startPos = endPos;
  -                endPos = value.indexOf('}', startPos);
  -                if (endPos == -1)
  -                    endPos = lastPos;
  -                oldKey = varKey;
  -                varKey = value.substring(startPos, endPos);
  -                if (!oldKey.equals(varKey))
  -                    varValue = dictionary.get(varKey);
  -                if (varValue != null)
  -                {
  -                    if (logger.isDebugEnabled()) logger.debug("Substituting var: " + varKey + " --> " + varValue);
  -                    result.append(varValue);
  -                }
  -                else
  -                {
  -                    if (logger.isWarnEnabled()) logger.warn(name + ": var not found: " + varKey);
  -                    result.append('{').append(varKey).append('}');
  -                }
  -                if (endPos < lastPos)
  -                    endPos++;
  -            }
  -        }
  -        return result.toString();
  -    }
  -
  -    /**
  -     * Set the logger.
  -     *
  -     * @param logger the logger
  -     */
  -    public void setLogger( final Logger logger )
  -    {
  -        this.logger = logger;
  -    }
  -
  -    /**
  -     * Return an Object by key.
  -     * Implementation of the ResourceBundle abstract method.
  -     *
  -     * @param key the key
  -     * @return the object
  -     */
  -    protected Object handleGetObject(String key)
  -        throws MissingResourceException
  -    {
  -        return (Object) getString(key, null);
  -    }
  -
  -    /**
  -     * Return an enumeration of the keys.
  -     * Implementation of the ResourceBundle abstract method.
  -     *
  -     * @return the enumeration of keys
  -     */
  -    public Enumeration getKeys()
  -    {
  -        return cache.keys();
  +    public void dispose() {
  +        this.manager.release((Component)this.processor);
       }
   }
  
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/ResourceBundleSelector.java
  
  Index: ResourceBundleSelector.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.avalon.excalibur.i18n;
  
  import org.apache.avalon.framework.component.Composable;
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.component.DefaultComponentSelector;
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.logger.Loggable;
  import org.apache.avalon.framework.thread.ThreadSafe;
  import org.apache.log.Logger;
  
  import java.util.HashMap;
  import java.util.Locale;
  import java.util.Map;
  
  /**
   * This is the method for getting instances of ResourceBundles.
   *
   * @author <a href="mailto:neeme@one.lv">Neeme Praks</a>
   * @author <a href="mailto:mengelhart@earthtrip.com">Mike Engelhart</a>
   * @version $Id: ResourceBundleSelector.java,v 1.1 2001/10/29 23:10:24 neeme Exp $
   */
  
  public class ResourceBundleSelector
      extends DefaultComponentSelector
      implements Configurable, Loggable, ThreadSafe, Composable
  {
  
      /**
      * The role implemented by an <code>ResourceBundle</code>.
      */
      String ROLE = "org.apache.avalon.excalibur.i18n.ResourceBundleSelector";
  
      /** Component Manager */
      protected ComponentManager manager = null;
  
      public void compose(ComponentManager manager) {
          this.manager = manager;
          try {
              this.mapper = (LocaleToUriMapper) manager.lookup(LocaleToUriMapper.ROLE);
          } catch (Exception e) {
              logger.error("cannot obtain LocaleToUriMapper", e);
          }
      }
  
      /** Should we load bundles to cache on startup or not? */
      protected boolean cacheAtStartup = false;
  
      /** LocaleToUri mapper */
      protected LocaleToUriMapper mapper;
  
      /** Cache for the names of the bundles that were not found */
      protected Map cacheNotFound = new HashMap();
  
      /** The logger */
      protected Logger logger;
  
      /** Constants for configuration keys */
      public static class ConfigurationKeys {
          public static final String CACHE_AT_STARTUP = "cache-at-startup";
      }
  
      /**
       * Set the logger.
       *
       * @param logger the logger
       */
      public void setLogger(final Logger logger) {
          this.logger = logger;
      }
  
      /**
       * Configure the component.
       *
       * @param configuration the configuration
       */
      public void configure(Configuration configuration) throws ConfigurationException {
          this.cacheAtStartup = configuration.getChild(ConfigurationKeys.CACHE_AT_STARTUP).getValueAsBoolean(false);
      }
  
      /**
       * Select a bundle based on bundle name and locale.
       *
       * @param name              bundle name
       * @param locale            locale
       * @return                  the bundle
       * @exception ComponentException if a bundle is not found
       */
      public Component select(String name, Locale locale) throws ComponentException {
          return select(name, locale, cacheAtStartup);
      }
  
      /**
       * Select a bundle based on bundle name and locale.
       *
       * @param name              bundle name
       * @param locale            locale
       * @param cacheAtStartup    cache all the keys when constructing?
       * @return                  the bundle
       * @exception ComponentException if a bundle is not found
       */
      public Component select(String name, Locale loc, boolean cacheAtStartup) throws ComponentException {
          Component bundle = _select(name, loc, cacheAtStartup);
          if (bundle == null)
              throw new ComponentException("Unable to locate resource: " + name);
          return bundle;
      }
  
      /**
       * Select a bundle based on bundle name and locale.
       *
       * @param name              bundle name
       * @param locale            locale
       * @return                  the bundle
       */
      private Component _select(String name, Locale loc) {
          return _select(name, loc, cacheAtStartup);
      }
  
      /**
       * Select the parent bundle of the current bundle, based on
       * bundle name and locale.
       *
       * @param name              bundle name
       * @param locale            locale
       * @return                  the bundle
       */
      protected Component selectParent(String name, Locale loc) {
          return selectParent(name, loc, cacheAtStartup);
      }
  
      /**
       * Select the parent bundle of the current bundle, based on
       * bundle name and locale.
       *
       * @param name              bundle name
       * @param locale            locale
       * @param cacheAtStartup    cache all the keys when constructing?
       * @return                  the bundle
       */
      protected Component selectParent(String name, Locale loc, boolean cacheAtStartup) {
          return _select(name, getParentLocale(loc), cacheAtStartup);
      }
  
      /**
       * Select a bundle based on bundle name and locale name.
       *
       * @param name              bundle name
       * @param localeName        locale name
       * @return                  the bundle
       * @exception ComponentException if a bundle is not found
       */
      public Component select(String name, String localeName)
      throws ComponentException {
          return select(name, new Locale(localeName, localeName) );
      }
  
      /**
       * Select a bundle based on source file name.
       *
       * @param fileName          file name
       * @return                  the bundle
       * @exception ComponentException if a bundle is not found
       */
      public Component selectFromFilename(String fileName)
      throws ComponentException {
          return selectFromFilename(fileName, cacheAtStartup);
      }
  
      /**
       * Select a bundle based on source file name.
       *
       * @param fileName          file name
       * @param cacheAtStartup    cache all the keys when constructing?
       * @return                  the bundle
       * @exception ComponentException if a bundle is not found
       */
      public Component selectFromFilename(String fileName, boolean cacheAtStartup)
      throws ComponentException {
          Component bundle = _select(fileName, null, cacheAtStartup);
          if (bundle == null)
              throw new ComponentException("Unable to locate resource: " + fileName);
          return bundle;
      }
  
      /**
       * Select a bundle based on bundle name and locale.
       *
       * @param name              bundle name
       * @param locale            locale
       * @param cacheAtStartup    cache all the keys when constructing?
       * @return                  the bundle
       * @exception ComponentException if a bundle is not found
       */
      private Component _select(String name, Locale loc, boolean cacheAtStartup) {
          if (logger.isDebugEnabled()) logger.debug("_getBundle: " + name + ", locale " + loc);
          String fileName = mapper.getUri(name, loc);
          ResourceBundle bundle = (ResourceBundle) selectCached(fileName);
          if (bundle == null && !isNotFoundBundle(fileName)) {
              if (logger.isDebugEnabled()) logger.debug("not found in cache, loading: " + fileName);
              synchronized(this) {
                  bundle = (ResourceBundle) selectCached(fileName);
                  if (bundle == null && !isNotFoundBundle(fileName)) {
                      if (logger.isDebugEnabled()) logger.debug("synchronized: not found in cache, loading: " + fileName);
                      bundle = _loadBundle(name, fileName, loc, cacheAtStartup);
                      Locale parentLoc = loc;
                      String parentBundleName;
                      while (bundle == null && parentLoc != null && !parentLoc.getLanguage().equals("")) {
                          if (logger.isDebugEnabled()) logger.debug("synchronized: still not found, trying parent: " + fileName);
                          parentLoc = getParentLocale(parentLoc);
                          parentBundleName = mapper.getUri(name, parentLoc);
                          bundle = _loadBundle(name, parentBundleName, parentLoc, cacheAtStartup);
                          updateCache(parentBundleName, bundle);
                      }
                      updateCache(fileName, bundle);
                  }
              }
          }
          return (Component) bundle;
      }
  
      /**
       * Construct a bundle based on bundle name, file name and locale.
       *
       * @param name              bundle name
       * @param fileName          full path to source file
       * @param locale            locale
       * @param cacheAtStartup    cache all the keys when constructing?
       * @return                  the bundle, null if loading failed
       */
      private ResourceBundle _loadBundle(String name, String fileName, Locale loc, boolean cacheAtStartup) {
          if (logger.isDebugEnabled()) logger.debug("Trying to load bundle: " + name + ", locale " + loc + ", filename " + fileName);
          ResourceBundle bundle = null;
          ResourceBundle parentBundle = null;
          try {
              if (loc != null && !loc.getLanguage().equals(""))
                  parentBundle = (ResourceBundle) selectParent(name, loc);
              bundle = (ResourceBundle) manager.lookup(ResourceBundle.ROLE);
              if (bundle instanceof Loggable) ((Loggable)bundle).setLogger(logger);
              bundle.setName(name);
              bundle.setLocale(loc);
              bundle.setParent(parentBundle);
              if (bundle instanceof Composable) ((Composable)bundle).compose(this.manager);
              bundle.init(fileName, cacheAtStartup);
              bundle.setLastModified(System.currentTimeMillis());
          }
          catch (Exception e) {
              logger.error("Error while loading resource: " + name + ", locale " + loc + ", bundleName " + fileName, e);
  	    bundle = null;
          }
          return bundle;
      }
  
      /**
       * Returns the next locale up the parent hierarchy.
       * E.g. the parent of new Locale("en","us","mac") would be
       * new Locale("en", "us", "").
       *
       * @param locale            the locale
       * @return                  the parent locale
       */
      protected Locale getParentLocale(Locale loc) {
          Locale newloc;
          if (loc.getVariant().equals("")) {
              if (loc.getCountry().equals(""))
                  newloc = new Locale("","","");
              else
                  newloc = new Locale(loc.getLanguage(), "", "");
          }
          else
              newloc = new Locale(loc.getLanguage(), loc.getCountry(), "");
  
          return newloc;
      }
  
      /**
       * Selects a bundle from the cache.
       *
       * @param fileName          file name of the bundle
       * @return                  the cached bundle; null, if not found
       */
      protected Component selectCached(String fileName) {
          Component bundle = null;
          try {
              bundle = super.select(fileName);
              if (logger.isDebugEnabled()) logger.debug("Returning from cache: " + fileName);
          }
          catch (ComponentException e) {
              if (logger.isDebugEnabled()) logger.debug("Not found in cache: " + fileName);
          }
          return bundle;
      }
  
      /**
       * Checks if the bundle is in the &quot;not-found&quot; cache.
       *
       * @param fileName          file name of the bundle
       * @return                  true, if the bundle wasn't found already before;
       *                          otherwise, false.
       */
      protected boolean isNotFoundBundle(String fileName) {
          String result = (String)(cacheNotFound.get(fileName));
          if (result != null) {
              if (logger.isDebugEnabled()) logger.debug("Returning from not_found_cache: " + fileName);
          }
          else {
              if (logger.isDebugEnabled()) logger.debug("Not found in not_found_cache: " + fileName);
          }
          return result != null;
      }
  
      /**
       * Checks if the bundle is in the &quot;not-found&quot; cache.
       *
       * @param fileName          file name of the bundle
       * @return                  true, if the bundle wasn't found already before;
       *                          otherwise, false.
       */
      protected void updateCache(String fileName, ResourceBundle bundle) {
          if (bundle == null) {
              if (logger.isDebugEnabled()) logger.debug("Updating not_found_cache: " + fileName);
              cacheNotFound.put(fileName, fileName);
          }
          else {
              if (logger.isDebugEnabled()) logger.debug("Updating cache: " + fileName);
              super.put((Object) fileName, (Component) bundle);
          }
      }
  
      /**
       * Should we load bundles to cache on startup or not?
       *
       * @return true if pre-loading all resources; false otherwise
       */
      public boolean cacheAtStartup() {
          return cacheAtStartup;
      }
  }
  
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/ResourceBundle.java
  
  Index: ResourceBundle.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.avalon.excalibur.i18n;
  
  import java.util.Map;
  import java.util.Locale;
  
  /**
   * This is the interface of the ResourceBundle, for used for i18n support.
   *
   * @author <a href="mailto:neeme@apache.org">Neeme Praks</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/10/29 23:10:24 $ $Author: neeme $
   */
  public interface ResourceBundle {
  
      /**
      * The role implemented by an <code>ResourceBundle</code>.
      */
      String ROLE = "org.apache.avalon.excalibur.i18n.ResourceBundle";
  
      /**
       * Initalize the bundle
       *
       * @param fileName          name of the XML source file
       * @param cacheAtStartup    cache all the keys when constructing?
       */
      public void init(String fileName, boolean cacheAtStartup) throws Exception;
  
      /**
       * Get the name of the bundle.
       *
       * @return              the name
       */
      public String getName();
  
      /**
       * Set the name of the bundle.
       *
       * @param name  the name
       */
      void setName(String name);
  
      /**
       * Get the locale of the bundle.
       *
       * @return the locale
       */
      public Locale getLocale();
  
      /**
       * Sets the locale of the bundle.
       *
       * @param name  the locale
       */
      void setLocale(Locale locale);
  
      /**
       * Get the parent bundle of the current bundle.
       *
       * @return the parent bundle
       */
      public ResourceBundle getParent();
  
      /**
       * Set the parent bundle of the current bundle.
       *
       * @param parent    the parent bundle
       */
      void setParent(ResourceBundle parent);
  
      /**
       * Get value by key.
       *
       * @param key           key
       * @return              value
       * @exception MissingResourceException if value was not found
       */
      public String getString(String key);
  
      /**
       * Get value by key and substitute variables.
       *
       * @param key           key
       * @param values        map with variable values
       * @return              value with variable values substituted
       * @exception MissingResourceException if value was not found
       */
      public String getString(String key, Map values);
  
      /**
       * Convert the &quot;user view&quot; of the lookup key to the
       * &quot;system view&quot;. Used to hide the implemented storage
       * mechanism and/or XML file schema.
       *
       * @param key           user key
       * @return              system key
       */
      public String convertKey(String userKey);
  
      /**
       * Returns the last modification time of this bundle, in milliseconds.
       *
       * @return              last modification time, -1 if N/A
       */
      public long getLastModified();
  
      /**
       * Sets the last modification time of this bundle, in milliseconds.
       *
       * @param lastModified  last modification time
       */
      void setLastModified(long lastModified);
  
  }
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/PrefixedMapper.java
  
  Index: PrefixedMapper.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.avalon.excalibur.i18n;
  
  import java.util.Locale;
  
  import org.apache.log.Logger;
  import org.apache.avalon.framework.logger.Loggable;
  import org.apache.avalon.framework.thread.ThreadSafe;
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  
  /**
   * Used to map locale information to URI space, to find the relevant bundle.
   *
   * @author <a href="mailto:neeme@apache.org">Neeme Praks</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/10/29 23:10:24 $ $Author: neeme $
   */
  
  public abstract class PrefixedMapper implements Loggable, Configurable, ThreadSafe {
  
      /** Constants for configuration keys */
      public static class ConfigurationKeys {
          public static final String PREFIX = "prefix";
      }
  
      /** Root directory to all bundle names */
      protected String prefix;
  
      /** The logger */
      protected Logger logger;
  
      /**
       * Set the logger.
       *
       * @param logger the logger
       */
      public void setLogger(final Logger logger) {
          this.logger = logger;
      }
  
      /**
       * Configure the component.
       *
       * @param configuration the configuration
       */
      public void configure(Configuration configuration) throws ConfigurationException {
          try {
              this.prefix = configuration.getChild(ConfigurationKeys.PREFIX, true).getValue();
          }
          catch (ConfigurationException e) {
              if (logger.isWarnEnabled()) logger.warn("Prefix not provided in configuration, using none.");
              this.prefix = "";
          }
  
          if (logger.isDebugEnabled()) {
              logger.debug("PrefixedMapper configured with prefix = '" + this.prefix + "'");
          }
      }
  
      /**
       * Returns the root directory to all bundles.
       *
       * @return the directory path
       */
      public String getPrefix() {
          return prefix;
      }
  }
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/LocaleToUriMapper.java
  
  Index: LocaleToUriMapper.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.avalon.excalibur.i18n;
  
  import java.util.Locale;
  
  /**
   * Used to map locale information to URI space, to find the relevant bundle.
   *
   * @author <a href="mailto:neeme@apache.org">Neeme Praks</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/10/29 23:10:24 $ $Author: neeme $
   */
  public interface LocaleToUriMapper {
  
      /**
      * The role implemented by a <code>LocaleToUriMapper</code>.
      */
      String ROLE = "org.apache.avalon.excalibur.i18n.LocaleToUriMapper";
  
      /**
       * Get the URI for the bundle, based on locale and filename.
       *
       * @return      the URI
       */
      public String getUri(String name, Locale loc);
  
  }
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/FlatXMLResourceBundle.java
  
  Index: FlatXMLResourceBundle.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.avalon.excalibur.i18n;
  
  /**
   * @author <a href="mailto:neeme@one.lv">Neeme Praks</a>
   * @version $Id: FlatXMLResourceBundle.java,v 1.1 2001/10/29 23:10:24 neeme Exp $
   */
  public class FlatXMLResourceBundle extends XMLResourceBundle {
  
      public static final String PREFIX = "/catalogue/message[@key='";
      public static final String SUFFIX = "']";
  
      /**
       * Convert the &quot;user view&quot; of the lookup key to the
       * &quot;system view&quot;. Used to hide the implemented storage
       * mechanism and/or XML file schema.
       *
       * @param key           user key
       * @return              system key
       */
      public String convertKey(String userKey) {
          return new StringBuffer(PREFIX).append(userKey).append(SUFFIX).toString();
      }
  
  }
  
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/DirectoryMapper.java
  
  Index: DirectoryMapper.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.avalon.excalibur.i18n;
  
  import java.util.Locale;
  
  /**
   * Maps the locale using the standard Java mapping: en-US would be mapped to
   * file ending with en_US.
   *
   * @author <a href="mailto:neeme@apache.org">Neeme Praks</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/10/29 23:10:24 $ $Author: neeme $
   */
  
  public class DirectoryMapper extends PrefixedMapper {
  
      /**
       * Get the URI for the bundle, based on locale and filename.
       *
       * @return      the URI
       */
      public String getUri(String name, Locale loc) {
          StringBuffer sb = new StringBuffer(getPrefix());
          if (loc != null) {
              String lang = loc.getLanguage();
              String country = loc.getCountry();
              String variant = loc.getVariant();
  
              if (lang.length() > 0) sb.append("/").append(lang);
              if (country.length() > 0) sb.append("/").append(country);
              if (variant.length() > 0) sb.append("/").append(variant);
          }
          sb.append("/").append(name).append(".xml");
  
          String result = sb.toString();
          if (logger.isDebugEnabled()) logger.debug("Resolving bundle name to file name: " + name + ", locale " + loc + " --> " + result);
          return result;
      }
  
  }
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/DefaultMapper.java
  
  Index: DefaultMapper.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.avalon.excalibur.i18n;
  
  import java.util.Locale;
  
  /**
   * Maps the locale using the standard Java mapping: en-US would be mapped to
   * file ending with en_US.
   *
   * @author <a href="mailto:neeme@apache.org">Neeme Praks</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/10/29 23:10:24 $ $Author: neeme $
   */
  
  public class DefaultMapper extends PrefixedMapper {
  
      /**
       * Get the URI for the bundle, based on locale and filename.
       *
       * @return      the URI
       */
      public String getUri(String name, Locale loc) {
          StringBuffer sb = new StringBuffer(getPrefix());
          sb.append('/').append(name);
          if (loc != null)
          {
              if (! loc.getLanguage().equals(""))
              {
                  sb.append("_");
                  sb.append(loc.getLanguage());
              }
              if (! loc.getCountry().equals(""))
              {
                  sb.append("_");
                  sb.append(loc.getCountry());
              }
              if (! loc.getVariant().equals(""))
              {
                  sb.append("_");
                  sb.append(loc.getVariant());
              }
          }
          sb.append(".xml");
  
          String result = sb.toString();
          if (logger.isDebugEnabled()) logger.debug("Resolving bundle name to file name: " + name + ", locale " + loc + " --> " + result);
          return result;
      }
  
  }
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/AbstractResourceBundle.java
  
  Index: AbstractResourceBundle.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.avalon.excalibur.i18n;
  
  import java.util.Map;
  import java.util.Locale;
  
  import org.apache.log.Logger;
  import org.apache.avalon.framework.logger.Loggable;
  import org.apache.avalon.framework.component.Component;
  
  public abstract class AbstractResourceBundle
      implements ResourceBundle, Component, Loggable {
  
      /** Logger */
      protected Logger logger;
  
      /** Bundle name */
      private String name = "";
  
      /** Locale of the bundle */
      private Locale locale;
  
      /** Parent of the current bundle */
      private ResourceBundle parent = null;
  
      /** Last modification time */
      private long lastModified = -1;
  
      /**
       * Set the logger.
       *
       * @param logger the logger
       */
      public void setLogger( final Logger logger ) {
          this.logger = logger;
      }
  
      /**
       * Get the name of the bundle.
       *
       * @return      the name
       */
      public String getName() {
          return this.name;
      }
  
      /**
       * Set the name of the bundle.
       *
       * @param name  the name
       */
      public void setName(String name) {
          this.name = name;
      }
  
      /**
       * Gets the locale of the bundle.
       *
       * @return      the locale
       */
      public Locale getLocale() {
          return this.locale;
      }
  
      /**
       * Sets the locale of the bundle.
       *
       * @param name  the locale
       */
      public void setLocale(Locale locale) {
          this.locale = locale;
      }
  
      /**
       * Get the parent bundle of the current bundle.
       *
       * @return          the parent bundle
       */
      public ResourceBundle getParent() {
          return this.parent;
      }
  
      /**
       * Set the parent bundle of the current bundle.
       *
       * @param parent    the parent bundle
       */
      public void setParent(ResourceBundle parent) {
          this.parent = parent;
      }
  
      /**
       * Returns the last modification time of this bundle, in milliseconds.
       *
       * @return              last modification time, -1 if N/A
       */
      public long getLastModified() {
          return this.lastModified;
      }
  
      /**
       * Sets the last modification time of this bundle, in milliseconds.
       *
       * @param lastModified  last modification time
       */
      public void setLastModified(long lastModified) {
          this.lastModified = lastModified;
      }
  
      /**
       * Get value by key and substitute variables.
       *
       * @param key           key
       * @param variables     map with variable values
       * @return              value with variable values substituted
       * @exception MissingResourceException if value was not found
       */
      public String getString(String key, Map variables) {
          return convertKey(getString(key));
      }
  
      /**
       * Convert the &quot;user view&quot; of the lookup key to the
       * &quot;system view&quot;. Used to hide the implemented storage
       * mechanism and/or XML file schema.
       *
       * The default implementation just returns the key &quot;as-is&quot;.
       *
       * @param key           user key
       * @return              system key
       */
      public String convertKey(String userKey) {
          return userKey;
      }
  
      /**
       * Substitute the &quot;variables&quot; in the string with the values
       * provided in the map.
       *
       * @param value         value where to search for variables
       * @param dictionary    map with variable values
       * @return              value with variable values substituted
       */
      public String substitute(String value, Map values) {
          if (value == null || values == null) return value;
  
          StringBuffer result = new StringBuffer(value.length());
          int startPos = 0;
          int endPos = 0;
          int lastPos = value.length();
          Object varValue = "";
          String varKey = "", oldKey = "";
          while (endPos < lastPos) {
              startPos = endPos;
              endPos = value.indexOf('{', startPos);
              if (endPos == -1)
                  endPos = lastPos;
              result.append(value.substring(startPos, endPos));
              if (endPos < lastPos)
                  endPos++;
              if (endPos < lastPos) {
                  startPos = endPos;
                  endPos = value.indexOf('}', startPos);
                  if (endPos == -1)
                      endPos = lastPos;
                  oldKey = varKey;
                  varKey = value.substring(startPos, endPos);
                  if (!oldKey.equals(varKey))
                      varValue = values.get(varKey);
                  if (varValue != null) {
                      if (logger.isDebugEnabled()) logger.debug("Substituting var: " + varKey + " --> " + varValue);
                      result.append(varValue);
                  }
                  else {
                      if (logger.isWarnEnabled()) logger.warn(name + ": var not found: " + varKey);
                      result.append('{').append(varKey).append('}');
                  }
                  if (endPos < lastPos)
                      endPos++;
              }
          }
          return result.toString();
      }
  
  }
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>