| /** |
| * 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; |
| } |
| } |