replaced.append(sql.substring(index, (last_token ? token_matcher.start() : sql.length())));
    StatementParser(final String sql)    {        // attempt to short circuit        if (sql.indexOf(":") == -1)        {            // no named params, short circuit            this.tokens = EMPTY;            this.replaced = sql;            return;        }        final Matcher token_matcher = TOKEN_PATTERN.matcher(sql);        final Matcher quote_matcher = QUOTE_PATTERN.matcher(sql);        boolean last_quote;        boolean last_token;        if (!(last_quote = quote_matcher.find()))        {            // we have no quotes, just replace tokens if there are any and exit            this.replaced = token_matcher.replaceAll("?");            final List tokens = new ArrayList();            while (token_matcher.find())            {                tokens.add(token_matcher.group().substring(token_matcher.group().indexOf(":") + 1));            }            this.tokens = (String[]) tokens.toArray(new String[tokens.size()]);        }        else if (last_token = token_matcher.find())        {            // we have quotes and tokens, juggling time            final List tokens = new ArrayList();            final StringBuffer replaced = new StringBuffer();            // copy everything up to beginning of first match into buffer            final int end = quote_matcher.start() > token_matcher.start()                            ? token_matcher.start()                            : quote_matcher.start();            replaced.append(sql.substring(0, end));            do            {                if (last_token && last_quote)                {                    // token and quote, crappo                    if (token_matcher.end() < quote_matcher.start())                    {                        // token is before the quote                        // append ? and stuff to start of quote                        replaced.append("?");                        replaced.append(sql.substring(token_matcher.end(), quote_matcher.start()));                        last_token = token_matcher.find();                        continue;                    }                    else if (token_matcher.start() > quote_matcher.end())                    {                        // token is after quote                        replaced.append(sql.substring(quote_matcher.start(), token_matcher.start()));                        last_quote = quote_matcher.find();                        continue;                    }                    else                    {                        // token is inside quote                        replaced.append(sql.substring(quote_matcher.start(), quote_matcher.end()));                        // iterate through tokens until we escape the quote                        while (last_token = token_matcher.find())                        {                            if (token_matcher.start() > quote_matcher.end())                            {                                // found a token after the quote                                break;                            }                        } // or iterated through string and no more tokens                        last_quote = quote_matcher.find();                        continue;                    }                }                else if (last_token)                {                    // found a token, but no more quotes                    replaced.append("?");                    last_token = token_matcher.find();                }                else if (last_quote)                {                    // quote, but no more tokens                    replaced.append(sql.substring(quote_matcher.start(), sql.length()));                    last_quote = false;                }            }            while (last_token || last_quote);            this.replaced = replaced.toString();            this.tokens = (String[]) tokens.toArray(new String[tokens.size()]);        }        else        {            // no quotes, no tokens, piece of cake            tokens = EMPTY;            replaced = sql;        }    }