You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by mm...@apache.org on 2007/10/25 15:38:13 UTC

svn commit: r588233 [4/5] - in /myfaces/portlet-bridge/trunk: ./ api/ api/src/ api/src/main/ api/src/main/java/ api/src/main/java/javax/ api/src/main/java/javax/portlet/ api/src/main/java/javax/portlet/faces/ api/src/main/java/javax/portlet/faces/compo...

Added: myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/HTTPUtils.java
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/HTTPUtils.java?rev=588233&view=auto
==============================================================================
--- myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/HTTPUtils.java (added)
+++ myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/HTTPUtils.java Thu Oct 25 06:38:05 2007
@@ -0,0 +1,791 @@
+/* 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.portlet.faces.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * This class contains various utility methods for encoding and decoding URIs <code>String</code>
+ * using the set of characters allowed in a URI, as defined in <a
+ * href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396 - "Uniform Resource Identifiers (URI):
+ * Generic Syntax"</a>.
+ * 
+ * The method does not suffer from the limitations of the <code>java.net.URLEncoder</code> in that
+ * it provides control over the character encoding used for character to byte conversions and is
+ * capable of encoding characters that are encoded by a sequence of several bytes.
+ * <p>
+ * 
+ * To convert a <code>String</code>, each character is examined in turn:
+ * <ul>
+ * <li>The ASCII characters '<code>a</code>' through '<code>z</code>', '<code>A</code>'
+ * through '<code>Z</code>', and '<code>0</code>' through '<code>9</code>' remain the
+ * same.
+ * <li>Additional 'mark characters', i.e. '<code>-</code>', '<code>_</code>', '<code>.</code>', '<code>!</code>', '<code>~</code>', '<code>*</code>', '<code>'</code>' , '<code>(</code>', '<code>)</code>',
+ * remain the same.
+ * <li>The space character '<code>&nbsp;</code>' is converted into a plus sign '<code>+</code>'.
+ * <li>All other characters are converted into a sequence of bytes using the specified character
+ * encoding, and each of these bytes is then encoded as a string "<code>%<i>xy</i></code>",
+ * where <i>xy</i> is the two-digit hexadecimal representation of the byte value.
+ * </ul>
+ */
+public class HTTPUtils
+{
+  // An array mapping characters to booleans. True means the character can be
+  // included without Hex encoding
+  private static final boolean[] sValidChar = new boolean[128];
+
+  // An array mapping 4 bit values to uppercase Hex digits to speed things up
+  // a bit
+  private static final char[]    sHexLookup = new char[16];
+
+  // Lookup arrays used during base64 encoding/decoding
+  private static char[]          sCharLookup;
+  private static byte[]          sByteLookup;
+
+  // Static initializer block
+  static
+  {
+    // First initialize sValidChar
+    // Allow lower case alphabetic characters
+    for (char c = 'a'; c <= 'z'; c++)
+    {
+      sValidChar[c] = true;
+    }
+    // Allow upper case alphabetic characters
+    for (char c = 'A'; c <= 'Z'; c++)
+    {
+      sValidChar[c] = true;
+    }
+    // Allow numeric characters
+    for (char c = '0'; c <= '9'; c++)
+    {
+      sValidChar[c] = true;
+    }
+    // Allow various 'mark' characters
+    sValidChar['-'] = true;
+    sValidChar['_'] = true;
+    sValidChar['.'] = true;
+    sValidChar['!'] = true;
+    sValidChar['~'] = true;
+    sValidChar['*'] = true;
+    sValidChar['\''] = true;
+    sValidChar['('] = true;
+    sValidChar[')'] = true;
+
+    // Also initialize the sHexLookup table
+    for (byte b = 0; b < 16; b++)
+    {
+      sHexLookup[b] = Character.toUpperCase(Character.forDigit(b, 16));
+    }
+
+    // Populate arrays for base64 encoding/decoding
+    sCharLookup = new char[64];
+    sByteLookup = new byte[127];
+
+    byte i = 0;
+    char c;
+    for (c = 'A'; c <= 'Z'; c++)
+    {
+      sCharLookup[i] = c;
+      sByteLookup[c] = i++;
+    }
+    for (c = 'a'; c <= 'z'; c++)
+    {
+      sCharLookup[i] = c;
+      sByteLookup[c] = i++;
+    }
+    for (c = '0'; c <= '9'; c++)
+    {
+      sCharLookup[i] = c;
+      sByteLookup[c] = i++;
+    }
+    sCharLookup[i] = '+';
+    sByteLookup['+'] = i++;
+    sCharLookup[i] = '/';
+    sByteLookup['/'] = i;
+    // Signal EOF with -1 (It's safe because the other bytes are only 6
+    // bits)
+    sByteLookup['='] = -1;
+  }
+
+  /**
+   * Returns the date format used for expiry times in type zero (Netscape) cookies.
+   * 
+   * Also used as the date format for portlet session expiries. Creates a new instance, so thread
+   * safe.
+   * 
+   * @return the date format used for expiry times in type zero (Netscape) cookies
+   */
+  public static DateFormat getCookieDateFormat()
+  {
+    DateFormat format = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss z", Locale.US);
+    // Cookies use GMT Time zone by convention (Rule Britania!)
+    format.setTimeZone(TimeZone.getTimeZone("GMT"));
+    return format;
+  }
+
+  /**
+   * Build a fully qualified URL from the pieces provided.
+   * 
+   * This method does not call any encoding methods, so the path argument has to be properly
+   * encoded.
+   * 
+   * @param scheme
+   *          server protocol.
+   * @param host
+   *          server name.
+   * @param port
+   *          server port.
+   * @param path
+   *          properly encoded relative URL.
+   * @throws IllegalArgumentException
+   *           if scheme, host or port is null.
+   */
+  public static String buildUrlAsString(String scheme, String host, int port, String path)
+  {
+    if (scheme == null || scheme.equals("") || host == null || host.equals("") || port == 0)
+    {
+      throw new IllegalArgumentException("Cannot build a URL using following scheme: " + scheme
+                                         + " host: " + host + " port: " + port + " path: " + path);
+    }
+
+    SimpleStringBuffer url = new SimpleStringBuffer(200);
+
+    url.append(scheme).append("://").append(host);
+
+    // check for protocol default port number
+    if (scheme.equalsIgnoreCase("http") && port != 80 || scheme.equalsIgnoreCase("https")
+        && port != 443)
+    {
+      url.append(":").append(port);
+    }
+
+    if (path != null)
+    {
+      url.append(path);
+    }
+
+    return url.toString();
+  }
+
+  /**
+   * Encodes a String using the set of characters allowed in a URI.
+   * 
+   * @param value
+   *          <code>String</code> to be translated.
+   * @param encoding
+   *          the Java alias for the character encoding to be used to convert non-ASCII characters
+   *          into bytes (e.g. <code>"UTF8"</code>).
+   * @return the translated <code>String</code>.
+   * @exception UnsupportedEncodingException
+   *              if the given encoding is not a recognised character encoding.
+   */
+  public static String encode(String value, String encoding) throws UnsupportedEncodingException
+  {
+    // Create a buffer that is roughly 1.5 times bigger than the value to
+    // account for possible expansion of the resulting encoded string
+    int len = value.length();
+    SimpleStringBuffer out = new SimpleStringBuffer(len * 3 / 2);
+
+    for (int charIndex = 0; charIndex < len; charIndex++)
+    {
+      char aChar = value.charAt(charIndex);
+      if (aChar <= 127 && sValidChar[aChar])
+      {
+        out.append(aChar);
+      }
+      else if (aChar == ' ')
+      {
+        out.append('+');
+      }
+      else
+      {
+        byte[] charBytes = String.valueOf(aChar).getBytes(encoding);
+        // For each byte to encode this character, write a '%',
+        // followed by a 2 digit uppercase hex representation of the
+        // byte value
+        for (byte element : charBytes)
+        {
+          out.append('%');
+          // Convert into two Hex digits (and don't worry about the
+          // sign bit, unlike Integer.toHexString()
+          out.append(sHexLookup[(element & 0xF0) >> 4]);
+          out.append(sHexLookup[element & 0x0F]);
+        }
+      }
+    }
+    // The result string should be encodable in pure ASCII
+    return out.toString();
+  }
+
+  /**
+   * Encodes a String using the set of characters allowed in a URI. This method encodes a multibyte
+   * string in UTF8
+   * 
+   * @param value
+   *          <code>String</code> to be translated.
+   * @return the translated <code>String</code>.
+   */
+  public static String encodeUTF(String value)
+  {
+    String encodedValue = null;
+    try
+    {
+      encodedValue = encode(value, "UTF8");
+    }
+    catch (UnsupportedEncodingException e)
+    {
+      // TODO - error handling
+      // should never happen for this method because the
+      // character encoding is constant
+    }
+    return encodedValue;
+  }
+
+  /**
+   * Decodes an encoded String.
+   * 
+   * @param value
+   *          <code>String</code> to be translated.
+   * @param encoding
+   *          the Java alias for the character encoding to be used to convert byte sequences into
+   *          characters(e.g. <code>"UTF8"</code>).
+   * @return the translated <code>String</code>.
+   * @exception UnsupportedEncodingException
+   *              if the given encoding is not a recognised character encoding.
+   */
+  public static String decode(String value, String encoding) throws UnsupportedEncodingException
+  {
+    // optimization!
+    // determine if decoding is actually required
+    if (!needsDecoding(value))
+    {
+      return value;
+    }
+
+    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    int charIndex = 0;
+    int length = value.length();
+    while (charIndex < length)
+    {
+      char aChar = value.charAt(charIndex);
+      if (aChar == '%')
+      {
+        do
+        {
+          int byteVal = Character.digit(value.charAt(charIndex + 1), 16) << 4
+                        | Character.digit(value.charAt(charIndex + 2), 16);
+          bos.write(byteVal);
+          charIndex += 3;
+        } while (charIndex < length && (aChar = value.charAt(charIndex)) == '%');
+      }
+      else
+      {
+        if (aChar == '+')
+        {
+          bos.write(' ');
+        }
+        else
+        {
+          bos.write(aChar);
+        }
+        charIndex++;
+      }
+    }
+    return bos.toString(encoding);
+  }
+
+  /**
+   * Decodes an encoded UTF8 String.
+   * 
+   * @param value
+   *          <code>String</code> to be translated.
+   * @return the translated <code>String</code>.
+   */
+  public static String decodeUTF(String value)
+  {
+    String decodedValue = null;
+    try
+    {
+      decodedValue = decode(value, "UTF8");
+    }
+    catch (UnsupportedEncodingException e)
+    {
+      // TODO - error handling
+      // should never happen for this method because the
+      // character encoding is constant
+    }
+    return decodedValue;
+  }
+
+  /**
+   * Determines whether the given HTTP status code denotes a client or server error. According to
+   * the HTTP 1.1 spec, the codes have the following categories:
+   * <ul>
+   * <li> 1xx - Informational (new in HTTP/1.1)
+   * <li> 2xx - Success
+   * <li> 3xx - Redirection
+   * <li> 4xx - Client Error
+   * <li> 5xx - Server Error
+   * </ul>
+   * 
+   * @param statusCode
+   *          an HTTP response status code
+   * @return true if the given status code denotes a client or server error
+   */
+  public static boolean isErrorStatusCode(int statusCode)
+  {
+    return statusCode >= 400 && statusCode < 600;
+  }
+
+  /**
+   * Determine if a value needs to be decoded using HTTPUtils.decode()
+   * 
+   * This method assumes that a value is encoded. As such, the existence of '%' (leading character
+   * of an encoded sequence) or '+' (sometimes used to replace <space> characters indicates that
+   * decoding IS required.
+   */
+  public static final boolean needsDecoding(String token)
+  {
+    if (token == null)
+    {
+      return false;
+    }
+
+    return token.indexOf('+') != -1 || token.indexOf('%') != -1;
+  }
+
+  /**
+   * Extracts a cookie value from a list of cookies based on the name.
+   * 
+   * @param cookieName
+   *          Name of the cookie to search for.
+   * @param cookies
+   *          List of cookies of the form "name1=value1; name2=value2; ...; nameN=valueN".
+   * @return The cookie value stored as a String object. null if cookie not found.
+   */
+  public static String getCookie(String cookieName, String cookies)
+  {
+    return getParameterValue(cookieName, cookies);
+  }
+
+  /**
+   * Extracts a parameter value from a list of parameters based on the name.
+   * 
+   * @param paramName
+   *          name of the parameter to search forn
+   * @param params
+   *          list of parameters as a String of the form: "name1=value1; name2=value2; ...;
+   *          nameN=valueN".
+   * @return the parameter value or <code>null</code> if not found
+   */
+  protected static String getParameterValue(String paramName, String params)
+  {
+    int paramNameLen;
+    int paramsLen;
+
+    // Obvious error in arguments?
+    if (paramName == null || (paramNameLen = paramName.length()) == 0)
+    {
+      return null;
+    }
+
+    if (params == null || (paramsLen = params.length()) == 0)
+    {
+      return null;
+    }
+
+    // Try to extract the parameter from the list of parameters
+    // Parameters are defined in a string of the form
+    // name1=value1; name2=value2; ...; nameN=valueN
+    //
+    // The following parse copies the behaviour of
+    // wpugcinb_GetCookieInNewBuffer in /m/wwg/src/wpu.c
+    // and
+    // wpdsesgcv_get_cookie_value in /m/wwg/src/wpdses.c
+    //
+
+    int index = 0;
+
+    for (;;)
+    {
+      // First, eat all the white spaces
+      while (params.charAt(index) == ' ' && index < paramsLen)
+      {
+        index++;
+      }
+
+      if (params.startsWith(paramName, index))
+      {
+        int equalsIndex = params.indexOf('=', index);
+
+        if (equalsIndex != -1)
+        {
+          int i = equalsIndex - 1;
+
+          while (params.charAt(i) == ' ' && i > index)
+          {
+            i--;
+          }
+
+          if (paramNameLen == i - index + 1)
+          {
+            // We've found our parameter
+
+            // Move 1 char past '='
+            int beginIndex = equalsIndex + 1;
+
+            // Check for empty parameter - if so return ""
+            if (beginIndex == paramsLen)
+            {
+              return "";
+            }
+
+            // Find end of token
+            int endIndex = params.indexOf(';', beginIndex);
+            if (endIndex == -1)
+            {
+              endIndex = paramsLen - 1;
+            }
+            else
+            {
+              // Check for empty parameter - if so return ""
+              if (beginIndex == endIndex)
+              {
+                return "";
+              }
+              endIndex--;
+            }
+            // Eat white spaces out in front
+            while (params.charAt(beginIndex) == ' ' && beginIndex < endIndex)
+            {
+              beginIndex++;
+            }
+
+            // Eat white spaces out at end
+            while (params.charAt(endIndex) == ' ' && endIndex > beginIndex)
+            {
+              endIndex--;
+            }
+
+            // Please note that substring takes the endIndex and
+            // subtracts 1
+            // from it. Since our endIndex points to where we want
+            // it to be,
+            // we need to add 1 to it so that everything works out.
+            String paramValue = params.substring(beginIndex, endIndex + 1);
+
+            // check for empty cookie - if so return ""
+            if (0 == paramValue.length())
+            {
+              return "";
+            }
+
+            return paramValue;
+          }
+        }
+      }
+
+      // no match - advance to next parameter
+      index = params.indexOf(';', index);
+
+      // Check for end cases
+      if (index == -1) // no more tokens
+      {
+        return null;
+      }
+      if (index == paramsLen - 1) // nothing following last ';'
+      {
+        return null;
+      }
+
+      index++;
+    }
+  }
+
+  /**
+   * Converts a Java Locale to a String in the format specified for the Accept-Language and xml:lang
+   * attribute, i.e. <a href="http://www.ietf.org/rfc/rfc1766.txt">RFC 1766</a>.
+   */
+  public static String toHTTPLocale(Locale locale)
+  {
+    String language = locale.getLanguage();
+    String country = locale.getCountry();
+    String variant = locale.getVariant();
+    if (country.length() > 0 || variant.length() > 0)
+    {
+      SimpleStringBuffer buff = new SimpleStringBuffer(20).append(language).append('-')
+                                                          .append(country);
+      if (variant.length() > 0)
+      {
+        buff.append('-').append(variant);
+      }
+      return buff.toString();
+    }
+    else
+    {
+      return language;
+    }
+  }
+
+  /**
+   * Sets a cookie value in a list of cookies based on the name. If the cookie value is null, the
+   * cookie is removed from the list.
+   * 
+   * @param cookieName
+   *          Name of the cookie to set.
+   * @param cookieValue
+   *          Value of the cookie to set.
+   * @param cookies
+   *          List of cookies of the form "name1=value1; name2=value2; ...; nameN=valueN".
+   * @return The new cookie string.
+   */
+  public static String setCookie(String cookieName, String cookieValue, String cookies)
+  {
+    int cookieNameLen;
+    int cookiesLen;
+    String newCookies = cookies;
+
+    // Obvious error in arguments?
+    if (cookieName == null || (cookieNameLen = cookieName.length()) == 0)
+    {
+      return cookies;
+    }
+
+    if (cookies == null || (cookiesLen = cookies.length()) == 0)
+    {
+      if (cookieValue != null)
+      {
+        newCookies = cookieName + "=" + cookieValue;
+      }
+    }
+    else
+    {
+      int index = 0;
+
+      // Set the cookie in the list of cookies
+      // Cookie is a string of the form
+      // name1=value1; name2=value2; ...; nameN=valueN
+      //
+
+      for (;;)
+      {
+        // First, eat all the white spaces
+        while (index < cookiesLen && cookies.charAt(index) == ' ')
+        {
+          index++;
+        }
+
+        if (cookies.startsWith(cookieName, index))
+        {
+          int equalsIndex = cookies.indexOf('=', index);
+
+          if (equalsIndex != -1)
+          {
+            int i = equalsIndex - 1;
+
+            while (cookies.charAt(i) == ' ' && i > index)
+            {
+              i--;
+            }
+
+            if (cookieNameLen == i - index + 1)
+            {
+              // We've found our cookie
+
+              // Move 1 char past '='
+              int beginIndex = equalsIndex + 1;
+
+              // Find end of token
+              int endIndex = cookies.indexOf(';', beginIndex);
+              if (endIndex == -1)
+              {
+                endIndex = cookiesLen - 1;
+              }
+              else
+              {
+                endIndex--;
+              }
+
+              if (cookieValue == null)
+              {
+                newCookies = cookies.substring(0, index) + cookies.substring(endIndex + 1);
+              }
+              else
+              {
+                newCookies = cookies.substring(0, beginIndex) + cookieValue
+                             + cookies.substring(endIndex + 1);
+              }
+
+              return newCookies;
+            }
+          }
+        }
+
+        // no match - advance to next cookie
+        index = cookies.indexOf(';', index);
+
+        // Check for end cases
+        if (index == -1 || // no more tokens
+            index == cookiesLen - 1) // nothing following last
+        // ';'
+        {
+          if (cookieValue != null)
+          {
+            if (newCookies.length() != 0)
+            {
+              newCookies = newCookies + ";";
+            }
+            newCookies = newCookies + cookieName + "=" + cookieValue;
+          }
+          return newCookies;
+        }
+
+        index++;
+      }
+    }
+
+    return newCookies;
+  }
+
+  /**
+   * Base64 encodes the supplied bytes array, using the standard base 64 encoding algorithm.
+   * 
+   * @param bytes
+   *          The byte array to encode
+   * @return The base 64 encoded string representing the byte array
+   */
+  public static String base64Encode(byte[] bytes)
+  {
+
+    /*
+     * The base 64 encoding algorithm works as follows:
+     * 
+     * Divide the input bytes stream into blocks of 3 bytes. Divide the 24 bits of a 3-byte block
+     * into 4 groups of 6 bits. Map each group of 6 bits to 1 printable character, based on the
+     * 6-bit value. If the last 3-byte block has only 1 byte of input data, pad 2 bytes of zero
+     * (\x0000). After encoding it as a normal block, override the last 2 characters with 2 equal
+     * signs (==), so the decoding process knows 2 bytes of zero were padded. If the last 3-byte
+     * block has only 2 bytes of input data, pad 1 byte of zero (\x00). After encoding it as a
+     * normal block, override the last 1 character with 1 equal signs (=), so the decoding process
+     * knows 1 byte of zero was padded.
+     */
+
+    int i = 0;
+    int bytesToWrite = bytes.length;
+
+    SimpleStringBuffer buff = new SimpleStringBuffer(bytes.length * 4 / 3);
+
+    while (bytesToWrite >= 3)
+    {
+      buff.append(sCharLookup[bytes[i] >>> 2 & 63]);
+
+      buff.append(sCharLookup[((bytes[i] & 3) << 4) + (bytes[i + 1] >>> 4 & 15)]);
+      buff.append(sCharLookup[((bytes[i + 1] & 15) << 2) + (bytes[i + 2] >>> 6 & 3)]);
+      buff.append(sCharLookup[bytes[i + 2] & 63]);
+
+      bytesToWrite -= 3;
+      i = i + 3;
+    }
+    switch (bytesToWrite)
+    {
+      case 2:
+        buff.append(sCharLookup[bytes[i] >>> 2 & 63]);
+        buff.append(sCharLookup[((bytes[i] & 3) << 4) + (bytes[i + 1] >>> 4 & 15)]);
+        buff.append(sCharLookup[((bytes[i + 1] & 15) << 2)]);
+        buff.append('=');
+        break;
+      case 1:
+        buff.append(sCharLookup[bytes[i] >> 2 & 63]);
+        buff.append(sCharLookup[(bytes[i] & 3) << 4]);
+        buff.append('=');
+        buff.append('=');
+    }
+    return buff.toString();
+  }
+
+  /**
+   * Decodes the supplied base 64 encoded string into its original byte array, using the standard
+   * base 64 decoding algorithm.
+   * 
+   * @param string
+   *          The base 64 encoded string to decode
+   * @return The decoded byte array
+   */
+  public static byte[] base64Decode(String string)
+  {
+    // Blocks of encoded data may have newline characters which
+    // must be ignored
+    string = TextUtils.globalReplace(string, "\n", "");
+    string = TextUtils.globalReplace(string, "\r", "");
+
+    char[] chars = string.toCharArray();
+    int i = 0;
+    int charsToWrite = chars.length;
+    ByteArrayOutputStream buff = new ByteArrayOutputStream(chars.length);
+    byte[] b = new byte[4];
+    while (charsToWrite >= 4)
+    {
+      try
+      {
+        // If we can't get one complete byte, then something has gone
+        // wrong
+        if ((b[0] = sByteLookup[chars[i++]]) == -1 || (b[1] = sByteLookup[chars[i++]]) == -1)
+        {
+          throw new IllegalArgumentException(string);
+        }
+        buff.write(b[0] << 2 | b[1] >>> 4);
+        if ((b[2] = sByteLookup[chars[i++]]) == -1)
+        {
+          charsToWrite -= 4;
+          break;
+        }
+        buff.write(b[1] << 4 | b[2] >>> 2);
+        if ((b[3] = sByteLookup[chars[i++]]) == -1)
+        {
+          charsToWrite -= 4;
+          break;
+        }
+        buff.write(b[2] << 6 | b[3]);
+        charsToWrite -= 4;
+      }
+      // If any of the byte lookups go out of bounds, this can't be a
+      // valid base 64 encoding
+      catch (ArrayIndexOutOfBoundsException aiobe)
+      {
+        throw new IllegalArgumentException(string);
+      }
+    }
+    // If we have any odd characters at the end, then something has gone
+    // wrong
+    if (charsToWrite > 0)
+    {
+      throw new IllegalArgumentException(string);
+    }
+    return buff.toByteArray();
+  }
+}

