org.python.pydev.core.docutils.StringUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.python.pydev.core.docutils.StringUtils.java

Source

/**
 * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the Eclipse Public License (EPL).
 * Please see the license.txt included with this distribution for details.
 * Any modifications to this file must keep this entire header intact.
 */
/*
 * Created on 03/09/2005
 */
package org.python.pydev.core.docutils;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.swing.text.Document;
import javax.swing.text.EditorKit;
import javax.swing.text.html.HTMLEditorKit;

import org.apache.commons.codec.binary.Base64;
import org.eclipse.core.runtime.Assert;
import org.python.pydev.core.ObjectsPool;
import org.python.pydev.core.log.Log;

import com.aptana.shared_core.cache.Cache;
import com.aptana.shared_core.cache.LRUCache;
import com.aptana.shared_core.string.FastStringBuffer;
import com.aptana.shared_core.structure.Tuple;

public final class StringUtils {

    /**
     * @author fabioz
     *
     */
    private static final class IterLines implements Iterator<String> {
        private final String string;
        private final int len;
        private int i;
        private boolean calculatedNext;
        private boolean hasNext;
        private String next;

        private IterLines(String string) {
            this.string = string;
            this.len = string.length();
        }

        public boolean hasNext() {
            if (!calculatedNext) {
                calculatedNext = true;
                hasNext = calculateNext();
            }
            return hasNext;
        }

        private boolean calculateNext() {
            next = null;
            char c;
            int start = i;

            for (; i < len; i++) {
                c = string.charAt(i);

                if (c == '\r') {
                    if (i < len - 1 && string.charAt(i + 1) == '\n') {
                        i++;
                    }
                    i++;
                    next = string.substring(start, i);
                    return true;

                } else if (c == '\n') {
                    i++;
                    next = string.substring(start, i);
                    return true;
                }
            }
            if (start != i) {
                next = string.substring(start, i);
                i++;
                return true;
            }
            return false;
        }

        public String next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            String n = next;
            calculatedNext = false;
            next = null;
            return n;
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    private StringUtils() {

    }

    public static final String EMPTY = "";

    private static final boolean DEBUG = false;

    /**
     * <p>Find the last position of a character which matches a given regex.</p>
     * 
     * <p>This method is similar to {@link java.lang.String#lastIndexOf(String)} 
     * except it allows for comparing characters akin to <i>wildcard</i> searches, i.e.
     * find the position of the last character classified as alphanumeric, without 
     * the need to implement dozens of method variations where each method takes the 
     * same parameters but does a slightly different search.</p>
     * 
     * @param string - the string to search through, e.g. the <i>haystack</i>
     * @param regex -  a string containing a compilable {@link java.util.regex.Pattern}.
     * @return the last position of the character that matches the pattern<br>
     *         or <tt>-1</tt> if no match or some of the parameters are invalid.
     * @note the string is iterated over one char at a time, so the pattern will be
     * compared at most to one character strings. 
     */
    public static int lastIndexOf(final String string, final String regex) {

        int index = -1;

        if (null == string || null == regex || string.length() == 0 || regex.length() == 0) {
            return index;
        }

        Pattern pat;
        try {
            pat = Pattern.compile(regex);
        } catch (PatternSyntaxException pse) {
            return index;
        }

        int len = string.length();
        int i = len - 1;
        char c = '\0';
        Matcher mat = null;

        while (i >= 0) {
            c = string.charAt(i);
            mat = pat.matcher(String.valueOf(c));
            if (mat.matches()) {
                index = i;
                break;
            }
            i--;
        }
        return index;
    }

    /**
     * <p>Find the index of <tt>character</tt> in a <tt>string</tt>.</p>
     * 
     * <p>This method is like {@link java.lang.String#indexOf(int)} 
     * but has the additional ability to ignore occurrences of 
     * <tt>character</tt> in Python string literals (e.g. enclosed
     * by single, double or triple quotes). This is done by employing
     * a very simple statemachine.</p>
     * 
     * @param string - the source string, e.g. the <i>haystack</i>
     * @param character - the character to retrieve the index for
     * @param ignoreInStringLiteral - if <tt>true</tt>, ignore occurrences 
     *        of <tt>character</tt> in Python string literals
     * @return the position of the character in string.<br>
     *         if <tt>string</tt> is <tt>null</tt> or empty, or
     *         if <tt>(int)character < 0</tt>, returns <tt>-1</tt>.
     * @note escaped (i.e. <tt>\"</tt>) characters are ignored and 
     *       control characters, e.g. line delimiters etc., are treated 
     *       normally like every other character.
     */
    public static int indexOf(final String string, final char character, final boolean ignoreInStringLiteral) {

        if (null == string || ((int) character < 0) || string.length() == 0) {
            return -1;
        }

        int index = string.indexOf(character);

        if (-1 == index) {
            return index;
        }

        if (ignoreInStringLiteral) {
            final int len = string.length();
            boolean inString = false;
            char nextc = '\0';
            char c = '\0';

            int i = -1;

            try {
                while (i < len) {
                    i++;
                    c = string.charAt(i);
                    if ((i + 1) < len) {
                        nextc = string.charAt(i + 1);
                    }
                    if ('\\' == c) { // ignore escapes
                        i++;
                        continue;
                    }
                    if (!inString && character == c) {
                        index = i;
                        break;
                    }
                    if ('"' == c || '\'' == c) {
                        if ('"' == nextc || '\'' == nextc) {
                            i++;
                            continue;
                        } else {
                            if (inString) {
                                inString = false;
                            } else {
                                inString = true;
                            }
                        }
                    }
                }
            } catch (StringIndexOutOfBoundsException e) {
                // malformed Python string literals may throw a SIOOBE
                if (DEBUG) {
                    System.err.print(e.getMessage());
                }
                index = -1;
            }
        }
        return index;
    }

