/*
        * Transforms obj["foo"] into obj.foo whenever possible, saving 3 bytes.
        */

        private static void OptimizeObjectMemberAccess(IList tokens)
        {
            string tv;
            int i, length;
            JavaScriptToken token;


            for (i = 0, length = tokens.Count; i < length; i++)
            {
                if (((JavaScriptToken) tokens[i]).TokenType == Token.LB &&
                    i > 0 && i < length - 2 &&
                    ((JavaScriptToken) tokens[i - 1]).TokenType == Token.NAME &&
                    ((JavaScriptToken) tokens[i + 1]).TokenType == Token.STRING &&
                    ((JavaScriptToken) tokens[i + 2]).TokenType == Token.RB)
                {
                    token = (JavaScriptToken) tokens[i + 1];
                    tv = token.Value;
                    tv = tv.Substring(1, tv.Length - 2);
                    if (IsValidIdentifier(tv))
                    {
                        tokens[i] = new JavaScriptToken(Token.DOT, ".");
                        tokens[i + 1] = new JavaScriptToken(Token.NAME, tv);
                        tokens.RemoveAt(i + 2);
                        i = i + 2;
                        length = length - 1;
                    }
                }
            }
        }
        /*
         * Transforms 'foo': ... into foo: ... whenever possible, saving 2 bytes.
        */

        private static void OptimizeObjLitMemberDecl(IList tokens)
        {
            string tv;
            int i, length;
            JavaScriptToken token;


            for (i = 0, length = tokens.Count; i < length; i++)
            {
                if (((JavaScriptToken) tokens[i]).TokenType == Token.OBJECTLIT &&
                    i > 0 && ((JavaScriptToken) tokens[i - 1]).TokenType == Token.STRING)
                {
                    token = (JavaScriptToken) tokens[i - 1];
                    tv = token.Value;
                    tv = tv.Substring(1, tv.Length - 2);
                    if (IsValidIdentifier(tv))
                    {
                        tokens[i - 1] = new JavaScriptToken(Token.NAME, tv);
                    }
                }
            }
        }
        private static void ProcessStringLiterals(IList tokens, bool merge)
        {
            string tv;
            int i, length = tokens.Count;
            JavaScriptToken token, prevToken, nextToken;

            if (merge)
            {
                // Concatenate string literals that are being appended wherever
                // it is safe to do so. Note that we take care of the case:
                //     "a" + "b".toUpperCase()

                for (i = 0; i < length; i++)
                {
                    token = (JavaScriptToken) tokens[i];
                    switch (token.TokenType)
                    {
                        case Token.ADD:
                            if (i > 0 && i < length)
                            {
                                prevToken = (JavaScriptToken) tokens[i - 1];
                                nextToken = (JavaScriptToken) tokens[i + 1];
                                if (prevToken.TokenType == Token.STRING && nextToken.TokenType == Token.STRING &&
                                    (i == length - 1 || ((JavaScriptToken) tokens[i + 2]).TokenType != Token.DOT))
                                {
                                    tokens[i - 1] = new JavaScriptToken(Token.STRING,
                                                                        prevToken.Value + nextToken.Value);
                                    tokens.RemoveAt(i + 1);
                                    tokens.RemoveAt(i);
                                    i = i - 1;
                                    length = length - 2;
                                    break;
                                }
                            }
                            break;
                    }
                }
            }

            // Second pass...
            for (i = 0; i < length; i++)
            {
                token = (JavaScriptToken) tokens[i];
                if (token.TokenType == Token.STRING)
                {
                    tv = token.Value;

                    // Finally, add the quoting characters and escape the string. We use
                    // the quoting character that minimizes the amount of escaping to save
                    // a few additional bytes.

                    int singleQuoteCount = CountChar(tv, '\'');
                    int doubleQuoteCount = CountChar(tv, '"');
                    char quotechar = doubleQuoteCount <= singleQuoteCount ? '"' : '\'';

                    tv = quotechar + EscapeString(tv, quotechar) + quotechar;

                    // String concatenation transforms the old script scheme:
                    //     '<scr'+'ipt ...><'+'/script>'
                    // into the following:
                    //     '<script ...></script>'
                    // which breaks if this code is embedded inside an HTML document.
                    // Since this is not the right way to do this, let's fix the code by
                    // transforming all "</script" into "<\/script"

                    if (tv.IndexOf("</script", StringComparison.OrdinalIgnoreCase) >= 0)
                    {
                        tv = tv.Replace("<\\/script", "<\\\\/script");
                    }

                    tokens[i] = new JavaScriptToken(Token.STRING, tv);
                }
            }
        }