Added: myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/QueryString.java
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/QueryString.java?rev=588233&view=auto
==============================================================================
--- myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/QueryString.java (added)
+++ myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/QueryString.java Thu Oct 25 06:38:05 2007
@@ -0,0 +1,502 @@
+/* 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.portlet.faces.util;
+
+import java.io.UnsupportedEncodingException;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class encapsulating an HTTP query string.
+ */
+public final class QueryString
+{
+  private String mQueryString;
+  private String mCharacterEncoding;
+  private Map    mParameterMap;
+  private List   mParameterList;
+  private List   mParameterNames;
+
+  /**
+   * Construct a <code>QueryString</code> from a pre-encoded string.
+   */
+  public QueryString(String queryString, String characterEncoding)
+  {
+    mQueryString = queryString;
+    mCharacterEncoding = characterEncoding;
+  }
+
+  /**
+   * Makes a copy of an existing <code>QueryString</code>.
+   */
+  public QueryString(QueryString source)
+  {
+    mQueryString = source.mQueryString;
+    mCharacterEncoding = source.mCharacterEncoding;
+    if (source.mParameterList != null)
+    {
+      mParameterList = new ArrayList(source.mParameterList);
+    }
+  }
+
+  /**
+   * Constructs an empty query string (parameters may be added later).
+   */
+  public QueryString(String characterEncoding)
+  {
+    mCharacterEncoding = characterEncoding;
+  }
+
+  /**
+   * Constructs a query string from an old-fashioned array of PRE-ENCODED name-value pairs
+   */
+  public QueryString(String[][] args, String characterEncoding)
+  {
+    this(characterEncoding);
+    for (String[] element : args)
+    {
+      addParameter(element[0], element[1], true);
+    }
+  }
+
+  /**
+   * Constructs a query string from a list of PRE-ENCODED name-value pairs
+   */
+  public QueryString(List params, String characterEncoding)
+  {
+    this(characterEncoding);
+
+    Iterator pairs = params.iterator();
+    while (pairs.hasNext())
+    {
+      String[] pair = (String[]) pairs.next();
+      addParameter(pair[0], pair[1], true);
+    }
+  }
+
+  /**
+   * Converts this object into an encoded query string.
+   */
+  @Override
+  public String toString()
+  {
+    // Use appendTo to concatenate the parameters together
+    if (mQueryString == null)
+    {
+      appendTo(new SimpleStringBuffer(200));
+    }
+    return mQueryString;
+  }
+
+  /**
+   * Appends the contents of this object to the given buffer in encoded query string form.
+   * 
+   * @param buff
+   *          the buffer to append to
+   */
+  public void appendTo(SimpleStringBuffer buff)
+  {
+    // If we don't have a cached query string yet, generate it
+    if (mQueryString == null)
+    {
+      // Remember the start position in the buffer, so that we can also
+      // cache the
+      // concatenated string in mQueryString
+      int startPos = buff.length();
+      Iterator i;
+      if (mParameterList != null && (i = mParameterList.iterator()).hasNext())
+      {
+        Parameter param = (Parameter) i.next();
+        buff.append(param.getEncodedName()).append('=').append(param.getEncodedValue());
+        while (i.hasNext())
+        {
+          param = (Parameter) i.next();
+          buff.append('&').append(param.getEncodedName()).append('=')
+              .append(param.getEncodedValue());
+        }
+        mQueryString = buff.substring(startPos);
+      }
+      // If we don't have any parameters at all, cache the empty string
+      else
+      {
+        mQueryString = "";
+      }
+    }
+    // If we have a cached query string, reuse it
+    else
+    {
+      buff.append(mQueryString);
+    }
+  }
+
+  public Enumeration getParameterNames()
+  {
+    initParameterMap();
+    return Collections.enumeration(mParameterNames);
+  }
+
+  public String getParameter(String name)
+  {
+    initParameterMap();
+    List values = (List) mParameterMap.get(name);
+    return values == null ? null : ((Parameter) values.get(0)).getValue();
+  }
+
+  public Enumeration getParameterValues(String name)
+  {
+    initParameterMap();
+    List params = (List) mParameterMap.get(name);
+    if (params == null)
+    {
+      return Collections.enumeration(Collections.EMPTY_LIST);
+    }
+    List values = new ArrayList(params.size());
+    Iterator i = params.iterator();
+    Parameter param;
+    while (i.hasNext())
+    {
+      param = (Parameter) i.next();
+      values.add(param.getValue());
+    }
+    return Collections.enumeration(values);
+  }
+
+  public void addParameter(String name, String value)
+  {
+    addParameter(name, value, false);
+  }
+
+  public void addParameter(String name, String value, boolean isEncoded)
+  {
+    if (value == null)
+    {
+      return;
+    }
+    initParameterList();
+
+    // Invalidate the query string
+    mQueryString = null;
+
+    // Update the parameter list
+    Parameter param = new Parameter(name, value, isEncoded);
+    mParameterList.add(param);
+
+    // Update the parameter map if it is initialized
+    if (mParameterMap != null)
+    {
+      String decodedName = param.getName();
+      List values = (List) mParameterMap.get(decodedName);
+      if (values == null)
+      {
+        values = new ArrayList(4);
+        mParameterMap.put(decodedName, values);
+        // Only add UNIQUE parameter names (preserving order)
+        mParameterNames.add(decodedName);
+      }
+      values.add(param);
+    }
+  }
+
+  public void setParameter(String name, String value)
+  {
+    setParameter(name, value, false);
+  }
+
+  public void setParameter(String name, String value, boolean isEncoded)
+  {
+    if (value == null)
+    {
+      removeParameter(name, isEncoded);
+      return;
+    }
+    initParameterMap();
+
+    // Invalidate the query string
+    mQueryString = null;
+
+    // Update the map
+    Parameter param = new Parameter(name, value, isEncoded);
+    String decodedName = param.getName();
+    List values = (List) mParameterMap.get(decodedName);
+    if (values == null)
+    {
+      values = new ArrayList(4);
+      mParameterMap.put(decodedName, values);
+      // Only add UNIQUE parameter names (preserving order)
+      mParameterNames.add(decodedName);
+      mParameterList.add(param);
+    }
+    else
+    {
+      values.clear();
+
+      // First, replace the existing occurence of the parameter
+      int i = mParameterList.indexOf(param);
+      mParameterList.set(i, param);
+
+      // Now, remove any subsequent occurrences
+      int j;
+      while ((j = mParameterList.lastIndexOf(param)) > i)
+      {
+        mParameterList.remove(j);
+      }
+    }
+    values.add(param);
+  }
+
+  public String removeParameter(String name)
+  {
+    return removeParameter(name, false);
+  }
+
+  public String removeParameter(String name, boolean isEncoded)
+  {
+    initParameterList();
+
+    // Invalidate the query string
+    mQueryString = null;
+
+    // Create a template parameter for comparisons, so that we can avoid
+    // decoding all parameter names in the list
+    Parameter templateParam = new Parameter(name, "", isEncoded);
+
+    // Update the parameter list
+    Iterator i = mParameterList.iterator();
+    Parameter param = null, firstParam = null;
+    while (i.hasNext())
+    {
+      param = (Parameter) i.next();
+      // Compare the parameter with our template (only the template name
+      // will
+      // be encoded / decoded if necessary)
+      if (templateParam.equals(param))
+      {
+        if (firstParam == null)
+        {
+          firstParam = param;
+        }
+        i.remove();
+      }
+    }
+
+    // Update the map, if it is initialized and we found a parameter
+    if (mParameterMap != null && firstParam != null)
+    {
+      String decodedName = templateParam.getName();
+      List values = (List) mParameterMap.remove(decodedName);
+      if (values != null)
+      {
+        mParameterNames.remove(decodedName);
+      }
+    }
+
+    return firstParam == null ? null : isEncoded ? firstParam.getEncodedValue()
+                                                : firstParam.getValue();
+  }
+
+  private void initParameterMap()
+  {
+    if (mParameterMap == null)
+    {
+      initParameterList();
+
+      mParameterMap = new HashMap(30);
+      mParameterNames = new ArrayList(30);
+      if (mParameterList.size() == 0)
+      {
+        return;
+      }
+      String decodedName;
+      Parameter param;
+      List values;
+      Iterator i = mParameterList.iterator();
+      while (i.hasNext())
+      {
+        param = (Parameter) i.next();
+        decodedName = param.getName();
+        values = (List) mParameterMap.get(decodedName);
+        if (values == null)
+        {
+          values = new ArrayList(4);
+          mParameterMap.put(decodedName, values);
+          // Only add UNIQUE parameter names (preserving order)
+          mParameterNames.add(decodedName);
+        }
+        values.add(param);
+      }
+    }
+  }
+
+  private void initParameterList()
+  {
+    if (mParameterList == null)
+    {
+      mParameterList = new ArrayList(30);
+      int length;
+      if (mQueryString == null || (length = mQueryString.length()) == 0)
+      {
+        return;
+      }
+      Parameter param;
+      int lastPos = 0, nextPos, sepPos;
+      do
+      {
+        nextPos = mQueryString.indexOf('&', lastPos);
+        if (nextPos == -1)
+        {
+          nextPos = length;
+        }
+        sepPos = mQueryString.indexOf('=', lastPos);
+        if (sepPos != -1 && sepPos < nextPos)
+        {
+          param = new Parameter(mQueryString.substring(lastPos, sepPos),
+                                mQueryString.substring(sepPos + 1, nextPos), true);
+        }
+        else
+        {
+          param = new Parameter(mQueryString.substring(lastPos, nextPos), "", true);
+        }
+        mParameterList.add(param);
+        lastPos = nextPos + 1;
+      } while (nextPos < length);
+    }
+  }
+
+  private class Parameter
+  {
+    private String mName;
+    private String mEncodedName;
+
+    private String mValue;
+    private String mEncodedValue;
+
+    public Parameter(String name, String value, boolean encoded)
+    {
+      if (encoded)
+      {
+        mEncodedName = name;
+        mEncodedValue = value;
+      }
+      else
+      {
+        mName = name;
+        mValue = value;
+      }
+    }
+
+    public String getName()
+    {
+      if (mName == null)
+      {
+        try
+        {
+          mName = HTTPUtils.decode(mEncodedName, mCharacterEncoding);
+        }
+        catch (UnsupportedEncodingException uee)
+        {
+          handleUnsupportedEncoding();
+        }
+      }
+      return mName;
+    }
+
+    public String getEncodedName()
+    {
+      if (mEncodedName == null)
+      {
+        try
+        {
+          mEncodedName = HTTPUtils.encode(mName, mCharacterEncoding);
+        }
+        catch (UnsupportedEncodingException uee)
+        {
+          handleUnsupportedEncoding();
+        }
+      }
+      return mEncodedName;
+    }
+
+    public String getValue()
+    {
+      if (mValue == null)
+      {
+        try
+        {
+          mValue = HTTPUtils.decode(mEncodedValue, mCharacterEncoding);
+        }
+        catch (UnsupportedEncodingException uee)
+        {
+          handleUnsupportedEncoding();
+        }
+      }
+      return mValue;
+    }
+
+    public String getEncodedValue()
+    {
+      if (mEncodedValue == null)
+      {
+        try
+        {
+          mEncodedValue = HTTPUtils.encode(mValue, mCharacterEncoding);
+        }
+        catch (UnsupportedEncodingException uee)
+        {
+          handleUnsupportedEncoding();
+        }
+      }
+      return mEncodedValue;
+    }
+
+    /**
+     * Compares two parameters for name equality.
+     * 
+     * Attempts not to invoke any lazy encoding or decoding in the passed in parameter - only in
+     * this one.
+     */
+    @Override
+    public boolean equals(Object o)
+    {
+      if (o == null || !(o instanceof Parameter))
+      {
+        return false;
+      }
+      Parameter p1 = (Parameter) o;
+      return p1.mName != null && getName().equals(p1.mName) || p1.mEncodedName != null
+             && getEncodedName().equals(p1.mEncodedName);
+    }
+  }
+
+  private void handleUnsupportedEncoding()
+  {
+    throw new IllegalArgumentException(
+                                       new SimpleStringBuffer(100)
+                                                                  .append(
+                                                                          "Unrecognized character encoding \"")
+                                                                  .append(mCharacterEncoding)
+                                                                  .append('"').toString());
+  }
+}