    /**
     * <p>Find the substring in <tt>string</tt> that starts from the first 
     * occurrence of <tt>character</tt>.</p>
     * 
     * <p>This method is similar to {@link java.lang.String#substring} 
     * but has the additional ability to ignore occurrences of 
     * <tt>character</tt> in Python string literals (e.g. enclosed
     * by single, double or triple single/double quotes).</p>
     * 
     * @param string - the source string, e.g. the <i>haystack</i>
     * @param character - the character that is the starting boundary of the searched substring
     * @param ignoreInStringLiteral - if <tt>true</tt>, ignore occurrences 
     *        of <tt>character</tt> in Python string literals
     * @return a substring from <tt>string</tt><br>or <tt>null</tt> if 
     *         {@link StringUtils#indexOf} returns <tt>-1</tt>
     * @see {@link StringUtils#indexOf} 
     */
    public static String findSubstring(final String string, final char character,
            final boolean ignoreInStringLiteral) {

        String result = null;
        int index = StringUtils.indexOf(string, character, ignoreInStringLiteral);

        if (index >= 0) {
            result = string.substring(index + 1);
        }
        return result;
    }

    /**
     * <p>Join the elements of an <tt>Iterable</tt> by using <tt>delimiter</tt> 
     * as separator.</p>
     * 
     * @see http://snippets.dzone.com/posts/show/91
     * 
     * @param objs - a collection which implements {@link java.lang.Iterable}
     * @param <T> - type in collection
     * @param delimiter - string used as separator
     * 
     * @throws IllegalArgumentException if <tt>objs</tt> or <tt>delimiter</tt> 
     *         is <tt>null</tt>.
     *         
     * @return joined string
     */
    public static <T> String joinIterable(final String delimiter, final Iterable<T> objs)
            throws IllegalArgumentException {

        if (null == objs) {
            throw new IllegalArgumentException("objs can't be null!");
        }
        if (null == delimiter) {
            throw new IllegalArgumentException("delimiter can't be null");
        }

        Iterator<T> iter = objs.iterator();
        if (!iter.hasNext())
            return "";
        String nxt = String.valueOf(iter.next());
        FastStringBuffer buffer = new FastStringBuffer(String.valueOf(nxt), nxt.length());
        while (iter.hasNext()) {
            buffer.append(delimiter).append(String.valueOf(iter.next()));
        }

        return buffer.toString();
    }

    /**
     * <p>Repeat a substring (a.k.a. <i>substring multiplication</i>).</p>
     * 
     * <p>Invalid Argument Values</p>
     * 
     * <ul>return an empty string if <tt>str</tt> is empty, or if 
     * <tt>times &lt;= 0</tt></ul>
     * <ul>if <tt>str</tt> is <tt>null</tt>, the string <tt>"null"</tt> 
     * will be repeated.</ul>
     * 
     * @param str - the substring to repeat<br>
     * @param times - how many copies
     * @return the repeated string
     */
    public static String repeatString(final String str, int times) {

        String s = String.valueOf(str);
        if (s.length() == 0 || times <= 0) {
            return "";
        }

        FastStringBuffer buffer = new FastStringBuffer();
        for (int i = 0; i < times; i++) {
            buffer.append(s);
        }
        return buffer.toString();
    }

    /**
     * Can be used instead of JDK 1.5's <tt>String.isEmpty()</tt> so that
     * code intentions can be made explicit while keeping support for 1.4 and below.
     * @param str
     * @return true if length of string is 0, otherwise false
     */
    public static Boolean isEmpty(String str) {
        return (0 == str.length());
    }

