blob: 587849f953433fa92374cf75c2819c91cab8060f [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses 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 org.apache.tajo.util;
import com.google.common.base.Function;
import io.netty.util.CharsetUtil;
import org.apache.commons.lang.CharUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.hadoop.util.Shell;
import org.apache.hadoop.util.ShutdownHookManager;
import org.apache.hadoop.util.SignalLogger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.util.Arrays;
import java.util.BitSet;
public class StringUtils {
/**
* Priority of the StringUtils shutdown hook.
*/
public static final int SHUTDOWN_HOOK_PRIORITY = 0;
/**
*
* Given the time in long milliseconds, returns a
* String in the format X hrs, Y mins, S sec, M msecs
*
* @param timeDiff The time difference to format
*/
public static String formatTime(long timeDiff){
StringBuilder buf = new StringBuilder();
long hours = timeDiff / (60*60*1000);
long rem = (timeDiff % (60*60*1000));
long minutes = rem / (60*1000);
rem = rem % (60*1000);
long seconds = rem / 1000;
if (hours != 0){
buf.append(hours);
buf.append(" hrs, ");
}
if (minutes != 0){
buf.append(minutes);
buf.append(" mins, ");
}
if (seconds != 0) {
buf.append(seconds);
buf.append(" sec");
}
if (timeDiff < 1000) {
buf.append(timeDiff);
buf.append(" msec");
}
return buf.toString();
}
/**
* Check Seven-bit ASCII
*/
public static boolean isPureAscii(String v) {
// get thread-safe encoder
CharsetEncoder asciiEncoder = CharsetUtil.getEncoder(CharsetUtil.US_ASCII);
return asciiEncoder.canEncode(v);
}
public static String quote(String str) {
return "'" + str + "'";
}
public static String doubleQuote(String str) {
return "\"" + str + "\"";
}
public static boolean isPartOfAnsiSQLIdentifier(char character) {
return
isLowerCaseAlphabet(character) ||
isUpperCaseAlphabet(character) ||
isDigit(character) ||
isUndersscore(character);
}
public static boolean isUndersscore(char character) {
return character == '_';
}
public static boolean isLowerCaseAlphabet(char character) {
return 'a' <= character && character <= 'z';
}
public static boolean isUpperCaseAlphabet(char character) {
return 'A' <= character && character <= 'Z';
}
public static boolean isDigit(char character) {
return '0' <= character && character <= '9';
}
private static final String REGEX_SPECIAL_CHARACTERS = "([.*${}?|\\^\\-\\[\\]])";
public static String escapeRegexp(String literal) {
return literal.replaceAll(REGEX_SPECIAL_CHARACTERS, "\\\\$1");
}
private static final String LIKE_SPECIAL_CHARACTERS = "([_%])";
public static String escapeLike(String literal) {
return literal.replaceAll(LIKE_SPECIAL_CHARACTERS, "\\\\$1");
}
/**
* Return a message for logging.
* @param prefix prefix keyword for the message
* @param msg content of the message
* @return a message for logging
*/
private static String toStartupShutdownString(String prefix, String [] msg) {
StringBuilder b = new StringBuilder(prefix);
b.append("\n/************************************************************");
for(String s : msg)
b.append("\n" + prefix + s);
b.append("\n************************************************************/");
return b.toString();
}
/**
* Print a log message for starting up and shutting down
* @param clazz the class of the server
* @param args arguments
* @param LOG the target log object
*/
public static void startupShutdownMessage(Class<?> clazz, String[] args,
final org.apache.commons.logging.Log LOG) {
final String hostname = org.apache.hadoop.net.NetUtils.getHostname();
final String classname = clazz.getSimpleName();
LOG.info(
toStartupShutdownString("STARTUP_MSG: ", new String[] {
"Starting " + classname,
" host = " + hostname,
" args = " + Arrays.asList(args),
" version = " + org.apache.tajo.util.VersionInfo.getVersion(),
" classpath = " + System.getProperty("java.class.path"),
" build = " + org.apache.tajo.util.VersionInfo.getUrl() + " -r "
+ org.apache.tajo.util.VersionInfo.getRevision()
+ "; compiled by '" + org.apache.tajo.util.VersionInfo.getUser()
+ "' on " + org.apache.tajo.util.VersionInfo.getDate(),
" java = " + System.getProperty("java.version") }
)
);
if (SystemUtils.IS_OS_UNIX) {
try {
SignalLogger.INSTANCE.register(LOG);
} catch (Throwable t) {
LOG.warn("failed to register any UNIX signal loggers: ", t);
}
}
ShutdownHookManager.get().addShutdownHook(
new Runnable() {
@Override
public void run() {
LOG.info(toStartupShutdownString("SHUTDOWN_MSG: ", new String[]{
"Shutting down " + classname + " at " + hostname}));
}
}, SHUTDOWN_HOOK_PRIORITY);
}
public static String unicodeEscapedDelimiter(String value) {
try {
String delimiter = StringEscapeUtils.unescapeJava(value);
StringBuilder builder = new StringBuilder();
for (char achar : delimiter.toCharArray()) {
builder.append(unicodeEscapedDelimiter(achar));
}
return builder.toString();
} catch (Throwable e) {
}
return value;
}
public static String unicodeEscapedDelimiter(char c) {
return CharUtils.unicodeEscaped(c);
}
/**
* The following lines of code that deals with escape characters is mostly copied from HIVE's FileUtils.java
*/
static BitSet charToEscape = new BitSet(128);
static {
for (char c = 0; c < ' '; c++) {
charToEscape.set(c);
}
/**
* ASCII 01-1F are HTTP control characters that need to be escaped.
* \u000A and \u000D are \n and \r, respectively.
*/
char[] clist = new char[] {'\u0001', '\u0002', '\u0003', '\u0004',
'\u0005', '\u0006', '\u0007', '\u0008', '\u0009', '\n', '\u000B',
'\u000C', '\r', '\u000E', '\u000F', '\u0010', '\u0011', '\u0012',
'\u0013', '\u0014', '\u0015', '\u0016', '\u0017', '\u0018', '\u0019',
'\u001A', '\u001B', '\u001C', '\u001D', '\u001E', '\u001F',
'"', '#', '%', '\'', '*', '/', ':', '=', '?', '\\', '\u007F', '{',
'[', ']', '^'};
for (char c : clist) {
charToEscape.set(c);
}
if(Shell.WINDOWS){
// On windows, following chars need to be escaped as well
char [] winClist = {' ', '<','>','|'};
for (char c : winClist) {
charToEscape.set(c);
}
}
}
static boolean needsEscaping(char c) {
return c >= 0 && c < charToEscape.size() && charToEscape.get(c);
}
public static String escapePathName(String path) {
return escapePathName(path, null);
}
/**
* Escapes a path name.
* @param path The path to escape.
* @param defaultPath
* The default name for the path, if the given path is empty or null.
* @return An escaped path name.
*/
public static String escapePathName(String path, String defaultPath) {
if (path == null || path.length() == 0) {
if (defaultPath == null) {
return "__TAJO_DEFAULT_PARTITION__";
} else {
return defaultPath;
}
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < path.length(); i++) {
char c = path.charAt(i);
if (needsEscaping(c)) {
sb.append('%');
sb.append(String.format("%1$02X", (int) c));
} else {
sb.append(c);
}
}
return sb.toString();
}
public static String unescapePathName(String path) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < path.length(); i++) {
char c = path.charAt(i);
if (c == '%' && i + 2 < path.length()) {
int code = -1;
try {
code = Integer.valueOf(path.substring(i + 1, i + 3), 16);
} catch (Exception e) {
code = -1;
}
if (code >= 0) {
sb.append((char) code);
i += 2;
continue;
}
}
sb.append(c);
}
return sb.toString();
}
public static char[][] padChars(char []...bytes) {
char[] startChars = bytes[0];
char[] endChars = bytes[1];
char[][] padded = new char[2][];
int max = Math.max(startChars.length, endChars.length);
padded[0] = new char[max];
padded[1] = new char[max];
System.arraycopy(startChars, 0, padded[0], 0, startChars.length);
for (int i = startChars.length; i < max; i++) {
padded[0][i] = 0;
}
System.arraycopy(endChars, 0, padded[1], 0, endChars.length);
for (int i = endChars.length; i < max; i++) {
padded[1][i] = 0;
}
return padded;
}
public static char[] convertBytesToChars(byte[] src, Charset charset) {
CharsetDecoder decoder = charset.newDecoder();
char[] resultArray = new char[(int) (src.length * decoder.maxCharsPerByte())];
if (src.length != 0) {
ByteBuffer byteBuffer = ByteBuffer.wrap(src);
CharBuffer charBuffer = CharBuffer.wrap(resultArray);
decoder.onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE);
decoder.reset();
CoderResult coderResult = decoder.decode(byteBuffer, charBuffer, true);
if (coderResult.isUnderflow()) {
coderResult = decoder.flush(charBuffer);
if (coderResult.isUnderflow()) {
if (resultArray.length != charBuffer.position()) {
resultArray = Arrays.copyOf(resultArray, charBuffer.position());
}
}
}
}
return resultArray;
}
public static byte[] convertCharsToBytes(char[] src, Charset charset) {
CharsetEncoder encoder = charset.newEncoder();
byte[] resultArray = new byte[(int) (src.length * encoder.maxBytesPerChar())];
if (src.length != 0) {
CharBuffer charBuffer = CharBuffer.wrap(src);
ByteBuffer byteBuffer = ByteBuffer.wrap(resultArray);
encoder.onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE);
encoder.reset();
CoderResult coderResult = encoder.encode(charBuffer, byteBuffer, true);
if (coderResult.isUnderflow()) {
coderResult = encoder.flush(byteBuffer);
if (coderResult.isUnderflow()) {
if (resultArray.length != byteBuffer.position()) {
resultArray = Arrays.copyOf(resultArray, byteBuffer.position());
}
}
}
}
return resultArray;
}
/**
* Concatenate all objects' string with a delimiter string
*
* @param objects Iterable objects
* @param delimiter Delimiter string
* @return A joined string
*/
public static String join(Iterable objects, String delimiter) {
boolean first = true;
StringBuilder sb = new StringBuilder();
for(Object object : objects) {
if (first) {
first = false;
} else {
sb.append(delimiter);
}
sb.append(object.toString());
}
return sb.toString();
}
/**
* Concatenate all objects' string with the delimiter ", "
*
* @param objects Iterable objects
* @return A joined string
*/
public static String join(Object[] objects) {
return join(objects, ", ");
}
/**
* Concatenate all objects' string with the delimiter ", "
*
* @param objects Iterable objects
* @return A joined string
*/
public static String join(Object[] objects, String delimiter) {
return join(objects, delimiter, 0, objects.length);
}
/**
* Concatenate all objects' string with a delimiter string
*
* @param objects object array
* @param delimiter Delimiter string
* @param startIndex the begin index to join
* @return A joined string
*/
public static String join(Object[] objects, String delimiter, int startIndex) {
return join(objects, delimiter, startIndex, objects.length);
}
/**
* Concatenate all objects' string with a delimiter string
*
* @param objects object array
* @param delimiter Delimiter string
* @param startIndex the begin index to join
* @param length the number of columns to be joined
* @return A joined string
*/
public static String join(Object[] objects, String delimiter, int startIndex, int length) {
return join(objects, delimiter, startIndex, length, new Function<Object, String>() {
@Override
public String apply(Object input) {
return input.toString();
}
});
}
/**
* Concatenate all objects' string with a delimiter string
*
* @param objects object array
* @param delimiter Delimiter string
* @param f convert from a type to string
* @return A joined string
*/
public static <T> String join(T [] objects, String delimiter, Function<T, String> f) {
return join(objects, delimiter, 0, objects.length, f);
}
public static <T> String join(T [] objects, String delimiter, int startIndex, int length, Function<T, String> f) {
boolean first = true;
StringBuilder sb = new StringBuilder();
int endIndex = startIndex + length;
for(int i = startIndex; i + startIndex < endIndex; i++) {
if (first) {
first = false;
} else {
sb.append(delimiter);
}
sb.append(f.apply(objects[i]));
}
return sb.toString();
}
/**
* <p>Checks if a String is empty ("") or null.</p>
*
* <pre>
* StringUtils.isEmpty(null) = true
* StringUtils.isEmpty("") = true
* StringUtils.isEmpty(" ") = false
* StringUtils.isEmpty("bob") = false
* StringUtils.isEmpty(" bob ") = false
* </pre>
*
* <p>NOTE: This method changed in Lang version 2.0.
* It no longer trims the String.
* That functionality is available in isBlank().</p>
*
* @param str the String to check, may be null
* @return <code>true</code> if the String is empty or null
*/
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
}