Added: myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/SimpleStringBuffer.java
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/SimpleStringBuffer.java?rev=588233&view=auto
==============================================================================
--- myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/SimpleStringBuffer.java (added)
+++ myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/SimpleStringBuffer.java Thu Oct 25 06:38:05 2007
@@ -0,0 +1,530 @@
+/* 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.portlet.faces.util;
+
+/**
+ * A non synchronized StringBuffer object that mimmicks the functionality of java.lang.StringBuffer.
+ */
+public final class SimpleStringBuffer
+{
+  private char mValue[];
+  private int  mLength;
+
+  public SimpleStringBuffer()
+  {
+    this(16);
+  }
+
+  public SimpleStringBuffer(int length)
+  {
+    mValue = new char[length];
+  }
+
+  public SimpleStringBuffer(String str)
+  {
+    this(str.length() + 16);
+    append(str);
+  }
+
+  /**
+   * Returns the length (character count) of this string buffer.
+   * 
+   * @return the number of characters in this string buffer.
+   */
+  public int length()
+  {
+    return mLength;
+  }
+
+  /**
+   * Returns the current capacity of the String buffer. The capacity is the amount of storage
+   * available for newly inserted characters; beyond which an allocation will occur.
+   * 
+   * @return the current capacity of this string buffer.
+   */
+  public int capacity()
+  {
+    return mValue.length;
+  }
+
+  /**
+   * Ensures that the capacity of the buffer is at least equal to the specified minimum. If the
+   * current capacity of this string buffer is less than the argument, then a new internal buffer is
+   * allocated with greater capacity. The new capacity is the larger of:
+   * <ul>
+   * <li>The <code>minimumCapacity</code> argument.
+   * <li>Twice the old capacity, plus <code>2</code>.
+   * </ul>
+   * If the <code>minimumCapacity</code> argument is nonpositive, this method takes no action and
+   * simply returns.
+   * 
+   * @param minimumCapacity
+   *          the minimum desired capacity.
+   */
+  public void ensureCapacity(int minimumCapacity)
+  {
+    if (minimumCapacity > mValue.length)
+    {
+      int newCapacity = (mValue.length + 1) * 2;
+      if (minimumCapacity > newCapacity)
+      {
+        newCapacity = minimumCapacity;
+      }
+
+      char newValue[] = new char[newCapacity];
+      System.arraycopy(mValue, 0, newValue, 0, mLength);
+      mValue = newValue;
+
+      // Debug Facility that dumps the stack trace when
+      // the buffer grows allowing sizing to be tweaked!
+      // SHOULD BE COMMENTED OUT
+      // printStackTrace(new Throwable());
+    }
+  }
+
+  public void setLength(int newLength)
+  {
+    if (newLength < 0)
+    {
+      throw new StringIndexOutOfBoundsException(newLength);
+    }
+
+    if (newLength > mValue.length)
+    {
+      ensureCapacity(newLength);
+    }
+
+    if (mLength < newLength)
+    {
+      for (; mLength < newLength; mLength++)
+      {
+        mValue[mLength] = '\0';
+      }
+    }
+    else
+    {
+      mLength = newLength;
+    }
+  }
+
+  public char charAt(int index)
+  {
+    if (index < 0 || index >= mLength)
+    {
+      throw new StringIndexOutOfBoundsException(index);
+    }
+    return mValue[index];
+  }
+
+  public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
+  {
+    if (srcBegin < 0 || srcBegin >= mLength)
+    {
+      throw new StringIndexOutOfBoundsException(srcBegin);
+    }
+
+    if (srcEnd < 0 || srcEnd > mLength)
+    {
+      throw new StringIndexOutOfBoundsException(srcEnd);
+    }
+
+    if (srcBegin < srcEnd)
+    {
+      System.arraycopy(mValue, srcBegin, dst, dstBegin, srcEnd - srcBegin);
+    }
+  }
+
+  public void setCharAt(int index, char ch)
+  {
+    if (index < 0 || index >= mLength)
+    {
+      throw new StringIndexOutOfBoundsException(index);
+    }
+    mValue[index] = ch;
+  }
+
+  public SimpleStringBuffer append(Object obj)
+  {
+    return append(String.valueOf(obj));
+  }
+
+  public SimpleStringBuffer append(String str)
+  {
+    if (str == null)
+    {
+      str = String.valueOf(str);
+    }
+
+    int len = str.length();
+    int newcount = mLength + len;
+    if (newcount > mValue.length)
+    {
+      ensureCapacity(newcount);
+    }
+    str.getChars(0, len, mValue, mLength);
+    mLength = newcount;
+    return this;
+  }
+
+  public SimpleStringBuffer append(char[] str)
+  {
+    int len = str.length;
+    int newcount = mLength + len;
+    if (newcount > mValue.length)
+    {
+      ensureCapacity(newcount);
+    }
+    System.arraycopy(str, 0, mValue, mLength, len);
+    mLength = newcount;
+    return this;
+  }
+
+  public SimpleStringBuffer append(char[] str, int offset, int len)
+  {
+    int newcount = mLength + len;
+    if (newcount > mValue.length)
+    {
+      ensureCapacity(newcount);
+    }
+    System.arraycopy(str, offset, mValue, mLength, len);
+    mLength = newcount;
+    return this;
+  }
+
+  public SimpleStringBuffer append(boolean b)
+  {
+    return append(String.valueOf(b));
+  }
+
+  public SimpleStringBuffer append(char c)
+  {
+    int newcount = mLength + 1;
+    if (newcount > mValue.length)
+    {
+      ensureCapacity(newcount);
+    }
+    mValue[mLength++] = c;
+    return this;
+  }
+
+  /**
+   */
+  public SimpleStringBuffer append(int i)
+  {
+    return append(String.valueOf(i));
+  }
+
+  /**
+   */
+  public SimpleStringBuffer append(long l)
+  {
+    return append(String.valueOf(l));
+  }
+
+  /**
+   */
+  public SimpleStringBuffer append(float f)
+  {
+    return append(String.valueOf(f));
+  }
+
+  /**
+   */
+  public SimpleStringBuffer append(double d)
+  {
+    return append(String.valueOf(d));
+  }
+
+  /**
+   */
+  public SimpleStringBuffer insert(int offset, Object obj)
+  {
+    return insert(offset, String.valueOf(obj));
+  }
+
+  /**
+   */
+  public SimpleStringBuffer insert(int offset, String str)
+  {
+    if (offset < 0 || offset > mLength)
+    {
+      throw new StringIndexOutOfBoundsException();
+    }
+    int len = str.length();
+    int newcount = mLength + len;
+    if (newcount > mValue.length)
+    {
+      ensureCapacity(newcount);
+    }
+    System.arraycopy(mValue, offset, mValue, offset + len, mLength - offset);
+    str.getChars(0, len, mValue, offset);
+    mLength = newcount;
+    return this;
+  }
+
+  /**
+   */
+  public SimpleStringBuffer insert(int offset, char[] str)
+  {
+    if (offset < 0 || offset > mLength)
+    {
+      throw new StringIndexOutOfBoundsException();
+    }
+    int len = str.length;
+    int newcount = mLength + len;
+    if (newcount > mValue.length)
+    {
+      ensureCapacity(newcount);
+    }
+    System.arraycopy(mValue, offset, mValue, offset + len, mLength - offset);
+    System.arraycopy(str, 0, mValue, offset, len);
+    mLength = newcount;
+    return this;
+  }
+
+  /**
+   */
+  public SimpleStringBuffer insert(int offset, boolean b)
+  {
+    return insert(offset, String.valueOf(b));
+  }
+
+  /**
+   */
+  public SimpleStringBuffer insert(int offset, char c)
+  {
+    int newcount = mLength + 1;
+    if (newcount > mValue.length)
+    {
+      ensureCapacity(newcount);
+    }
+    System.arraycopy(mValue, offset, mValue, offset + 1, mLength - offset);
+    mValue[offset] = c;
+    mLength = newcount;
+    return this;
+  }
+
+  /**
+   */
+  public SimpleStringBuffer insert(int offset, int i)
+  {
+    return insert(offset, String.valueOf(i));
+  }
+
+  /**
+   */
+  public SimpleStringBuffer insert(int offset, long l)
+  {
+    return insert(offset, String.valueOf(l));
+  }
+
+  /**
+   */
+  public SimpleStringBuffer insert(int offset, float f)
+  {
+    return insert(offset, String.valueOf(f));
+  }
+
+  /**
+   */
+  public SimpleStringBuffer insert(int offset, double d)
+  {
+    return insert(offset, String.valueOf(d));
+  }
+
+  /**
+   * Removes the characters in a substring of this <code>
+   * SimpleStringBuffer</code>. The substring
+   * begins at the specified <code>start</code> and extends to the character at index
+   * <code>end</code> - 1 or to the end of the SimpleStringBuffer if no such character exists. If
+   * start is equal to end, no changes are made.
+   * 
+   * @param start -
+   *          the beginning index, inclusive
+   * @param end -
+   *          the ending index, exclusive
+   * 
+   * @return This simple string buffer
+   * @exception StringIndexOutOfBoundsException -
+   *              if start is negative, greater than <code>length()</code>, or greater than
+   *              <code>end</code>.
+   * 
+   */
+  public SimpleStringBuffer delete(int start, int end)
+  {
+    if (start < 0)
+    {
+      throw new StringIndexOutOfBoundsException(start);
+    }
+    if (end > mLength)
+    {
+      end = mLength;
+    }
+    if (start > end)
+    {
+      throw new StringIndexOutOfBoundsException();
+    }
+    int numChars = end - start;
+    if (numChars > 0)
+    {
+      System.arraycopy(mValue, start + numChars, mValue, start, mLength - end);
+      mLength -= numChars;
+    }
+    return this;
+  }
+
+  /**
+   * Removes the character at the specified position in this <code>SimpleStringBuffer</code>
+   * (shortening the <code>SimpleStringBuffer</code> by one character).
+   * 
+   * @param index -
+   *          index of the character to remove
+   * 
+   * @return This simple string buffer
+   * @exception StringIndexOutOfBoundsException -
+   *              if the <code>index</code> is negative or greater than or equal to
+   *              <code>length()</code>.
+   * 
+   */
+  public SimpleStringBuffer deleteCharAt(int index)
+  {
+    if (index < 0 || index >= mLength)
+    {
+      throw new StringIndexOutOfBoundsException();
+    }
+    System.arraycopy(mValue, index + 1, mValue, index, mLength - index - 1);
+    mLength--;
+    return this;
+  }
+
+  /**
+   * Replaces the characters in a substring of this <code>SimpleStringBuffer</code> with
+   * characters in the specified String. The substring begins at the specified start and extends to
+   * the character at index <code>end</code> - 1 or to the end of the
+   * <code>SimpleStringBuffer</code> if no such character exists. First the characters in the
+   * substring are removed and then the specified String is inserted at start. (The
+   * <code>SimpleStringBuffer</code> will be lengthened to accommodate the specified String if
+   * necessary.)
+   * 
+   * @param start -
+   *          the beginning index, inclusive
+   * @param end -
+   *          the ending index, exclusive
+   * @param str -
+   *          string that will replace previous contents
+   * 
+   * @return This simple string buffer
+   * @exception StringIndexOutOfBoundsException -
+   *              if start is negative, greater than <code>length()</code>, or greater than
+   *              <code>end</code>.
+   * 
+   */
+  public SimpleStringBuffer replace(int start, int end, String str)
+  {
+    if (start < 0)
+    {
+      throw new StringIndexOutOfBoundsException(start);
+    }
+    if (end > mLength)
+    {
+      end = mLength;
+    }
+    if (start > end)
+    {
+      throw new StringIndexOutOfBoundsException();
+    }
+
+    int numChars = str.length();
+    int newLength = mLength + numChars - (end - start);
+
+    if (newLength > mValue.length)
+    {
+      ensureCapacity(newLength);
+    }
+    System.arraycopy(mValue, end, mValue, start + numChars, mLength - end);
+
+    str.getChars(0, numChars, mValue, start);
+    mLength = newLength;
+
+    return this;
+  }
+
+  /**
+   * Returns a new <code>String</code> that contains a subsequence of characters currently
+   * contained in this <code>SimpleStringBuffer</code>.The substring begins at the specified
+   * index and extends to the end of the <code>StringBuffer</code>.
+   * 
+   * @param start
+   *          The beginning index, inclusive.
+   * @return The new string.
+   * @exception StringIndexOutOfBoundsException
+   *              if <code>start</code> is less than zero, or greater than the length of this
+   *              <code>StringBuffer</code>.
+   */
+  public String substring(int start)
+  {
+    return substring(start, mLength);
+  }
+
+  /**
+   * Returns a new <code>String</code> that contains a subsequence of characters currently
+   * contained in this <code>SimpleStringBuffer</code>. The substring begins at the specified
+   * <code>start</code> and extends to the character at index <code>end -
+   * 1</code>.
+   * 
+   * @param start
+   *          The beginning index, inclusive.
+   * @param end
+   *          The ending index, exclusive.
+   * @return The new string.
+   * @exception StringIndexOutOfBoundsException
+   *              if <code>start</code> or <code>end</code> are negative or greater than
+   *              <code>length()</code>, or <code>start</code> is greater than <code>end</code>.
+   */
+  public String substring(int start, int end)
+  {
+    if (start < 0)
+    {
+      throw new StringIndexOutOfBoundsException(start);
+    }
+    if (end > mLength)
+    {
+      throw new StringIndexOutOfBoundsException(end);
+    }
+    if (start > end)
+    {
+      throw new StringIndexOutOfBoundsException(end - start);
+    }
+    return new String(mValue, start, end - start);
+  }
+
+  /**
+   */
+  @Override
+  public String toString()
+  {
+    return new String(getValue(), 0, length());
+  }
+
+  public char[] getValue()
+  {
+    return mValue;
+  }
+}

