/**
* Copyright 2015 Santhosh Kumar Tekuri
*
* The JLibs authors license this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package jlibs.core.lang;
import jlibs.core.graph.Convertor;
import jlibs.core.graph.Filter;
import jlibs.core.util.regex.TemplateMatcher;
import java.util.Collections;
import java.util.Iterator;
import java.util.StringTokenizer;
/**
* @author Santhosh Kumar T
*/
public class StringUtil{
@SuppressWarnings("StringEquality")
public static boolean equalsIgnoreCase(String s1, String s2){
return s1==s2 || (s1!=null && s2!=null && s1.equalsIgnoreCase(s2));
}
/**
* if <code>obj</code> is null, returns empty string.
* otherwise returns <code>obj.toString()</code>
*/
public static String toString(Object obj){
return obj==null ? "" : obj.toString();
}
/**
* returns true if <code>str</code> is null or
* its length is zero
*/
public static boolean isEmpty(CharSequence str){
return str==null || str.length()==0;
}
/**
* returns true if <code>str</code> is null or
* it contains only whitespaces.
* <p>
* {@link Character#isWhitespace(char)} is used
* to test for whitespace
*/
public static boolean isWhitespace(CharSequence str){
if(str!=null){
for(int i=0; i<str.length(); i++){
if(!Character.isWhitespace(str.charAt(i)))
return false;
}
}
return true;
}
/**
* Splits given string into tokens with delimiters specified.
* It uses StringTokenizer for tokenizing.
*
* @param str string to be tokenized
* @param delim delimiters used for tokenizing
* @param trim trim the tokens
*
* @return non-null token array
*/
public static String[] getTokens(String str, String delim, boolean trim){
StringTokenizer stok = new StringTokenizer(str, delim);
String tokens[] = new String[stok.countTokens()];
for(int i=0; i<tokens.length; i++){
tokens[i] = stok.nextToken();
if(trim)
tokens[i] = tokens[i].trim();
}
return tokens;
}
/**
* the pattern specified must have variable part ${i}
* example: test${i}.txt
*
* it will find a string using pattern, which is accepted by the specified filter.
* if tryEmptyVar is true, it searches in order:
* test.txt, test2.txt, test3.txt and so on
* if tryEmptyVar is false, it searches in order:
* test1.txt, test2.txt, test3.txt and so on
*
* @see jlibs.core.io.FileUtil#findFreeFile(java.io.File dir, String pattern, boolean tryEmptyVar)
*/
public static String suggest(Filter<String> filter, String pattern, boolean tryEmptyVar){
if(!pattern.contains("${i}"))
throw new IllegalArgumentException("pattern must have ${i}");
TemplateMatcher matcher = new TemplateMatcher("${", "}");
if(tryEmptyVar){
String value = matcher.replace(pattern, Collections.singletonMap("i", ""));
if(filter.select(value))
return value;
}
int i = tryEmptyVar ? 2 : 1;
while(true){
String value = matcher.replace(pattern, Collections.singletonMap("i", String.valueOf(i)));
if(filter.select(value))
return value;
i++;
}
}
/**
* Converts first character in <code>str</code> to uppercase.
* <p>
* This method can be called on string of any length.
*
* @param str string to be converted
* @return string with first letter changed to uppercase
*/
public static String capitalize(String str){
if(str==null)
return null;
switch(str.length()){
case 0:
return str;
case 1:
return str.toUpperCase();
default:
return Character.toUpperCase(str.charAt(0))+str.substring(1);
}
}
/**
* Makes an underscored form from the expression in the string.
* <p>
* Examples:
* <pre class="prettyprint">
* underscore("activeRecord") // "active_record"
* underscore("ActiveRecord") // "active_record"
* underscore("firstName") // "first_name"
* underscore("FirstName") // "first_name"
* underscore("name") // "name"
* </pre>
*
* @param camelCaseWord the camel-cased word that is to be converted;
* @return a lower-cased version of the input, with separate words delimited by the underscore character.
*/
public static String underscore(String camelCaseWord){
if(camelCaseWord==null)
return null;
camelCaseWord = camelCaseWord.trim();
if(camelCaseWord.length()==0)
return "";
camelCaseWord = camelCaseWord.replaceAll("([A-Z]+)([A-Z][a-z])", "$1_$2");
camelCaseWord = camelCaseWord.replaceAll("([a-z\\d])([A-Z])", "$1_$2");
return camelCaseWord.toLowerCase();
}
/**
* Turns a non-negative number into an ordinal string used to
* denote the position in an ordered sequence, such as 1st, 2nd,
* 3rd, 4th
*
* @param number the non-negative number
* @return the string with the number and ordinal suffix
*/
public static String ordinalize(int number){
int modulo = number%100;
if(modulo>=11 && modulo<=13)
return number+"th";
switch(number%10){
case 1:
return number+"st";
case 2:
return number+"nd";
case 3:
return number+"rd";
default:
return number+"th";
}
}
public static int[] toCodePoints(String str){
int count = str.codePointCount(0, str.length());
int[] codePoints = new int[count];
for(int cpIndex=0, charIndex=0; cpIndex<count; cpIndex++){
int cp = str.codePointAt(charIndex);
codePoints[cpIndex] = cp;
charIndex += Character.charCount(cp);
}
return codePoints;
}
/*-------------------------------------------------[ Array Join ]---------------------------------------------------*/
public static <T> String join(T[] array){
return join(array, ", ", null);
}
public static <T> String join(T[] array, String separator){
return join(array, separator, null);
}
public static <T> String join(T[] array, String separator, Convertor<T, String> convertor){
StringBuilder buff = new StringBuilder();
boolean addSeparator = false;
for(T item: array){
if(addSeparator)
buff.append(separator);
else
addSeparator = true;
if(item==null)
buff.append("null");
else
buff.append(convertor==null ? item.toString() : convertor.convert(item));
}
return buff.toString();
}
/*-------------------------------------------------[ Iterator Join ]---------------------------------------------------*/
public static <T> String join(Iterator<T> iter){
return join(iter, ", ", null);
}
public static <T> String join(Iterator<T> iter, String separator){
return join(iter, separator, null);
}
public static <T> String join(Iterator<T> iter, String separator, Convertor<T, String> convertor){
StringBuilder buff = new StringBuilder();
boolean addSeparator = false;
while(iter.hasNext()){
T item = iter.next();
if(addSeparator)
buff.append(separator);
else
addSeparator = true;
if(item==null)
buff.append("null");
else
buff.append(convertor==null ? item.toString() : convertor.convert(item));
}
return buff.toString();
}
/*-------------------------------------------------[ Literal ]---------------------------------------------------*/
public static String toLiteral(char ch, boolean useRaw){
if(ch=='\'')
return "\\'";
else if(ch=='"')
return "\"";
else
return StringUtil.toLiteral(String.valueOf(ch), useRaw);
}
public static String toLiteral(CharSequence str, boolean useRaw){
StringBuilder buf = new StringBuilder(str.length()+25);
for(int i=0,len=str.length(); i<len; i++){
char c = str.charAt(i);
switch(c){
case '\b': buf.append("\\b"); break;
case '\t': buf.append("\\t"); break;
case '\n': buf.append("\\n"); break;
case '\f': buf.append("\\f"); break;
case '\r': buf.append("\\r"); break;
case '\"': buf.append("\\\""); break;
case '\\': buf.append("\\\\"); break;
default:
if(c>=0x0020 && (useRaw || c<=0x007f)) // visible character in ascii
buf.append(c);
else{
buf.append("\\u");
String hex = Integer.toHexString(c);
for(int j=4-hex.length(); j>0; j--)
buf.append('0');
buf.append(hex);
}
}
}
return buf.toString();
}
public static String fromLiteral(String str){
StringBuilder buf = new StringBuilder();
for(int i=0,len=str.length(); i<len; i++){
char c = str.charAt(i);
switch(c){
case '\\':
if(i == str.length()-1){
buf.append('\\');
break;
}
c = str.charAt(++i);
switch(c){
case 'n':
buf.append('\n');
break;
case 't':
buf.append('\t');
break;
case 'r':
buf.append('\r');
break;
case 'u':
int value = 0;
for(int j=0; j<4; j++){
c = str.charAt(++i);
switch(c){
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
value = (value << 4) + c - '0';
break;
case 'a': case 'b': case 'c':
case 'd': case 'e': case 'f':
value = (value << 4) + 10 + c - 'a';
break;
case 'A': case 'B': case 'C':
case 'D': case 'E': case 'F':
value = (value << 4) + 10 + c - 'A';
break;
default:
throw new IllegalArgumentException("Malformed \\uxxxx encoding.");
}
}
buf.append((char)value);
break;
default:
buf.append(c);
break;
}
break;
default:
buf.append(c);
}
}
return buf.toString();
}
}