You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by jw...@apache.org on 2007/07/30 23:14:32 UTC

svn commit: r561122 - in /myfaces/trinidad/trunk/trinidad: trinidad-api/src/main/java/org/apache/myfaces/trinidad/skin/ trinidad-examples/trinidad-demo/src/main/java/org/apache/myfaces/trinidaddemo/resource/ trinidad-impl/src/main/java/org/apache/myfac...

Author: jwaldman
Date: Mon Jul 30 14:14:30 2007
New Revision: 561122

URL: http://svn.apache.org/viewvc?view=rev&rev=561122
Log:
TRINIDAD-105 enable registerResourceBundle to any skin

(on trunk)

1. added the bundle-name api for skin-additions in trinidad-skins.xml
2. added SkinAddition class and addSkinAddition and getSkinAdditions for Skin
3. Check in resource bundles from the skin-additions as well as the Skin's resource bundle for translatedValue's key.

Added:
    myfaces/trinidad/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/skin/SkinAddition.java
    myfaces/trinidad/trunk/trinidad/trinidad-examples/trinidad-demo/src/main/java/org/apache/myfaces/trinidaddemo/resource/SkinBundle_fr.java
Modified:
    myfaces/trinidad/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/skin/Skin.java
    myfaces/trinidad/trunk/trinidad/trinidad-examples/trinidad-demo/src/main/java/org/apache/myfaces/trinidaddemo/resource/SkinBundle.java
    myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinExtension.java
    myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinImpl.java
    myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinUtils.java
    myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/ui/laf/xml/parse/SkinAdditionNode.java
    myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/ui/laf/xml/parse/SkinAdditionNodeParser.java

Modified: myfaces/trinidad/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/skin/Skin.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/skin/Skin.java?view=diff&rev=561122&r1=561121&r2=561122
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/skin/Skin.java (original)
+++ myfaces/trinidad/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/skin/Skin.java Mon Jul 30 14:14:30 2007
@@ -18,6 +18,7 @@
  */
 package org.apache.myfaces.trinidad.skin;
 
+import java.util.List;
 import java.util.Map;
 import java.util.MissingResourceException;
 
@@ -72,7 +73,7 @@
   abstract public Map<String, String> getStyleClassMap(RenderingContext arc);
 
   /**
-   * Returns the name of the XSS style sheet for this Skin.
+   * Returns the name of the style sheet for this Skin.
    */
   abstract public String getStyleSheetName();
 
@@ -141,7 +142,7 @@
   abstract public void registerIcon(
     String  iconName,
     Icon    icon);
-    
+
   /**
    * Registers a style sheet which defines extension-specific
    * styles.  The styles specified by this style sheet will be
@@ -149,8 +150,40 @@
    * @param styleSheetName The name of the style sheet which
    *          defines the extension's styles.
    * @throws NullPointerException if styleSheetName is null.
+   * @deprecated Use addSkinAddition(SkinAddition) instead.
+   * @see #addSkinAddition(SkinAddition)
    */
   abstract public void registerStyleSheet(
     String styleSheetName
     );
+    
+  /**
+   * Adds a SkinAddition on this Skin. You can call this method as many times 
+   * as you like for the Skin, and it will add the SkinAddition to the list of 
+   * SkinAdditions.
+   * However, it does not make sense to call this method more than once
+   * with the same SkinAddition object.
+   * This is meant for the skin-addition use-cases, where a custom component 
+   * developer has a style sheet and/or resource bundle for their custom   
+   * components, and they want the style sheet and/or resource bundle 
+   * to work for this Skin and the children Skins.
+   * The stylesheets specified in the SkinAdditions will be merged with the 
+   * Skin's own styles.
+   * The resource bundles specified in the SkinAdditions will be looked into 
+   * if the translated key is not found in the Skin's own resource bundle 
+   * during the call to getTranslatedString or getTranslatedValue.
+   * 
+   * @param skinAddition The SkinAddition object to add to the Skin.
+   * @throws NullPointerException if SkinAddition is null.
+   */
+  abstract public void addSkinAddition (
+    SkinAddition skinAddition
+    );
+    
+  /**
+   * Gets a List of SkinAdditions that have been added on this Skin.
+   * @return List a List of SkinAdditions.
+   */
+  abstract public List<SkinAddition> getSkinAdditions ();
+    
 }

Added: myfaces/trinidad/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/skin/SkinAddition.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/skin/SkinAddition.java?view=auto&rev=561122
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/skin/SkinAddition.java (added)
+++ myfaces/trinidad/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/skin/SkinAddition.java Mon Jul 30 14:14:30 2007
@@ -0,0 +1,72 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.myfaces.trinidad.skin;
+
+import java.util.Collections;
+
+import org.apache.myfaces.trinidad.context.LocaleContext;
+
+/**
+ * SkinAdditions are defined in trinidad-skins.xml file &lt;skin-addition&gt;
+ * They are used by custom component developers who have created custom
+ * components, and they need a way to 'push' in their own stylesheet and 
+ * resource bundle for these components into some skin of their choosing, 
+ * most likely the simple skin.
+ * Skin objects contain zero or more SkinAdditions. The SkinAdditions' stylesheets
+ * are merged into the Skin's own stylesheet. The SkinAdditions' resource
+ * bundle is looked at along with the Skin's own resource bundle when Skin's
+ * getTranslatedValue is called.
+ *
+ */
+public class SkinAddition
+{
+
+
+  /**
+   * Constructor takes a styleSheet name and a resourceBundle name.
+   */
+  public SkinAddition (
+    String styleSheetName,
+    String resourceBundleName
+    )
+  {
+    _styleSheetName = styleSheetName;
+    _resourceBundleName = resourceBundleName;
+  }
+  
+  /**
+   * Gets the SkinAddition's style sheet name.
+   */
+  public String getStyleSheetName()
+  {
+    return _styleSheetName;
+  } 
+  
+  /**
+   * Gets the SkinAddition's resource bundle .     
+   */
+  public String getResourceBundleName()
+  {
+    return _resourceBundleName;
+  } 
+ 
+  private final String _styleSheetName;
+  private final String _resourceBundleName;
+  
+}

Modified: myfaces/trinidad/trunk/trinidad/trinidad-examples/trinidad-demo/src/main/java/org/apache/myfaces/trinidaddemo/resource/SkinBundle.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-examples/trinidad-demo/src/main/java/org/apache/myfaces/trinidaddemo/resource/SkinBundle.java?view=diff&rev=561122&r1=561121&r2=561122
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-examples/trinidad-demo/src/main/java/org/apache/myfaces/trinidaddemo/resource/SkinBundle.java (original)
+++ myfaces/trinidad/trunk/trinidad/trinidad-examples/trinidad-demo/src/main/java/org/apache/myfaces/trinidaddemo/resource/SkinBundle.java Mon Jul 30 14:14:30 2007
@@ -32,6 +32,11 @@
   {
     {"af_tableSelectMany.SELECT_COLUMN_HEADER", "Select A Lot"},
     {"af_tableSelectOne.SELECT_COLUMN_HEADER", "Select Just One"},
+    {"af_inputDate.LAUNCH_PICKER_TIP", "Purple Launch Picker"},
+    {"Birds.SELECT_MANY", "Purple Default Select Many"},
   };
 }
+
+
+
 