Added: myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/TextUtils.java
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/TextUtils.java?rev=588233&view=auto
==============================================================================
--- myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/TextUtils.java (added)
+++ myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/TextUtils.java Thu Oct 25 06:38:05 2007
@@ -0,0 +1,102 @@
+/* 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.portlet.faces.util;
+
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+
+// TODO once we figure out which class is shipped with the 2.0 container
+// we'll be using that class instead
+// Borrowed from oracle.portlet.utils
+
+public class TextUtils
+{
+
+  public static final String getString(ResourceBundle bundle, String key, Object[] args)
+  {
+    return formatMessage(bundle.getString(key), args);
+  }
+
+  public static final String getString(ResourceBundle bundle, String key)
+  {
+    return formatMessage(bundle.getString(key), null);
+  }
+
+  public static final String formatMessage(String message, Object[] args)
+  {
+    if (args != null)
+    {
+      return MessageFormat.format(message, args);
+    }
+    else
+    {
+      return message;
+    }
+  }
+
+  /**
+   * Provides the "global substring search and replace" functionality missing from the JDK.
+   * 
+   * @param orig
+   *          the original string to process
+   * @param search
+   *          the substring to search for in <code>orig</code>
+   * @param replace
+   *          the string to replace all occurrences of <code>search</code> with
+   * @return copy of <code>orig</code> with all occurrences of <code>search</code> replaced by
+   *         <code>replace</code>
+   */
+  public static final String globalReplace(String orig, String search, String replace)
+  {
+    // OPTIMIZATION: Return original string if it doesn't contain the search
+    // string
+    int searchLen = search.length();
+    if (searchLen == 0)
+    {
+      return orig;
+    }
+    int nextPos = orig.indexOf(search);
+    if (nextPos == -1)
+    {
+      return orig;
+    }
+    int origLen = orig.length();
+    int startPos = 0;
+    SimpleStringBuffer result = new SimpleStringBuffer(origLen + 100);
+
+    // Use 'do' loop, because we know the search string occurs at least once
+    do
+    {
+      if (nextPos > startPos)
+      {
+        result.append(orig.substring(startPos, nextPos));
+      }
+      result.append(replace);
+      startPos = nextPos + searchLen;
+    } while (startPos < origLen && (nextPos = orig.indexOf(search, startPos)) != -1);
+
+    if (startPos < origLen)
+    {
+      result.append(orig.substring(startPos));
+    }
+
+    return result.toString();
+  }
+}