    /**
     * Counts the number of %s in the string
     * 
     * @param str the string to be analyzed
     * @return the number of %s in the string
     */
    public static int countPercS(final String str) {
        int j = 0;

        final int len = str.length();
        for (int i = 0; i < len; i++) {
            char c = str.charAt(i);
            if (c == '%' && i + 1 < len) {
                char nextC = str.charAt(i + 1);
                if (nextC == 's') {
                    j++;
                    i++;
                }
            }
        }
        return j;
    }

    /**
     * Removes whitespaces and tabs at the end of the string.
     */
    public static String rightTrim(final String input) {
        int len = input.length();
        int st = 0;
        int off = 0;

        while ((st < len) && (input.charAt(off + len - 1) <= ' ')) {
            len--;
        }
        return input.substring(0, len);
    }

    /**
     * Removes whitespaces and tabs at the beginning of the string.
     */
    public static String leftTrim(String input) {
        int len = input.length();
        int off = 0;

        while ((off < len) && (input.charAt(off) <= ' ')) {
            off++;
        }
        return input.substring(off, len);
    }

    /**
     * Given a string remove all from the rightmost '.' onwards.
     * 
     * E.g.: bbb.t would return bbb
     * 
     * If it has no '.', returns the original string unchanged.
     */
    public static String stripExtension(String input) {
        return stripFromRigthCharOnwards(input, '.');
    }

    public static int rFind(String input, char ch) {
        int len = input.length();
        int st = 0;
        int off = 0;

        while ((st < len) && (input.charAt(off + len - 1) != ch)) {
            len--;
        }
        len--;
        return len;
    }

    private static String stripFromRigthCharOnwards(String input, char ch) {
        int len = rFind(input, ch);
        if (len == -1) {
            return input;
        }
        return input.substring(0, len);
    }

    public static String stripFromLastSlash(String input) {
        return stripFromRigthCharOnwards(input, '/');
    }

    /**
     * Removes the occurrences of the passed char in the beggining of the string.
     */
    public static String rightTrim(String input, char charToTrim) {
        int len = input.length();
        int st = 0;
        int off = 0;

        while ((st < len) && (input.charAt(off + len - 1) == charToTrim)) {
            len--;
        }
        return input.substring(0, len);
    }

    /**
     * Removes the occurrences of the passed char in the start and end of the string.
     */
    public static String leftAndRightTrim(String input, char charToTrim) {
        return rightTrim(leftTrim(input, charToTrim), charToTrim);
    }

    /**
     * Removes the occurrences of the passed char in the end of the string.
     */
    public static String leftTrim(String input, char charToTrim) {
        int len = input.length();
        int off = 0;

        while ((off < len) && (input.charAt(off) == charToTrim)) {
            off++;
        }
        return input.substring(off, len);
    }

    /**
     * Changes all backward slashes (\) for forward slashes (/)
     * 
     * @return the replaced string
     */
    public static String replaceAllSlashes(String string) {
        int len = string.length();
        char c = 0;

        for (int i = 0; i < len; i++) {
            c = string.charAt(i);

            if (c == '\\') { // only do some processing if there is a
                             // backward slash
                char[] ds = string.toCharArray();
                ds[i] = '/';
                for (int j = i; j < len; j++) {
                    if (ds[j] == '\\') {
                        ds[j] = '/';
                    }
                }
                return new String(ds);
            }

        }
        return string;
    }

    /**
     * Splits the given string in a list where each element is a line.
     * 
     * @param string string to be split.
     * @return list of strings where each string is a line.
     * 
     * @note the new line characters are also added to the returned string.
     */
    public static List<String> splitInLines(String string) {
        ArrayList<String> ret = new ArrayList<String>();
        int len = string.length();

        char c;
        FastStringBuffer buf = new FastStringBuffer();

        for (int i = 0; i < len; i++) {
            c = string.charAt(i);

            buf.append(c);

            if (c == '\r') {
                if (i < len - 1 && string.charAt(i + 1) == '\n') {
                    i++;
                    buf.append('\n');
                }
                ret.add(buf.toString());
                buf.clear();
            }
            if (c == '\n') {
                ret.add(buf.toString());
                buf.clear();

            }
        }
        if (buf.length() != 0) {
            ret.add(buf.toString());
        }
        return ret;
    }

    /**
     * Splits the given string in a list where each element is a line.
     * 
     * @param string string to be split.
     * @return list of strings where each string is a line.
     * 
     * @note the new line characters are also added to the returned string.
     * 
     * IMPORTANT: The line returned will be a substring of the initial line, so, it's recommended that a copy
     * is created if it should be kept in memory (otherwise the full initial string will also be kept in memory). 
     */
    public static Iterable<String> iterLines(final String string) {
        return new Iterable<String>() {

            public Iterator<String> iterator() {
                return new IterLines(string);
            }
        };

    }

