001/*
002 * Copyright (C) 2009-2011 Mathias Doenitz
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.parboiled.common;
018
019import org.parboiled.support.Characters;
020import org.parboiled.support.Chars;
021import org.parboiled.support.Chars;
022
023import java.util.Arrays;
024import java.util.Iterator;
025
026/**
027 * General utility methods for string manipulation.
028 */
029public final class StringUtils {
030
031    private StringUtils() {}
032
033    /**
034     * Replaces carriage returns, newlines, tabs, formfeeds and the special chars defined in {@link Characters}
035     * with their respective escape sequences.
036     *
037     * @param string the string
038     * @return the escaped string
039     */
040    public static String escape(String string) {
041        if (isEmpty(string)) return "";
042        StringBuilder sb = new StringBuilder();
043        char[] chars = string.toCharArray();
044        for (int i = 0; i < chars.length; i++) {
045            if (i == chars.length - 1 || chars[i] != '\r' || chars[i + 1] != '\n') {
046                sb.append(escape(chars[i]));
047            }
048        }
049        return sb.toString();
050    }
051
052    /**
053     * Replaces carriage returns, newlines, tabs, formfeeds and the special chars defined in {@link Characters}
054     * with their respective escape sequences.
055     *
056     * @param c the character to escape
057     * @return the escaped string
058     */
059    public static String escape(char c) {
060        switch (c) {
061            case '\r':
062                return "\\r";
063            case '\n':
064                return "\\n";
065            case '\t':
066                return "\\t";
067            case '\f':
068                return "\\f";
069            case Chars.DEL_ERROR:
070                return "DEL_ERROR";
071            case Chars.INS_ERROR:
072                return "INS_ERROR";
073            case Chars.RESYNC:
074                return "RESYNC";
075            case Chars.RESYNC_START:
076                return "RESYNC_START";
077            case Chars.RESYNC_END:
078                return "RESYNC_END";
079            case Chars.RESYNC_EOI:
080                return "RESYNC_EOI";
081            case Chars.INDENT:
082                return "INDENT";
083            case Chars.DEDENT:
084                return "DEDENT";
085            case Chars.EOI:
086                return "EOI";
087            default:
088                return String.valueOf(c);
089        }
090    }
091
092    /**
093     * Creates a string consisting of n times the given character.
094     *
095     * @param c the char
096     * @param n the number of times to repeat
097     * @return the string
098     */
099    public static String repeat(char c, int n) {
100        char[] array = new char[n];
101        Arrays.fill(array, c);
102        return String.valueOf(array);
103    }
104
105    //***********************************************************************************************
106    //**                 THE FOLLOWING CODE IS A PARTIAL, VERBATIM COPY OF                         **
107    //**                       org.apache.commons.lang.StringUtils                                 **
108    //**                         which is licensed under ASF 2.0                                   **
109    //***********************************************************************************************
110
111    /**
112     * <p>Joins the elements of the provided <code>Iterable</code> into
113     * a single String containing the provided elements.</p>
114     * <p>No delimiter is added before or after the list.
115     * A <code>null</code> separator is the same as an empty String ("").</p>
116     *
117     * @param iterable the <code>Iterable</code> of values to join together, may be null
118     * @param separator  the separator character to use, null treated as ""
119     * @return the joined String, <code>null</code> if null iterator input
120     */
121    public static String join(Iterable iterable, String separator) {
122        return iterable == null ? null : join(iterable.iterator(), separator);
123    }
124    
125    /**
126     * <p>Joins the elements of the provided <code>Iterator</code> into
127     * a single String containing the provided elements.</p>
128     * <p>No delimiter is added before or after the list.
129     * A <code>null</code> separator is the same as an empty String ("").</p>
130     *
131     * @param iterator  the <code>Iterator</code> of values to join together, may be null
132     * @param separator the separator character to use, null treated as ""
133     * @return the joined String, <code>null</code> if null iterator input
134     */
135    public static String join(Iterator iterator, String separator) {
136        // handle null, zero and one elements before building a buffer
137        if (iterator == null) return null;
138        if (!iterator.hasNext()) return "";
139        Object first = iterator.next();
140        if (!iterator.hasNext()) return Utils.toString(first);
141
142        // two or more elements
143        StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small
144        if (first != null) buf.append(first);
145
146        while (iterator.hasNext()) {
147            if (separator != null) buf.append(separator);
148            Object obj = iterator.next();
149            if (obj != null) buf.append(obj);
150        }
151        return buf.toString();
152    }
153
154    /**
155     * <p>Joins the elements of the provided array into a single String
156     * containing the provided list of elements.</p>
157     * <p>No delimiter is added before or after the list.
158     * A <code>null</code> separator is the same as an empty String ("").
159     * Null objects or empty strings within the array are represented by
160     * empty strings.</p>
161     *
162     * <pre>
163     * StringUtils.join(null, *)                = null
164     * StringUtils.join([], *)                  = ""
165     * StringUtils.join([null], *)              = ""
166     * StringUtils.join(["a", "b", "c"], "--")  = "a--b--c"
167     * StringUtils.join(["a", "b", "c"], null)  = "abc"
168     * StringUtils.join(["a", "b", "c"], "")    = "abc"
169     * StringUtils.join([null, "", "a"], ',')   = ",,a"
170     * </pre>
171     *
172     * @param array     the array of values to join together, may be null
173     * @param separator the separator character to use, null treated as ""
174     * @return the joined String, <code>null</code> if null array input
175     */
176    public static String join(Object[] array, String separator) {
177        return array == null ? null : join(array, separator, 0, array.length);
178    }
179
180    /**
181     * <p>Joins the elements of the provided array into a single String
182     * containing the provided list of elements.</p>
183     * <p>No delimiter is added before or after the list.
184     * A <code>null</code> separator is the same as an empty String ("").
185     * Null objects or empty strings within the array are represented by
186     * empty strings.</p>
187     * <pre>
188     * StringUtils.join(null, *)                = null
189     * StringUtils.join([], *)                  = ""
190     * StringUtils.join([null], *)              = ""
191     * StringUtils.join(["a", "b", "c"], "--")  = "a--b--c"
192     * StringUtils.join(["a", "b", "c"], null)  = "abc"
193     * StringUtils.join(["a", "b", "c"], "")    = "abc"
194     * StringUtils.join([null, "", "a"], ',')   = ",,a"
195     * </pre>
196     *
197     * @param array      the array of values to join together, may be null
198     * @param separator  the separator character to use, null treated as ""
199     * @param startIndex the first index to start joining from.  It is
200     *                   an error to pass in an end index past the end of the array
201     * @param endIndex   the index to stop joining from (exclusive). It is
202     *                   an error to pass in an end index past the end of the array
203     * @return the joined String, <code>null</code> if null array input
204     */
205    public static String join(Object[] array, String separator, int startIndex, int endIndex) {
206        if (array == null) return null;
207        if (separator == null) separator = "";
208
209        // lastIndex - firstIndex > 0:   Len = NofStrings *(len(firstString) + len(separator))
210        //           (Assuming that all Strings are roughly equally long)
211        int bufSize = (endIndex - startIndex);
212        if (bufSize <= 0) return "";
213        bufSize *= ((array[startIndex] == null ? 16 : array[startIndex].toString().length()) + separator.length());
214        StringBuilder buf = new StringBuilder(bufSize);
215
216        for (int i = startIndex; i < endIndex; i++) {
217            if (i > startIndex) buf.append(separator);
218            if (array[i] != null) buf.append(array[i]);
219        }
220        return buf.toString();
221    }
222
223    // Empty checks
224    //-----------------------------------------------------------------------
225
226    /**
227     * <p>Checks if a String is empty ("") or null.</p>
228     * <pre>
229     * StringUtils.isEmpty(null)      = true
230     * StringUtils.isEmpty("")        = true
231     * StringUtils.isEmpty(" ")       = false
232     * StringUtils.isEmpty("bob")     = false
233     * StringUtils.isEmpty("  bob  ") = false
234     * </pre>
235     *
236     * @param str the String to check, may be null
237     * @return <code>true</code> if the String is empty or null
238     */
239    public static boolean isEmpty(String str) {
240        return str == null || str.length() == 0;
241    }
242
243    /**
244     * <p>Checks if a String is not empty ("") and not null.</p>
245     * <pre>
246     * StringUtils.isNotEmpty(null)      = false
247     * StringUtils.isNotEmpty("")        = false
248     * StringUtils.isNotEmpty(" ")       = true
249     * StringUtils.isNotEmpty("bob")     = true
250     * StringUtils.isNotEmpty("  bob  ") = true
251     * </pre>
252     *
253     * @param str the String to check, may be null
254     * @return <code>true</code> if the String is not empty and not null
255     */
256    public static boolean isNotEmpty(String str) {
257        return !isEmpty(str);
258    }
259
260    /**
261     * Gets a String's length or <code>0</code> if the String is <code>null</code>.
262     *
263     * @param str a String or <code>null</code>
264     * @return String length or <code>0</code> if the String is <code>null</code>.
265     */
266    public static int length(String str) {
267        return str == null ? 0 : str.length();
268    }
269
270    /**
271     * <p>Compares two Strings, returning <code>true</code> if they are equal ignoring
272     * the case.</p>
273     * <p><code>null</code>s are handled without exceptions. Two <code>null</code>
274     * references are considered equal. Comparison is case insensitive.</p>
275     *
276     * <pre>
277     * StringUtils.equalsIgnoreCase(null, null)   = true
278     * StringUtils.equalsIgnoreCase(null, "abc")  = false
279     * StringUtils.equalsIgnoreCase("abc", null)  = false
280     * StringUtils.equalsIgnoreCase("abc", "abc") = true
281     * StringUtils.equalsIgnoreCase("abc", "ABC") = true
282     * </pre>
283     *
284     * @param str1 the first String, may be null
285     * @param str2 the second String, may be null
286     * @return <code>true</code> if the Strings are equal, case insensitive, or
287     *         both <code>null</code>
288     */
289    public static boolean equalsIgnoreCase(String str1, String str2) {
290        return str1 == null ? str2 == null : str1.equalsIgnoreCase(str2);
291    }
292
293    /**
294     * Test whether a string starts with a given prefix, handling null values without exceptions.
295     *
296     * StringUtils.startsWith(null, null)   = false
297     * StringUtils.startsWith(null, "abc")  = false
298     * StringUtils.startsWith("abc", null)  = true
299     * StringUtils.startsWith("abc", "ab")  = true
300     * StringUtils.startsWith("abc", "abc") = true
301     *
302     * @param string the string
303     * @param prefix the prefix
304     * @return true if string starts with prefix
305     */
306    public static boolean startsWith(String string, String prefix) {
307        return string != null && (prefix == null || string.startsWith(prefix));
308    }
309
310    /**
311     * <p>Gets a substring from the specified String avoiding exceptions.</p>
312     * <p>A negative start position can be used to start <code>n</code>
313     * characters from the end of the String.</p>
314     * <p>A <code>null</code> String will return <code>null</code>.
315     * An empty ("") String will return "".</p>
316     * <pre>
317     * StringUtils.substring(null, *)   = null
318     * StringUtils.substring("", *)     = ""
319     * StringUtils.substring("abc", 0)  = "abc"
320     * StringUtils.substring("abc", 2)  = "c"
321     * StringUtils.substring("abc", 4)  = ""
322     * StringUtils.substring("abc", -2) = "bc"
323     * StringUtils.substring("abc", -4) = "abc"
324     * </pre>
325     *
326     * @param str   the String to get the substring from, may be null
327     * @param start the position to start from, negative means
328     *              count back from the end of the String by this many characters
329     * @return substring from start position, <code>null</code> if null String input
330     */
331    public static String substring(String str, int start) {
332        if (str == null) {
333            return null;
334        }
335
336        // handle negatives, which means last n characters
337        if (start < 0) {
338            start = str.length() + start; // remember start is negative
339        }
340
341        if (start < 0) {
342            start = 0;
343        }
344        if (start > str.length()) {
345            return "";
346        }
347
348        return str.substring(start);
349    }
350
351    /**
352     * <p>Gets a substring from the specified String avoiding exceptions.</p>
353     * <p>A negative start position can be used to start/end <code>n</code>
354     * characters from the end of the String.</p>
355     * <p>The returned substring starts with the character in the <code>start</code>
356     * position and ends before the <code>end</code> position. All position counting is
357     * zero-based -- i.e., to start at the beginning of the string use
358     * <code>start = 0</code>. Negative start and end positions can be used to
359     * specify offsets relative to the end of the String.</p>
360     * <p>If <code>start</code> is not strictly to the left of <code>end</code>, ""
361     * is returned.</p>
362     * <pre>
363     * StringUtils.substring(null, *, *)    = null
364     * StringUtils.substring("", * ,  *)    = "";
365     * StringUtils.substring("abc", 0, 2)   = "ab"
366     * StringUtils.substring("abc", 2, 0)   = ""
367     * StringUtils.substring("abc", 2, 4)   = "c"
368     * StringUtils.substring("abc", 4, 6)   = ""
369     * StringUtils.substring("abc", 2, 2)   = ""
370     * StringUtils.substring("abc", -2, -1) = "b"
371     * StringUtils.substring("abc", -4, 2)  = "ab"
372     * </pre>
373     *
374     * @param str   the String to get the substring from, may be null
375     * @param start the position to start from, negative means
376     *              count back from the end of the String by this many characters
377     * @param end   the position to end at (exclusive), negative means
378     *              count back from the end of the String by this many characters
379     * @return substring from start position to end positon,
380     *         <code>null</code> if null String input
381     */
382    public static String substring(String str, int start, int end) {
383        if (str == null) {
384            return null;
385        }
386
387        // handle negatives
388        if (end < 0) {
389            end = str.length() + end; // remember end is negative
390        }
391        if (start < 0) {
392            start = str.length() + start; // remember start is negative
393        }
394
395        // check length next
396        if (end > str.length()) {
397            end = str.length();
398        }
399
400        // if start is greater than end, return ""
401        if (start > end) {
402            return "";
403        }
404
405        if (start < 0) {
406            start = 0;
407        }
408        if (end < 0) {
409            end = 0;
410        }
411
412        return str.substring(start, end);
413    }
414
415    // Left/Right/Mid
416    //-----------------------------------------------------------------------
417
418    /**
419     * <p>Gets the leftmost <code>len</code> characters of a String.</p>
420     * <p>If <code>len</code> characters are not available, or the
421     * String is <code>null</code>, the String will be returned without
422     * an exception. An exception is thrown if len is negative.</p>
423     * <pre>
424     * StringUtils.left(null, *)    = null
425     * StringUtils.left(*, -ve)     = ""
426     * StringUtils.left("", *)      = ""
427     * StringUtils.left("abc", 0)   = ""
428     * StringUtils.left("abc", 2)   = "ab"
429     * StringUtils.left("abc", 4)   = "abc"
430     * </pre>
431     *
432     * @param str the String to get the leftmost characters from, may be null
433     * @param len the length of the required String, must be zero or positive
434     * @return the leftmost characters, <code>null</code> if null String input
435     */
436    public static String left(String str, int len) {
437        if (str == null) {
438            return null;
439        }
440        if (len < 0) {
441            return "";
442        }
443        if (str.length() <= len) {
444            return str;
445        }
446        return str.substring(0, len);
447    }
448
449    /**
450     * <p>Gets the rightmost <code>len</code> characters of a String.</p>
451     * <p>If <code>len</code> characters are not available, or the String
452     * is <code>null</code>, the String will be returned without an
453     * an exception. An exception is thrown if len is negative.</p>
454     * <pre>
455     * StringUtils.right(null, *)    = null
456     * StringUtils.right(*, -ve)     = ""
457     * StringUtils.right("", *)      = ""
458     * StringUtils.right("abc", 0)   = ""
459     * StringUtils.right("abc", 2)   = "bc"
460     * StringUtils.right("abc", 4)   = "abc"
461     * </pre>
462     *
463     * @param str the String to get the rightmost characters from, may be null
464     * @param len the length of the required String, must be zero or positive
465     * @return the rightmost characters, <code>null</code> if null String input
466     */
467    public static String right(String str, int len) {
468        if (str == null) {
469            return null;
470        }
471        if (len < 0) {
472            return "";
473        }
474        if (str.length() <= len) {
475            return str;
476        }
477        return str.substring(str.length() - len);
478    }
479
480    /**
481     * <p>Gets <code>len</code> characters from the middle of a String.</p>
482     * <p>If <code>len</code> characters are not available, the remainder
483     * of the String will be returned without an exception. If the
484     * String is <code>null</code>, <code>null</code> will be returned.
485     * An exception is thrown if len is negative.</p>
486     * <pre>
487     * StringUtils.mid(null, *, *)    = null
488     * StringUtils.mid(*, *, -ve)     = ""
489     * StringUtils.mid("", 0, *)      = ""
490     * StringUtils.mid("abc", 0, 2)   = "ab"
491     * StringUtils.mid("abc", 0, 4)   = "abc"
492     * StringUtils.mid("abc", 2, 4)   = "c"
493     * StringUtils.mid("abc", 4, 2)   = ""
494     * StringUtils.mid("abc", -2, 2)  = "ab"
495     * </pre>
496     *
497     * @param str the String to get the characters from, may be null
498     * @param pos the position to start from, negative treated as zero
499     * @param len the length of the required String, must be zero or positive
500     * @return the middle characters, <code>null</code> if null String input
501     */
502    public static String mid(String str, int pos, int len) {
503        if (str == null) {
504            return null;
505        }
506        if (len < 0 || pos > str.length()) {
507            return "";
508        }
509        if (pos < 0) {
510            pos = 0;
511        }
512        if (str.length() <= (pos + len)) {
513            return str.substring(pos);
514        }
515        return str.substring(pos, pos + len);
516    }
517
518}