Added: myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/URLUtils.java
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/URLUtils.java?rev=588233&view=auto
==============================================================================
--- myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/URLUtils.java (added)
+++ myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/URLUtils.java Thu Oct 25 06:38:05 2007
@@ -0,0 +1,146 @@
+/* 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.portlet.faces.util;
+
+public class URLUtils
+{
+
+  /**
+   * Borrowed from package oracle.adfinternal.view.faces.share.url.EncoderUtils
+   */
+  public static String appendURLArguments(StringBuffer buffer, String baseURL,
+                                          String[] keysAndValues)
+  {
+
+    // Bug 1814825: the anchor has to stay on the end.
+    int anchorIndex = baseURL.indexOf('#');
+
+    if (anchorIndex >= 0)
+    {
+      buffer.append(baseURL.substring(0, anchorIndex));
+    }
+    else
+    {
+      buffer.append(baseURL);
+    }
+
+    boolean queryAppended = baseURL.indexOf('?') >= 0;
+
+    for (int i = 0; i < keysAndValues.length; i += 2)
+    {
+      String value = keysAndValues[i + 1];
+      if (value != null)
+      {
+        // only append '?' at start if the URL doesn't already contain
+        // arguments
+        if (!queryAppended)
+        {
+          queryAppended = true;
+          buffer.append('?');
+        }
+        else
+        {
+          buffer.append('&');
+        }
+
+        buffer.append(keysAndValues[i]);
+        buffer.append('=');
+        buffer.append(value);
+      }
+    }
+
+    String beforeEncode = buffer.toString();
+    return beforeEncode;
+  }
+
+  /**
+   * Borrowed from package oracle.adfinternal.view.faces.share.url.EncoderUtils
+   */
+  public static String appendURLArguments(String baseURL, String[] keysAndValues)
+  {
+    // buffer length = base + separators + keys + values
+    int bufferLength = baseURL.length() + keysAndValues.length;
+    for (int i = 0; i < keysAndValues.length; i += 2)
+    {
+      String value = keysAndValues[i + 1];
+      if (value != null)
+      {
+        bufferLength += keysAndValues[i].length() + value.length();
+      }
+    }
+
+    StringBuffer buffer = new StringBuffer(bufferLength);
+
+    return appendURLArguments(buffer, baseURL, keysAndValues);
+  }
+
+  public static String convertFromRelative(String currentPath, String relativeLoc)
+                                                                                  throws IllegalArgumentException
+  {
+    // determine if and how many levels we must walk up the currentPath
+    int levels = 0;
+    int i = 0, length = relativeLoc.length();
+    while (i + 1 < length)
+    {
+      if (relativeLoc.charAt(i) != '.')
+      {
+        break;
+      }
+      else if (relativeLoc.charAt(i) == '.' && relativeLoc.charAt(i + 1) == '/')
+      {
+        // no new level but prune the ./
+        i += 2;
+      }
+      else if (i + 2 < length && relativeLoc.charAt(i) == '.' && relativeLoc.charAt(i + 1) == '.'
+               && relativeLoc.charAt(i + 2) == '/')
+      {
+        levels += 1;
+        i += 3;
+      }
+    }
+
+    StringBuffer sb = new StringBuffer(currentPath);
+    if (currentPath.endsWith("/"))
+    {
+      sb = sb.deleteCharAt(currentPath.length() - 1);
+    }
+    for (int j = 0; j < levels; j++)
+    {
+      int loc = sb.lastIndexOf("/");
+      if (loc < 0)
+      {
+        throw new IllegalArgumentException("Location: " + relativeLoc
+                                           + "Can't be made relative to: " + currentPath);
+      }
+      sb = sb.delete(loc, sb.length());
+    }
+
+    // now sb should contain root path without trailing / so add one
+    sb = sb.append("/");
+
+    // now add the portion of the relativeLoc that doesn't contain
+    // the relative references
+    sb = sb.append(relativeLoc.substring(i));
+
+    return sb.toString();
+
+  }
+
+}