    public static String replaceAll(String string, String replace, String with) {
        FastStringBuffer ret = new FastStringBuffer(string, 16);
        return ret.replaceAll(replace, with).toString();
    }

    /**
     * Formats a docstring to be shown and adds the indentation passed to all the docstring lines but the 1st one.
     */
    public static String fixWhitespaceColumnsToLeftFromDocstring(String docString, String indentationToAdd) {
        FastStringBuffer buf = new FastStringBuffer();
        List<String> splitted = splitInLines(docString);
        for (int i = 0; i < splitted.size(); i++) {
            String initialString = splitted.get(i);
            if (i == 0) {
                buf.append(initialString);//first is unchanged
            } else {
                String string = StringUtils.leftTrim(initialString);
                buf.append(indentationToAdd);

                if (string.length() > 0) {
                    buf.append(string);
                } else {
                    int length = initialString.length();
                    if (length > 0) {
                        char c;
                        if (length > 1) {
                            //check 2 chars
                            c = initialString.charAt(length - 2);
                            if (c == '\n' || c == '\r') {
                                buf.append(c);
                            }
                        }
                        c = initialString.charAt(length - 1);
                        if (c == '\n' || c == '\r') {
                            buf.append(c);
                        }
                    }
                }
            }
        }

        //last line
        if (buf.length() > 0) {
            char c = buf.lastChar();
            if (c == '\r' || c == '\n') {
                buf.append(indentationToAdd);
            }
        }

        return buf.toString();
    }

    public static String removeWhitespaceColumnsToLeft(String hoverInfo) {
        FastStringBuffer buf = new FastStringBuffer();
        int firstCharPosition = Integer.MAX_VALUE;

        List<String> splitted = splitInLines(hoverInfo);
        for (String line : splitted) {
            if (line.trim().length() > 0) {
                int found = PySelection.getFirstCharPosition(line);
                firstCharPosition = Math.min(found, firstCharPosition);
            }
        }

        if (firstCharPosition != Integer.MAX_VALUE) {
            for (String line : splitted) {
                if (line.length() > firstCharPosition) {
                    buf.append(line.substring(firstCharPosition));
                }
            }
            return buf.toString();
        } else {
            return hoverInfo;//return initial
        }
    }

    public static String removeWhitespaceColumnsToLeftAndApplyIndent(String code, String indent,
            boolean indentCommentLinesAt0Pos) {
        FastStringBuffer buf = new FastStringBuffer();
        int firstCharPosition = Integer.MAX_VALUE;

        List<String> splitted = splitInLines(code);
        for (String line : splitted) {
            if (indentCommentLinesAt0Pos || !line.startsWith("#")) {
                if (line.trim().length() > 0) {
                    int found = PySelection.getFirstCharPosition(line);
                    firstCharPosition = Math.min(found, firstCharPosition);
                }
            }
        }

        if (firstCharPosition != Integer.MAX_VALUE) {
            for (String line : splitted) {
                if (indentCommentLinesAt0Pos || !line.startsWith("#")) {
                    buf.append(indent);
                    if (line.length() > firstCharPosition) {
                        buf.append(line.substring(firstCharPosition));
                    } else {
                        buf.append(line);
                    }
                } else {
                    buf.append(line);
                }
            }
            return buf.toString();
        } else {
            return code;//return initial
        }
    }

    /**
     * Given some html, extracts its text.
     */
    public static String extractTextFromHTML(String html) {
        try {
            EditorKit kit = new HTMLEditorKit();
            Document doc = kit.createDefaultDocument();

            // The Document class does not yet handle charset's properly.
            doc.putProperty("IgnoreCharsetDirective", Boolean.TRUE);

            // Create a reader on the HTML content.
            Reader rd = new StringReader(html);

            // Parse the HTML.
            kit.read(rd, doc, 0);

            //  The HTML text is now stored in the document
            return doc.getText(0, doc.getLength());
        } catch (Exception e) {
        }
        return "";
    }

    /**
     * Splits the passed string based on the toSplit string.
     */
    public static List<String> split(final String string, final char toSplit, int maxPartsToSplit) {
        Assert.isTrue(maxPartsToSplit > 0);
        ArrayList<String> ret = new ArrayList<String>();
        int len = string.length();

        int last = 0;

        char c = 0;

        for (int i = 0; i < len; i++) {
            c = string.charAt(i);
            if (c == toSplit) {
                if (last != i) {
                    if (ret.size() == maxPartsToSplit - 1) {
                        ret.add(string.substring(last, len));
                        return ret;
                    } else {
                        ret.add(string.substring(last, i));
                    }
                }
                while (c == toSplit && i < len - 1) {
                    i++;
                    c = string.charAt(i);
                }
                last = i;
            }
        }
        if (c != toSplit) {
            if (last == 0 && len > 0) {
                ret.add(string); //it is equal to the original (no char to split)

            } else if (last < len) {
                ret.add(string.substring(last, len));

            }
        }
        return ret;
    }

