package com.github.ompc.greys.core.util;
import com.github.ompc.greys.core.textui.TTable;
import com.github.ompc.greys.core.util.collection.GaStack;
import com.github.ompc.greys.core.util.collection.ThreadUnsafeGaStack;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import static java.lang.Integer.toHexString;
import static org.apache.commons.lang3.StringUtils.EMPTY;
/**
* 字符串工具类
* Created by oldmanpushcart@gmail.com on 15/5/18.
*/
public class GaStringUtils {
/**
* 命令提示符
*/
public static final String DEFAULT_PROMPT = "ga?>";
/**
* 中断提示
*/
public static final String ABORT_MSG = "Press Ctrl+D to abort.";
/**
* Spy类名
*/
public static final String SPY_CLASSNAME = "com.github.ompc.greys.agent.Spy";
/**
* 解析状态
*/
private enum SPLIT_FOR_ARGUMENT_STATE {
ESCAPE_CHAR,
READ_CHAR
}
/**
* 拆分参数,要求能将命令行字符串拆分成为多个数组
*
* @param argumentString 参数行
* @return 拆分后的参数数组
*/
public static String[] splitForArgument(String argumentString) {
final ArrayList<String> stringList = new ArrayList<String>();
if (StringUtils.isNotBlank(argumentString)) {
// 字符串片段
StringBuilder segmentSB = new StringBuilder();
// 解析状态
SPLIT_FOR_ARGUMENT_STATE state = SPLIT_FOR_ARGUMENT_STATE.READ_CHAR;
// 期待片段分隔符
char splitSegmentChar = ' ';
// 是否在片段中
boolean isInSegment = true;
for (char c : argumentString.toCharArray()) {
switch (state) {
case READ_CHAR: {
// 匹配到转义符时,任何时候都跳转到转义处理
if (GaCheckUtils.isEquals(c, '\\')) {
state = SPLIT_FOR_ARGUMENT_STATE.ESCAPE_CHAR;
break;
}
// 段落中的匹配
if (isInSegment) {
// 如果匹配到片段结束分隔符,则结束当前片段
if (GaCheckUtils.isEquals(c, splitSegmentChar)) {
final String segmentString = segmentSB.toString();
if (StringUtils.isNotBlank(segmentString)) {
stringList.add(segmentString);
}
segmentSB = new StringBuilder();
isInSegment = false;
}
// 其他情况则一律添加到片段中
else {
segmentSB.append(c);
}
}
// 非段落中的匹配
else {
// 过滤掉连续空格
if (GaCheckUtils.isEquals(c, ' ')) {
break;
}
// 命中片段分隔符
else if (GaCheckUtils.isIn(c, '\'', '"')) {
splitSegmentChar = c;
isInSegment = true;
}
// 其他字符则一律添加到片段中
// 同时认为以空格作为片段分隔符
else {
splitSegmentChar = ' ';
isInSegment = true;
segmentSB.append(c);
}
}
break;
}
case ESCAPE_CHAR: {
segmentSB.append(c);
state = SPLIT_FOR_ARGUMENT_STATE.READ_CHAR;
break;
}
}//switch
}//for
// 最后循环结束需要强制提交片段
final String segmentString = segmentSB.toString();
if (StringUtils.isNotBlank(segmentString)) {
stringList.add(segmentString);
}
}
return stringList.toArray(new String[stringList.size()]);
}
/**
* 获取异常的原因描述
*
* @param t 异常
* @return 异常原因
*/
public static String getCauseMessage(Throwable t) {
if (null != t.getCause()) {
return getCauseMessage(t.getCause());
}
return t.getMessage();
}
/**
* 展示logo<br/>
* 这个代码不忍直视,忍吧,不要问我怎么写出来的
*
* @return Logo信息
* @throws IOException resource not found.
*/
public static String getLogo() throws IOException {
final String logo = IOUtils.toString(GaStringUtils.class.getResourceAsStream("/com/github/ompc/greys/core/res/logo.txt"));
final String version = IOUtils.toString(GaStringUtils.class.getResourceAsStream("/com/github/ompc/greys/core/res/version"));
final char[] versionPrefixArray = "version:".toCharArray();
final String[] versionArray = StringUtils.split(version, '.');
final int FIX_VERSION_LENGTH = 4;
final Object[] objectArray = new Object[versionPrefixArray.length + versionArray.length + FIX_VERSION_LENGTH];
for (int index = 0; index < versionPrefixArray.length; index++) {
objectArray[index] = versionPrefixArray[index];
}
for (int index = versionPrefixArray.length; index < objectArray.length; index += 2) {
objectArray[index] = versionArray[(index - versionPrefixArray.length) / 2];
if (index < objectArray.length) {
objectArray[index + 1] = ".";
}
}
final TTable logoTable = new TTable(1).addRow(logo);
logoTable.getBorder().set(TTable.Border.BORDER_NON);
final TTable versionTable = new TTable(15).addRow(objectArray);
// versionTable.getBorder().setValue(TTable.Border.BORDER_NON);
final TTable returnTable = new TTable(new TTable.ColumnDefine[]{new TTable.ColumnDefine(TTable.Align.RIGHT)})
.addRow(logoTable.rendering())
.addRow(versionTable.rendering());
returnTable.getBorder().set(TTable.Border.BORDER_NON);
return returnTable.rendering();
}
/**
* 将一个对象转换为字符串
*
* @param obj 目标对象
* @return 字符串
*/
public static String newString(Object obj) {
if (null == obj) {
return EMPTY;
}
return obj.toString();
}
/**
* 翻译类名称
*
* @param clazz Java类
* @return 翻译值
*/
public static String tranClassName(Class<?> clazz) {
return clazz.getCanonicalName();
}
/**
* 翻译类名称<br/>
* 将 java/lang/String 的名称翻译成 java.lang.String
*
* @param className 类名称 java/lang/String
* @return 翻译后名称 java.lang.String
*/
public static String tranClassName(String className) {
return StringUtils.replace(className, "/", ".");
}
/**
* 翻译Modifier值
*
* @param mod modifier
* @return 翻译值
*/
public static String tranModifier(int mod) {
StringBuilder sb = new StringBuilder();
if (Modifier.isAbstract(mod)) {
sb.append("abstract,");
}
if (Modifier.isFinal(mod)) {
sb.append("final,");
}
if (Modifier.isInterface(mod)) {
sb.append("interface,");
}
if (Modifier.isNative(mod)) {
sb.append("native,");
}
if (Modifier.isPrivate(mod)) {
sb.append("private,");
}
if (Modifier.isProtected(mod)) {
sb.append("protected,");
}
if (Modifier.isPublic(mod)) {
sb.append("public,");
}
if (Modifier.isStatic(mod)) {
sb.append("static,");
}
if (Modifier.isStrict(mod)) {
sb.append("strict,");
}
if (Modifier.isSynchronized(mod)) {
sb.append("synchronized,");
}
if (Modifier.isTransient(mod)) {
sb.append("transient,");
}
if (Modifier.isVolatile(mod)) {
sb.append("volatile,");
}
if (sb.length() > 0) {
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
/**
* 统一获取线程信息
*
* @return 线程摘要信息(一行)
*/
public static String getThreadInfo() {
final Thread currentThread = Thread.currentThread();
return String.format("thread_name=\"%s\" thread_id=0x%s;is_daemon=%s;priority=%s;",
currentThread.getName(),
Long.toHexString(currentThread.getId()),
currentThread.isDaemon(),
currentThread.getPriority());
}
/**
* 获取方法执行堆栈信息
*
* @return 方法堆栈信息
*/
public static String getStack(final String title) {
final Thread currentThread = Thread.currentThread();
final StackTraceElement[] stackTraceElementArray = currentThread.getStackTrace();
final GaStack<StackTraceElement> elementStack = new ThreadUnsafeGaStack<StackTraceElement>();
final int length = stackTraceElementArray.length;
// 1. 找到com.github.ompc.greys的最后一条记录
// 2. 从这台记录开始skip掉4条反射调用的element
if (length > 0) {
for (int index = length - 1; index >= 0; index--) {
final StackTraceElement element = stackTraceElementArray[index];
if (StringUtils.startsWith(element.getClassName(), "com.github.ompc.greys.")) {
for (int step = 0; step < 4 && !elementStack.isEmpty(); step++) {
final StackTraceElement skipElement = elementStack.pop();
// JVM会对反射调用进行优化,最多4次,但有可能只有3次
// 所以如果提前遇到了java.lang.reflect.Method.invoke,则认为skip可以提前结束
if (StringUtils.startsWith(skipElement.getClassName(), "java.lang.reflect.Method")
&& StringUtils.equals(skipElement.getMethodName(), "invoke")) {
break;
}
}
break;
}
elementStack.push(element);
}//for
}//if
// final String title = getThreadInfo();
final StackTraceElement locationStackTraceElement = elementStack.pop();
final StringBuilder locationSB = new StringBuilder(" @")
.append(locationStackTraceElement.getClassName()).append(".").append(locationStackTraceElement.getMethodName())
.append("(").append(locationStackTraceElement.getFileName()).append(":").append(locationStackTraceElement.getLineNumber()).append(")");
final StringBuilder stSB = new StringBuilder()
.append(title).append("\n")
.append(locationSB.toString()).append("\n");
while (!elementStack.isEmpty()) {
final StackTraceElement ste = elementStack.pop();
stSB
.append(" at ")
.append(ste.getClassName()).append(".")
.append(ste.getMethodName())
.append("(").append(ste.getFileName()).append(":").append(ste.getLineNumber()).append(")\n");
}
return stSB.toString();
}
/**
* 自动换行
*
* @param string 字符串
* @param width 行宽
* @return 换行后的字符串
*/
public static String wrap(String string, int width) {
final StringBuilder sb = new StringBuilder();
final char[] buffer = string.toCharArray();
int count = 0;
for (char c : buffer) {
if (count == width) {
count = 0;
sb.append('\n');
if (c == '\n') {
continue;
}
}
if (c == '\n') {
count = 0;
} else {
count++;
}
sb.append(c);
}
return sb.toString();
}
/**
* 将对象hashCode转换16进制字符串
*
* @param object 目标对象
* @return 16进制字符串
*/
public static String hashCodeToHexString(Object object) {
return object == null
? "NULL"
: "0x" + toHexString(object.hashCode());
}
}