Added: myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/config/WebConfigurationProcessor.java
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/config/WebConfigurationProcessor.java?rev=588233&view=auto
==============================================================================
--- myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/config/WebConfigurationProcessor.java (added)
+++ myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/config/WebConfigurationProcessor.java Thu Oct 25 06:38:05 2007
@@ -0,0 +1,252 @@
+/* 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.portlet.faces.util.config;
+
+import java.io.StringReader;
+
+import java.util.Vector;
+
+import javax.portlet.PortletContext;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class WebConfigurationProcessor
+{
+
+  private static final String WEB_XML_PATH = "/WEB-INF/web.xml";
+
+  private Vector              mMappings    = null;
+
+  /**
+   * <p>
+   * When instantiated, the web.xml of the current application will be scanned looking for a
+   * references to the <code>FacesServlet</code>. <code>isFacesServletPresent()</code> will
+   * return the appropriate value based on the scan.
+   * </p>
+   * 
+   * @param context
+   *          the <code>ServletContext</code> for the application of interest
+   */
+  public WebConfigurationProcessor(PortletContext context)
+  {
+
+    if (context != null)
+    {
+      scanForFacesMappings(context);
+    }
+
+  } // END WebXmlProcessor
+
+  public Vector getFacesMappings()
+  {
+
+    return mMappings;
+
+  } // END getFacesMappings
+
+  /**
+   * <p>
+   * Parse the web.xml for the current application and scan for a FacesServlet entry, if found, set
+   * the <code>facesServletPresent</code> property to true.
+   * 
+   * @param context
+   *          the ServletContext instance for this application
+   */
+  private void scanForFacesMappings(PortletContext context)
+  {
+
+    SAXParserFactory factory = getSAXFactory();
+    try
+    {
+      SAXParser parser = factory.newSAXParser();
+      parser.parse(context.getResourceAsStream(WEB_XML_PATH), new WebXmlHandler());
+    }
+    catch (Exception e)
+    {
+      // TODO add logging
+      // Do nothing
+      ;
+    }
+
+  } // END scanForFacesMappings
+
+  /**
+   * <p>
+   * Return a <code>SAXParserFactory</code> instance that is non-validating and is namespace
+   * aware.
+   * </p>
+   * 
+   * @return configured <code>SAXParserFactory</code>
+   */
+  private SAXParserFactory getSAXFactory()
+  {
+
+    SAXParserFactory factory = SAXParserFactory.newInstance();
+    factory.setValidating(false);
+    factory.setNamespaceAware(true);
+    return factory;
+
+  } // END getConfiguredFactory
+
+  /**
+   * <p>
+   * A simple SAX handler to process the elements of interested within a web application's
+   * deployment descriptor.
+   * </p>
+   */
+  private class WebXmlHandler extends DefaultHandler
+  {
+
+    private static final String SERVLET_ELEMENT          = "servlet";
+    private static final String SERVLET_NAME_ELEMENT     = "servlet-name";
+    private static final String SERVLET_CLASS_ELEMENT    = "servlet-class";
+    private static final String SERVLET_MAPPING_ELEMENT  = "servlet-mapping";
+    private static final String URL_PATTERN_ELEMENT      = "url-pattern";
+    private static final String FACES_SERVLET_DATA       = "javax.faces.webapp.FacesServlet";
+
+    private boolean             mInServletElement        = false;
+    private boolean             mInServletNameElement    = false;
+    private boolean             mInServletClassElement   = false;
+
+    private boolean             mInServletMappingElement = false;
+    private boolean             mInURLPatternElement     = false;
+
+    private String              mName                    = null;
+    private String              mClass                   = null;
+
+    private String              mFacesServletName        = null;
+    private StringBuffer        mContent;
+
+    @Override
+    public InputSource resolveEntity(String publicId, String systemId) throws SAXException
+    {
+
+      return new InputSource(new StringReader(""));
+
+    } // END resolveEntity
+
+    @Override
+    public void startElement(String uri, String localName, String qName, Attributes attributes)
+                                                                                               throws SAXException
+    {
+
+      boolean parseContent = false;
+
+      if (SERVLET_ELEMENT.equals(localName))
+      {
+        mInServletElement = true;
+      }
+      else if (SERVLET_MAPPING_ELEMENT.equals(localName))
+      {
+        mInServletMappingElement = true;
+      }
+      else if (mInServletElement)
+      {
+        if (SERVLET_CLASS_ELEMENT.equals(localName))
+        {
+          mInServletClassElement = parseContent = true;
+        }
+        else if (SERVLET_NAME_ELEMENT.equals(localName))
+        {
+          mInServletNameElement = parseContent = true;
+        }
+      }
+      else if (mInServletMappingElement)
+      {
+        if (URL_PATTERN_ELEMENT.equals(localName))
+        {
+          mInURLPatternElement = parseContent = true;
+        }
+        else if (SERVLET_NAME_ELEMENT.equals(localName))
+        {
+          mInServletNameElement = parseContent = true;
+        }
+      }
+      if (parseContent)
+      {
+        mContent = new StringBuffer();
+      }
+
+    } // END startElement
+
+    @Override
+    public void characters(char[] ch, int start, int length) throws SAXException
+    {
+
+      if (mContent != null)
+      {
+        mContent.append(ch, start, length);
+      }
+
+    } // END characters
+
+    @Override
+    public void endElement(String uri, String localName, String qName) throws SAXException
+    {
+
+      if (mInServletClassElement)
+      {
+        mClass = mContent.toString().trim();
+        mInServletClassElement = false;
+      }
+      else if (mInServletNameElement)
+      {
+        mName = mContent.toString().trim();
+        mInServletNameElement = false;
+      }
+      else if (mInURLPatternElement)
+      {
+        if (mInServletMappingElement && mName != null && mFacesServletName != null
+            && mName.equals(mFacesServletName))
+        {
+          if (mMappings == null)
+          {
+            mMappings = new Vector();
+          }
+          mMappings.add(mContent.toString().trim());
+        }
+        mInURLPatternElement = false;
+      }
+      else if (SERVLET_ELEMENT.equals(localName))
+      {
+        if (mName != null && mClass != null && mClass.equals(FACES_SERVLET_DATA))
+        {
+          mFacesServletName = mName;
+        }
+        mInServletElement = false;
+      }
+      else if (SERVLET_MAPPING_ELEMENT.equals(localName))
+      {
+        mInServletMappingElement = false;
+      }
+
+      mContent = null;
+
+    } // END endElement
+
+  } // END WebXmlHandler
+
+}