Added: myfaces/trinidad/trunk/trinidad/trinidad-examples/trinidad-demo/src/main/java/org/apache/myfaces/trinidaddemo/resource/SkinBundle_fr.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-examples/trinidad-demo/src/main/java/org/apache/myfaces/trinidaddemo/resource/SkinBundle_fr.java?view=auto&rev=561122
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-examples/trinidad-demo/src/main/java/org/apache/myfaces/trinidaddemo/resource/SkinBundle_fr.java (added)
+++ myfaces/trinidad/trunk/trinidad/trinidad-examples/trinidad-demo/src/main/java/org/apache/myfaces/trinidaddemo/resource/SkinBundle_fr.java Mon Jul 30 14:14:30 2007
@@ -0,0 +1,39 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.myfaces.trinidaddemo.resource;
+
+import java.util.ListResourceBundle;
+
+public class SkinBundle_fr extends ListResourceBundle
+{
+  @Override
+  public Object[][] getContents()
+  {
+    return _CONTENTS;
+  }
+
+  static private final Object[][] _CONTENTS =
+  {
+    {"af_tableSelectMany.SELECT_COLUMN_HEADER", "Select A Lot"},
+    {"af_tableSelectOne.SELECT_COLUMN_HEADER", "Select Just One"},
+    {"af_inputDate.LAUNCH_PICKER_TIP", "French Purple Launch Picker"},
+    {"Birds.SELECT_MANY", "Purple French Select Many"},
+  };
+}
+

Modified: myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinExtension.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinExtension.java?view=diff&rev=561122&r1=561121&r2=561122
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinExtension.java (original)
+++ myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinExtension.java Mon Jul 30 14:14:30 2007
@@ -19,15 +19,9 @@
 package org.apache.myfaces.trinidadinternal.skin;
 
 import java.io.IOException;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Locale;
 import java.util.Map;
 import java.util.MissingResourceException;
-import java.util.ResourceBundle;
 import java.util.Stack;
-import java.util.concurrent.ConcurrentHashMap;
 
 import javax.faces.context.FacesContext;
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
@@ -72,14 +66,19 @@
    *               and render-kit-id match will be chosen.
    *               Must be non-null.
    * @param renderKitId The render-kit-id that this Skin is designed for.
+   * @param styleSheetName The name of the stylesheet for this Skin.
+   * @param resourceBundleName The name of the resource bundle for this Skin.
+
    * @throws NullPointerException if baseSkin, id, or family is null.
-   *
+   * 
    */
   public SkinExtension(
     Skin baseSkin,
     String id,
     String family,
-    String renderKitId
+    String renderKitId,
+    String styleSheetName,
+    String resourceBundleName
     )
   {
     if (baseSkin == null)
@@ -96,6 +95,45 @@
     _id = id;
     _family = family;
     _renderKitId = renderKitId;
+    _styleSheetName = styleSheetName;
+    _bundleName = resourceBundleName;
+  }
+  
+  /**
+   * Creates a Skin which extends the specified base
+   * Skin.
+   *
+   * @param baseSkin The base Skin that this custom
+   *        Skin "extends". If it is a Skin designed for "org.apache.myfaces.trinidad.desktop"
+   *        render-kit-id, then its base skin should be SimpleDesktopSkin.
+   *        If it is a Skin designed for "org.apache.myfaces.trinidad.pda" render-kit-id,
+   *        then its base skin should be SimplePdaSkin.
+   *        Must be non-null.
+   * @param id A string which can be used to uniquely identify the
+   *           Skin .
+   *           Must be non-null.
+   * @param family The Skin family name that this
+   *               SkinExtension belongs to. For example, you might have
+   *               a Skin that makes your pages look purple for the
+   *               desktop renderkit and a Skin that makes your pages
+   *               look purple for the pda renderkit.
+   *               You can set the skin-family to "purple" in
+   *               trinidad-config.xml, and the Skin with skin-family
+   *               and render-kit-id match will be chosen.
+   *               Must be non-null.
+   * @param renderKitId The render-kit-id that this Skin is designed for.
+   * @throws NullPointerException if baseSkin, id, or family is null.
+   * @deprecated Use the constructor that also contains styleSheetName and resourceBundleName
+   *
+   */
+  public SkinExtension(
+    Skin baseSkin,
+    String id,
+    String family,
+    String renderKitId
+    )
+  {
+    this(baseSkin, id, family, renderKitId, null, null);
   }
 
 
@@ -129,9 +167,9 @@
   }
 
   /**
-   * Returns the name of the XSS style sheet for this Skin if
-   * on has been set
-   * @see setStyleSheetName
+   * Returns the name of the style sheet for this Skin if
+   * one has been set
+   * @see #setStyleSheetName(String)
    */
   @Override
   public String getStyleSheetName()
@@ -150,21 +188,22 @@
 
 
   /**
-   * Returns the name of the bundle for the extension.
+   * Returns the name of the bundle for the SkinExtension.
    */
   @Override
   public String getBundleName()
   {
-    if (_bundleName != null)
-      return _bundleName;
-    return _baseSkin.getBundleName();
+    return _bundleName;
   }
 
   /**
-   * Returns the name of the bundle for the extension.
+   * Returns the name of the bundle for this SkinExtension.
+   * @deprecated Use the constructor that takes a resourceBundleName instead.
    */
   public void setBundleName(String bundleName)
   {
+     // TODO take out method once sufficient time has past since deprecation
+    // in July, 2007
     _bundleName = bundleName;
   }
 
@@ -197,72 +236,30 @@
     return _baseSkin.getStyleClassMap(arc);
   }
 
+
+
   /**
-   * Override of Skin.getTranslatedValue() which
-   * supports pulling translations from component providers
-   * as well as the base Skin.
-   */
+    * Override of Skin.getTranslatedValue() which
+    * supports pulling translations from Skin and if not found from the base Skin.
+  */
   @Override
   public Object getTranslatedValue(
     LocaleContext lContext,
     String        key
     ) throws MissingResourceException
   {
-    // Short-circuit when there is no customized translation
-    if (_bundleName == null)
-    {
+    // Look for the skin's translated value (first bundle name, then registered bundles)
+    // if that's not found, then look in the base skin's translated value.
+    // getCachedTranslatedValue will protect against MissingResourceExceptions
+    Object translatedValue = super.getCachedTranslatedValue(lContext, key);
+    // TODO Cache base skin's non-null translatedValue with this skin to
+    // make it faster.
+    if (translatedValue == null)
       return getBaseSkin().getTranslatedValue(lContext, key);
-    }
-
-    // First, check the local translations cache
-    Object value = _getCachedTranslatedValue(lContext, key);
-    if (value != null)
-    {
-      // testTranslationKey(key);//jmw test for testing translation keys
-      if (value == _NULL_TRANSLATION)
-        return null;
-
-      return value;
-    }
-
-    // Next, check to see if we can get a translation from
-    // a bundle that has been explicitly registered on this
-    // SkinExtension instance.  (We can just use
-    // Skin.getTranslatedValue() for this.)
-    // Note: In order to avoid MissingResourceExceptions, we
-    // first check to see if the translation key is available
-    // before looking up the message in the ResourceBundle.
-    if (_isTranslationKeyAvailable(lContext, key))
-    {
-      try
-      {
-        value = super.getTranslatedValue(lContext, key);
-      }
-      catch (MissingResourceException e)
-      {
-        // It is possible that the call to getBundle() might
-        // fail with a MissingResourceException if the customer
-        // has only provided a custom bundle for certain languages.
-        // This is okay, so we just eat these exceptions.
-        ;
-      }
-    }
-
-    // If we didn't find a value in the local bundle, try getting
-    // the translation from the base Skin.
-    if (value == null)
-    {
-      Skin baseSkin = getBaseSkin();
-      value = baseSkin.getTranslatedValue(lContext, key);
-    }
-
-    // If we found an translation, store it in the cache so that
-    // we don't have to search for it again next time.
-    _putCachedTranslatedValue(lContext, key, value);
-
-    return value;
+    else
+      return translatedValue;
   }
