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 22:38:57 UTC

cvs commit: jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n AbstractBundle.java Bundle.java BundleInfo.java BundleInfoMapper.java BundleLoader.java BundleMatcher.java BundleSelector.java DefaultBundleLoader.java FlatXmlBundle.java XmlBundle.java DefaultMapper.java DirectoryMapper.java AbstractResourceBundle.java FlatXMLResourceBundle.java LocaleToUriMapper.java PrefixedMapper.java ResourceBundle.java ResourceBundleSelector.java XMLResourceBundle.java

neeme       01/10/30 13:38:57

  Modified:    src/scratchpad/org/apache/avalon/excalibur/i18n
                        DefaultMapper.java DirectoryMapper.java
  Added:       src/scratchpad/org/apache/avalon/excalibur/i18n
                        AbstractBundle.java Bundle.java BundleInfo.java
                        BundleInfoMapper.java BundleLoader.java
                        BundleMatcher.java BundleSelector.java
                        DefaultBundleLoader.java FlatXmlBundle.java
                        XmlBundle.java
  Removed:     src/scratchpad/org/apache/avalon/excalibur/i18n
                        AbstractResourceBundle.java
                        FlatXMLResourceBundle.java LocaleToUriMapper.java
                        PrefixedMapper.java ResourceBundle.java
                        ResourceBundleSelector.java XMLResourceBundle.java
  Log:
  Changes:
  * renamed all classes :-), to make the names shorter. Resourcebundles are from now on called simply bundles.
  * added a notion of BundleLoaders that are more or less object factories for different bundles + they are responsible for extended functionality for pre-loading all bundles when Avalon/Cocoon/whatever starts up the first time
  * added BundleInfo helper class, to encapsulate all possible parameters the bundle selection process could depend on
  * BundleSelector now properly implements the Selector interface, as there is only one "hint" object (BundleInfo) passed to the .select() method
  * the whole system is highly configurable and supports pluggable Bundle, BundleLoader and BundleInfoMapper implementations
  * added the notion of bundlematchers that enable multiple bundle implementations to be used in parallel (matched against BundleInfo) in a working system... but I'm still not sure about the implementation, though...
  
  Revision  Changes    Path
  1.2       +6 -5      jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/DefaultMapper.java
  
  Index: DefaultMapper.java
  ===================================================================
  RCS file: /home/cvs/jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/DefaultMapper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- DefaultMapper.java	2001/10/29 23:10:24	1.1
  +++ DefaultMapper.java	2001/10/30 21:38:56	1.2
  @@ -14,18 +14,20 @@
    * 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 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/10/30 21:38:56 $ $Author: neeme $
    */
   
  -public class DefaultMapper extends PrefixedMapper {
  +public class DefaultMapper implements BundleInfoMapper {
   
       /**
        * 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());
  +    public String map(BundleInfo bundleInfo) {
  +        String name = bundleInfo.getName();
  +        Locale loc = bundleInfo.getLocale();
  +        StringBuffer sb = new StringBuffer();
           sb.append('/').append(name);
           if (loc != null)
           {
  @@ -48,7 +50,6 @@
           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.2       +6 -5      jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/DirectoryMapper.java
  
  Index: DirectoryMapper.java
  ===================================================================
  RCS file: /home/cvs/jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/DirectoryMapper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- DirectoryMapper.java	2001/10/29 23:10:24	1.1
  +++ DirectoryMapper.java	2001/10/30 21:38:56	1.2
  @@ -14,18 +14,20 @@
    * 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 $
  + * @version CVS $Revision: 1.2 $ $Date: 2001/10/30 21:38:56 $ $Author: neeme $
    */
   
  -public class DirectoryMapper extends PrefixedMapper {
  +public class DirectoryMapper implements BundleInfoMapper {
   
       /**
        * 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());
  +    public String map(BundleInfo bundleInfo) {
  +        String name = bundleInfo.getName();
  +        Locale loc = bundleInfo.getLocale();
  +        StringBuffer sb = new StringBuffer();
           if (loc != null) {
               String lang = loc.getLanguage();
               String country = loc.getCountry();
  @@ -38,7 +40,6 @@
           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/AbstractBundle.java
  
  Index: AbstractBundle.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 AbstractBundle
      implements Bundle, Component, Loggable {
  
      /** Logger */
      protected Logger logger;
  
      /** bundle info */
      private BundleInfo bundleInfo;
  
      /** Parent of the current bundle */
      private Bundle 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 bundle info.
       */
      public BundleInfo getBundleInfo() {
          return this.bundleInfo;
      }
  
      /**
       * Set the bundle info.
       */
      public void setBundleInfo(BundleInfo bundleInfo) {
          this.bundleInfo = bundleInfo;
      }
  
      /**
       * Get the parent bundle of the current bundle.
       *
       * @return          the parent bundle
       */
      public Bundle getParent() {
          return this.parent;
      }
  
      /**
       * Set the parent bundle of the current bundle.
       *
       * @param parent    the parent bundle
       */
      public void setParent(Bundle 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(bundleInfo + ": var not found: " + varKey);
                      result.append('{').append(varKey).append('}');
                  }
                  if (endPos < lastPos)
                      endPos++;
              }
          }
          return result.toString();
      }
  
  }
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/Bundle.java
  
  Index: Bundle.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/30 21:38:56 $ $Author: neeme $
   */
  public interface Bundle {
  
      /**
       * 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 bundle info.
       */
      public BundleInfo getBundleInfo();
  
      /**
       * Set the bundle info.
       */
      public void setBundleInfo(BundleInfo bundleInfo);
  
      /**
       * Get the parent bundle of the current bundle.
       *
       * @return the parent bundle
       */
      public Bundle getParent();
  
      /**
       * Set the parent bundle of the current bundle.
       *
       * @param parent    the parent bundle
       */
      void setParent(Bundle 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/BundleInfo.java
  
  Index: BundleInfo.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/30 21:38:56 $ $Author: neeme $
   */
  public class BundleInfo {
  
      private String name;
      private Locale locale;
      private String ext;
  
      public BundleInfo(String name, Locale locale, String ext) {
          this.name = name;
          this.locale = locale;
          this.ext = ext;
      }
  
      public BundleInfo(String name, Locale locale) {
          this(name, locale, null);
      }
  
      public BundleInfo(Locale locale) {
          this(null, locale);
      }
  
      public String getName() {
          return this.name;
      }
  
      public Locale getLocale() {
          return this.locale;
      }
  
      public String getExtensionParameter() {
          return this.ext;
      }
  
      public String toString() {
          return "BundleInfo(" + this.name + "," + this.locale + "," + this.ext + ")";
      }
  
      public BundleInfo getParent() {
          if (this.locale != null && !this.locale.getLanguage().equals(""))
              return new BundleInfo(this.name, this.getParentLocale(), this.ext);
          else
              return null;
      }
  
      /**
       * 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 newloc;
          if (this.locale.getVariant().equals("")) {
              if (this.locale.getCountry().equals(""))
                  newloc = new Locale("","","");
              else
                  newloc = new Locale(this.locale.getLanguage(), "", "");
          }
          else
              newloc = new Locale(this.locale.getLanguage(), this.locale.getCountry(), "");
  
          return newloc;
      }
  
  }
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/BundleInfoMapper.java
  
  Index: BundleInfoMapper.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;
  
  /**
   * Used to map bundle information to string representation (e.g. URI),
   * to find the relevant bundle.
   *
   * @author <a href="mailto:neeme@apache.org">Neeme Praks</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/10/30 21:38:56 $ $Author: neeme $
   */
  public interface BundleInfoMapper {
  
      /**
       * Get the string form of the bundle, based on bundle info.
       *
       * @return      the string form
       */
      public String map(BundleInfo bundleInfo);
  
  }
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/BundleLoader.java
  
  Index: BundleLoader.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.configuration.Configurable;
  
  /**
   * 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/30 21:38:56 $ $Author: neeme $
   */
  public interface BundleLoader extends Configurable {
  
      /**
       * Load a bundle, based on bundleInfo.
       *
       * @return      the bundle
       */
      public Bundle loadBundle(BundleInfo bundleInfo);
  
      public void setBundleSelector(BundleSelector bundleSelector);
  
  }
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/BundleMatcher.java
  
  Index: BundleMatcher.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.List;
  import java.util.LinkedList;
  
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  
  /**
   * Used to map bundle information to string representation (e.g. URI),
   * to find the relevant bundle.
   *
   * @author <a href="mailto:neeme@apache.org">Neeme Praks</a>
   * @version CVS $Revision: 1.1 $ $Date: 2001/10/30 21:38:56 $ $Author: neeme $
   */
  public abstract class BundleMatcher implements Configurable {
  
      private List matchers = new LinkedList();
      private String type;
  
      /**
       * Get the string form of the bundle, based on bundle info.
       *
       * @return      the string form
       */
      public abstract boolean match(BundleInfo bundleInfo);
  
      public String getType(BundleInfo bundleInfo) {
          if (match(bundleInfo)) {
              for (int i = 0; i < matchers.size(); i++) {
                  String type = ((BundleMatcher) matchers.get(i)).getType(bundleInfo);
                  if (type != null) return type;
              }
              return this.type;
          }
          return null;
      }
  
      /**
       * Configure the component.
       *
       * @param configuration the configuration
       */
      public void configure(Configuration configuration) throws ConfigurationException {
          this.type = configuration.getAttribute("type");
          Configuration[] matcherConfs = configuration.getChildren("matcher");
          for (int i = 0; i < matcherConfs.length; i++) {
              BundleMatcher matcher = getBundleMatcherInstance(configuration.getAttribute("class"));
              matcher.configure(matcherConfs[i]);
              this.matchers.add(matcher);
          }
      }
  
      private BundleMatcher getBundleMatcherInstance(String className) throws ConfigurationException {
          try {
              return (BundleMatcher) Class.forName(className).newInstance();
          }
          catch (Exception e) {
              throw new ConfigurationException("unable to load bundleMatcher", e);
          }
      }
  }
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/BundleSelector.java
  
  Index: BundleSelector.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.HashMap;
  import java.util.List;
  import java.util.LinkedList;
  import java.util.Locale;
  
  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;
  
  /**
   * 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: BundleSelector.java,v 1.1 2001/10/30 21:38:56 neeme Exp $
   */
  
  public class BundleSelector
      extends DefaultComponentSelector
      implements Configurable, Loggable, ThreadSafe, Composable
  {
  
      /**
      * The role implemented by an <code>BundleSelector</code>.
      */
      String ROLE = "org.apache.avalon.excalibur.i18n.BundleSelector";
  
      /** Component Manager */
      protected ComponentManager manager = null;
  
      public void compose(ComponentManager manager) {
          this.manager = manager;
      }
  
      /** Cache for the names of the bundles that were not found */
      protected Map cacheNotFound = new HashMap();
  
      /** Map of all bundle loaders */
      private Map loaders = new HashMap();
  
      /** List of matchers */
      private List matchers = new LinkedList();
  
      /** the default loader */
      private BundleLoader defaultLoader;
  
      /** 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 {
          Configuration[] loaderConfs = configuration.getChild("loaders").getChildren("loader");
          for (int i = 0; i < loaderConfs.length; i++) {
              BundleLoader loader = (BundleLoader) getInstance(loaderConfs[i].getAttribute("class"));
              loader.configure(loaderConfs[i]);
              loader.setBundleSelector(this);
              this.loaders.put(loaderConfs[i].getAttribute("type-name"), loader);
          }
          this.defaultLoader = (BundleLoader) this.loaders.get(configuration.getChild("matchers").getAttribute("default-type"));
          if (defaultLoader == null) throw new ConfigurationException("default loader not specified (matchers/@default-type)");
          Configuration[] matcherConfs = configuration.getChild("matchers").getChildren("matcher");
          for (int i = 0; i < matcherConfs.length; i++) {
              BundleMatcher matcher = (BundleMatcher) getInstance(configuration.getAttribute("class"));
              matcher.configure(matcherConfs[i]);
              this.matchers.add(matcher);
          }
      }
  
      private Object getInstance(String className) throws ConfigurationException {
          try {
              return Class.forName(className).newInstance();
          }
          catch (Exception e) {
              throw new ConfigurationException("unable to load object", e);
          }
      }
  
      /**
       * 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(Object hint) throws ComponentException {
          Component bundle = select((BundleInfo) hint);
          if (bundle == null)
              throw new ComponentException("Unable to locate bundle: " + hint);
          return bundle;
      }
  
      /**
       * 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(new BundleInfo(name, new Locale(localeName, localeName)));
      }
  
      /**
       * 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(BundleInfo bundleInfo) {
          if (logger.isDebugEnabled()) logger.debug("_getBundle: " + bundleInfo);
          Bundle bundle = (Bundle) selectCached(bundleInfo);
          if (bundle == null && !isNotFoundBundle(bundleInfo)) {
              if (logger.isDebugEnabled()) logger.debug("not found in cache, loading: " + bundleInfo);
              synchronized(this) {
                  bundle = (Bundle) selectCached(bundleInfo);
                  if (bundle == null && !isNotFoundBundle(bundleInfo)) {
                      if (logger.isDebugEnabled()) logger.debug("synchronized: not found in cache, loading: " + bundleInfo);
                      bundle = loadBundle(bundleInfo);
                      BundleInfo parentBundleInfo = bundleInfo.getParent();
                      while (bundle == null && parentBundleInfo != null) {
                          if (logger.isDebugEnabled()) logger.debug("synchronized: still not found, trying parent: " + parentBundleInfo);
                          parentBundleInfo = bundleInfo.getParent();
                          bundle = loadBundle(parentBundleInfo);
                          updateCache(parentBundleInfo, bundle);
                      }
                      updateCache(bundleInfo, bundle);
                  }
              }
          }
          return (Component) bundle;
      }
  
      private Bundle loadBundle(BundleInfo bundleInfo) {
          String type = null;
          for (int i = 0; i < matchers.size(); i++) {
              type = ((BundleMatcher) matchers.get(i)).getType(bundleInfo);
              if (type != null) break;
          }
          BundleLoader loader = null;
          if (type != null)
              loader = (BundleLoader) loaders.get(type);
          if (loader == null)
              loader = defaultLoader;
          return loader.loadBundle(bundleInfo);
      }
  
      /**
       * Selects a bundle from the cache.
       *
       * @param fileName          file name of the bundle
       * @return                  the cached bundle; null, if not found
       */
      protected Component selectCached(BundleInfo bundleInfo) {
          Component bundle = null;
          try {
              bundle = super.select(bundleInfo);
              if (logger.isDebugEnabled()) logger.debug("Returning from cache: " + bundleInfo);
          }
          catch (ComponentException e) {
              if (logger.isDebugEnabled()) logger.debug("Not found in cache: " + bundleInfo);
          }
          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(BundleInfo bundleInfo) {
          BundleInfo result = (BundleInfo)(cacheNotFound.get(bundleInfo));
          if (result != null) {
              if (logger.isDebugEnabled()) logger.debug("Returning from not_found_cache: " + bundleInfo);
          }
          else {
              if (logger.isDebugEnabled()) logger.debug("Not found in not_found_cache: " + bundleInfo);
          }
          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(BundleInfo bundleInfo, Bundle bundle) {
          if (bundle == null) {
              if (logger.isDebugEnabled()) logger.debug("Updating not_found_cache: " + bundleInfo);
              cacheNotFound.put(bundleInfo, bundleInfo);
          }
          else {
              if (logger.isDebugEnabled()) logger.debug("Updating cache: " + bundleInfo);
              super.put((Object) bundleInfo, (Component) bundle);
          }
      }
  
  }
  
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/DefaultBundleLoader.java
  
  Index: DefaultBundleLoader.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.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.component.Composable;
  import org.apache.avalon.framework.component.ComponentManager;
  
  /**
   * 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/30 21:38:56 $ $Author: neeme $
   */
  
  public class DefaultBundleLoader implements BundleLoader, Loggable, Composable {
  
      /** Constants for configuration keys */
      public static class ConfigurationKeys {
          public static final String PREFIX = "prefix";
          public static final String SUFFIX = "suffix";
          public static final String BUNDLE = "bundle";
          public static final String MAPPER = "mapper";
          public static final String CACHE_ALL = "preload-all-values";
      }
  
      /** FQCN of the returned bundles */
      private String bundleClassName;
  
      /** FQCN of the used bundle info mapper */
      private String bundleInfoMapperClassName;
  
      /** bundle info mapper */
      private BundleInfoMapper mapper;
  
      /** cache all bundle values when bundles are created */
      private boolean cacheAll;
  
      /** bundle name prefix */
      private String prefix;
  
      /** bundle name suffix */
      private String suffix;
  
      /** bundle selector */
      protected BundleSelector selector;
  
      /** The logger */
      protected Logger logger;
  
      /** Component Manager */
      protected ComponentManager manager = null;
  
      /**
       * Set the logger.
       *
       * @param logger the logger
       */
      public void setLogger(final Logger logger) {
          this.logger = logger;
      }
  
      /**
       * Set the bundle selector.
       *
       * @param logger the selector
       */
      public void setBundleSelector(BundleSelector selector) {
          this.selector = selector;
      }
  
      public void compose(ComponentManager manager) {
          this.manager = manager;
      }
  
      /**
       * Configure the component.
       *
       * @param configuration the configuration
       */
      public void configure(Configuration configuration) throws ConfigurationException {
          this.bundleClassName = configuration.getAttribute(ConfigurationKeys.BUNDLE);
          this.bundleInfoMapperClassName = configuration.getAttribute(ConfigurationKeys.MAPPER);
          this.cacheAll = configuration.getAttributeAsBoolean(ConfigurationKeys.CACHE_ALL, true);
  
          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 = "";
          }
          try {
              this.suffix = configuration.getChild(ConfigurationKeys.SUFFIX, true).getValue();
          }
          catch (ConfigurationException e) {
              if (logger.isWarnEnabled()) logger.warn("Suffix not provided in configuration, using none.");
              this.suffix = "";
          }
  
          if (logger.isDebugEnabled()) {
              logger.debug(
                  "BundleLoader configured with bundle=" + this.bundleClassName +
                  ", mapper=" + this.bundleInfoMapperClassName +
                  ", prefix='" + this.prefix +
                  "' and suffix='" + this.suffix + "'"
              );
          }
          try {
              this.mapper = (BundleInfoMapper) Class.forName(this.bundleInfoMapperClassName).newInstance();
          }
          catch (Exception e) {
              throw new ConfigurationException("unable to load mapper", e);
          }
      }
  
      protected Bundle getBundleInstance() {
          try {
              return (Bundle) Class.forName(this.bundleClassName).newInstance();
          }
          catch (Exception e) {
              logger.error("could not create bundle instance", e);
          }
          return null;
      }
  
      /**
       * Returns the bundle info mapper
       *
       * @return the mapper
       */
      protected BundleInfoMapper getMapper() {
          return this.mapper;
      }
  
      /**
       * Should we pre-cache all the values for the bundles created by this
       * loader?
       */
      protected boolean cacheAllValues() {
          return this.cacheAll;
      }
  
      /**
       * Returns the prefix to all bundle names.
       *
       * @return the prefix
       */
      public String getPrefix() {
          return this.prefix;
      }
  
      /**
       * Returns the suffix to all bundle names.
       *
       * @return the suffix
       */
      public String getSuffix() {
          return this.suffix;
      }
  
      /**
       * Load a bundle, based on bundleInfo.
       *
       * @return      the bundle
       */
      public Bundle loadBundle(BundleInfo bundleInfo) {
          if (logger.isDebugEnabled()) logger.debug("Mapping bundle: " + bundleInfo);
          String uri = getPrefix() + getMapper().map(bundleInfo) + getSuffix();
          Bundle bundle = null;
          Bundle parentBundle = null;
          BundleInfo parentBundleInfo = bundleInfo.getParent();
          try {
              if (logger.isDebugEnabled()) logger.debug("Loading bundle from URI: " + uri);
              if (parentBundleInfo != null)
                  parentBundle = (Bundle) selector.select(parentBundleInfo);
              bundle = getBundleInstance();
              if (bundle instanceof Loggable) ((Loggable)bundle).setLogger(logger);
              bundle.setBundleInfo(bundleInfo);
              bundle.setParent(parentBundle);
              if (bundle instanceof Composable) ((Composable)bundle).compose(this.manager);
              bundle.init(uri, cacheAllValues());
              bundle.setLastModified(System.currentTimeMillis());
          }
          catch (Exception e) {
              logger.error("Error while loading bundle: " + bundleInfo + ", uri " + uri, e);
  	    bundle = null;
          }
          return bundle;
      }
  }
  
  
  1.1                  jakarta-avalon-excalibur/src/scratchpad/org/apache/avalon/excalibur/i18n/FlatXmlBundle.java
  
  Index: FlatXmlBundle.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@apache.org">Neeme Praks</a>
   * @version $Id: FlatXmlBundle.java,v 1.1 2001/10/30 21:38:56 neeme Exp $
   */
  public class FlatXmlBundle extends XmlBundle {
  
      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/XmlBundle.java
  
  Index: XmlBundle.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;
  
  /** JDK 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.xml.sax.SAXException;
  
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  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@apache.org">Neeme Praks</a>
   * @author <a href="mailto:oleg@one.lv">Oleg Podolsky</a>
   * @version $Id: XmlBundle.java,v 1.1 2001/10/30 21:38:56 neeme Exp $
   */
  public class XmlBundle extends AbstractBundle
  implements Disposable, Composable {
  
      /** DOM factory */
      private static DocumentBuilderFactory docfactory =
      	DocumentBuilderFactory.newInstance();
  
      /** Cache for storing string values for existing XPaths */
      private Map cache = new HashMap();
  
      /** Cache for storing non-existing XPaths */
      private Map cacheNotFound = new HashMap();
  
      /** DOM-tree containing the bundle content */
      private Document doc;
  
      /** Component Manager */
      protected ComponentManager manager = null;
  
      /** XPath Processor */
      private XPathProcessor processor = null;
  
      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 fileName          name of the XML source file
       * @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: " + getBundleInfo());
          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 String convertKey(String userKey) {
          return '/' + userKey.replace('.', '/');
      }
  
      /**
       * Load the DOM tree, based on the file name.
       *
       * @param fileName          name of the XML source file
       * @return the DOM tree
       * @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
       */
      protected static Document loadResourceBundle(String fileName)
      throws IOException, ParserConfigurationException, SAXException {
          DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
          DocumentBuilder builder = factory.newDocumentBuilder();
          return builder.parse(fileName);
      }
  
      /**
       * Gets the source DOM tree of the bundle.
       *
       * @return the DOM tree
       */
      public Document getDocument() {
          return this.doc;
      }
  
      /**
       * 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
       */
      protected boolean cacheContains(String key) {
          if (logger.isDebugEnabled()) logger.debug(getBundleInfo() + ": cache contains: " + key);
          return cache.containsKey(key);
      }
  
      /**
       * Does the &quot;key-not-found-cache&quot; contain such key?
       *
       * @param   key     the key to the value to be returned
       * @return  true if contains, false otherwise
       */
      protected boolean cacheNotFoundContains(String key) {
          if (logger.isDebugEnabled()) logger.debug(getBundleInfo() + ": cache_not_found contains: " + key);
          return cacheNotFound.containsKey(key);
      }
  
      /**
       * Cache the key and value in &quot;key-cache&quot;.
       *
       * @param   key     the key
       * @param   value   the value
       */
      protected void cacheKey(String key, String value) {
          if (logger.isDebugEnabled()) logger.debug(getBundleInfo() + ": caching: " + key + " = " + value);
          cache.put(key, value);
      }
  
      /**
       * Cache the key in &quot;key-not-found-cache&quot;.
       *
       * @param   key     the key
       */
      protected void cacheNotFoundKey(String key) {
          if (logger.isDebugEnabled()) logger.debug(getBundleInfo() + ": caching not_found: " + key);
          cacheNotFound.put(key, "");
      }
  
      /**
       * Gets the value by the key from the &quot;key-cache&quot;.
       *
       * @param   key     the key
       * @return          the value
       */
      protected String getFromCache(String key) {
          if (logger.isDebugEnabled()) logger.debug(getBundleInfo() + ": returning from cache: " + key);
          return (String) cache.get(key);
      }
  
      /**
       * Steps through the bundle tree and stores all text element values
       * in bundle's cache. Also stores attributes for all element nodes.
       *
       * @param   parent          parent node, must be an element
       * @param   pathToParent    XPath to the parent node
       */
      protected void cacheAll(Node parent, String pathToParent) {
          NodeList children = parent.getChildNodes();
          int childnum = children.getLength();
  
          for(int i = 0; i < childnum; i++) {
              Node child = children.item(i);
  
              if(child.getNodeType() == Node.ELEMENT_NODE) {
                  StringBuffer pathToChild = new StringBuffer(pathToParent).append('/').append(child.getNodeName());
  
                  NamedNodeMap attrs = child.getAttributes();
                  if(attrs != null) {
                      Node temp = null;
                      String pathToAttr = null;
                      int attrnum = attrs.getLength();
                      for(int j = 0; j < attrnum; j++) {
                          temp = attrs.item(j);
                          if (!temp.getNodeName().equalsIgnoreCase("xml:lang"))
                              pathToChild.append("[@").append(temp.getNodeName())
                                      .append("='").append(temp.getNodeValue())
                                      .append("']");
                      }
                  }
  
                  String childValue = getTextValue(child);
                  if(childValue != null)
                      cacheKey(pathToChild.toString(), childValue);
                  else
                      cacheAll(child, pathToChild.toString());
              }
          }
      }
  
      /**
       * Get value by key.
       *
       * @param key           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,
                      XmlBundle.class.getName(),
                      key);
          else
              return value;
      }
  
      /**
       * Get value by key.
       *
       * @param   key     the key
       * @return          the value
       */
      protected String _getString(String key) {
          if (key == null) return null;
          String value = getFromCache(key);
  
          if (value == null && !cacheNotFoundContains(key))
          {
  	    if (doc != null)
                  value = _getString(this.doc.getDocumentElement(), key);
  
              if (value == null)
              {
                  if (getParent() != null)
                      value = getParent().getString(key);
              }
  
              if (value != null)
                  cacheKey(key, value);
              else
                  cacheNotFoundKey(key);
          }
          return value;
      }
  
      /**
       * Get value by key from a concrete node.
       *
       * @param   node    the node
       * @param   key     the key
       * @return          the value
       */
      protected String _getString(Node node, String key) {
          String value = null;
          try {
              value = getTextValue(_getNode(node, key));
          }
          catch (Exception e) {
              logger.error(getBundleInfo() + ": error while locating resource: " + key, e);
          }
          return value;
      }
  
      /**
       * Get the text value of the node.
       *
       * @param   node    the node
       * @return          the value
       */
      protected static String getTextValue(Node element) {
          if (element == null) return null;
          NodeList list = element.getChildNodes();
          int listsize = list.getLength();
  
          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;
  
              return itemValue;
          }
          return null;
      }
  
      /**
       * Get the node with the supplied XPath key.
       *
       * @param   key     the key
       * @return          the node
       */
      protected Node _getNode(String key) {
          return _getNode(this.doc.getDocumentElement(), key);
      }
  
      /**
       * Get the node with the supplied XPath key, starting from concrete
       * root node.
       *
       * @param   rootNode    the root node
       * @param   key         the key
       * @return              the node
       */
      protected Node _getNode(Node rootNode, String key) {
          Node node = null;
          try {
              node = this.processor.selectSingleNode(rootNode, key);
          }
          catch (Exception e) {
              logger.error("Error while locating resource with key: " + key, e);
          }
          return node;
      }
  
      public void dispose() {
          this.manager.release((Component)this.processor);
      }
  }
  
  
  

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