/* ************************************************************************
#
# DivConq
#
# http://divconq.com/
#
# Copyright:
# Copyright 2014 eTimeline, LLC. All rights reserved.
#
# License:
# See the license.txt file in the project's top-level directory for details.
#
# Authors:
# * Andy White
#
************************************************************************ */
package divconq.util;
import java.util.Iterator;
import java.util.regex.Pattern;
import divconq.lang.Memory;
import divconq.lang.op.OperationContext;
import divconq.struct.builder.JsonMemoryBuilder;
public class StringUtil {
private static final int PAD_LIMIT = 8192;
private static final Pattern ASCII_PATTERN = Pattern.compile("[^\\p{ASCII}]+");
/**
* check that characters are compatible with IntegerStruct value range
*
* @param value characters to scan
* @return true if is an Integer
*/
// as in - integer (long) that is database (mumps) compatible
public static boolean isDataInteger(CharSequence value) {
if ((value == null) || (value.length() > 18))
return false;
for (int i = 0; i < value.length(); i++) {
char ch = value.charAt(i);
if ((i==0) && ((ch != '-') && !Character.isDigit(ch)))
return false;
if (!Character.isDigit(ch))
return false;
}
return true;
}
/**
* Attempts to parse a string into a long, using a default value if parse fails.
* (note that API wise, DivConq considers Integer to mean 64 bit)
*
* @param value string to parse
* @param defvalue value to use if parse fails
* @return parsed number or default
*/
public static long parseInt(String value, long defvalue) {
try {
return Long.parseLong(value);
}
catch (Exception x) {
}
return defvalue;
}
/**
* Attempts to parse a string into a Long
*
* @param value string to parse
* @return parsed number or null
*/
public static Long parseInt(String value) {
if (StringUtil.isEmpty(value))
return null;
try {
return Long.parseLong(value);
}
catch (Exception x) {
}
return null;
}
public static Long parseLeadingInt(String value) {
if (StringUtil.isEmpty(value))
return null;
int finalpos = 0;
for (int i = 0; i < value.length(); i++) {
char ch = value.charAt(i);
if ((i==0) && ((ch != '-') && !Character.isDigit(ch)))
return null;
if (!Character.isDigit(ch))
break;
finalpos = i;
}
try {
return Long.parseLong(value.substring(0, finalpos + 1));
}
catch (Exception x) {
}
return null;
}
/**
* check to see if characters contains anything but whitespace
*
* @param str string to check
* @return true if contains non-whitespace
*/
public static boolean isNotEmpty(CharSequence str) {
return !StringUtil.isEmpty(str);
}
/**
* check to see if characters contains only whitespace
*
* @param str string to check
* @return true if contains only whitespace
*/
public static boolean isEmpty(CharSequence str) {
if (str == null)
return true;
int strLen = str.length();
for (int i = 0; i < strLen; i++)
if (!Character.isWhitespace(str.charAt(i)))
return false;
return true;
}
public static String toEmpty(String css) {
if (css == null)
return "";
return css;
}
/*
* scan through characters and remove excess whitespace - per XML
* whitespace rules.
*/
public static String stripWhitespacePerXml(CharSequence str) {
if (StringUtil.isEmpty(str))
return null;
int sz = str.length();
char[] chs = new char[sz];
int count = 0;
boolean needSpace = false;
boolean fndAnyNonSpace = false;
for (int i = 0; i < sz; i++) {
if (!Character.isWhitespace(str.charAt(i))) {
if (needSpace)
chs[count++] = ' ';
chs[count++] = str.charAt(i);
fndAnyNonSpace = true;
needSpace = false;
}
else
needSpace = fndAnyNonSpace;
}
return new String(chs, 0, count);
}
/*
* remove leading whitespace
* whitespace rules.
*/
public static String stripLeadingWhitespace(String str) {
if (StringUtil.isEmpty(str))
return str;
int strLen = str.length();
int start = 0;
while (start != strLen && Character.isWhitespace(str.charAt(start)))
start++;
return str.substring(start);
}
public static String stripTrailingWhitespace(String str) {
if (StringUtil.isEmpty(str))
return str;
int end = str.length();
while (end != 0 && Character.isWhitespace(str.charAt(end - 1)))
end--;
return str.substring(0, end);
}
public static String stripWhitespace(String str) {
if (StringUtil.isEmpty(str))
return str;
int strLen = str.length();
int start = 0;
while (start != strLen && Character.isWhitespace(str.charAt(start)))
start++;
int end = strLen;
while (end != 0 && Character.isWhitespace(str.charAt(end - 1)))
end--;
return str.substring(start, end);
}
// Conversion
//-----------------------------------------------------------------------
// Padding
//-----------------------------------------------------------------------
/**
* <p>Repeat a String {@code repeat} times to form a
* new String.</p>
*
* <pre>
* StringUtils.repeat(null, 2) = null
* StringUtils.repeat("", 0) = ""
* StringUtils.repeat("", 2) = ""
* StringUtils.repeat("a", 3) = "aaa"
* StringUtils.repeat("ab", 2) = "abab"
* StringUtils.repeat("a", -2) = ""
* </pre>
*
* @param str the String to repeat, may be null
* @param repeat number of times to repeat str, negative treated as zero
* @return a new String consisting of the original String repeated,
* {@code null} if null String input
*/
public static String repeat(final String str, final int repeat) {
// Performance tuned for 2.0 (JDK1.4)
if (str == null) {
return null;
}
if (repeat <= 0) {
return " ";
}
final int inputLength = str.length();
if (repeat == 1 || inputLength == 0) {
return str;
}
if (inputLength == 1 && repeat <= PAD_LIMIT) {
return repeat(str.charAt(0), repeat);
}
final int outputLength = inputLength * repeat;
switch (inputLength) {
case 1 :
return repeat(str.charAt(0), repeat);
case 2 :
final char ch0 = str.charAt(0);
final char ch1 = str.charAt(1);
final char[] output2 = new char[outputLength];
for (int i = repeat * 2 - 2; i >= 0; i--, i--) {
output2[i] = ch0;
output2[i + 1] = ch1;
}
return new String(output2);
default :
final StringBuilder buf = new StringBuilder(outputLength);
for (int i = 0; i < repeat; i++) {
buf.append(str);
}
return buf.toString();
}
}
/**
* <p>Repeat a String {@code repeat} times to form a
* new String, with a String separator injected each time. </p>
*
* <pre>
* StringUtils.repeat(null, null, 2) = null
* StringUtils.repeat(null, "x", 2) = null
* StringUtils.repeat("", null, 0) = ""
* StringUtils.repeat("", "", 2) = ""
* StringUtils.repeat("", "x", 3) = "xxx"
* StringUtils.repeat("?", ", ", 3) = "?, ?, ?"
* </pre>
*
* @param str the String to repeat, may be null
* @param separator the String to inject, may be null
* @param repeat number of times to repeat str, negative treated as zero
* @return a new String consisting of the original String repeated,
* {@code null} if null String input
* @since 2.5
*/
public static String repeat(final String str, final String separator, final int repeat) {
if(str == null || separator == null) {
return repeat(str, repeat);
}
// given that repeat(String, int) is quite optimized, better to rely on it than try and splice this into it
final String result = repeat(str + separator, repeat);
return removeEnd(result, separator);
}
/**
* <p>Removes a substring only if it is at the end of a source string,
* otherwise returns the source string.</p>
*
* <p>A {@code null} source string will return {@code null}.
* An empty ("") source string will return the empty string.
* A {@code null} search string will return the source string.</p>
*
* <pre>
* StringUtils.removeEnd(null, *) = null
* StringUtils.removeEnd("", *) = ""
* StringUtils.removeEnd(*, null) = *
* StringUtils.removeEnd("www.domain.com", ".com.") = "www.domain.com"
* StringUtils.removeEnd("www.domain.com", ".com") = "www.domain"
* StringUtils.removeEnd("www.domain.com", "domain") = "www.domain.com"
* StringUtils.removeEnd("abc", "") = "abc"
* </pre>
*
* @param str the source String to search, may be null
* @param remove the String to search for and remove, may be null
* @return the substring with the string removed if found,
* {@code null} if null String input
* @since 2.1
*/
public static String removeEnd(final String str, final String remove) {
if (isEmpty(str) || isEmpty(remove)) {
return str;
}
if (str.endsWith(remove)) {
return str.substring(0, str.length() - remove.length());
}
return str;
}
/**
* <p>Returns padding using the specified delimiter repeated
* to a given length.</p>
*
* <pre>
* StringUtils.repeat('e', 0) = ""
* StringUtils.repeat('e', 3) = "eee"
* StringUtils.repeat('e', -2) = ""
* </pre>
*
* <p>Note: this method doesn't not support padding with
* <a href="http://www.unicode.org/glossary/#supplementary_character">Unicode Supplementary Characters</a>
* as they require a pair of {@code char}s to be represented.
* If you are needing to support full I18N of your applications
* consider using {@link #repeat(String, int)} instead.
* </p>
*
* @param ch character to repeat
* @param repeat number of times to repeat char, negative treated as zero
* @return String with repeated character
* @see #repeat(String, int)
*/
public static String repeat(final char ch, final int repeat) {
final char[] buf = new char[repeat];
for (int i = repeat - 1; i >= 0; i--) {
buf[i] = ch;
}
return new String(buf);
}
/**
* <p>Right pad a String with spaces (' ').</p>
*
* <p>The String is padded to the size of {@code size}.</p>
*
* <pre>
* StringUtils.rightPad(null, *) = null
* StringUtils.rightPad("", 3) = " "
* StringUtils.rightPad("bat", 3) = "bat"
* StringUtils.rightPad("bat", 5) = "bat "
* StringUtils.rightPad("bat", 1) = "bat"
* StringUtils.rightPad("bat", -1) = "bat"
* </pre>
*
* @param str the String to pad out, may be null
* @param size the size to pad to
* @return right padded String or original String if no padding is necessary,
* {@code null} if null String input
*/
public static String rightPad(final String str, final int size) {
return rightPad(str, size, ' ');
}
/**
* <p>Right pad a String with a specified character.</p>
*
* <p>The String is padded to the size of {@code size}.</p>
*
* <pre>
* StringUtils.rightPad(null, *, *) = null
* StringUtils.rightPad("", 3, 'z') = "zzz"
* StringUtils.rightPad("bat", 3, 'z') = "bat"
* StringUtils.rightPad("bat", 5, 'z') = "batzz"
* StringUtils.rightPad("bat", 1, 'z') = "bat"
* StringUtils.rightPad("bat", -1, 'z') = "bat"
* </pre>
*
* @param str the String to pad out, may be null
* @param size the size to pad to
* @param padChar the character to pad with
* @return right padded String or original String if no padding is necessary,
* {@code null} if null String input
* @since 2.0
*/
public static String rightPad(final String str, final int size, final char padChar) {
if (str == null) {
return null;
}
final int pads = size - str.length();
if (pads <= 0) {
return str; // returns original String when possible
}
if (pads > PAD_LIMIT) {
return rightPad(str, size, String.valueOf(padChar));
}
return str.concat(repeat(padChar, pads));
}
/**
* <p>Right pad a String with a specified String.</p>
*
* <p>The String is padded to the size of {@code size}.</p>
*
* <pre>
* StringUtils.rightPad(null, *, *) = null
* StringUtils.rightPad("", 3, "z") = "zzz"
* StringUtils.rightPad("bat", 3, "yz") = "bat"
* StringUtils.rightPad("bat", 5, "yz") = "batyz"
* StringUtils.rightPad("bat", 8, "yz") = "batyzyzy"
* StringUtils.rightPad("bat", 1, "yz") = "bat"
* StringUtils.rightPad("bat", -1, "yz") = "bat"
* StringUtils.rightPad("bat", 5, null) = "bat "
* StringUtils.rightPad("bat", 5, "") = "bat "
* </pre>
*
* @param str the String to pad out, may be null
* @param size the size to pad to
* @param padStr the String to pad with, null or empty treated as single space
* @return right padded String or original String if no padding is necessary,
* {@code null} if null String input
*/
public static String rightPad(final String str, final int size, String padStr) {
if (str == null) {
return null;
}
if (isEmpty(padStr)) {
padStr = " ";
}
final int padLen = padStr.length();
final int strLen = str.length();
final int pads = size - strLen;
if (pads <= 0) {
return str; // returns original String when possible
}
if (padLen == 1 && pads <= PAD_LIMIT) {
return rightPad(str, size, padStr.charAt(0));
}
if (pads == padLen) {
return str.concat(padStr);
} else if (pads < padLen) {
return str.concat(padStr.substring(0, pads));
} else {
final char[] padding = new char[pads];
final char[] padChars = padStr.toCharArray();
for (int i = 0; i < pads; i++) {
padding[i] = padChars[i % padLen];
}
return str.concat(new String(padding));
}
}
/**
* <p>Left pad a String with spaces (' ').</p>
*
* <p>The String is padded to the size of {@code size}.</p>
*
* <pre>
* StringUtils.leftPad(null, *) = null
* StringUtils.leftPad("", 3) = " "
* StringUtils.leftPad("bat", 3) = "bat"
* StringUtils.leftPad("bat", 5) = " bat"
* StringUtils.leftPad("bat", 1) = "bat"
* StringUtils.leftPad("bat", -1) = "bat"
* </pre>
*
* @param str the String to pad out, may be null
* @param size the size to pad to
* @return left padded String or original String if no padding is necessary,
* {@code null} if null String input
*/
public static String leftPad(final String str, final int size) {
return leftPad(str, size, ' ');
}
/**
* <p>Left pad a String with a specified character.</p>
*
* <p>Pad to a size of {@code size}.</p>
*
* <pre>
* StringUtils.leftPad(null, *, *) = null
* StringUtils.leftPad("", 3, 'z') = "zzz"
* StringUtils.leftPad("bat", 3, 'z') = "bat"
* StringUtils.leftPad("bat", 5, 'z') = "zzbat"
* StringUtils.leftPad("bat", 1, 'z') = "bat"
* StringUtils.leftPad("bat", -1, 'z') = "bat"
* </pre>
*
* @param str the String to pad out, may be null
* @param size the size to pad to
* @param padChar the character to pad with
* @return left padded String or original String if no padding is necessary,
* {@code null} if null String input
* @since 2.0
*/
public static String leftPad(final String str, final int size, final char padChar) {
if (str == null) {
return null;
}
final int pads = size - str.length();
if (pads <= 0) {
return str; // returns original String when possible
}
if (pads > PAD_LIMIT) {
return leftPad(str, size, String.valueOf(padChar));
}
return repeat(padChar, pads).concat(str);
}
/**
* <p>Left pad a String with a specified String.</p>
*
* <p>Pad to a size of {@code size}.</p>
*
* <pre>
* StringUtils.leftPad(null, *, *) = null
* StringUtils.leftPad("", 3, "z") = "zzz"
* StringUtils.leftPad("bat", 3, "yz") = "bat"
* StringUtils.leftPad("bat", 5, "yz") = "yzbat"
* StringUtils.leftPad("bat", 8, "yz") = "yzyzybat"
* StringUtils.leftPad("bat", 1, "yz") = "bat"
* StringUtils.leftPad("bat", -1, "yz") = "bat"
* StringUtils.leftPad("bat", 5, null) = " bat"
* StringUtils.leftPad("bat", 5, "") = " bat"
* </pre>
*
* @param str the String to pad out, may be null
* @param size the size to pad to
* @param padStr the String to pad with, null or empty treated as single space
* @return left padded String or original String if no padding is necessary,
* {@code null} if null String input
*/
public static String leftPad(final String str, final int size, String padStr) {
if (str == null) {
return null;
}
if (isEmpty(padStr)) {
padStr = " ";
}
final int padLen = padStr.length();
final int strLen = str.length();
final int pads = size - strLen;
if (pads <= 0) {
return str; // returns original String when possible
}
if (padLen == 1 && pads <= PAD_LIMIT) {
return leftPad(str, size, padStr.charAt(0));
}
if (pads == padLen) {
return padStr.concat(str);
} else if (pads < padLen) {
return padStr.substring(0, pads).concat(str);
} else {
final char[] padding = new char[pads];
final char[] padChars = padStr.toCharArray();
for (int i = 0; i < pads; i++) {
padding[i] = padChars[i % padLen];
}
return new String(padding).concat(str);
}
}
/**
* Gets a CharSequence length or {@code 0} if the CharSequence is
* {@code null}.
*
* @param cs
* a CharSequence or {@code null}
* @return CharSequence length or {@code 0} if the CharSequence is
* {@code null}.
* @since 2.4
* @since 3.0 Changed signature from length(String) to length(CharSequence)
*/
public static int length(final CharSequence cs) {
return cs == null ? 0 : cs.length();
}
// Centering
//-----------------------------------------------------------------------
/**
* <p>Centers a String in a larger String of size {@code size}
* using the space character (' ').</p>
*
* <p>If the size is less than the String length, the String is returned.
* A {@code null} String returns {@code null}.
* A negative size is treated as zero.</p>
*
* <p>Equivalent to {@code center(str, size, " ")}.</p>
*
* <pre>
* StringUtils.center(null, *) = null
* StringUtils.center("", 4) = " "
* StringUtils.center("ab", -1) = "ab"
* StringUtils.center("ab", 4) = " ab "
* StringUtils.center("abcd", 2) = "abcd"
* StringUtils.center("a", 4) = " a "
* </pre>
*
* @param str the String to center, may be null
* @param size the int size of new String, negative treated as zero
* @return centered String, {@code null} if null String input
*/
public static String center(final String str, final int size) {
return center(str, size, ' ');
}
/**
* <p>Centers a String in a larger String of size {@code size}.
* Uses a supplied character as the value to pad the String with.</p>
*
* <p>If the size is less than the String length, the String is returned.
* A {@code null} String returns {@code null}.
* A negative size is treated as zero.</p>
*
* <pre>
* StringUtils.center(null, *, *) = null
* StringUtils.center("", 4, ' ') = " "
* StringUtils.center("ab", -1, ' ') = "ab"
* StringUtils.center("ab", 4, ' ') = " ab "
* StringUtils.center("abcd", 2, ' ') = "abcd"
* StringUtils.center("a", 4, ' ') = " a "
* StringUtils.center("a", 4, 'y') = "yayy"
* </pre>
*
* @param str the String to center, may be null
* @param size the int size of new String, negative treated as zero
* @param padChar the character to pad the new String with
* @return centered String, {@code null} if null String input
* @since 2.0
*/
public static String center(String str, final int size, final char padChar) {
if (str == null || size <= 0) {
return str;
}
final int strLen = str.length();
final int pads = size - strLen;
if (pads <= 0) {
return str;
}
str = leftPad(str, strLen + pads / 2, padChar);
str = rightPad(str, size, padChar);
return str;
}
/**
* <p>Centers a String in a larger String of size {@code size}.
* Uses a supplied String as the value to pad the String with.</p>
*
* <p>If the size is less than the String length, the String is returned.
* A {@code null} String returns {@code null}.
* A negative size is treated as zero.</p>
*
* <pre>
* StringUtils.center(null, *, *) = null
* StringUtils.center("", 4, " ") = " "
* StringUtils.center("ab", -1, " ") = "ab"
* StringUtils.center("ab", 4, " ") = " ab "
* StringUtils.center("abcd", 2, " ") = "abcd"
* StringUtils.center("a", 4, " ") = " a "
* StringUtils.center("a", 4, "yz") = "yayz"
* StringUtils.center("abc", 7, null) = " abc "
* StringUtils.center("abc", 7, "") = " abc "
* </pre>
*
* @param str the String to center, may be null
* @param size the int size of new String, negative treated as zero
* @param padStr the String to pad the new String with, must not be null or empty
* @return centered String, {@code null} if null String input
* @throws IllegalArgumentException if padStr is {@code null} or empty
*/
public static String center(String str, final int size, String padStr) {
if (str == null || size <= 0) {
return str;
}
if (isEmpty(padStr)) {
padStr = " ";
}
final int strLen = str.length();
final int pads = size - strLen;
if (pads <= 0) {
return str;
}
str = leftPad(str, strLen + pads / 2, padStr);
str = rightPad(str, size, padStr);
return str;
}
/**
* replace single quotes with \'
*
* @param str content to escape
* @return escaped content or null
*/
public static String escapeSingleQuotes(String str) {
if (str == null)
return null;
return str.replace("\'", "\\\'");
}
/**
* return Memory (utf-8) containing given string
*
* @param att string to copy into memory
* @return memory containing the string
*/
public static Memory toMemory(String att) {
JsonMemoryBuilder rb = new JsonMemoryBuilder();
rb.write(att);
return rb.getMemory();
}
/**
* generate a user friendly security code - for user confirmation, password resets and the like
*
* @return 12 character security code
*/
public static String buildSecurityCode() {
return randomChars(12, "ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789").toString();
}
/*
* generate a user friendly security code - for user confirmation, password resets and the like
*
* @return security code
*/
public static String buildSecurityCode(int chars) {
return randomChars(chars, "ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789").toString();
}
public static String buildSimpleCode(int chars) {
return randomChars(chars, "ABCDEFGHIJKLMNPQRSTUVWXYZ").toString();
}
public static CharSequence randomChars(int count, String charPool) {
if (isEmpty(charPool) || (count < 1))
return "";
StringBuilder buf = new StringBuilder(count);
for (int i = 0; i < count; i++)
buf.append(charPool.charAt(FileUtil.testrnd.nextInt(charPool.length())));
return buf;
}
/**
* check characters to see if they contain restricted characters. character x7F is not
* allowed. characters x00 - x08 are not allowed. characters x0E - x1F are not allowed.
*
* @param str characters to check
* @return true if any restricted characters are present
*/
public static boolean containsRestrictedChars(CharSequence str) {
if (str == null)
return false;
for (int i = 0; i < str.length(); i++) {
int ch = str.charAt(i);
if ((ch < (int)0x9) || ((ch < (int)0x20) && (ch > (int)0xD)) || (ch == (int)0x7F))
return true;
}
return false;
}
/**
* check characters to see if they contain restricted characters. character x7F is not
* allowed. characters x00 - x08 are not allowed. characters x0E - x1F are not allowed.
*
* @param ch character to check
* @return true if any restricted characters are present
*/
public static boolean isRestrictedChar(int ch) {
if ((ch < (int)0x9) || ((ch < (int)0x20) && (ch > (int)0xD)) || (ch == (int)0x7F))
return true;
return false;
}
static public String stripAllNonAscci(String input) {
if (input == null)
return null;
return ASCII_PATTERN.matcher(input).replaceAll(" ");
}
/**
* translate the token and parameters using the current or default locale
*
* @param token name of the dictionary item to lookup
* @param params parameters to use when formatting the output
* @return translated and formatted text
*/
static public String tr(String token, Object... params) {
return OperationContext.getOrHub().tr(token, params);
}
/**
* translate a token and parameters using the current or default locale.
* if the first parameter is numeric and 1 then use the singular token
* otherwise lookup the plural token
*
* @param pluraltoken name of the dictionary item to lookup
* @param singulartoken name of the dictionary item to lookup
* @param params parameters to use when formatting the output
* @return translated and formatted text
*/
static public String trp(String pluraltoken, String singulartoken, Object... params) {
return OperationContext.getOrHub().trp(pluraltoken, singulartoken, params);
}
public static String alignRight(String val, char pad, int width) {
if (val.length() > width) {
val = val.substring(0, width - 1) + "*";
return val;
}
while (val.length() < width)
val = pad + val;
return val;
}
public static String alignLeft(String val, char pad, int width) {
if (val == null)
val = "";
if (val.length() > width) {
val = "*" + val.substring(0, width - 1);
return val;
}
while (val.length() < width)
val = val + pad;
return val;
}
/*
*/
public static String join(String[] strings, String separator) {
if ((strings == null) || (strings.length == 0))
return "";
if (strings.length == 1)
return strings[0];
// two or more elements
StringBuilder buf = new StringBuilder(1024);
for (int i = 0; i < strings.length; i++) {
if ((i > 0) && isNotEmpty(separator))
buf.append(separator);
String el = strings[i];
if (isNotEmpty(el))
buf.append(el);
}
return buf.toString();
}
public static String join(String[] strings, String separator, int startIndex, int endIndex) {
if ((strings == null) || (strings.length == 0))
return "";
int noOfItems = endIndex - startIndex;
if (noOfItems <= 0)
return "";
StringBuilder buf = new StringBuilder(noOfItems * 24);
for (int i = startIndex; i < endIndex; i++) {
if ((i > startIndex) && isNotEmpty(separator))
buf.append(separator);
String el = strings[i];
if (isNotEmpty(el))
buf.append(el);
}
return buf.toString();
}
/*
*/
public static String join(Iterator<String> strings, String separator) {
if (strings == null)
return null;
if (!strings.hasNext())
return "";
String first = strings.next();
if (!strings.hasNext())
return first;
StringBuilder buf = new StringBuilder(1024);
if (isNotEmpty(first))
buf.append(first);
while (strings.hasNext()) {
if (isNotEmpty(separator))
buf.append(separator);
String el = strings.next();
if (isNotEmpty(el))
buf.append(el);
}
return buf.toString();
}
/*
*/
public static String join(Iterable<String> strings, String separator) {
if (strings == null)
return null;
return join(strings.iterator(), separator);
}
}