You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rave.apache.org by zh...@apache.org on 2011/04/01 02:29:38 UTC

svn commit: r1087520 [4/35] - in /incubator/rave/donations/ogce-gadget-container: ./ config/ config/shindig-1.1-BETA5/ config/shindig-2.0.0/ db-cleaner/ examples/ examples/src/ examples/src/main/ examples/src/main/java/ examples/src/main/java/cgl/ exam...

Added: incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Text.java
URL: http://svn.apache.org/viewvc/incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Text.java?rev=1087520&view=auto
==============================================================================
--- incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Text.java (added)
+++ incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Text.java Fri Apr  1 00:29:22 2011
@@ -0,0 +1,789 @@
+package cgl.shindig.common;
+/*
+ *
+ * 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.
+ *
+ */
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Properties;
+
+/**
+ * This Class provides some text related utilities
+ */
+public class Text {
+
+    /**
+     * Hidden constructor.
+     */
+    private Text() {
+    }
+
+    /**
+     * used for the md5
+     */
+    public static final char[] hexTable = "0123456789abcdef".toCharArray();
+
+    /**
+     * Calculate an MD5 hash of the string given.
+     *
+     * @param data the data to encode
+     * @param enc  the character encoding to use
+     * @return a hex encoded string of the md5 digested input
+     */
+    public static String md5(String data, String enc)
+            throws UnsupportedEncodingException {
+        try {
+            return digest("MD5", data.getBytes(enc));
+        } catch (NoSuchAlgorithmException e) {
+            throw new InternalError("MD5 digest not available???");
+        }
+    }
+
+    /**
+     * Calculate an MD5 hash of the string given using 'utf-8' encoding.
+     *
+     * @param data the data to encode
+     * @return a hex encoded string of the md5 digested input
+     */
+    public static String md5(String data) {
+        try {
+            return md5(data, "utf-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new InternalError("UTF8 digest not available???");
+        }
+    }
+
+    /**
+     * Digest the plain string using the given algorithm.
+     *
+     * @param algorithm The alogrithm for the digest. This algorithm must be
+     *                  supported by the MessageDigest class.
+     * @param data      The plain text String to be digested.
+     * @param enc       The character encoding to use
+     * @return The digested plain text String represented as Hex digits.
+     * @throws java.security.NoSuchAlgorithmException     if the desired algorithm is not supported by
+     *                                      the MessageDigest class.
+     * @throws java.io.UnsupportedEncodingException if the encoding is not supported
+     */
+    public static String digest(String algorithm, String data, String enc)
+            throws NoSuchAlgorithmException, UnsupportedEncodingException {
+
+        return digest(algorithm, data.getBytes(enc));
+    }
+
+    /**
+     * Digest the plain string using the given algorithm.
+     *
+     * @param algorithm The alogrithm for the digest. This algorithm must be
+     *                  supported by the MessageDigest class.
+     * @param data      the data to digest with the given algorithm
+     * @return The digested plain text String represented as Hex digits.
+     * @throws java.security.NoSuchAlgorithmException if the desired algorithm is not supported by
+     *                                  the MessageDigest class.
+     */
+    public static String digest(String algorithm, byte[] data)
+            throws NoSuchAlgorithmException {
+
+        MessageDigest md = MessageDigest.getInstance(algorithm);
+        byte[] digest = md.digest(data);
+        StringBuffer res = new StringBuffer(digest.length * 2);
+        for (int i = 0; i < digest.length; i++) {
+            byte b = digest[i];
+            res.append(hexTable[(b >> 4) & 15]);
+            res.append(hexTable[b & 15]);
+        }
+        return res.toString();
+    }
+
+    /**
+     * returns an array of strings decomposed of the original string, split at
+     * every occurance of 'ch'. if 2 'ch' follow each other with no intermediate
+     * characters, empty "" entries are avoided.
+     *
+     * @param str the string to decompose
+     * @param ch  the character to use a split pattern
+     * @return an array of strings
+     */
+    public static String[] explode(String str, int ch) {
+        return explode(str, ch, false);
+    }
+
+    /**
+     * returns an array of strings decomposed of the original string, split at
+     * every occurance of 'ch'.
+     *
+     * @param str          the string to decompose
+     * @param ch           the character to use a split pattern
+     * @param respectEmpty if <code>true</code>, empty elements are generated
+     * @return an array of strings
+     */
+    public static String[] explode(String str, int ch, boolean respectEmpty) {
+        if (str == null || str.length() == 0) {
+            return new String[0];
+        }
+
+        ArrayList strings = new ArrayList();
+        int pos;
+        int lastpos = 0;
+
+        // add snipples
+        while ((pos = str.indexOf(ch, lastpos)) >= 0) {
+            if (pos - lastpos > 0 || respectEmpty) {
+                strings.add(str.substring(lastpos, pos));
+            }
+            lastpos = pos + 1;
+        }
+        // add rest
+        if (lastpos < str.length()) {
+            strings.add(str.substring(lastpos));
+        } else if (respectEmpty && lastpos == str.length()) {
+            strings.add("");
+        }
+
+        // return stringarray
+        return (String[]) strings.toArray(new String[strings.size()]);
+    }
+
+    /**
+     * Concatenates all strings in the string array using the specified delimiter.
+     * @param arr
+     * @param delim
+     * @return the concatenated string
+     */
+    public static String implode(String[] arr, String delim) {
+        StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < arr.length; i++) {
+            if (i > 0) {
+                buf.append(delim);
+            }
+            buf.append(arr[i]);
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Replaces all occurences of <code>oldString</code> in <code>text</code>
+     * with <code>newString</code>.
+     *
+     * @param text
+     * @param oldString old substring to be replaced with <code>newString</code>
+     * @param newString new substring to replace occurences of <code>oldString</code>
+     * @return a string
+     */
+    public static String replace(String text, String oldString, String newString) {
+        if (text == null || oldString == null || newString == null) {
+            throw new IllegalArgumentException("null argument");
+        }
+        int pos = text.indexOf(oldString);
+        if (pos == -1) {
+            return text;
+        }
+        int lastPos = 0;
+        StringBuffer sb = new StringBuffer(text.length());
+        while (pos != -1) {
+            sb.append(text.substring(lastPos, pos));
+            sb.append(newString);
+            lastPos = pos + oldString.length();
+            pos = text.indexOf(oldString, lastPos);
+        }
+        if (lastPos < text.length()) {
+            sb.append(text.substring(lastPos));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Replaces illegal XML characters in the given string by their corresponding
+     * predefined entity references.
+     *
+     * @param text text to be escaped
+     * @return a string
+     */
+    public static String encodeIllegalXMLCharacters(String text) {
+        if (text == null) {
+            throw new IllegalArgumentException("null argument");
+        }
+        StringBuffer buf = null;
+        int length = text.length();
+        int pos = 0;
+        for (int i = 0; i < length; i++) {
+            int ch = text.charAt(i);
+            switch (ch) {
+                case '<':
+                case '>':
+                case '&':
+                case '"':
+                case '\'':
+                    if (buf == null) {
+                        buf = new StringBuffer();
+                    }
+                    if (i > 0) {
+                        buf.append(text.substring(pos, i));
+                    }
+                    pos = i + 1;
+                    break;
+                default:
+                    continue;
+            }
+            if (ch == '<') {
+                buf.append("&lt;");
+            } else if (ch == '>') {
+                buf.append("&gt;");
+            } else if (ch == '&') {
+                buf.append("&amp;");
+            } else if (ch == '"') {
+                buf.append("&quot;");
+            } else if (ch == '\'') {
+                buf.append("&apos;");
+            }
+        }
+        if (buf == null) {
+            return text;
+        } else {
+            if (pos < length) {
+                buf.append(text.substring(pos));
+            }
+            return buf.toString();
+        }
+    }
+
+    /**
+     * The list of characters that are not encoded by the <code>escape()</code>
+     * and <code>unescape()</code> METHODS. They contains the characters as
+     * defined 'unreserved' in section 2.3 of the RFC 2396 'URI generic syntax':
+     * <p/>
+     * <pre>
+     * unreserved  = alphanum | mark
+     * mark        = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+     * </pre>
+     */
+    public static BitSet URISave;
+
+    /**
+     * Same as {@link #URISave} but also contains the '/'
+     */
+    public static BitSet URISaveEx;
+
+    static {
+        URISave = new BitSet(256);
+        int i;
+        for (i = 'a'; i <= 'z'; i++) {
+            URISave.set(i);
+        }
+        for (i = 'A'; i <= 'Z'; i++) {
+            URISave.set(i);
+        }
+        for (i = '0'; i <= '9'; i++) {
+            URISave.set(i);
+        }
+        URISave.set('-');
+        URISave.set('_');
+        URISave.set('.');
+        URISave.set('!');
+        URISave.set('~');
+        URISave.set('*');
+        URISave.set('\'');
+        URISave.set('(');
+        URISave.set(')');
+
+        URISaveEx = (BitSet) URISave.clone();
+        URISaveEx.set('/');
+    }
+
+    /**
+     * Does an URL encoding of the <code>string</code> using the
+     * <code>escape</code> character. The characters that don't need encoding
+     * are those defined 'unreserved' in section 2.3 of the 'URI generic syntax'
+     * RFC 2396, but without the escape character.
+     *
+     * @param string the string to encode.
+     * @param escape the escape character.
+     * @return the escaped string
+     * @throws NullPointerException if <code>string</code> is <code>null</code>.
+     */
+    public static String escape(String string, char escape) {
+        return escape(string, escape, false);
+    }
+
+    /**
+     * Does an URL encoding of the <code>string</code> using the
+     * <code>escape</code> character. The characters that don't need encoding
+     * are those defined 'unreserved' in section 2.3 of the 'URI generic syntax'
+     * RFC 2396, but without the escape character. If <code>isPath</code> is
+     * <code>true</code>, additionally the slash '/' is ignored, too.
+     *
+     * @param string the string to encode.
+     * @param escape the escape character.
+     * @param isPath if <code>true</code>, the string is treated as path
+     * @return the escaped string
+     * @throws NullPointerException if <code>string</code> is <code>null</code>.
+     */
+    public static String escape(String string, char escape, boolean isPath) {
+        try {
+            BitSet validChars = isPath ? URISaveEx : URISave;
+            byte[] bytes = string.getBytes("utf-8");
+            StringBuffer out = new StringBuffer(bytes.length);
+            for (int i = 0; i < bytes.length; i++) {
+                int c = bytes[i] & 0xff;
+                if (validChars.get(c) && c != escape) {
+                    out.append((char) c);
+                } else {
+                    out.append(escape);
+                    out.append(hexTable[(c >> 4) & 0x0f]);
+                    out.append(hexTable[(c) & 0x0f]);
+                }
+            }
+            return out.toString();
+        } catch (UnsupportedEncodingException e) {
+            throw new InternalError(e.toString());
+        }
+    }
+
+    /**
+     * Does a URL encoding of the <code>string</code>. The characters that
+     * don't need encoding are those defined 'unreserved' in section 2.3 of
+     * the 'URI generic syntax' RFC 2396.
+     *
+     * @param string the string to encode
+     * @return the escaped string
+     * @throws NullPointerException if <code>string</code> is <code>null</code>.
+     */
+    public static String escape(String string) {
+        return escape(string, '%');
+    }
+
+    /**
+     * Does a URL encoding of the <code>path</code>. The characters that
+     * don't need encoding are those defined 'unreserved' in section 2.3 of
+     * the 'URI generic syntax' RFC 2396. In contrast to the
+     * {@link #escape(String)} method, not the entire path string is escaped,
+     * but every individual part (i.e. the slashes are not escaped).
+     *
+     * @param path the path to encode
+     * @return the escaped path
+     * @throws NullPointerException if <code>path</code> is <code>null</code>.
+     */
+    public static String escapePath(String path) {
+        return escape(path, '%', true);
+    }
+
+    /**
+     * Does a URL decoding of the <code>string</code> using the
+     * <code>escape</code> character. Please note that in opposite to the
+     * {@link java.net.URLDecoder} it does not transform the + into spaces.
+     *
+     * @param string the string to decode
+     * @param escape the escape character
+     * @return the decoded string
+     * @throws NullPointerException           if <code>string</code> is <code>null</code>.
+     * @throws IllegalArgumentException       if the 2 characters following the escape
+     *                                        character do not represent a hex-number
+     *                                        or if not enough characters follow an
+     *                                        escape character
+     */
+    public static String unescape(String string, char escape)  {
+        try {
+            byte[] utf8 = string.getBytes("utf-8");
+
+            // Check whether escape occurs at invalid position
+            if ((utf8.length >= 1 && utf8[utf8.length - 1] == escape) ||
+                (utf8.length >= 2 && utf8[utf8.length - 2] == escape)) {
+                throw new IllegalArgumentException("Premature end of escape sequence at end of input");
+            }
+
+            ByteArrayOutputStream out = new ByteArrayOutputStream(utf8.length);
+            for (int k = 0; k < utf8.length; k++) {
+                byte b = utf8[k];
+                if (b == escape) {
+                    out.write((decodeDigit(utf8[++k]) << 4) + decodeDigit(utf8[++k]));
+                }
+                else {
+                    out.write(b);
+                }
+            }
+
+            return new String(out.toByteArray(), "utf-8");
+        }
+        catch (UnsupportedEncodingException e) {
+            throw new InternalError(e.toString());
+        }
+    }
+
+    /**
+     * Does a URL decoding of the <code>string</code>. Please note that in
+     * opposite to the {@link java.net.URLDecoder} it does not transform the +
+     * into spaces.
+     *
+     * @param string the string to decode
+     * @return the decoded string
+     * @throws NullPointerException           if <code>string</code> is <code>null</code>.
+     * @throws ArrayIndexOutOfBoundsException if not enough character follow an
+     *                                        escape character
+     * @throws IllegalArgumentException       if the 2 characters following the escape
+     *                                        character do not represent a hex-number.
+     */
+    public static String unescape(String string) {
+        return unescape(string, '%');
+    }
+
+    /**
+     * Escapes all illegal JCR name characters of a string.
+     * The encoding is loosely modeled after URI encoding, but only encodes
+     * the characters it absolutely needs to in order to make the resulting
+     * string a valid JCR name.
+     * Use {@link #unescapeIllegalJcrChars(String)} for decoding.
+     * <p/>
+     * QName EBNF:<br>
+     * <xmp>
+     * simplename ::= onecharsimplename | twocharsimplename | threeormorecharname
+     * onecharsimplename ::= (* Any Unicode character except: '.', '/', ':', '[', ']', '*', '|' or any whitespace character *)
+     * twocharsimplename ::= '.' onecharsimplename | onecharsimplename '.' | onecharsimplename onecharsimplename
+     * threeormorecharname ::= nonspace string nonspace
+     * string ::= char | string char
+     * char ::= nonspace | ' '
+     * nonspace ::= (* Any Unicode character except: '/', ':', '[', ']', '*', '|' or any whitespace character *)
+     * </xmp>
+     *
+     * @param name the name to escape
+     * @return the escaped name
+     */
+    public static String escapeIllegalJcrChars(String name) {
+        StringBuffer buffer = new StringBuffer(name.length() * 2);
+        for (int i = 0; i < name.length(); i++) {
+            char ch = name.charAt(i);
+            if (ch == '%' || ch == '/' || ch == ':' || ch == '[' || ch == ']'
+                || ch == '*' || ch == '|'
+                || (ch == '.' && name.length() < 3)
+                || (ch == ' ' && (i == 0 || i == name.length() - 1))
+                || ch == '\t' || ch == '\r' || ch == '\n') {
+                buffer.append('%');
+                buffer.append(Character.toUpperCase(Character.forDigit(ch / 16, 16)));
+                buffer.append(Character.toUpperCase(Character.forDigit(ch % 16, 16)));
+            } else {
+                buffer.append(ch);
+            }
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Escapes all illegal XPath search characters of a string.
+     * <p>Example:<br>
+     * A search string like 'test?' will run into a ParseException
+     * documented in http://issues.apache.org/jira/browse/JCR-1248
+     *
+     * @param s the string to encode
+     * @return the escaped string
+     */
+    public static String escapeIllegalXpathSearchChars(String s) {
+        StringBuffer sb = new StringBuffer();
+        sb.append(s.substring(0, (s.length() - 1)));
+        char c = s.charAt(s.length() - 1);
+        // NOTE: keep this in sync with _ESCAPED_CHAR below!
+        if (c == '!' || c == '(' || c == ':' || c == '^'
+            || c == '[' || c == ']' || c == '\"' || c == '{'
+            || c == '}' || c == '?') {
+            sb.append('\\');
+        }
+        sb.append(c);
+        return sb.toString();
+    }
+
+    /**
+     * Unescapes previously escaped jcr chars.
+     * <p/>
+     * Please note, that this does not exactly the same as the url related
+     * {@link #unescape(String)}, since it handles the byte-encoding
+     * differently.
+     *
+     * @param name the name to unescape
+     * @return the unescaped name
+     */
+    public static String unescapeIllegalJcrChars(String name) {
+        StringBuffer buffer = new StringBuffer(name.length());
+        int i = name.indexOf('%');
+        while (i > -1 && i + 2 < name.length()) {
+            buffer.append(name.toCharArray(), 0, i);
+            int a = Character.digit(name.charAt(i + 1), 16);
+            int b = Character.digit(name.charAt(i + 2), 16);
+            if (a > -1 && b > -1) {
+                buffer.append((char) (a * 16 + b));
+                name = name.substring(i + 3);
+            } else {
+                buffer.append('%');
+                name = name.substring(i + 1);
+            }
+            i = name.indexOf('%');
+        }
+        buffer.append(name);
+        return buffer.toString();
+    }
+
+    /**
+     * Returns the name part of the path. If the given path is already a name
+     * (i.e. contains no slashes) it is returned.
+     *
+     * @param path the path
+     * @return the name part or <code>null</code> if <code>path</code> is <code>null</code>.
+     */
+    public static String getName(String path) {
+        return getName(path, '/');
+    }
+
+    /**
+     * Returns the name part of the path, delimited by the given <code>delim</code>.
+     * If the given path is already a name (i.e. contains no <code>delim</code>
+     * characters) it is returned.
+     *
+     * @param path the path
+     * @param delim the delimiter
+     * @return the name part or <code>null</code> if <code>path</code> is <code>null</code>.
+     */
+    public static String getName(String path, char delim) {
+        return path == null
+                ? null
+                : path.substring(path.lastIndexOf(delim) + 1);
+    }
+
+    /**
+     * Same as {@link #getName(String)} but adding the possibility
+     * to pass paths that end with a trailing '/'
+     *
+     * @see #getName(String)
+     */
+    public static String getName(String path, boolean ignoreTrailingSlash) {
+        if (ignoreTrailingSlash && path != null && path.endsWith("/") && path.length() > 1) {
+            path = path.substring(0, path.length()-1);
+        }
+        return getName(path);
+    }
+
+    /**
+     * Returns the namespace prefix of the given <code>qname</code>. If the
+     * prefix is missing, an empty string is returned. Please note, that this
+     * method does not validate the name or prefix.
+     * </p>
+     * the qname has the format: qname := [prefix ':'] local;
+     *
+     * @param qname a qualified name
+     * @return the prefix of the name or "".
+     *
+     * @see #getLocalName(String)
+     *
+     * @throws NullPointerException if <code>qname</code> is <code>null</code>
+     */
+    public static String getNamespacePrefix(String qname) {
+        int pos = qname.indexOf(':');
+        return pos >=0 ? qname.substring(0, pos) : "";
+    }
+
+    /**
+     * Returns the local name of the given <code>qname</code>. Please note, that
+     * this method does not validate the name.
+     * </p>
+     * the qname has the format: qname := [prefix ':'] local;
+     *
+     * @param qname a qualified name
+     * @return the localname
+     *
+     * @see #getNamespacePrefix(String)
+     *
+     * @throws NullPointerException if <code>qname</code> is <code>null</code>
+     */
+    public static String getLocalName(String qname) {
+        int pos = qname.indexOf(':');
+        return pos >=0 ? qname.substring(pos+1) : qname;
+    }
+
+    /**
+     * Determines, if two paths denote hierarchical siblins.
+     *
+     * @param p1 first path
+     * @param p2 second path
+     * @return true if on same level, false otherwise
+     */
+    public static boolean isSibling(String p1, String p2) {
+        int pos1 = p1.lastIndexOf('/');
+        int pos2 = p2.lastIndexOf('/');
+        return (pos1 == pos2 && pos1 >= 0 && p1.regionMatches(0, p2, 0, pos1));
+    }
+
+    /**
+     * Determines if the <code>descendant</code> path is hierarchical a
+     * descendant of <code>path</code>.
+     *
+     * @param path     the current path
+     * @param descendant the potential descendant
+     * @return <code>true</code> if the <code>descendant</code> is a descendant;
+     *         <code>false</code> otherwise.
+     */
+    public static boolean isDescendant(String path, String descendant) {
+        String pattern = path.endsWith("/") ? path : path + "/";
+        return !pattern.equals(descendant) &&
+                descendant.startsWith(pattern);
+    }
+
+    /**
+     * Determines if the <code>descendant</code> path is hierarchical a
+     * descendant of <code>path</code> or equal to it.
+     *
+     * @param path       the path to check
+     * @param descendant the potential descendant
+     * @return <code>true</code> if the <code>descendant</code> is a descendant
+     *         or equal; <code>false</code> otherwise.
+     */
+    public static boolean isDescendantOrEqual(String path, String descendant) {
+        if (path.equals(descendant)) {
+            return true;
+        } else {
+            String pattern = path.endsWith("/") ? path : path + "/";
+            return descendant.startsWith(pattern);
+        }
+    }
+
+    /**
+     * Returns the n<sup>th</sup> relative parent of the path, where n=level.
+     * <p>Example:<br>
+     * <code>
+     * Text.getRelativeParent("/foo/bar/test", 1) == "/foo/bar"
+     * </code>
+     *
+     * @param path the path of the page
+     * @param level  the level of the parent
+     */
+    public static String getRelativeParent(String path, int level) {
+        int idx = path.length();
+        while (level > 0) {
+            idx = path.lastIndexOf('/', idx - 1);
+            if (idx < 0) {
+                return "";
+            }
+            level--;
+        }
+        return (idx == 0) ? "/" : path.substring(0, idx);
+    }
+
+    /**
+     * Same as {@link #getRelativeParent(String, int)} but adding the possibility
+     * to pass paths that end with a trailing '/'
+     *
+     * @see #getRelativeParent(String, int)
+     */
+    public static String getRelativeParent(String path, int level, boolean ignoreTrailingSlash) {
+        if (ignoreTrailingSlash && path.endsWith("/") && path.length() > 1) {
+            path = path.substring(0, path.length()-1);
+        }
+        return getRelativeParent(path, level);
+    }
+
+    /**
+     * Returns the n<sup>th</sup> absolute parent of the path, where n=level.
+     * <p>Example:<br>
+     * <code>
+     * Text.getAbsoluteParent("/foo/bar/test", 1) == "/foo/bar"
+     * </code>
+     *
+     * @param path the path of the page
+     * @param level  the level of the parent
+     */
+    public static String getAbsoluteParent(String path, int level) {
+        int idx = 0;
+        int len = path.length();
+        while (level >= 0 && idx < len) {
+            idx = path.indexOf('/', idx + 1);
+            if (idx < 0) {
+                idx = len;
+            }
+            level--;
+        }
+        return level >= 0 ? "" : path.substring(0, idx);
+    }
+
+    /**
+     * Performs variable replacement on the given string value.
+     * Each <code>${...}</code> sequence within the given value is replaced
+     * with the value of the named parser variable. If a variable is not found
+     * in the properties an IllegalArgumentException is thrown unless
+     * <code>ignoreMissing</code> is <code>true</code>. In the later case, the
+     * missing variable is replaced by the empty string.
+     *
+     * @param value         the original value
+     * @param ignoreMissing if <code>true</code>, missing variables are replaced
+     *                      by the empty string.
+     * @return value after variable replacements
+     * @throws IllegalArgumentException if the replacement of a referenced
+     *                                  variable is not found
+     */
+    public static String replaceVariables(Properties variables, String value,
+                                          boolean ignoreMissing)
+            throws IllegalArgumentException {
+        StringBuffer result = new StringBuffer();
+
+        // Value:
+        // +--+-+--------+-+-----------------+
+        // |  |p|-->     |q|-->              |
+        // +--+-+--------+-+-----------------+
+        int p = 0, q = value.indexOf("${");                // Find first ${
+        while (q != -1) {
+            result.append(value.substring(p, q));          // Text before ${
+            p = q;
+            q = value.indexOf("}", q + 2);                 // Find }
+            if (q != -1) {
+                String variable = value.substring(p + 2, q);
+                String replacement = variables.getProperty(variable);
+                if (replacement == null) {
+                    if (ignoreMissing) {
+                        replacement = "";
+                    } else {
+                        throw new IllegalArgumentException(
+                                "Replacement not found for ${" + variable + "}.");
+                    }
+                }
+                result.append(replacement);
+                p = q + 1;
+                q = value.indexOf("${", p);                // Find next ${
+            }
+        }
+        result.append(value.substring(p, value.length())); // Trailing text
+
+        return result.toString();
+    }
+
+    private static byte decodeDigit(byte b) {
+        if (b >= 0x30 && b <= 0x39) {
+            return (byte) (b - 0x30);
+        }
+        else if (b >= 0x41 && b <= 0x46) {
+            return (byte) (b - 0x37);
+        }
+        else if (b >= 0x61 && b <= 0x66) {
+            return (byte) (b - 0x57);
+        }
+        else {
+            throw new IllegalArgumentException("Escape sequence is not hexadecimal: " + (char)b);
+        }
+    }
+
+}

Added: incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Uri.java
URL: http://svn.apache.org/viewvc/incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Uri.java?rev=1087520&view=auto
==============================================================================
--- incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Uri.java (added)
+++ incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Uri.java Fri Apr  1 00:29:22 2011
@@ -0,0 +1,357 @@
+/*
+ * 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 cgl.shindig.common;
+
+import org.apache.commons.lang.StringUtils;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+* Represents a Uniform Resource Identifier (URI) reference as defined by <a
+* href="http://tools.ietf.org/html/rfc3986">RFC 3986</a>.
+*
+* Assumes that all url components are UTF-8 encoded.
+*/
+public final class Uri {
+  private final String text;
+  private final String scheme;
+  private final String authority;
+  private final String path;
+  private final String query;
+  private final String fragment;
+
+  private final Map<String, List<String>> queryParameters;
+
+  private static UriParser parser = new DefaultUriParser();
+
+  @Inject(optional = true)
+  public static void setUriParser(UriParser uriParser) {
+    parser = uriParser;
+  }
+
+  Uri(UriBuilder builder) {
+    scheme = builder.getScheme();
+    authority = builder.getAuthority();
+    path = builder.getPath();
+    query = builder.getQuery();
+    fragment = builder.getFragment();
+    queryParameters
+        = Collections.unmodifiableMap(Maps.newLinkedHashMap(builder.getQueryParameters()));
+
+    StringBuilder out = new StringBuilder();
+
+    if (scheme != null) {
+      out.append(scheme).append(':');
+    }
+    if (authority != null) {
+      out.append("//").append(authority);
+    }
+    if (path != null) {
+      out.append(path);
+    }
+    if (query != null) {
+      out.append('?').append(query);
+    }
+    if (fragment != null) {
+      out.append('#').append(fragment);
+    }
+    text = out.toString();
+  }
+
+  /**
+   * Produces a new Uri from a text representation.
+   *
+   * @param text The text uri.
+   * @return A new Uri, parsed into components.
+   */
+  public static Uri parse(String text) {
+    return parser.parse(text);
+  }
+
+  /**
+   * Convert a java.net.URI to a Uri.
+   */
+  public static Uri fromJavaUri(URI uri) {
+    if (uri.isOpaque()) {
+      throw new IllegalArgumentException("No support for opaque Uris " + uri.toString());
+    }
+    return new UriBuilder()
+        .setScheme(uri.getScheme())
+        .setAuthority(uri.getRawAuthority())
+        .setPath(uri.getRawPath())
+        .setQuery(uri.getRawQuery())
+        .setFragment(uri.getRawFragment())
+        .toUri();
+  }
+
+  /**
+   * @return a java.net.URI equal to this Uri.
+   */
+  public URI toJavaUri() {
+    try {
+      return new URI(toString());
+    } catch (URISyntaxException e) {
+      // Shouldn't ever happen.
+      throw new IllegalArgumentException(e);
+    }
+  }
+
+  /**
+   * Derived from Harmony
+   * Resolves a given url relative to this url. Resolution rules are the same as for
+   * {@code java.net.URI.resolve(URI)}
+   */
+  public Uri resolve(Uri relative) {
+    if (relative == null) {
+      return null;
+    }
+    if (relative.isAbsolute()) {
+      return relative;
+    }
+
+    UriBuilder result;
+    if (StringUtils.isEmpty(relative.path) && relative.scheme == null
+        && relative.authority == null && relative.query == null
+        && relative.fragment != null) {
+      // if the relative URI only consists of fragment,
+      // the resolved URI is very similar to this URI,
+      // except that it has the fragement from the relative URI.
+      result = new UriBuilder(this);
+      result.setFragment(relative.fragment);
+    } else if (relative.scheme != null) {
+      result = new UriBuilder(relative);
+    } else if (relative.authority != null) {
+      // if the relative URI has authority,
+      // the resolved URI is almost the same as the relative URI,
+      // except that it has the scheme of this URI.
+      result = new UriBuilder(relative);
+      result.setScheme(scheme);
+    } else {
+      // since relative URI has no authority,
+      // the resolved URI is very similar to this URI,
+      // except that it has the query and fragment of the relative URI,
+      // and the path is different.
+      result = new UriBuilder(this);
+      result.setFragment(relative.fragment);
+      result.setQuery(relative.query);
+      String relativePath = (relative.path == null) ? "" : relative.path;
+      if (relativePath.startsWith("/")) { //$NON-NLS-1$
+        result.setPath(relativePath);
+      } else {
+        // resolve a relative reference
+        int endindex = path.lastIndexOf('/') + 1;
+        result.setPath(normalizePath(path.substring(0, endindex) + relativePath));
+      }
+    }
+    Uri resolved = result.toUri();
+    validate(resolved);
+    return resolved;
+  }
+
+  private static void validate(Uri uri) {
+    if (StringUtils.isEmpty(uri.authority) &&
+        StringUtils.isEmpty(uri.path) &&
+        StringUtils.isEmpty(uri.query)) {
+      throw new IllegalArgumentException("Invalid scheme-specific part");
+    }
+  }
+
+  /**
+   * Dervived from harmony
+   * normalize path, and return the resulting string
+   */
+  private static String normalizePath(String path) {
+    // count the number of '/'s, to determine number of segments
+    int index = -1;
+    int pathlen = path.length();
+    int size = 0;
+    if (pathlen > 0 && path.charAt(0) != '/') {
+      size++;
+    }
+    while ((index = path.indexOf('/', index + 1)) != -1) {
+      if (index + 1 < pathlen && path.charAt(index + 1) != '/') {
+        size++;
+      }
+    }
+
+    String[] seglist = new String[size];
+    boolean[] include = new boolean[size];
+
+    // break the path into segments and store in the list
+    int current = 0;
+    int index2 = 0;
+    index = (pathlen > 0 && path.charAt(0) == '/') ? 1 : 0;
+    while ((index2 = path.indexOf('/', index + 1)) != -1) {
+      seglist[current++] = path.substring(index, index2);
+      index = index2 + 1;
+    }
+
+    // if current==size, then the last character was a slash
+    // and there are no more segments
+    if (current < size) {
+      seglist[current] = path.substring(index);
+    }
+
+    // determine which segments get included in the normalized path
+    for (int i = 0; i < size; i++) {
+      include[i] = true;
+      if (seglist[i].equals("..")) { //$NON-NLS-1$
+        int remove = i - 1;
+        // search back to find a segment to remove, if possible
+        while (remove > -1 && !include[remove]) {
+          remove--;
+        }
+        // if we find a segment to remove, remove it and the ".."
+        // segment
+        if (remove > -1 && !seglist[remove].equals("..")) { //$NON-NLS-1$
+          include[remove] = false;
+          include[i] = false;
+        }
+      } else if (seglist[i].equals(".")) { //$NON-NLS-1$
+        include[i] = false;
+      }
+    }
+
+    // put the path back together
+    StringBuilder newpath = new StringBuilder();
+    if (path.startsWith("/")) { //$NON-NLS-1$
+      newpath.append('/');
+    }
+
+    for (int i = 0; i < seglist.length; i++) {
+      if (include[i]) {
+        newpath.append(seglist[i]);
+        newpath.append('/');
+      }
+    }
+
+    // if we used at least one segment and the path previously ended with
+    // a slash and the last segment is still used, then delete the extra
+    // trailing '/'
+    if (!path.endsWith("/") && seglist.length > 0 //$NON-NLS-1$
+        && include[seglist.length - 1]) {
+      newpath.deleteCharAt(newpath.length() - 1);
+    }
+
+    String result = newpath.toString();
+
+    // check for a ':' in the first segment if one exists,
+    // prepend "./" to normalize
+    index = result.indexOf(':');
+    index2 = result.indexOf('/');
+    if (index != -1 && (index < index2 || index2 == -1)) {
+      newpath.insert(0, "./"); //$NON-NLS-1$
+      result = newpath.toString();
+    }
+    return result;
+  }
+
+  /**
+   * @return True if the Uri is absolute.
+   */
+  public boolean isAbsolute() {
+    return scheme != null && authority != null;
+  }
+
+  /**
+   * @return The scheme part of the uri, or null if none was specified.
+   */
+  public String getScheme() {
+    return scheme;
+  }
+
+  /**
+   * @return The authority part of the uri, or null if none was specified.
+   */
+  public String getAuthority() {
+    return authority;
+  }
+
+  /**
+   * @return The path part of the uri, or null if none was specified.
+   */
+  public String getPath() {
+    return path;
+  }
+
+  /**
+   * @return The query part of the uri, or null if none was specified.
+   */
+  public String getQuery() {
+    return query;
+  }
+
+  /**
+   * @return The query part of the uri, separated into component parts.
+   */
+  public Map<String, List<String>> getQueryParameters() {
+    return queryParameters;
+  }
+
+  /**
+   * @return All query parameters with the given name.
+   */
+  public Collection<String> getQueryParameters(String name) {
+    return queryParameters.get(name);
+  }
+
+  /**
+   * @return The first query parameter value with the given name.
+   */
+  public String getQueryParameter(String name) {
+    Collection<String> values = queryParameters.get(name);
+    if (values == null || values.isEmpty()) {
+      return null;
+    }
+    return values.iterator().next();
+  }
+
+  /**
+   * @return The uri fragment.
+   */
+  public String getFragment() {
+    return fragment;
+  }
+
+  @Override
+  public String toString() {
+    return text;
+  }
+
+  @Override
+  public int hashCode() {
+    return text.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {return true;}
+    if (!(obj instanceof Uri)) {return false;}
+    return Objects.equal(text, ((Uri)obj).text);
+  }
+}

Added: incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/UriBuilder.java
URL: http://svn.apache.org/viewvc/incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/UriBuilder.java?rev=1087520&view=auto
==============================================================================
--- incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/UriBuilder.java (added)
+++ incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/UriBuilder.java Fri Apr  1 00:29:22 2011
@@ -0,0 +1,289 @@
+/*
+ * 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 cgl.shindig.common;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Constructs Uris from inputs.
+ *
+ * Note that the builder will only automatically encode query parameters that are added. Other
+ * parameters must be encoded explicitly.
+ */
+public class UriBuilder {
+  private static final Pattern QUERY_PATTERN = Pattern.compile("([^&=]+)=([^&=]*)");
+
+  private String scheme;
+  private String authority;
+  private String path;
+  private String query;
+  private String fragment;
+  private final Map<String, List<String>> queryParameters;
+
+
+  /**
+   * Construct a new builder from an existing uri.
+   */
+  public UriBuilder(Uri uri) {
+    scheme = uri.getScheme();
+    authority = uri.getAuthority();
+    path = uri.getPath();
+    query = uri.getQuery();
+    fragment = uri.getFragment();
+
+    queryParameters = Maps.newLinkedHashMap(uri.getQueryParameters());
+  }
+
+  /**
+   * Create an empty builder.
+   */
+  public UriBuilder() {
+    queryParameters =  Maps.newLinkedHashMap();
+  }
+
+  /**
+   * Construct a builder by parsing a string.
+   */
+  public static UriBuilder parse(String text) {
+    return new UriBuilder(Uri.parse(text));
+  }
+
+  /**
+   * Convert the builder to a Uri.
+   */
+  public Uri toUri() {
+    return new Uri(this);
+  }
+
+  /**
+   * @return The scheme part of the uri, or null if none was specified.
+   */
+  public String getScheme() {
+    return scheme;
+  }
+
+  public UriBuilder setScheme(String scheme) {
+    this.scheme = scheme;
+    return this;
+  }
+
+  /**
+   * @return The authority part of the uri, or null if none was specified.
+   */
+  public String getAuthority() {
+    return authority;
+  }
+
+  public UriBuilder setAuthority(String authority) {
+    this.authority = authority;
+    return this;
+  }
+
+  /**
+   * @return The path part of the uri, or null if none was specified.
+   */
+  public String getPath() {
+    return path;
+  }
+
+  /**
+   * Sets the path component of the Uri.
+   */
+  public UriBuilder setPath(String path) {
+    this.path = path;
+    return this;
+  }
+
+  /**
+   * @return The query part of the uri, or null if none was specified.
+   */
+  public String getQuery() {
+    if (query == null) {
+      query = joinParameters(queryParameters);
+    }
+    return query;
+  }
+
+  /**
+   * Assigns the specified query string as the query portion of the uri, automatically decoding
+   * parameters to populate the parameter map for calls to getParameter.
+   */
+  public UriBuilder setQuery(String query) {
+    queryParameters.clear();
+    queryParameters.putAll(splitParameters(query));
+    this.query = query;
+    return this;
+  }
+
+  public UriBuilder addQueryParameter(String name, String value) {
+    query = null;
+    List<String> params = queryParameters.get(name);
+    if (params == null) {
+      params = Lists.newArrayList();
+      queryParameters.put(name, params);
+    }
+    params.add(value);
+    return this;
+  }
+
+  public UriBuilder addQueryParameters(Map<String, String> parameters) {
+    query = null;
+    for (Map.Entry<String, String> entry : parameters.entrySet()) {
+      addQueryParameter(entry.getKey(), entry.getValue());
+    }
+    return this;
+  }
+
+  /**
+   * Force overwrites a given query parameter with the given value.
+   */
+  public UriBuilder putQueryParameter(String name, String... values) {
+    query = null;
+    queryParameters.put(name, Lists.newArrayList(values));
+    return this;
+  }
+
+  /**
+   * Force overwrites a given query parameter with the given value.
+   */
+  public UriBuilder putQueryParameter(String name, Iterable<String> values) {
+    query = null;
+    queryParameters.put(name, Lists.newArrayList(values));
+    return this;
+  }
+  
+  /**
+   * Removes a query parameter.
+   */
+  public UriBuilder removeQueryParameter(String name) {
+    query = null;
+    queryParameters.remove(name);
+    return this;
+  }
+  
+  /**
+   * @return The queryParameters part of the uri, separated into component parts.
+   */
+  public Map<String, List<String>> getQueryParameters() {
+    return queryParameters;
+  }
+
+  /**
+   * @return All queryParameters parameters with the given name.
+   */
+  public List<String> getQueryParameters(String name) {
+    return queryParameters.get(name);
+  }
+
+  /**
+   * @return The first queryParameters parameter value with the given name.
+   */
+  public String getQueryParameter(String name) {
+    Collection<String> values = queryParameters.get(name);
+    if (values == null || values.isEmpty()) {
+      return null;
+    }
+    return values.iterator().next();
+  }
+
+  /**
+   * @return The queryParameters fragment.
+   */
+  public String getFragment() {
+    return fragment;
+  }
+
+  public UriBuilder setFragment(String fragment) {
+    this.fragment = fragment;
+    return this;
+  }
+
+  /**
+   * Utility method for joining key / value pair parameters into a url-encoded string.
+   */
+  static String joinParameters(Map<String, List<String>> query) {
+    if (query.isEmpty()) {
+      return null;
+    }
+    StringBuilder buf = new StringBuilder();
+    boolean firstDone = false;
+    for (Map.Entry<String, List<String>> entry : query.entrySet()) {
+      String name = Utf8UrlCoder.encode(entry.getKey());
+      for (String value : entry.getValue()) {
+        if (firstDone) {
+          buf.append('&');
+        }
+        firstDone = true;
+
+        buf.append(name)
+           .append('=')
+           .append(Utf8UrlCoder.encode(value));
+      }
+    }
+    return buf.toString();
+  }
+
+  /**
+   * Utility method for splitting a parameter string into key / value pairs.
+   */
+  public static Map<String, List<String>> splitParameters(String query) {
+    if (query == null) {
+      return Collections.emptyMap();
+    }
+    Map<String, List<String>> params = Maps.newHashMap();
+    Matcher paramMatcher = QUERY_PATTERN.matcher(query);
+    while (paramMatcher.find()) {
+      String name = Utf8UrlCoder.decode(paramMatcher.group(1));
+      String value = Utf8UrlCoder.decode(paramMatcher.group(2));
+      List<String> values = params.get(name);
+      if (values == null) {
+        values = Lists.newArrayList();
+        params.put(name, values);
+      }
+      values.add(value);
+    }
+    return Collections.unmodifiableMap(params);
+  }
+
+  @Override
+  public String toString() {
+    return toUri().toString();
+  }
+
+  @Override
+  public int hashCode() {
+    return toUri().hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {return true;}
+    if (!(obj instanceof UriBuilder)) {return false;}
+
+    return toString().equals(obj.toString());
+  }
+}

Added: incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/UriParser.java
URL: http://svn.apache.org/viewvc/incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/UriParser.java?rev=1087520&view=auto
==============================================================================
--- incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/UriParser.java (added)
+++ incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/UriParser.java Fri Apr  1 00:29:22 2011
@@ -0,0 +1,32 @@
+/*
+ * 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 cgl.shindig.common;
+
+/**
+ * An injectable interface for parsing Uris out of String text.
+ */
+public interface UriParser {
+  /**
+   * Produces a new Uri from a text representation.
+   *
+   * @param text The text uri.
+   * @return A new Uri, parsed into components.
+   */
+  public Uri parse(String text);
+}

Added: incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Utf8UrlCoder.java
URL: http://svn.apache.org/viewvc/incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Utf8UrlCoder.java?rev=1087520&view=auto
==============================================================================
--- incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Utf8UrlCoder.java (added)
+++ incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Utf8UrlCoder.java Fri Apr  1 00:29:22 2011
@@ -0,0 +1,47 @@
+/*
+ * 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 cgl.shindig.common;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+
+/**
+ * Performs url encoding / decoding with forced utf-8. Automatically takes care
+ * of boilerplate exception handling.
+ */
+public class Utf8UrlCoder {
+
+  public static String encode(String input) {
+    try {
+      return URLEncoder.encode(input, "UTF-8");
+    } catch (UnsupportedEncodingException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public static String decode(String input) {
+    try {
+      return URLDecoder.decode(input, "UTF-8");
+    } catch (UnsupportedEncodingException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+}

Added: incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Util.java
URL: http://svn.apache.org/viewvc/incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Util.java?rev=1087520&view=auto
==============================================================================
--- incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Util.java (added)
+++ incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/Util.java Fri Apr  1 00:29:22 2011
@@ -0,0 +1,35 @@
+/**
+ * 
+ */
+package cgl.shindig.common;
+/*
+ *
+ * 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.
+ *
+ */
+
+
+/**
+ * @author gerald
+ *
+ */
+public class Util {
+	public static boolean isEmpty(String str) {
+		return str == null || str.length() == 0;
+	}
+}

Added: incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/X509CertUtil.java
URL: http://svn.apache.org/viewvc/incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/X509CertUtil.java?rev=1087520&view=auto
==============================================================================
--- incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/X509CertUtil.java (added)
+++ incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/common/X509CertUtil.java Fri Apr  1 00:29:22 2011
@@ -0,0 +1,52 @@
+/**
+ * 
+ */
+package cgl.shindig.common;
+/*
+ *
+ * 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.
+ *
+ */
+
+
+import java.security.cert.X509Certificate;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * @author gerald
+ *
+ */
+public class X509CertUtil {
+	public static String extractCN(X509Certificate cert) {
+		X500Principal principal = cert.getSubjectX500Principal();
+        String rfc2253dn = principal.getName(X500Principal.RFC2253);
+        String parts[] = rfc2253dn.split(",");
+        for (int i = 0; i < parts.length; ++i) {
+        	String part = parts[i];
+        	String keyvalue[] = part.split("=");
+        	if (keyvalue == null || keyvalue.length != 2)
+        		continue;
+        	String key = keyvalue[0], value = keyvalue[1];
+        	if (key.equalsIgnoreCase("CN")) {
+        		return value;
+        	}
+        }
+        return null;
+	}
+}

Added: incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/AccessManagerConfig.java
URL: http://svn.apache.org/viewvc/incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/AccessManagerConfig.java?rev=1087520&view=auto
==============================================================================
--- incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/AccessManagerConfig.java (added)
+++ incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/AccessManagerConfig.java Fri Apr  1 00:29:22 2011
@@ -0,0 +1,45 @@
+package cgl.shindig.config;
+/*
+ *
+ * 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.
+ *
+ */
+
+
+/**
+ * Access manager configuration. This bean configuration class
+ * is used to create configured access manager objects.
+ * <p>
+ * This class is currently only used to assign a static type to
+ * more generic bean configuration information.
+ *
+ * @see SecurityConfig#getAccessManagerConfig()
+ */
+public class AccessManagerConfig extends BeanConfig {
+
+    /**
+     * Creates an access manager configuration object from the
+     * given bean configuration.
+     *
+     * @param config bean configuration
+     */
+    public AccessManagerConfig(BeanConfig config) {
+        super(config);
+    }
+
+}

Added: incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/BeanConfig.java
URL: http://svn.apache.org/viewvc/incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/BeanConfig.java?rev=1087520&view=auto
==============================================================================
--- incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/BeanConfig.java (added)
+++ incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/BeanConfig.java Fri Apr  1 00:29:22 2011
@@ -0,0 +1,270 @@
+package cgl.shindig.config;
+/*
+ *
+ * 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.
+ *
+ */
+
+
+import org.apache.commons.collections.BeanMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Properties;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Collections;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * Bean configuration class. BeanConfig instances contain the class name
+ * and property information required to instantiate a class that conforms
+ * with the JavaBean conventions.
+ */
+public class BeanConfig<T> {
+
+    private static Logger log = LoggerFactory.getLogger(BeanConfig.class);
+
+    private static final Map<String, String> DEPRECATIONS;
+
+    static {
+        try {
+            Map<String, String> temp = new HashMap<String, String>();
+            Properties props = new Properties();
+            InputStream in = BeanConfig.class.getResourceAsStream("deprecated-classes.properties");
+            if (in!=null) {
+                try {
+                    props.load(in);
+                } finally {
+                    in.close();
+                }
+            }
+            for (Map.Entry<Object, Object> entry : props.entrySet()) {
+                temp.put(entry.getKey().toString(), entry.getValue().toString());
+            }
+            DEPRECATIONS = Collections.unmodifiableMap(temp);
+        } catch (IOException e) {
+            throw new InternalError("failed to read deprecated classes");
+        }
+    }
+
+    /** The default class loader used by all instances of this class */
+    private static ClassLoader defaultClassLoader =
+        BeanConfig.class.getClassLoader();
+
+    /**
+     * The current class loader used by this instance to create instances of
+     * configured classes.
+     */
+    private ClassLoader classLoader = getDefaultClassLoader();
+
+    /**
+     * The class name of the configured bean.
+     */
+    private final String className;
+
+    /**
+     * The initial properties of the configured bean.
+     */
+    private final Properties properties;
+
+    /**
+     * Flag to validate the configured bean property names against
+     * the configured bean class. By default this is <code>true</code>
+     * to prevent incorrect property names. However, in some cases this
+     * validation should not be performed as client classes may access
+     * the configuration parameters directly through the
+     * {@link #getParameters()} method.
+     *
+     * @see <a href="https://issues.apache.org/jira/browse/JCR-1920">JCR-1920</a>
+     */
+    private boolean validate = true;
+
+    /**
+     * Creates a bean configuration. Note that a copy of the given
+     * bean properties is stored as a part of the created configuration
+     * object. Thus the caller is free to modify the given properties
+     * once the configuration object has been created.
+     *
+     * @param className class name of the bean
+     * @param properties initial properties of the bean
+     */
+    public BeanConfig(String className, Properties properties) {
+        if (DEPRECATIONS.containsKey(className)) {
+            String replacement = DEPRECATIONS.get(className);
+            log.info("{} is deprecated. Please use {} instead", className, replacement);
+            className = replacement;
+        }
+        this.className = className;
+        this.properties = (Properties) properties.clone();
+    }
+
+    /**
+     * Copies a bean configuration.
+     *
+     * @param config the configuration to be copied
+     */
+    public BeanConfig(BeanConfig config) {
+        this(config.getClassName(), config.getParameters());
+    }
+
+    /**
+     * Allows subclasses to control whether the configured bean property
+     * names should be validated.
+     *
+     * @param validate flag to validate the configured property names
+     */
+    protected void setValidate(boolean validate) {
+        this.validate = validate;
+    }
+
+    /**
+     * Returns the class name of the configured bean.
+     *
+     * @return class name of the bean
+     */
+    public String getClassName() {
+        return className;
+    }
+
+    /**
+     * Returns the initial properties of the configured bean.
+     *
+     * @return initial properties of the bean
+     */
+    public Properties getParameters() {
+        return properties;
+    }
+
+    /**
+     * Creates a new instance of the configured bean class.
+     *
+     * @return new bean instance
+     * @throws ConfigurationException on bean configuration errors
+     */
+    public Object newInstance() throws ConfigurationException {
+        try {
+            // Instantiate the object using the default constructor
+            Class<?> objectClass =
+                Class.forName(getClassName(), true, getClassLoader());
+            Object object = objectClass.newInstance();
+
+            // Set all configured bean properties
+            BeanMap map = new BeanMap(object);
+            for (Object key : map.keySet()) {
+                String value = properties.getProperty(key.toString());
+                if (value != null) {
+                    map.put(key, value);
+                }
+            }
+
+            if (validate) {
+                // Check that no invalid property names were configured
+                for (Object key : properties.keySet()) {
+                    if (!map.containsKey(key)
+                            && properties.getProperty(key.toString()) != null) {
+                        String msg =
+                            "Configured class " + object.getClass().getName()
+                            + " does not contain the property " + key
+                            + ". Please fix the repository configuration.";
+                        log.error(msg);
+                        throw new ConfigurationException(msg);
+                    }
+                }
+            }
+
+            return (T) object;
+        } catch (ClassNotFoundException e) {
+            throw new ConfigurationException(
+                    "Configured bean implementation class " + getClassName()
+                    + " was not found.", e);
+        } catch (InstantiationException e) {
+            throw new ConfigurationException(
+                    "Configured bean implementation class " + getClassName()
+                    + " can not be instantiated.", e);
+        } catch (IllegalAccessException e) {
+            throw new ConfigurationException(
+                    "Configured bean implementation class " + getClassName()
+                    + " is protected.", e);
+        }
+    }
+
+    //---------- Configurable class loader support ----------------------------
+
+    /**
+     * Returns the current <code>ClassLoader</code> used to instantiate objects
+     * in the {@link #newInstance()} method.
+     *
+     * @see #newInstance()
+     * @see #setClassLoader(ClassLoader)
+     * @see #getDefaultClassLoader()
+     * @see #setDefaultClassLoader(ClassLoader)
+     */
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+    /**
+     * Sets the <code>ClassLoader</code> used to instantiate objects in the
+     * {@link #newInstance()} method.
+     *
+     * @param classLoader The class loader to set on this instance. If this is
+     *      <code>null</code> the system class loader will be used, which may
+     *      lead to unexpected class loading failures.
+     *
+     * @see #newInstance()
+     * @see #getClassLoader()
+     * @see #getDefaultClassLoader()
+     * @see #setDefaultClassLoader(ClassLoader)
+     */
+    public void setClassLoader(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    /**
+     * Returns the current <code>ClassLoader</code> used for new instances of
+     * this class as the loader used to instantiate objects in the
+     * {@link #newInstance()} method.
+     *
+     * @see #newInstance()
+     * @see #getClassLoader()
+     * @see #setClassLoader(ClassLoader)
+     * @see #setDefaultClassLoader(ClassLoader)
+     */
+    public static ClassLoader getDefaultClassLoader() {
+        return defaultClassLoader;
+    }
+
+    /**
+     * Sets the <code>ClassLoader</code> used for new instances of this class as
+     * the loader to instantiate objects in the {@link #newInstance()} method.
+     *
+     * @param classLoader The class loader to set as the default class loader.
+     *      If this is <code>null</code> the system class loader will be used,
+     *      which may lead to unexpected class loading failures.
+     *
+     * @see #newInstance()
+     * @see #getClassLoader()
+     * @see #setClassLoader(ClassLoader)
+     * @see #getDefaultClassLoader()
+     */
+    public static void setDefaultClassLoader(ClassLoader classLoader) {
+        defaultClassLoader = classLoader;
+    }
+}

Added: incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/ConfigurationException.java
URL: http://svn.apache.org/viewvc/incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/ConfigurationException.java?rev=1087520&view=auto
==============================================================================
--- incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/ConfigurationException.java (added)
+++ incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/ConfigurationException.java Fri Apr  1 00:29:22 2011
@@ -0,0 +1,48 @@
+package cgl.shindig.config;
+/*
+ *
+ * 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.
+ *
+ */
+
+
+/**
+ * Exception class used for configuration errors.
+ */
+public class ConfigurationException extends Exception {
+
+    /**
+     * Creates a configuration exception.
+     *
+     * @param message configuration message
+     */
+    public ConfigurationException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a configuration exception that is caused by another exception.
+     *
+     * @param message configuration error message
+     * @param cause root cause of the configuration error
+     */
+    public ConfigurationException(String message, Exception cause) {
+        super(message, cause);
+    }
+
+}

Added: incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/ConfigurationParser.java
URL: http://svn.apache.org/viewvc/incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/ConfigurationParser.java?rev=1087520&view=auto
==============================================================================
--- incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/ConfigurationParser.java (added)
+++ incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/ConfigurationParser.java Fri Apr  1 00:29:22 2011
@@ -0,0 +1,320 @@
+package cgl.shindig.config;
+/*
+ *
+ * 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.
+ *
+ */
+
+
+// import org.apache.jackrabbit.util.Text;
+
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.SubnodeConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.commons.configuration.tree.ConfigurationNode;
+
+
+public class ConfigurationParser {
+
+    /** Name of the bean parameter configuration element. */
+    public static final String PARAM_ELEMENT = "param";
+
+    /** Name of the bean implementation class configuration attribute. */
+    public static final String CLASS_ATTRIBUTE = "class";
+
+    /** Name of the bean parameter name configuration attribute. */
+    public static final String NAME_ATTRIBUTE = "name";
+
+    /** Name of the bean parameter value configuration attribute. */
+    public static final String VALUE_ATTRIBUTE = "value";
+
+    /**
+     * The configuration parser variables. These name-value pairs
+     * are used to substitute <code>${...}</code> variable references
+     * with context-dependent values in the configuration.
+     *
+     * @see #replaceVariables(String)
+     */
+    private final Properties variables;
+
+    /**
+     * Creates a new configuration parser with the given parser variables.
+     *
+     * @param variables parser variables
+     */
+    public ConfigurationParser(Properties variables) {
+        this.variables = variables;
+    }
+
+    /**
+     * Returns the variables.
+     * @return the variables.
+     */
+    public Properties getVariables() {
+        return variables;
+    }
+
+    protected BeanConfig parseBeanConfig(HierarchicalConfiguration parent, String name)
+            throws ConfigurationException {
+        // Bean configuration element
+        HierarchicalConfiguration config = getSubConfig(parent, name);
+
+        // Bean implementation class
+        String className = getAttribute(config, CLASS_ATTRIBUTE);
+
+        // Bean properties
+        Properties properties = parseParameters(config);
+
+        return new BeanConfig(className, properties);
+    }
+
+    /**
+     * Parses a named bean configuration from the given element.
+     * Bean configuration uses the following format:
+     * <pre>
+     *   &lt;BeanName class="..."&gt;
+     *     &lt;param name="..." value="..."/&gt;
+     *     ...
+     *   &lt;/BeanName&gt;
+     * </pre>
+     * <p>
+     * The returned bean configuration object contains the configured
+     * class name and configuration parameters. Variable replacement
+     * is performed on the parameter values.
+     *
+     * @param element
+     * @return bean configuration,
+     * @throws ConfigurationException if the configuration element does not
+     *                                exist or is broken
+     */
+    protected BeanConfig parseBeanConfig(HierarchicalConfiguration element)
+            throws ConfigurationException {
+        // Bean implementation class
+        String className = getAttribute(element, CLASS_ATTRIBUTE);
+
+        // Bean properties
+        Properties properties = parseParameters(element);
+
+        return new BeanConfig(className, properties);
+    }
+
+    /**
+     * Parses the configuration parameters of the given element.
+     * Parameters are stored as
+     * <code>&lt;param name="..." value="..."/&gt;</code>
+     * child elements. This method parses all param elements,
+     * performs {@link #replaceVariables(String) variable replacement}
+     * on parameter values, and returns the resulting name-value pairs.
+     *
+     * @param element configuration element
+     * @return configuration parameters
+     * @throws ConfigurationException if a <code>param</code> element does
+     *                                not contain the <code>name</code> and
+     *                                <code>value</code> attributes
+     */
+    protected Properties parseParameters(HierarchicalConfiguration config)
+            throws ConfigurationException {
+        Properties parameters = new Properties();
+
+        List params = config.configurationsAt(PARAM_ELEMENT);
+        for(Iterator it = params.iterator(); it.hasNext();) {
+            // HierarchicalConfiguration sub = (HierarchicalConfiguration)it.next();
+            SubnodeConfiguration sub = (SubnodeConfiguration)it.next();
+
+            String name = null;
+            String value = null;
+            // String name = sub.getString("@" + NAME_ATTRIBUTE);
+            // String value = sub.getString("@" + VALUE_ATTRIBUTE);
+            List nameAtts = sub.getRootNode().getAttributes(NAME_ATTRIBUTE);
+            List valueAtts = sub.getRootNode().getAttributes(VALUE_ATTRIBUTE);
+            if (nameAtts != null && nameAtts.size() > 0) {
+                ConfigurationNode nameAttNode = (ConfigurationNode)nameAtts.get(0);
+                name = (String)nameAttNode.getValue();
+            }
+            if (valueAtts != null && valueAtts.size() > 0) {
+                ConfigurationNode valueAttNode = (ConfigurationNode)valueAtts.get(0);
+                value = (String)valueAttNode.getValue();
+            }
+
+            if (name == null && value != null) {
+                throw new ConfigurationException("Parameter name not set");
+            } else if (name != null && value == null) {
+                throw new ConfigurationException("Parameter value not set");
+            } else if (name == null && value == null) {
+                throw new ConfigurationException("Parameter name and value not set");
+            }
+            parameters.put(name.trim(), replaceVariables(value));
+        }
+        return parameters;
+    }
+
+    /**
+     * Performs variable replacement on the given string value.
+     * Each <code>${...}</code> sequence within the given value is replaced
+     * with the value of the named parser variable. The replacement is not
+     * done if the named variable does not exist.
+     *
+     * @param value original value
+     * @return value after variable replacements
+     * @throws ConfigurationException if the replacement of a referenced
+     *                                variable is not found
+     */
+    protected String replaceVariables(String value)
+            throws ConfigurationException {
+        try {
+            // return Text.replaceVariables(variables, value, false);
+            return value;
+        } catch (IllegalArgumentException e) {
+            throw new ConfigurationException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Parses the given XML document and returns the DOM root element.
+     * A custom entity resolver is used to make the included configuration
+     * file DTD available using the specified public identifiers.
+     *
+     * @see ConfigurationEntityResolver
+     * @param xml xml document
+     * @param validate wheter the XML should be validated
+     * @return root element
+     * @throws ConfigurationException if the configuration document could
+     *                                not be read or parsed
+     */
+    protected HierarchicalConfiguration parseXML(InputStream xml, boolean validate)
+            throws ConfigurationException {
+        try {
+            XMLConfiguration config = new XMLConfiguration();
+            config.setValidating(validate);
+            config.load(xml);
+            return config;
+        } catch (Exception e) {
+            throw new ConfigurationException(
+                    "Configuration file could not be read.", e);
+        }
+    }
+
+    /**
+     * Returns the named child of the given parent element.
+     *
+     * @param parent parent element
+     * @param name name of the child element
+     * @return named child element
+     * @throws ConfigurationException
+     * @throws ConfigurationException if the child element is not found
+     */
+    protected HierarchicalConfiguration getSubConfig(HierarchicalConfiguration parent, String name)
+            throws ConfigurationException {
+        return getSubConfig(parent, name, true);
+    }
+
+    /**
+     * Returns the named child of the given parent element.
+     *
+     * @param parent parent element
+     * @param name name of the child element
+     * @param required indicates if the child element is required
+     * @return named child element, or <code>null</code> if not found and
+     *         <code>required</code> is <code>false</code>.
+     * @throws ConfigurationException if the child element is not found and
+     *         <code>required</code> is <code>true</code>;
+     *         or if more than one element with this name exists.
+     */
+    protected HierarchicalConfiguration getSubConfig(
+            HierarchicalConfiguration parent, String name, boolean required)
+            throws ConfigurationException {
+
+        List subs = parent.configurationsAt(name);
+        if (subs.size() > 1) {
+            throw new ConfigurationException(
+                    "Duplicate configuration element " + name + " in "
+                    + parent.toString() + ".");
+        }
+        if (required && subs.size() == 0) {
+            throw new ConfigurationException(
+                    "Configuration element " + name + " not found in "
+                    + parent.toString() + ".");
+        }
+        return (HierarchicalConfiguration)subs.get(0);
+    }
+
+    /**
+     * Returns the value of the named attribute of the given element.
+     *
+     * @param element element
+     * @param name attribute name
+     * @return attribute value
+     * @throws ConfigurationException if the attribute is not found
+     */
+    protected String getAttribute(HierarchicalConfiguration config, String name)
+            throws ConfigurationException {
+        // String attribute = config.getString("@" + name);
+        SubnodeConfiguration xmlconfig = (SubnodeConfiguration)config;
+        ConfigurationNode rootNode = xmlconfig.getRootNode();
+        String rootNodeName = rootNode.getName();
+        List atts = rootNode.getAttributes(name);
+        String attribute = null;
+        // Object obj = config.getProperty(name);
+        if (atts != null && atts.size() > 0) {
+            attribute = (String)((ConfigurationNode)atts.get(0)).getValue();
+        }
+        System.out.println("attribute is:" + attribute);
+        if (attribute != null) {
+            return attribute;
+        } else {
+            throw new ConfigurationException(
+                    "Configuration attribute " + name + " not found in "
+                    + config.toString() + ".\n" + rootNodeName);
+        }
+    }
+
+    /**
+     * Returns the value of the named attribute of the given element.
+     * If the attribute is not found, then the given default value is returned.
+     *
+     * @param element element
+     * @param name attribute name
+     * @param def default value
+     * @return attribute value, or the default value
+     */
+    protected String getAttribute(HierarchicalConfiguration config, String name, String def) {
+        String attribute = null;
+        Object obj = config.getProperty(name);
+        if (obj != null) {
+            if (obj instanceof Collection) {
+                attribute = (String)((Collection)obj).iterator().next();
+            } else {
+                attribute = (String)obj;
+            }
+        }
+
+        if (attribute != null) {
+            return attribute;
+        } else {
+            return def;
+        }
+    }
+}
+

Added: incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/LoginModuleConfig.java
URL: http://svn.apache.org/viewvc/incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/LoginModuleConfig.java?rev=1087520&view=auto
==============================================================================
--- incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/LoginModuleConfig.java (added)
+++ incubator/rave/donations/ogce-gadget-container/ishindig-webapp/src/main/java/cgl/shindig/config/LoginModuleConfig.java Fri Apr  1 00:29:22 2011
@@ -0,0 +1,55 @@
+package cgl.shindig.config;
+/*
+ *
+ * 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.
+ *
+ */
+
+
+import javax.security.auth.spi.LoginModule;
+
+public class LoginModuleConfig extends BeanConfig {
+
+    public static final String PARAM_ANONYMOUS_ID = "anonymousId";
+    
+    public static final String PARAM_ADMIN_ID = "adminId";
+
+    public static final String PARAM_PRINCIPAL_PROVIDER_CLASS = "principalProvider";
+
+    /**
+     * Creates an access manager configuration object from the
+     * given bean configuration.
+     *
+     * @param config bean configuration
+     */
+    public LoginModuleConfig(BeanConfig config) {
+        super(config);
+        setValidate(false); // JCR-1920
+    }
+
+    public LoginModule getLoginModule() throws ConfigurationException {
+        Object result = newInstance();
+        if (result instanceof LoginModule) {
+            return (LoginModule) result;
+        } else {
+            throw new ConfigurationException("Invalid login module implementation class "
+                    + getClassName() + ".");
+        }
+    }
+}
+