    /**
     * Splits the passed string based on the toSplit string.
     */
    public static List<String> split(final String string, final String toSplit) {
        if (toSplit.length() == 1) {
            return split(string, toSplit.charAt(0));
        }
        ArrayList<String> ret = new ArrayList<String>();
        if (toSplit.length() == 0) {
            ret.add(string);
            return ret;
        }

        int len = string.length();

        int last = 0;

        char c = 0;

        for (int i = 0; i < len; i++) {
            c = string.charAt(i);
            if (c == toSplit.charAt(0) && matches(string, toSplit, i)) {
                if (last != i) {
                    ret.add(string.substring(last, i));
                }
                last = i + toSplit.length();
                i += toSplit.length() - 1;
            }
        }

        if (last < len) {
            ret.add(string.substring(last, len));
        }

        return ret;
    }

    private static boolean matches(final String string, final String toSplit, int i) {
        int length = string.length();
        int toSplitLen = toSplit.length();
        if (length - i >= toSplitLen) {
            for (int j = 0; j < toSplitLen; j++) {
                if (string.charAt(i + j) != toSplit.charAt(j)) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    /**
     * Splits some string given some char (that char will not appear in the returned strings)
     * Empty strings are also never added.
     */
    public static void splitWithIntern(String string, char toSplit, Collection<String> addTo) {
        synchronized (ObjectsPool.lock) {
            int len = string.length();

            int last = 0;

            char c = 0;

            for (int i = 0; i < len; i++) {
                c = string.charAt(i);
                if (c == toSplit) {
                    if (last != i) {
                        addTo.add(ObjectsPool.internUnsynched(string.substring(last, i)));
                    }
                    while (c == toSplit && i < len - 1) {
                        i++;
                        c = string.charAt(i);
                    }
                    last = i;
                }
            }
            if (c != toSplit) {
                if (last == 0 && len > 0) {
                    addTo.add(ObjectsPool.internUnsynched(string)); //it is equal to the original (no char to split)

                } else if (last < len) {
                    addTo.add(ObjectsPool.internUnsynched(string.substring(last, len)));
                }
            }
        }
    }

    /**
     * Splits some string given some char (that char will not appear in the returned strings)
     * Empty strings are also never added.
     */
    public static List<String> split(String string, char toSplit) {
        ArrayList<String> ret = new ArrayList<String>();
        int len = string.length();

        int last = 0;

        char c = 0;

        for (int i = 0; i < len; i++) {
            c = string.charAt(i);
            if (c == toSplit) {
                if (last != i) {
                    ret.add(string.substring(last, i));
                }
                while (c == toSplit && i < len - 1) {
                    i++;
                    c = string.charAt(i);
                }
                last = i;
            }
        }
        if (c != toSplit) {
            if (last == 0 && len > 0) {
                ret.add(string); //it is equal to the original (no char to split)

            } else if (last < len) {
                ret.add(string.substring(last, len));

            }
        }
        return ret;
    }

    /**
     * Helper to process parts of a string.
     */
    public static interface ICallbackOnSplit {

        /**
         * @param substring the part found
         * @return false to stop processing the string (and true to check the next part).
         */
        boolean call(String substring);

    }

    /**
     * Splits some string given some char (that char will not appear in the returned strings)
     * Empty strings are also never added.
     * 
     * @return true if the onSplit callback only returned true (and false if it stopped before).
     * @note: empty strings may be yielded.
     */
    public static boolean split(String string, char toSplit, ICallbackOnSplit onSplit) {
        int len = string.length();
        int last = 0;
        char c = 0;

        for (int i = 0; i < len; i++) {
            c = string.charAt(i);
            if (c == toSplit) {
                if (last != i) {
                    if (!onSplit.call(string.substring(last, i))) {
                        return false;
                    }
                }
                while (c == toSplit && i < len - 1) {
                    i++;
                    c = string.charAt(i);
                }
                last = i;
            }
        }
        if (c != toSplit) {
            if (last == 0 && len > 0) {
                if (!onSplit.call(string)) { //it is equal to the original (no char to split)
                    return false;
                }

            } else if (last < len) {
                if (!onSplit.call(string.substring(last, len))) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Splits some string given many chars
     */
    public static List<String> split(String string, char... toSplit) {
        ArrayList<String> ret = new ArrayList<String>();
        int len = string.length();

        int last = 0;

        char c = 0;

        for (int i = 0; i < len; i++) {
            c = string.charAt(i);

            if (contains(c, toSplit)) {
                if (last != i) {
                    ret.add(string.substring(last, i));
                }
                while (contains(c, toSplit) && i < len - 1) {
                    i++;
                    c = string.charAt(i);
                }
                last = i;
            }
        }
        if (!contains(c, toSplit)) {
            if (last == 0 && len > 0) {
                ret.add(string); //it is equal to the original (no dots)

            } else if (last < len) {
                ret.add(string.substring(last, len));

            }
        }
        return ret;
    }

    public static List<String> splitAndRemoveEmptyNotTrimmed(String string, char c) {
        List<String> split = split(string, c);
        for (int i = split.size() - 1; i >= 0; i--) {
            if (split.get(i).length() == 0) {
                split.remove(i);
            }
        }
        return split;
    }

    public static List<String> splitAndRemoveEmptyTrimmed(String string, char c) {
        List<String> split = split(string, c);
        for (int i = split.size() - 1; i >= 0; i--) {
            if (split.get(i).trim().length() == 0) {
                split.remove(i);
            }
        }
        return split;
    }

    private static boolean contains(char c, char[] toSplit) {
        for (char ch : toSplit) {
            if (c == ch) {
                return true;
            }
        }
        return false;
    }

    /**
     * Splits some string given some char in 2 parts. If the separator is not found, 
     * everything is put in the 1st part.
     */
    public static Tuple<String, String> splitOnFirst(String fullRep, char toSplit) {
        int i = fullRep.indexOf(toSplit);
        if (i != -1) {
            return new Tuple<String, String>(fullRep.substring(0, i), fullRep.substring(i + 1));
        } else {
            return new Tuple<String, String>(fullRep, "");
        }
    }

    /**
     * Splits some string given some char in 2 parts. If the separator is not found, 
     * everything is put in the 1st part.
     */
    public static Tuple<String, String> splitOnFirst(String fullRep, String toSplit) {
        int i = fullRep.indexOf(toSplit);
        if (i != -1) {
            return new Tuple<String, String>(fullRep.substring(0, i), fullRep.substring(i + toSplit.length()));
        } else {
            return new Tuple<String, String>(fullRep, "");
        }
    }

    /**
     * Splits the string as would string.split("\\."), but without yielding empty strings
     */
    public static List<String> dotSplit(String string) {
        return splitAndRemoveEmptyTrimmed(string, '.');
    }

    /**
     * Adds a char to an array of chars and returns the new array. 
     * 
     * @param c The chars to where the new char should be appended
     * @param toAdd the char to be added
     * @return a new array with the passed char appended.
     */
    public static char[] addChar(char[] c, char toAdd) {
        char[] c1 = new char[c.length + 1];

        System.arraycopy(c, 0, c1, 0, c.length);
        c1[c.length] = toAdd;
        return c1;

    }

    public static String[] addString(String[] c, String toAdd) {
        String[] c1 = new String[c.length + 1];

        System.arraycopy(c, 0, c1, 0, c.length);
        c1[c.length] = toAdd;
        return c1;
    }

    public static String replaceNewLines(String message, String string) {
        message = message.replaceAll("\r\n", string);
        message = message.replaceAll("\r", string);
        message = message.replaceAll("\n", string);

        return message;
    }

    public static String removeNewLineChars(String message) {
        return message.replaceAll("\r", "").replaceAll("\n", "");
    }

    private static final int STATE_LOWER = 0;
    private static final int STATE_UPPER = 1;
    private static final int STATE_NUMBER = 2;

    public static String asStyleLowercaseUnderscores(String string) {
        int len = string.length();
        FastStringBuffer buf = new FastStringBuffer(len * 2);

        int lastState = 0;
        for (int i = 0; i < len; i++) {
            char c = string.charAt(i);
            if (Character.isUpperCase(c)) {
                if (lastState != STATE_UPPER) {
                    if (buf.length() > 0 && buf.lastChar() != '_') {
                        buf.append('_');
                    }
                }
                buf.append(Character.toLowerCase(c));
                lastState = STATE_UPPER;

            } else if (Character.isDigit(c)) {
                if (lastState != STATE_NUMBER) {
                    if (buf.length() > 0 && buf.lastChar() != '_') {
                        buf.append('_');
                    }
                }

                buf.append(c);
                lastState = STATE_NUMBER;
            } else {
                buf.append(c);
                lastState = STATE_LOWER;
            }
        }
        return buf.toString();
    }

    public static boolean isAllUpper(String string) {
        int len = string.length();
        for (int i = 0; i < len; i++) {
            char c = string.charAt(i);
            if (Character.isLetter(c) && !Character.isUpperCase(c)) {
                return false;
            }
        }
        return true;
    }

    public static String asStyleCamelCaseFirstLower(String string) {
        if (isAllUpper(string)) {
            string = string.toLowerCase();
        }

        int len = string.length();
        FastStringBuffer buf = new FastStringBuffer(len);
        boolean first = true;
        int nextUpper = 0;

        for (int i = 0; i < len; i++) {
            char c = string.charAt(i);
            if (first) {
                if (c == '_') {
                    //underscores at the start
                    buf.append(c);
                    continue;
                }
                buf.append(Character.toLowerCase(c));
                first = false;
            } else {

                if (c == '_') {
                    nextUpper += 1;
                    continue;
                }
                if (nextUpper > 0) {
                    c = Character.toUpperCase(c);
                    nextUpper = 0;
                }

                buf.append(c);
            }
        }

        if (nextUpper > 0) {
            //underscores at the end
            buf.appendN('_', nextUpper);
        }
        return buf.toString();
    }

    public static String asStyleCamelCaseFirstUpper(String string) {
        string = asStyleCamelCaseFirstLower(string);
        if (string.length() > 0) {
            return Character.toUpperCase(string.charAt(0)) + string.substring(1);
        }
        return string;
    }

    public static boolean endsWith(FastStringBuffer str, char c) {
        if (str.length() == 0) {
            return false;
        }
        if (str.charAt(str.length() - 1) == c) {
            return true;
        }
        return false;
    }

    public static boolean endsWith(final String str, char c) {
        int len = str.length();
        if (len == 0) {
            return false;
        }
        if (str.charAt(len - 1) == c) {
            return true;
        }
        return false;
    }

    public static boolean endsWith(final StringBuffer str, char c) {
        int len = str.length();
        if (len == 0) {
            return false;
        }
        if (str.charAt(len - 1) == c) {
            return true;
        }
        return false;
    }

    /**
     * Tests whether each character in the given
     * string is a letter.
     *
     * @param str
     * @return <code>true</code> if the given string is a word
     */
    public static boolean isWord(final String str) {
        int len = str.length();
        if (str == null || len == 0)
            return false;

        for (int i = 0; i < len; i++) {
            if (!Character.isJavaIdentifierPart(str.charAt(i)))
                return false;
        }
        return true;
    }

    /**
     * An array of Python pairs of characters that you will find in any Python code.
     * 
     * Currently, the set contains:
     * <ul>
     * <ol>left and right brackets: [, ]</ol>
     * <ol>right and right parentheses: (, )
     * </ul>
     */
    public static final char[] BRACKETS = { '{', '}', '(', ')', '[', ']' };
    public static final char[] CLOSING_BRACKETS = { '}', ')', ']' };

    public static char getPeer(char c) {
        switch (c) {
        case '{':
            return '}';
        case '}':
            return '{';
        case '(':
            return ')';
        case ')':
            return '(';
        case '[':
            return ']';
        case ']':
            return '[';
        }

        throw new NoPeerAvailableException("Unable to find peer for :" + c);
    }

    public static String getWithClosedPeer(char c) {
        switch (c) {
        case '{':
            return "{}";
        case '(':
            return "()";
        case '[':
            return "[]";
        case '\'':
            return "''";
        case '"':
            return "\"\"";
        }

        throw new NoPeerAvailableException("Unable to find peer for :" + c);
    }

    public static boolean isOpeningPeer(char lastChar) {
        return lastChar == '(' || lastChar == '[' || lastChar == '{';
    }

    public static boolean isClosingPeer(char lastChar) {
        return lastChar == ')' || lastChar == ']' || lastChar == '}';
    }

    public static boolean hasOpeningBracket(String trimmedLine) {
        return trimmedLine.indexOf('{') != -1 || trimmedLine.indexOf('(') != -1 || trimmedLine.indexOf('[') != -1;
    }

    public static boolean hasClosingBracket(String trimmedLine) {
        return trimmedLine.indexOf('}') != -1 || trimmedLine.indexOf(')') != -1 || trimmedLine.indexOf(']') != -1;
    }

    public static boolean hasUnbalancedClosingPeers(final String line) {
        Map<Character, Integer> stack = new HashMap<Character, Integer>();
        final int len = line.length();
        for (int i = 0; i < len; i++) {
            char c = line.charAt(i);
            switch (c) {
            case '(':
            case '{':
            case '[':
                Integer iStack = stack.get(c);
                if (iStack == null) {
                    iStack = 0;
                }
                iStack++;
                stack.put(c, iStack);
                break;

            case ')':
            case '}':
            case ']':
                char peer = StringUtils.getPeer((char) c);
                iStack = stack.get(peer);
                if (iStack == null) {
                    iStack = 0;
                }
                iStack--;
                stack.put(peer, iStack);
                break;
            }
        }
        for (int i : stack.values()) {
            if (i < 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * Small cache to hold strings only with spaces (so that each width has a created string).
     */
    private static Cache<Integer, String> widthToSpaceString = new LRUCache<Integer, String>(8);

    /**
     * Creates a string of spaces of the designated length.
     * @param width number of spaces you want to create a string of
     * @return the created string
     */
    public static String createSpaceString(int width) {
        String existing = StringUtils.widthToSpaceString.getObj(width);
        if (existing != null) {
            return existing;
        }
        FastStringBuffer buf = new FastStringBuffer(width);
        buf.appendN(' ', width);
        String newStr = buf.toString();
        StringUtils.widthToSpaceString.add(width, newStr);
        return newStr;
    }

    public static int count(String name, char c) {
        int count = 0;
        final int len = name.length();
        for (int i = 0; i < len; i++) {
            if (name.charAt(i) == c) {
                count++;
            }
        }
        return count;
    }

    public static String urlEncodeKeyValuePair(String key, String value) {
        String result = null;

        try {
            result = URLEncoder.encode(key, "UTF-8") + "=" + URLEncoder.encode(value, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            Log.log(e);
        }

        return result;
    }

    public static boolean containsWhitespace(final String name) {
        final int len = name.length();
        for (int i = 0; i < len; i++) {
            if (Character.isWhitespace(name.charAt(i))) {
                return true;
            }
        }
        return false;
    }

    /**
     * //Python 3.0 can use unicode identifiers. So, the letter construct deals with that...
     *  TOKEN : * Python identifiers *
     *  {
     *      < NAME: <LETTER> ( <LETTER> | <DIGIT>)* >
     *  |
     *      < #LETTER: 
     *      [
     *         "a"-"z",
     *         "A"-"Z",
     *         "_",
     *         "\u0080"-"\uffff" //Anything more than 128 is considered valid (unicode range)
     *      
     *      ] 
     *  >
     *  }
     * @param param
     * @return
     */
    public static boolean isPythonIdentifier(final String param) {
        final int len = param.length();
        if (len == 0) {
            return false;
        }
        char c = param.charAt(0);
        if (!Character.isLetter(c) && c != '_' && c <= 128) {
            return false;
        }
        for (int i = 1; i < len; i++) {
            c = param.charAt(i);
            if ((!Character.isLetter(c) && !Character.isDigit(c) && c != '_') && (c <= 128)) {
                return false;
            }
        }
        return true;
    }

    public static String getWithFirstUpper(final String creationStr) {
        final int len = creationStr.length();
        if (len == 0) {
            return creationStr;
        }
        char upperCase = Character.toUpperCase(creationStr.charAt(0));
        return upperCase + creationStr.substring(1);

    }

    public static String indentTo(String source, String indent) {
        return indentTo(source, indent, true);
    }

    public static String indentTo(final String source, final String indent, boolean indentFirstLine) {
        final int indentLen = indent.length();
        if (indent == null || indentLen == 0) {
            return source;
        }
        List<String> splitInLines = splitInLines(source);
        final int sourceLen = source.length();
        FastStringBuffer buf = new FastStringBuffer(sourceLen + (splitInLines.size() * indentLen) + 2);

        for (int i = 0; i < splitInLines.size(); i++) {
            String line = splitInLines.get(i);
            if (indentFirstLine || i > 0) {
                buf.append(indent);
            }
            buf.append(line);
        }
        return buf.toString();
    }

    public static String getExeAsFileSystemValidPath(String executableOrJar) {
        return "v1_" + com.aptana.shared_core.string.StringUtils.md5(executableOrJar);
    }

    /**
     * Decodes some string that was encoded as base64
     */
    public static byte[] decodeBase64(String persisted) {
        return Base64.decodeBase64(persisted.getBytes());
    }

    /**
     * @param o the object we want as a string
     * @return the string representing the object as base64
     */
    public static String getObjAsStr(Object o) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            ObjectOutputStream stream = new ObjectOutputStream(out);
            stream.writeObject(o);
            stream.close();
        } catch (Exception e) {
            Log.log(e);
            throw new RuntimeException(e);
        }

        return new String(encodeBase64(out));
    }

    /**
     * @return the contents of the passed ByteArrayOutputStream as a byte[] encoded with base64.
     */
    public static byte[] encodeBase64(ByteArrayOutputStream out) {
        byte[] byteArray = out.toByteArray();
        return encodeBase64(byteArray);
    }

    /**
     * @return the contents of the passed byteArray[] as a byte[] encoded with base64.
     */
    public static byte[] encodeBase64(byte[] byteArray) {
        return Base64.encodeBase64(byteArray);
    }
}