Added: myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/LocalesIterator.java
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/LocalesIterator.java?rev=588233&view=auto
==============================================================================
--- myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/LocalesIterator.java (added)
+++ myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/LocalesIterator.java Thu Oct 25 06:38:05 2007
@@ -0,0 +1,48 @@
+/* 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.portlet.faces.util.map;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+
+public class LocalesIterator implements Iterator
+{
+  private Enumeration mLocales;
+
+  public LocalesIterator(Enumeration locales)
+  {
+    mLocales = locales;
+  }
+
+  public boolean hasNext()
+  {
+    return mLocales.hasMoreElements();
+  }
+
+  public Object next()
+  {
+    return mLocales.nextElement();
+  }
+
+  public void remove()
+  {
+    throw new UnsupportedOperationException();
+  }
+}

Added: myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/PortletAbstractMap.java
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/PortletAbstractMap.java?rev=588233&view=auto
==============================================================================
--- myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/PortletAbstractMap.java (added)
+++ myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/PortletAbstractMap.java Thu Oct 25 06:38:05 2007
@@ -0,0 +1,340 @@
+/* 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.portlet.faces.util.map;
+
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * Helper class for different portlet external context maps
+ */
+public abstract class PortletAbstractMap implements Map
+{
+  static final String ILLEGAL_ARGUMENT = "Only supported in a portlet environment";
+
+  private Set         mKeySet;
+  private Collection  mValues;
+  private Set         mEntrySet;
+
+  public void clear()
+  {
+    List names = new ArrayList();
+    for (Enumeration e = getAttributeNames(); e.hasMoreElements();)
+    {
+      names.add(e.nextElement());
+    }
+
+    for (Iterator it = names.iterator(); it.hasNext();)
+    {
+      removeAttribute((String) it.next());
+    }
+  }
+
+  public boolean containsKey(Object key)
+  {
+    return getAttribute(key.toString()) != null;
+  }
+
+  public boolean containsValue(Object findValue)
+  {
+    if (findValue == null)
+    {
+      return false;
+    }
+
+    for (Enumeration e = getAttributeNames(); e.hasMoreElements();)
+    {
+      Object value = getAttribute((String) e.nextElement());
+      if (findValue.equals(value))
+      {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  public Set entrySet()
+  {
+    return mEntrySet != null ? mEntrySet : (mEntrySet = new EntrySet());
+  }
+
+  public Object get(Object key)
+  {
+    return getAttribute(key.toString());
+  }
+
+  public boolean isEmpty()
+  {
+    return !getAttributeNames().hasMoreElements();
+  }
+
+  public Set keySet()
+  {
+    return mKeySet != null ? mKeySet : (mKeySet = new KeySet());
+  }
+
+  public Object put(Object key, Object value)
+  {
+    String localKey = key.toString();
+    Object retval = getAttribute(localKey);
+    setAttribute(localKey, value);
+    return retval;
+  }
+
+  public void putAll(Map t)
+  {
+    for (Iterator it = t.entrySet().iterator(); it.hasNext();)
+    {
+      Entry entry = (Entry) it.next();
+      setAttribute(entry.getKey().toString(), entry.getValue());
+    }
+  }
+
+  public Object remove(Object key)
+  {
+    String localKey = key.toString();
+    Object retval = getAttribute(localKey);
+    removeAttribute(localKey);
+    return retval;
+  }
+
+  public int size()
+  {
+    int size = 0;
+    for (Enumeration e = getAttributeNames(); e.hasMoreElements();)
+    {
+      size++;
+      e.nextElement();
+    }
+    return size;
+  }
+
+  public Collection values()
+  {
+    return mValues != null ? mValues : (mValues = new Values());
+  }
+
+  protected abstract Object getAttribute(String key);
+
+  protected abstract void setAttribute(String key, Object value);
+
+  protected abstract void removeAttribute(String key);
+
+  protected abstract Enumeration getAttributeNames();
+
+  private class KeySet extends AbstractSet
+  {
+    @Override
+    public Iterator iterator()
+    {
+      return new KeyIterator();
+    }
+
+    @Override
+    public boolean isEmpty()
+    {
+      return PortletAbstractMap.this.isEmpty();
+    }
+
+    @Override
+    public int size()
+    {
+      return PortletAbstractMap.this.size();
+    }
+
+    @Override
+    public boolean contains(Object o)
+    {
+      return containsKey(o);
+    }
+
+    @Override
+    public boolean remove(Object o)
+    {
+      return PortletAbstractMap.this.remove(o) != null;
+    }
+
+    @Override
+    public void clear()
+    {
+      PortletAbstractMap.this.clear();
+    }
+  }
+
+  private class KeyIterator implements Iterator
+  {
+    protected final Enumeration mEnum = getAttributeNames();
+    protected Object            mKey;
+
+    public void remove()
+    {
+      if (mKey == null)
+      {
+        throw new NoSuchElementException();
+      }
+      PortletAbstractMap.this.remove(mKey);
+    }
+
+    public boolean hasNext()
+    {
+      return mEnum.hasMoreElements();
+    }
+
+    public Object next()
+    {
+      return mKey = mEnum.nextElement();
+    }
+  }
+
+  private class Values extends KeySet
+  {
+    @Override
+    public Iterator iterator()
+    {
+      return new ValuesIterator();
+    }
+
+    @Override
+    public boolean contains(Object o)
+    {
+      return containsValue(o);
+    }
+
+    @Override
+    public boolean remove(Object o)
+    {
+      if (o == null)
+      {
+        return false;
+      }
+
+      for (Iterator it = iterator(); it.hasNext();)
+      {
+        if (o.equals(it.next()))
+        {
+          it.remove();
+          return true;
+        }
+      }
+
+      return false;
+    }
+  }
+
+  private class ValuesIterator extends KeyIterator
+  {
+    @Override
+    public Object next()
+    {
+      super.next();
+      return get(mKey);
+    }
+  }
+
+  private class EntrySet extends KeySet
+  {
+    @Override
+    public Iterator iterator()
+    {
+      return new EntryIterator();
+    }
+
+    @Override
+    public boolean contains(Object o)
+    {
+      if (!(o instanceof Entry))
+      {
+        return false;
+      }
+
+      Entry entry = (Entry) o;
+      Object key = entry.getKey();
+      Object value = entry.getValue();
+      if (key == null || value == null)
+      {
+        return false;
+      }
+
+      return value.equals(get(key));
+    }
+
+    @Override
+    public boolean remove(Object o)
+    {
+      if (!(o instanceof Entry))
+      {
+        return false;
+      }
+
+      Entry entry = (Entry) o;
+      Object key = entry.getKey();
+      Object value = entry.getValue();
+      if (key == null || value == null || !value.equals(get(key)))
+      {
+        return false;
+      }
+
+      return PortletAbstractMap.this.remove(((Entry) o).getKey()) != null;
+    }
+  }
+
+  private class EntryIterator extends KeyIterator
+  {
+    @Override
+    public Object next()
+    {
+      super.next();
+      return new EntrySetEntry(mKey);
+    }
+  }
+
+  private class EntrySetEntry implements Entry
+  {
+    private final Object mKey;
+
+    public EntrySetEntry(Object currentKey)
+    {
+      mKey = currentKey;
+    }
+
+    public Object getKey()
+    {
+      return mKey;
+    }
+
+    public Object getValue()
+    {
+      return get(mKey);
+    }
+
+    public Object setValue(Object value)
+    {
+      return put(mKey, value);
+    }
+  }
+}

Added: myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/PortletApplicationMap.java
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/PortletApplicationMap.java?rev=588233&view=auto
==============================================================================
--- myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/PortletApplicationMap.java (added)
+++ myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/PortletApplicationMap.java Thu Oct 25 06:38:05 2007
@@ -0,0 +1,88 @@
+/* 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.portlet.faces.util.map;
+
+import java.util.Enumeration;
+
+import javax.portlet.PortletContext;
+
+/**
+ * Map of portlet context attributes
+ */
+public class PortletApplicationMap extends PortletAbstractMap
+{
+  private final PortletContext mPortletContext;
+
+  public PortletApplicationMap(Object context)
+  {
+    if (context instanceof PortletContext)
+    {
+      mPortletContext = (PortletContext) context;
+    }
+    else
+    {
+      throw new IllegalArgumentException(ILLEGAL_ARGUMENT);
+    }
+  }
+
+  @Override
+  public Object getAttribute(String key)
+  {
+    if (mPortletContext != null)
+    {
+      return mPortletContext.getAttribute(key);
+    }
+    else
+    {
+      throw new IllegalArgumentException(ILLEGAL_ARGUMENT);
+    }
+  }
+
+  @Override
+  public void setAttribute(String key, Object value)
+  {
+    if (mPortletContext != null)
+    {
+      mPortletContext.setAttribute(key, value);
+    }
+  }
+
+  @Override
+  public void removeAttribute(String key)
+  {
+    if (mPortletContext != null)
+    {
+      mPortletContext.removeAttribute(key);
+    }
+  }
+
+  @Override
+  public Enumeration getAttributeNames()
+  {
+    if (mPortletContext != null)
+    {
+      return mPortletContext.getAttributeNames();
+    }
+    else
+    {
+      throw new IllegalArgumentException(ILLEGAL_ARGUMENT);
+    }
+  }
+}

Added: myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/PortletInitParameterMap.java
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/PortletInitParameterMap.java?rev=588233&view=auto
==============================================================================
--- myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/PortletInitParameterMap.java (added)
+++ myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/util/map/PortletInitParameterMap.java Thu Oct 25 06:38:05 2007
@@ -0,0 +1,82 @@
+/* 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.portlet.faces.util.map;
+
+import java.util.Enumeration;
+
+import javax.portlet.PortletContext;
+
+/**
+ * Map of portlet context init params
+ */
+public class PortletInitParameterMap extends PortletAbstractMap
+{
+  private final PortletContext mPortletContext;
+
+  public PortletInitParameterMap(Object context)
+  {
+    if (context instanceof PortletContext)
+    {
+      mPortletContext = (PortletContext) context;
+    }
+    else
+    {
+      throw new IllegalArgumentException(ILLEGAL_ARGUMENT);
+    }
+  }
+
+  @Override
+  public Object getAttribute(String key)
+  {
+    if (mPortletContext != null)
+    {
+      return mPortletContext.getInitParameter(key);
+    }
+    else
+    {
+      throw new IllegalArgumentException(ILLEGAL_ARGUMENT);
+    }
+  }
+
+  @Override
+  public void setAttribute(String key, Object value)
+  {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void removeAttribute(String key)
+  {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public Enumeration getAttributeNames()
+  {
+    if (mPortletContext != null)
+    {
+      return mPortletContext.getInitParameterNames();
+    }
+    else
+    {
+      throw new IllegalArgumentException(ILLEGAL_ARGUMENT);
+    }
+  }
+}