-
+   
   /**
    * Try to pull a locally set property, if null
    * pull a property from the base skin.
@@ -357,6 +354,7 @@
 
   /**
    * Sets the name of the style sheet for this Skin.
+   * @deprecated Use the SkinExtension constructor that takes a styleSheetName instead.
    */
   public void setStyleSheetName(String styleSheetName)
   {
@@ -407,133 +405,6 @@
     }
   }
 
-
-  // Gets the translated value from the local translations cache.
-  private Object _getCachedTranslatedValue(
-    LocaleContext lContext,
-    String        key
-    )
-  {
-    // Get the translation Locale
-    Locale locale = lContext.getTranslationLocale();
-
-    // Get all of the translations for the current translation Locale
-    Map<String, Object> localeTranslations = _translations.get(locale);
-
-    if (localeTranslations != null)
-      return localeTranslations.get(key);
-
-    return null;
-  }
-
-  // Stores the translated value in the local translations cache
-  // Note the synchronization!  This is necessary because we may be
-  // putting translations into the cache from multiple request threads.
-  synchronized private void _putCachedTranslatedValue(
-    LocaleContext lContext,
-    String        key,
-    Object        value
-    )
-  {
-    // Use a placeholder to mark null translations
-    if (value == null)
-      value = _NULL_TRANSLATION;
-
-    // Get the translation Locale
-    Locale locale = lContext.getTranslationLocale();
-
-    // Get all of the translations for the current translation locale
-    Map<String, Object> localeTranslations = _translations.get(locale);
-
-    if (localeTranslations == null)
-    {
-      // If we didn't previously have any translations for this
-      // Locale, create storage for translations now...
-      localeTranslations = new ConcurrentHashMap<String, Object>();
-      _translations.put(locale, localeTranslations);
-    }
-
-    // Store the new component translations array
-    localeTranslations.put(key, value);
-  }
-
-  // Checks whether there is a translation with the specified
-  // key in the custom ResourceBundle.  We call this method
-  // before calling ResourceBundle.getObject() because custom
-  // bundles are not required to provide translations for all
-  // messages.  If we didn't first check _isTranslationKeyAvailable(),
-  // we might see a lot of MissingResourceExceptions.
-  private boolean _isTranslationKeyAvailable(
-    LocaleContext lContext,
-    String        key
-    )
-  {
-    String bundleName = getBundleName();
-    if (bundleName == null)
-      return false;
-
-    Map<String, Boolean> keys = _getTranslationKeys(lContext);
-
-    return keys.containsKey(key);
-  }
-
-  // Returns the a Map which contains the translation keys for
-  // the specified locale.
-  @SuppressWarnings("unchecked")
-  private Map<String, Boolean> _getTranslationKeys(
-    LocaleContext lContext
-    )
-  {
-   // We store the translation keys map in the translation cache
-    Map<String, Boolean> keys =
-      (Map<String, Boolean>)_getCachedTranslatedValue(lContext,
-                                              _TRANSLATION_KEYS_KEY);
-
-    if (keys == null)
-    {
-      String bundleName = getBundleName();
-      assert bundleName != null;
-
-      keys = new HashMap<String, Boolean>();
-
-      ResourceBundle bundle = null;
-
-      try
-      {
-        bundle = lContext.getBundle(bundleName);
-      }
-      catch (MissingResourceException e)
-      {
-        // It is possible that the call to getBundle() might
-        // fail with a MissingResourceException if the customer
-        // has only provided a custom bundle for certain languages.
-        // This is okay, so we just eat these exceptions.
-        ;
-      }
-
-      if (bundle != null)
-      {
-        Enumeration<String> en = bundle.getKeys();
-
-        if (en != null)
-        {
-          while (en.hasMoreElements())
-            keys.put(en.nextElement(), Boolean.TRUE);
-        }
-      }
-
-      if (keys.isEmpty())
-        keys = Collections.emptyMap();
-      else
-        keys = Collections.unmodifiableMap(keys);
-
-      _putCachedTranslatedValue(lContext,
-                                _TRANSLATION_KEYS_KEY,
-                                keys);
-    }
-
-    return keys;
-  }
   /**
    * Find the actual icon
    * @param refIcon a ReferenceIcon instance
@@ -636,21 +507,7 @@
   private String      _styleSheetName;
   private String      _bundleName;
 
-  // Now that we look into possibly multiple ResourceBundles
-  // to find a translation (eg. the local bundle, a component
-  // provider's bundle, the super-LAFs bundle), translation lookups
-  // can become expensive.  So, we keep a local cache all translated
-  // resources which is populated as we go along.  Translations are
-  // hashed by Locale/component name/key.  This is more
-  // hashing than usual to get a translation, but at least this is
-  // a finite/fixed expense.  At least this way translation lookup
-  // performance won't degrade if a custom bundle is provided - or
-  // if we need to pull the translation from some other Skin.
-  //
-  // This HashMap hashes Locales -> HashMaps.
-  // The HashMaps map translation key to message.
-  private ConcurrentHashMap<Locale, Map<String, Object>> _translations =
-    new ConcurrentHashMap<Locale, Map<String, Object>>(13);
+
 
   // The StyleSheetDocument for the base LookAndFeel's style sheet
   private StyleSheetDocument _baseStyleSheetDocument;
@@ -663,15 +520,8 @@
   // the SkinExtension.
   private StyleSheetDocument _fullStyleSheetDocument;
 
-  // Special key that we use for storing the translation keys
-  // in the translation cache.
-  private static final String _TRANSLATION_KEYS_KEY = "_uixLafTransKeys";
-
   // Placeholder for null icons
   private static final Icon _NULL_ICON = new NullIcon();
-
-  // Placeholder for null translations
-  private static final Object _NULL_TRANSLATION = new Object();
 
   // Error messages
   private static final String _CIRCULAR_INCLUDE_ERROR =

Modified: myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinImpl.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinImpl.java?view=diff&rev=561122&r1=561121&r2=561122
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinImpl.java (original)
+++ myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinImpl.java Mon Jul 30 14:14:30 2007
@@ -19,14 +19,20 @@
 package org.apache.myfaces.trinidadinternal.skin;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.MissingResourceException;
+import java.util.ResourceBundle;
 import java.util.Stack;
 
 import java.util.concurrent.ConcurrentHashMap;
 
+import java.util.concurrent.atomic.AtomicInteger;
+
 import javax.faces.context.ExternalContext;
 
 import javax.faces.context.FacesContext;
@@ -37,6 +43,7 @@
 import org.apache.myfaces.trinidad.skin.Icon;
 import org.apache.myfaces.trinidad.skin.Skin;
 
+import org.apache.myfaces.trinidad.skin.SkinAddition;
 import org.apache.myfaces.trinidadinternal.renderkit.core.CoreRenderingContext;
 import org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.SkinProperties;
 import org.apache.myfaces.trinidadinternal.share.config.Configuration;
@@ -52,7 +59,6 @@
 /**
  * Defines the components (icons, styles, etc)
  * which are used to implement a particular skin.
- * @todo. look through UIExtension comments.
  *
  * This implementation class adds the details that should
  * not be exposed outside of this API.
@@ -64,6 +70,7 @@
  */
 abstract public class SkinImpl extends Skin
 {
+
   /**
    * Returns an string identifier which uniquely identies
    * this Skin implementation.  Skin implementations
@@ -112,7 +119,7 @@
   }
 
   /**
-   * Returns the name of the XSS style sheet for this Skin.
+   * Returns the name of the style sheet for this Skin.
    */
   @Override
   abstract public String getStyleSheetName();
@@ -141,6 +148,8 @@
    *                 Cannot be null.
    * @param key The key of the translation to retrieve. Cannot be null.
    * @throws NullPointerException if lContext or key is null.
+   * @throws MissingResourceException if the resource key cannot be found in the skin's bundle
+   * or the skin additions' bundles.
    */
   @Override
   public Object getTranslatedValue(
@@ -148,74 +157,33 @@
     String        key
     ) throws MissingResourceException
   {
-    //testTranslationKey(key); //jmw test
-
     if (lContext == null)
       throw new NullPointerException(_LOG.getMessage(
         "NULL_LOCALE_CONTEXT"));
     if (key == null)
       throw new NullPointerException("Null key");
 
-    String bundleName = getBundleName();
-    if (bundleName == null)
+    List<String> resourceBundleNames = _getResourceBundleNames();
+    
+    // if there is nothing to check, return null
+    if (resourceBundleNames.size() == 0)
       return null;
-
-    return lContext.getBundle(bundleName).getObject(key);
-  }
-
-
-
-  /**
-   *  This is a test function.
-   *  This looks at the stack that I have on the request map for the
-   *  current component being rendered -- I store the prefix in
-   *  UIComponentUINode.
-   * @param key
-   */
-   /********* jmw for testing translation keys
-  public static void testTranslationKey(
-    String key)
-  {
-
-    javax.faces.context.FacesContext fcontext =
-      javax.faces.context.FacesContext.getCurrentInstance();
-    Stack translationKeyStack  = (Stack)fcontext.getExternalContext().
-                                        getRequestMap().get("TRANSLATION_KEY");
-
-
-    String translationKeyPrefix = null;
-
-    if ((translationKeyStack != null) && !translationKeyStack.empty())
-      translationKeyPrefix = (String)translationKeyStack.peek();
-
-
- //   System.out.println(translationKeyPrefix + " / " + key); //jmw test
-
-    String keyPrefix = null;
-    int index = key.indexOf('.');
-    if (index != -1)
-      keyPrefix = key.substring(0, index);
-
-    if (translationKeyPrefix != null)
-    {
-      if (!(translationKeyPrefix.equalsIgnoreCase(keyPrefix)))
-      {
-        System.out.println("***NO MATCH " + translationKeyPrefix + " / " + key + "");
-      }
-    }
-    if ((translationKeyPrefix == null) && (key != null) &&
-      !(key.equals("WINDOW_CREATION_ERROR") || key.equals("NO_SCRIPT_MESSAGE")))
+      
+    Object translatedValue = getCachedTranslatedValue(lContext, key);
+    if (translatedValue == null)
     {
-      System.out.println("***nothing was rendered, but I have a key of " + key);
+      throw new MissingResourceException("Can't find resource for bundle "
+                                         +resourceBundleNames
+                                         +", key "+key,
+                                         getBundleName(),
+                                         key);
     }
+    
+    return translatedValue;
+
 
   }
-  ****/
 
-  /**
-   * Returns the name of the ResourceBundle for the Skin.
-   */
-  abstract protected String getBundleName();
 
   /**
    * Our renderers call this to get the icon. This returns a renderable
@@ -260,56 +228,6 @@
   }
 
   /**
-   * Find the actual icon
-   * @param refIcon a ReferenceIcon instance
-   * @param referencedIconStack  The stack of reference icon names which have
-   *          already been visited.  Used to detect circular dependencies.
-   * @return icon which is resolved. i.e., it is not a ReferenceIcon.
-   */
-  private Icon _resolveReferenceIcon(
-    ReferenceIcon refIcon,
-    Stack<String> referencedIconStack)
-  {
-    String refName = refIcon.getName();
-
-    // make sure we don't have a circular dependency
-    if ( _stackContains(referencedIconStack, refName))
-    {
-      if (_LOG.isWarning())
-        _LOG.warning(_CIRCULAR_INCLUDE_ERROR + refName);
-      return null;
-    }
-    
-    if (referencedIconStack == null)
-    {
-      referencedIconStack = new Stack<String>();
-    }
-
-    referencedIconStack.push(refName);
-
-    Icon icon = getIcon(refName, false);
-
-    if ((icon instanceof ReferenceIcon) && (icon != null))
-    {
-
-      return _resolveReferenceIcon((ReferenceIcon)icon,
-                                    referencedIconStack);
-
-    }
-
-    return icon;
-  }
-
-    // Tests whether the value is present in the (possibly null) stack.
-  private static boolean _stackContains(Stack<String> stack, Object value)
-  {
-    if (stack == null)
-      return false;
-
-    return stack.contains(value);
-  }
-
-  /**
    * Registers an Icon for the specified icon name.
    * @param iconName  The name of the icon. Cannot be null.
    * @param icon      The Icon to register.
@@ -327,45 +245,58 @@
 
     _icons.put(iconName, icon);
   }
-
+ 
   /**
-   * Registers a style sheet which defines extension-specific
-   * styles.  The styles specified by this style sheet will be
-   * merged with the Skin's own styles.  The full set
-   * of styles can be obtained by calling getStyleSheetDocument().
-   * @todo Is this even supported???
-   * @param styleSheetName The name of the style sheet which
-   *          defines the extension's styles.  This style sheet
-   *          should be installed under the directory specified by
-   *          Configuration.STYLES_DIRECTORY path.
-   * @see #getStyleSheetDocument
-   * @throws NullPointerException if styleSheetName is null.
+   * Adds a SkinAddition on this Skin. You can call this method as many times 
+   * as you like for the Skin, and it will add the SkinAddition to the list of 
+   * SkinAdditions.
+   * However, it does not make sense to call this method more than once
+   * with the same SkinAddition object.
+   * This is meant for the skin-addition use-cases, where a custom component 
+   * developer has a style sheet and/or resource bundle for their custom   
+   * components, and they want the style sheet and/or resource bundle 
+   * to work for this Skin and the children Skins.
+   * The stylesheets specified in the SkinAdditions will be merged with the 
+   * Skin's own styles.
+   * The resource bundles specified in the SkinAdditions will be looked into 
+   * if the translated key is not found in the Skin's own resource bundle 
+   * during the call to getTranslatedString or getTranslatedValue.
+   * 
+   * @param skinAddition The SkinAddition object to add to the Skin.
+   * @throws NullPointerException if SkinAddition is null.
    */
-  @Override
-  public void registerStyleSheet(
-    String styleSheetName
+  public void addSkinAddition (
+    SkinAddition skinAddition
     )
   {
-    if (styleSheetName == null)
-      throw new NullPointerException(_LOG.getMessage(
-        "NULL_STYLESHEETNAME"));
-
-    if (_extensionStyleSheetNames == null)
-    {
-      _extensionStyleSheetNames = new ArrayList<String>();
-    }
+    // TODO change error message to use the error message resource bundle.
+     if (skinAddition == null)
+       throw new NullPointerException(
+               "A null SkinAddition object was passed to addSkinAddition.");
 
-    _extensionStyleSheetNames.add(styleSheetName);
+     if (_skinAdditions == null)
+     {
+       _skinAdditions = new ArrayList<SkinAddition>();
+     }
+     _skinAdditions.add(skinAddition);
   }
-
+  
   /**
-   * Returns the style class map, or null if there is no map.
-   * @param arc RenderingContext
-   * @return Map<String, String> It should be a map that contains the full style class name as 
-   * the key, and the value could be a shortened style class name,
-   * or a portlet style class name, etc.
+   * Gets an unmodifiable List of SkinAdditions that have been added 
+   * on this Skin. To add to the SkinAdditions List, 
+   * call addSkinAddition(SkinAddition)
+   * @return List an unmodifiable List of SkinAdditions.
+   * @see #addSkinAddition(SkinAddition)
    */
-   
+  public List<SkinAddition> getSkinAdditions()
+  {
+    if (_skinAdditions == null)
+    {
+      return Collections.emptyList();
+    }
+    else  
+      return Collections.unmodifiableList(_skinAdditions);
+  }
 
    /**
     * Returns the style class map, or null if there is no map.
@@ -374,8 +305,9 @@
     * short style classes can be used instead of the full style class
     * names to reduce the overall size of generated content.
     * @param arc RenderingContext
-    * @return Map&lt;String, String&gt; The default implemention returns a map of full
-    * style class names to shortened style classes.
+    * @return Map&lt;String, String&gt; It should be a map that contains the full style class name
+    * as the key, and the value could be a shortened style class name,
+    * or a portlet style class name, etc.
     */
   @Override
    public Map<String, String> getStyleClassMap(
@@ -397,7 +329,7 @@
   /**
    * Returns the StyleSheetDocument object which defines all of the
    * styles for this Skin, including any styles that are
-   * contributed by UIExtensions.
+   * contributed by skin-additions.
    */
   public StyleSheetDocument getStyleSheetDocument(StyleContext context)
   {
@@ -443,6 +375,130 @@
   {
     _properties.put(key, value);
   }
+  
+  /**
+   * Returns a translated value in the LocaleContext's translation Locale, or null
+   * if the key could not be found.
+   * This value may or may not be a String, and developers should avoid
+   * calling toString() unless absolutely necessary.
+   * This method protects against MissingResourceExceptions by checking that the key exists 
+   * before calling the bundle's getObject method. It eats any MissingResourceExceptions as
+   * a result of not finding the bundle, since there can be multiple bundles per skin, and
+   * we could get a lot of MissingResourceExceptions otherwise.
+   * Then the method caches the value once it is found in a particular
+   * resource bundle. 
+   * This method is useful for SkinExtensions which will also check their ancestor skins
+   * for the resource if it is not found in the SkinExtension. MissingResourceExceptions would
+   * be numerous if we didn't protect against them.
+   * If you want to throw a MissingResourceException once all the ancestor skins and their
+   * bundles and registered bundles are checked, then you should call getTranslatedValue for the
+   * most base skin, and it will throw a MissingResourceException if the
+   * key was not found in any of the bundles.
+   * @see #getTranslatedValue(LocaleContext, String)
+   * @param lContext The LocaleContext which provides the translation Locale.
+   *                 Cannot be null.
+   * @param key The key of the translation to retrieve. Cannot be null.
+   * @throws NullPointerException if lContext or key is null.
+   * @return Object translated value of the key;
+   *         null if bundleName and skin-addition bundleNames are null for this Skin;
+   *         null if the key cannot be found in the bundle or registered bundles -or-
+   *         
+
+   */
+  protected Object getCachedTranslatedValue(
+    LocaleContext lContext,
+    String        key
+    )
+  {
+    if (lContext == null)
+      throw new NullPointerException(_LOG.getMessage(
+        "NULL_LOCALE_CONTEXT"));
+    if (key == null)
+      throw new NullPointerException("Null key");
+
+    List<String> resourceBundleNames = _getResourceBundleNames();
+    
+    // if there is nothing to check, return null
+    if (resourceBundleNames.size() == 0)
+      return null;
+      
+   
+    return _getCachedTranslationValueFromLocale(lContext, 
+                                                resourceBundleNames, key);
+    
+  }
+
+  /**
+   * @param styleSheetName
+   * @see #addSkinAddition(SkinAddition)
+   * @deprecated Use addSkinAddition instead
+   */
+  public void registerStyleSheet(String styleSheetName) 
+  {
+    //TODO Take out deprecated after sufficient amount of time has passed
+    // deprecated July, 2007
+    SkinAddition addition = new SkinAddition(styleSheetName, null);
+    addSkinAddition(addition);
+  }
+
+  /**
+  * Returns the name of the ResourceBundle for this Skin instance.
+  * This does not include the SkinAddition resource bundles.
+  * We differentiate between the two types of resource bundles so that
+  * the Skin's own resource bundle can take precedence.
+  */
+  abstract protected String getBundleName();
+
+
+  /**
+   * Find the actual icon
+   * @param refIcon a ReferenceIcon instance
+   * @param referencedIconStack  The stack of reference icon names which have
+   *          already been visited.  Used to detect circular dependencies.
+   * @return icon which is resolved. i.e., it is not a ReferenceIcon.
+   */
+  private Icon _resolveReferenceIcon(
+    ReferenceIcon refIcon,
+    Stack<String> referencedIconStack)
+  {
+    String refName = refIcon.getName();
+
+    // make sure we don't have a circular dependency
+    if ( _stackContains(referencedIconStack, refName))
+    {
+      if (_LOG.isWarning())
+        _LOG.warning(_CIRCULAR_INCLUDE_ERROR + refName);
+      return null;
+    }
+    
+    if (referencedIconStack == null)
+    {
+      referencedIconStack = new Stack<String>();
+    }
+
+    referencedIconStack.push(refName);
+
+    Icon icon = getIcon(refName, false);
+
+    if ((icon instanceof ReferenceIcon) && (icon != null))
+    {
+
+      return _resolveReferenceIcon((ReferenceIcon)icon,
+                                    referencedIconStack);
+
+    }
+
+    return icon;
+  }
+
+  // Tests whether the value is present in the (possibly null) stack.
+  private static boolean _stackContains(Stack<String> stack, Object value)
+  {
+    if (stack == null)
+      return false;
+
+    return stack.contains(value);
+  }
 
   // Checks to see whether any of our style sheets have been updated
   private boolean _checkStylesModified(
@@ -454,16 +510,16 @@
     if (_skinStyleSheet != null)
       modified = _skinStyleSheet.checkModified(context);
 
-    // We also check all of the UIExtension style sheets even
+    // We also check all of the skin-addition style sheets even
     // if we already know that the skin's style sheet has been
     // modified.  We need to do this because we want to call
     // StyleSheetEntry.checkModified() for each entry - otherwise
     // out of date StyleSheetEntries may not get updated.
-    if (_extensionStyleSheets != null)
+    if (_skinAdditionStyleSheets != null)
     {
-      for (int i = 0; i < _extensionStyleSheets.length; i++)
+      for (int i = 0; i < _skinAdditionStyleSheets.length; i++)
       {
-        StyleSheetEntry entry = _extensionStyleSheets[i];
+        StyleSheetEntry entry = _skinAdditionStyleSheets[i];
         if (entry.checkModified(context))
           modified = true;
       }
@@ -547,8 +603,8 @@
         _registerIconsAndPropertiesFromStyleSheetEntry(_skinStyleSheet);
       }
 
-      // Now create entries for UIExtension-specific style sheets.
-      _extensionStyleSheets = _getExtensionStyleSheets(context);
+      // Now create entries for skin-addition-specific style sheets.
+      _skinAdditionStyleSheets = _getSkinAdditionsStyleSheets(context);
     }
 
     // Now merge all of the documents provided by all of our
@@ -558,25 +614,25 @@
     if (_skinStyleSheet != null)
       document = _skinStyleSheet.getDocument();
 
-    // Merge in any UIExtension style sheets on top of
+    // Merge in any skin-addition style sheets on top of
     // the skin's style sheet
-    if (_extensionStyleSheets != null)
+    if (_skinAdditionStyleSheets != null)
     {
-      for (int i = 0; i < _extensionStyleSheets.length; i++)
+      for (int i = 0; i < _skinAdditionStyleSheets.length; i++)
       {
-        StyleSheetEntry entry = _extensionStyleSheets[i];
+        StyleSheetEntry entry = _skinAdditionStyleSheets[i];
         if (entry != null)
         {
           // add the icons and properties that are in the 
-          // extensionDocument's StyleSheetEntry
+          // skin-addition's StyleSheetEntry
            _registerIconsAndPropertiesFromStyleSheetEntry(entry);
            
           // now merge the css properties
-          StyleSheetDocument extensionDocument = entry.getDocument();
+          StyleSheetDocument additionDocument = entry.getDocument();
   
-          if (extensionDocument != null)
+          if (additionDocument != null)
           {
-            // Merge the UIExtension's StyleSheetDocument on top of
+            // Merge the skin-addition's StyleSheetDocument on top of
             // the current StyleSheetDocument.  Note: This is not
             // exactly efficient - we would be better off creating
             // an array of StyleSheetDocuments and merging them all
@@ -584,7 +640,7 @@
             // executed, this shouldn't be a bottleneck...
             document = StyleSheetDocumentUtils.mergeStyleSheetDocuments(
                                                  document,
-                                                 extensionDocument);
+                                                 additionDocument);
   
           }
         }
@@ -604,19 +660,20 @@
                                   StyleSheetDocument.UNKNOWN_TIMESTAMP);
   }
 
-  // Gets the StyleSheetEntries for UIExtensions
-  private StyleSheetEntry[] _getExtensionStyleSheets(StyleContext context)
+  // Gets the StyleSheetEntries for skin-additions
+  private StyleSheetEntry[] _getSkinAdditionsStyleSheets(StyleContext context)
   {
-    if (_extensionStyleSheetNames == null)
+    List<String> skinAdditionStyleSheetNames = _getSkinAdditionsStyleSheetNames();
+    if (skinAdditionStyleSheetNames.size() == 0)
       return null;
 
     // Create a list to hold our StyleSheetEntries
-    int count = _extensionStyleSheetNames.size();
+    int count = skinAdditionStyleSheetNames.size();
     List<StyleSheetEntry> entries = new ArrayList<StyleSheetEntry>(count);
 
     // Loop through all registered style sheet names and
     // try to create a StyleSheetEntry for each name.
-    for(String name : _extensionStyleSheetNames)
+    for(String name : skinAdditionStyleSheetNames)
     {
       StyleSheetEntry entry = StyleSheetEntry.createEntry(context, name);
       if (entry != null)
@@ -627,30 +684,328 @@
 
     if (!entries.isEmpty())
     {
-      _extensionStyleSheets = new StyleSheetEntry[entries.size()];
-      return entries.toArray(_extensionStyleSheets);
+      _skinAdditionStyleSheets = new StyleSheetEntry[entries.size()];
+      return entries.toArray(_skinAdditionStyleSheets);
     }
 
     return null;
   }
+  
+  /*
+   * Returns an  List of skin-addition style sheets for the Skin. 
+   * These stylesheets are added with addSkinAddition.
+   * This List does not include the skin's own stylesheet.
+   * @return List<String> of skin addition stylesheet names. It will
+   * return a List of size 0 if no skin addition stylesheets exist.
+   * @see #addSkinAddition(String, String)
+   * @see #getStyleSheetName()
+   */
+  private List<String> _getSkinAdditionsStyleSheetNames() 
+  {
+    // Get all the SkinAdditions's style sheet names.
+    // Get the style sheet names and create a List
+    // Cache this list in an instance variable
+
+    if (_skinAdditionStyleSheetNames != null)
+      return _skinAdditionStyleSheetNames;
+  
+    // loop through all the SkinAdditions and get the resource bundles
+    List<SkinAddition> additions = getSkinAdditions();
+
+    List<String> styleSheetNames = new ArrayList<String>(additions.size());
+
+    for (SkinAddition addition : additions)
+    {
+      String name = addition.getStyleSheetName();
+      if (name != null)
+      {
+        styleSheetNames.add(name);
+      }
+    }
+    
+    // cache in instance variable
+    _skinAdditionStyleSheetNames = styleSheetNames;
+
+    return _skinAdditionStyleSheetNames;
+  }
+  
+  /*
+   * Returns the List of all the ResourceBundles for the Skin --
+   * including Skin's bundle and the skin addition bundles
+   * These resourceBundles are added with addSkinAddition.
+   * @see #addSkinAddition(String, String)
+   * @see #getBundleName()
+   */
+  private List<String> _getResourceBundleNames() 
+  {
+    // Get all the SkinAdditions's resource bundles.
+    // Get the resource bundle names and create a List
+    // Cache this list in instance variable
+    
+    // return if already cached
+    if (_resourceBundleNames != null)
+      return _resourceBundleNames;
+  
+    // We haven't retrieved the bundle names yet, so do so now.
+    String bundleName = getBundleName();
+    
+    List<SkinAddition> additions = getSkinAdditions();
+    
+    int resourceBundleCount = additions.size();
+    if (bundleName != null)
+      resourceBundleCount++;
+    
+    List<String> bundleNameList = new ArrayList<String>(resourceBundleCount);
+    
+    if (bundleName != null)
+        bundleNameList.add(bundleName);
+    
+    for (SkinAddition addition : additions)
+    {
+      String name = addition.getResourceBundleName();
+      if (name != null)
+      {
+        bundleNameList.add(name);
+      }
+    }
+ 
+    // cache in instance variable
+    _resourceBundleNames = bundleNameList;
+    
+    return _resourceBundleNames;
+  }
+  
+  // get the cached value for the locale and key from the _translations map.
+  // If the value does not exist, then find it in the resource bundles,
+  // searching the Skin's bundle first, then each skin addition resource
+  // bundle until it is found. This method fills in the cached key/value map
+  // as we look for the key/value. It keeps track of which bundles we looked
+  // in so that we don't have to look in them any more for this session.
+  private Object _getCachedTranslationValueFromLocale(
+    LocaleContext lContext,
+    List<String> resourceBundleNames,
+    String        key
+    )
+  {
+    Locale locale = lContext.getTranslationLocale();
+    
+    KeyValueMapStatus keyValueMapStatus = _translations.get(locale);
+    Map keyValueMap = null;
+    
+    if (keyValueMapStatus != null)
+    {
+      keyValueMap = keyValueMapStatus.getKeyValueMap();
+      if (keyValueMap != null)
+      {
+        Object value = keyValueMap.get(key);
+        if (value != null)
+        {
+          return value; 
+        }
+      }
+    }
+    else
+    {
+      // create the keyValueMapStatus object and put it on the locale
+      synchronized (_translations)
+      {
+        if (!_translations.contains(locale))
+        {
+          keyValueMapStatus = new KeyValueMapStatus();
+          keyValueMap = keyValueMapStatus.getKeyValueMap();
+          _translations.put(locale, keyValueMapStatus);
+        }
+      }
+    }
+    
+    // at this point the keyValueMapStatus is set on the locale, 
+    // and we know we have to fill it in.
+    // getProcessedBundlesIndex will tell us which resource bundles
+    // we have already processed (locale bundle + skin-addition bundles)
+    // we increment this number after we look in each bundle and update
+    // the keyValueMap.
+            
+    int numberOfBundleNames = resourceBundleNames.size();
+    
+    // in theory, multiple threads could get the same processedBundleIndex
+    // here, so we could get all these threads updating the same map, but
+    // it will eventually update the index, so I won't worry about this now.
+    int startIndex = keyValueMapStatus.getProcessedBundlesIndex();
+    for (int i=startIndex; i < numberOfBundleNames;)
+    {
+      String bundleName = resourceBundleNames.get(i);
+      // 'true' means to check if the key already exists in the keyValueMap and 
+      // if so do not override.
+      _fillInKeyValueMap(lContext, bundleName, keyValueMap, (i==0));
+      i = keyValueMapStatus.incrementAndGetProcessedBundlesIndex();
+      Object value = keyValueMap.get(key);
+      if (value != null)
+      {
+        return value;
+      } 
+    }   
+    
+    // nothing was found
+    return null;
+
+  }
+
+  /**
+   * Fill in the keyValueMap with all the keys and values for the ResourceBundle.
+   * The ResourceBundle is found by calling lContext.getBundle(bundleName).
+   * MissingResourceExceptions are not thrown, since it is possible that 
+   * SkinExtensions have only provided custom bundles for certain languages.
+   * @param lContext LocaleContext,  LocaleContext maintains a cache of found ResourceBundles
+   * @param bundleName the resource bundle's name.
+   * @param keyValueMap A Map of bundle keys to their values
+   * @param checkForKey If true, we will check if the key is already in the map 
+   *                    and not re-add it. If false, we don't bother checking, 
+   *                    we just add it. When this method is called for the
+   *                    first bundle, then we know we do not need to check.
+   */
+  private void _fillInKeyValueMap(
+    LocaleContext lContext, 
+    String        bundleName,
+    Map           keyValueMap,
+    boolean       checkForKey)
+  {
+  
+    ResourceBundle bundle = null;
+    
+    try
+    {
+      bundle = lContext.getBundle(bundleName);
+    }
+    catch (MissingResourceException e)
+    {
+       // It is possible that the call to getBundle() might
+       // fail with a MissingResourceException if the customer
+       // has only provided a custom bundle for certain languages.
+       // This is okay, so we just eat these exceptions.
+       ;
+    }
+    
+    if (bundle != null)
+    { 
+      Enumeration<String> en = bundle.getKeys();
+    
+      if (en != null)
+      {
+        while (en.hasMoreElements())
+        {
+          String bundleKey = en.nextElement();
+          // if checkForKey is true, don't override an existing key/value
+          if (checkForKey)
+          {
+            if (!keyValueMap.containsKey(bundleKey))
+            {
+              Object value = bundle.getObject(bundleKey);
+              if (value != null)
+                keyValueMap.put(bundleKey, value);
+            }            
+          }
+          else
+          {
+            Object value = bundle.getObject(bundleKey);
+            if (value != null)
+              keyValueMap.put(bundleKey, value);             
+          }
+        }
+      }
+    }       
+  }
+  
+  // This is the 'value' of the _translations map.
+  // This contains a translation key/value map which contains
+  // all the translation keys and values in the resource bundles
+  // we have processed thus far for a particular locale.
+  // It also contains an index which keeps track of how
+  // many of the bundles we have checked so far, so that
+  // we don't recheck a bundle.
+  // This is to help with performance, since getting
+  // values from a resource bundle is expensive.
+  private static class KeyValueMapStatus
+  {
+   
+    KeyValueMapStatus()
+    {
+      _keyValueMap = new ConcurrentHashMap<String, Object>();
+      _processedBundlesIndex = new AtomicInteger(0);
+    }
+    
+    // get the current key/value Map
+    public Map<String, Object> getKeyValueMap()
+    {
+      return _keyValueMap;
+    }
+    
+    // Get the current value of processedBundlesIndex.
+    public int getProcessedBundlesIndex()
+    {
+      return _processedBundlesIndex.get();
+    } 
+    
+    // Atomically increment by one the current value of processBundlesIndex.
+    // @return the updated value
+    public int incrementAndGetProcessedBundlesIndex()
+    {
+      return _processedBundlesIndex.incrementAndGet();
+    }         
+
+    Map<String, Object> _keyValueMap;
+    // This keeps track of the number of bundles that have been processed.
+    // A Skin can have multiple bundles registered on it -- a local resource 
+    // bundle + any number of skin-addition bundles.  
+    // When we get a key (getTranslatedValue), we check each bundle
+    // and fill in the keyValueMap until we find the key. 
+    // We update this index after we process each
+    // bundle, so that we don't recheck a bundle. We only have to check a bundle 
+    // once per locale per session, because we cache the keys/values for each
+    // bundle we check in the _keyValueMap.
+    AtomicInteger       _processedBundlesIndex;
+  }
+    
+  // Now that we look into possibly multiple ResourceBundles
+  // to find a translation (eg. the local bundle + a skin-addition's
+  // bundle), translation lookups can become expensive.
+  // To get a value, we call: lContext.getBundle(name).getObject(key).
+  // We speed things up by caching the translations
+  // in this _translations map. 
+  // As we get a call to getTranslatedValue with a key, we
+  // look through our keyValueMap. If it isn't there, we loop
+  // through each resource bundle we haven't yet checked, 
+  // and we get all the keys and values
+  // and when we have all the keys/values for the
+  // bundle, we return if the key/value is there. Otherwise,
+  // we check the next bundle and so on.
+  // If we never get a request for a key in bundle X, that bundle X's
+  // keys/values will never be put in the keyvalue map. This is a good thing.
+  private ConcurrentHashMap<Locale, KeyValueMapStatus> _translations =
+    new ConcurrentHashMap<Locale, KeyValueMapStatus>(13);
 
   // HashMap that maps icon name to Icons
   private ConcurrentHashMap<String, Icon> _icons = new ConcurrentHashMap<String, Icon>();
 
   // The StyleSheetDocument which contains all of the styles
-  // for this Skin - including styles contributed by UIExtensions.
+  // for this Skin - including styles contributed by skin-additions.
   private StyleSheetDocument _styleSheetDocument;
 
   // A StyleSheetEntry which defines the styles that are
   // provided by this Skin's style sheet only (does
-  // not include UIExtension styles).
+  // not include skin-additions styles).
   private StyleSheetEntry _skinStyleSheet;
 
-  // List of extension style sheet names
-  private List<String> _extensionStyleSheetNames;
+  // List of skin-additions style sheet names for this Skin
+  private List<String> _skinAdditionStyleSheetNames;
 
-  // Array of UIExtension StyleSheetEntries
-  private StyleSheetEntry[] _extensionStyleSheets;
+  // Array of skin-additions StyleSheetEntries
+  private StyleSheetEntry[] _skinAdditionStyleSheets;
+  
+  // List of all the resource bundle names (bundleName + skin-additions)
+  private List<String> _resourceBundleNames; 
+  
+  // List of skin-additions for this Skin
+  private List<SkinAddition> _skinAdditions;  
 
   // HashMap of Skin properties
   private ConcurrentHashMap<Object, Object> _properties= new ConcurrentHashMap<Object, Object>();

Modified: myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinUtils.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinUtils.java?view=diff&rev=561122&r1=561121&r2=561122
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinUtils.java (original)
+++ myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/SkinUtils.java Mon Jul 30 14:14:30 2007
@@ -41,6 +41,7 @@
 
 
 import org.apache.myfaces.trinidad.skin.Skin;
+import org.apache.myfaces.trinidad.skin.SkinAddition;
 import org.apache.myfaces.trinidadinternal.renderkit.core.skin.MinimalDesktopSkinExtension;
 import org.apache.myfaces.trinidadinternal.renderkit.core.skin.MinimalPdaSkinExtension;
 import org.apache.myfaces.trinidadinternal.renderkit.core.skin.MinimalPortletSkinExtension;
@@ -96,7 +97,7 @@
   }
   
   /**
-   * Register any custom skin extensions found in the
+   * Register any custom skin extensions (and skin-additions) found in the
    * trinidad-skins.xml file with the SkinFactory.
    * 
    * Make sure the SkinFactory.getFactory() does not return null before
@@ -118,7 +119,7 @@
       skinFactory = SkinFactory.getFactory();
     }
 
-    _registerSkinExtensions(context, skinFactory);
+    _registerSkinExtensionsAndAdditions(context, skinFactory);
 
   }
 
@@ -286,7 +287,7 @@
   }
 
   /**
-   * Parse the trinidad-skins.xml file for SkinExtensions and add each
+   * Parse the trinidad-skins.xml file for SkinExtensions and SkinAdditionNodes and add each
    * SkinExtension to the skinFactory.
    * First find all the trinidad-skins.xml files that are in META-INF directory, and 
    * add those skins to the skin factory.
@@ -296,7 +297,7 @@
    * @param context
    * @param skinFactory
    */
-  private static void _registerSkinExtensions(
+  private static void _registerSkinExtensionsAndAdditions(
     ExternalContext context,
     SkinFactory skinFactory)
   {
@@ -340,6 +341,9 @@
     // register all the skin additions from META-INF trinidad-skins.xml and WEB-INF
     // trinidad-skins.xml that we have stored in the metaInfSkinsNodeList object and the
     // webInfSkinsNode object
+    // skin-additions are additions to a skin, not extensions. They are used by
+    // custom component developers that want to add a stylesheet for their components
+    // to a particular skin, like the simple skin.
     FacesContext fContext = FacesContext.getCurrentInstance();
     // register skin-additions from META-INF/trinidad-skins.xml files
     _registerMetaInfSkinAdditions(fContext, skinFactory, metaInfSkinsNodeList);
@@ -654,16 +658,21 @@
     {
       String skinId = skinAdditionNode.getSkinId();
       String styleSheetName = skinAdditionNode.getStyleSheetName();
-  
+      String resourceBundleName = skinAdditionNode.getResourceBundleName();
+
       Skin skin = skinFactory.getSkin(fContext, skinId);
-      if (skin != null && styleSheetName != null)
-      {  
+      if (skin != null && (styleSheetName != null) || (resourceBundleName != null))
+      {
         // If the styleSheetName is in the META-INF/trinidad-skins.xml file, then
         // we prepend META-INF to the styleSheetName if it doesn't begin with '/'.
         // This way we can find the file when we go to parse it later.
-        if (isMetaInfFile)
-          styleSheetName = _prependMetaInf(styleSheetName);
-        skin.registerStyleSheet(styleSheetName); 
+        if (isMetaInfFile && (styleSheetName != null))
+            styleSheetName = _prependMetaInf(styleSheetName);
+
+
+        // we need to create a SkinAddition and add it to the skin
+        SkinAddition addition = new SkinAddition(styleSheetName, resourceBundleName);
+        skin.addSkinAddition(addition);
       }
     }    
   }

Modified: myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/ui/laf/xml/parse/SkinAdditionNode.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/ui/laf/xml/parse/SkinAdditionNode.java?view=diff&rev=561122&r1=561121&r2=561122
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/ui/laf/xml/parse/SkinAdditionNode.java (original)
+++ myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/ui/laf/xml/parse/SkinAdditionNode.java Mon Jul 30 14:14:30 2007
@@ -33,11 +33,13 @@
    */
   public SkinAdditionNode (
     String skinId,
-    String styleSheetName
+    String styleSheetName,
+    String resourceBundleName
     )
   {
     _styleSheetName = styleSheetName;
     _skinId = skinId;
+    _resourceBundleName = resourceBundleName;
   }
   
   public String getSkinId()
@@ -60,6 +62,16 @@
     _styleSheetName = ssName;
   }
   
+  public String getResourceBundleName()
+  {
+    return _resourceBundleName;
+  } 
+  
+  public void setResourceBundleName(String rbName)
+  {
+    _resourceBundleName = rbName;
+  }  
+  
   // Sort by the name of the stylesheet
   public int compareTo(SkinAdditionNode node)
   {
@@ -68,5 +80,6 @@
   
   private String _skinId;
   private String _styleSheetName;
+  private String _resourceBundleName;
 
 }

Modified: myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/ui/laf/xml/parse/SkinAdditionNodeParser.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/ui/laf/xml/parse/SkinAdditionNodeParser.java?view=diff&rev=561122&r1=561121&r2=561122
==============================================================================
--- myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/ui/laf/xml/parse/SkinAdditionNodeParser.java (original)
+++ myfaces/trinidad/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/ui/laf/xml/parse/SkinAdditionNodeParser.java Mon Jul 30 14:14:30 2007
@@ -60,11 +60,13 @@
     // id and family are required. log a severe error if they are null.
     if ((_skinId == null) && (_LOG.isWarning()))
       _LOG.severe("REQUIRED_ELEMENT_SKINID_NOT_FOUND");
+      /*
     if ((_styleSheetName == null) && (_LOG.isWarning()))
       _LOG.severe("REQUIRED_ELEMENT_STYLE_SHEET_NAME_NOT_FOUND");
+      */
 
       
-    return new SkinAdditionNode(_skinId, _styleSheetName);
+    return new SkinAdditionNode(_skinId, _styleSheetName, _resourceBundleName);
   }
 
   @Override
@@ -77,7 +79,8 @@
   {
   
     if ("skin-id".equals(localName) ||
-        "style-sheet-name".equals(localName))
+        "style-sheet-name".equals(localName) ||
+        "bundle-name".equals(localName))
 
     {
       return new StringParser();
@@ -99,11 +102,14 @@
       _skinId = (String) child;
     else if ("style-sheet-name".equals(localName))
       _styleSheetName = (String) child;
+    else if ("bundle-name".equals(localName))
+      _resourceBundleName = (String) child;
   }
 
 
   private String      _skinId;
   private String      _styleSheetName;
+  private String      _resourceBundleName;
 
 
   private static final TrinidadLogger _LOG =