mirror of
https://gitee.com/arthas/arthas.git
synced 2024-12-04 21:28:10 +08:00
polish #899 , improve grep command
This commit is contained in:
parent
ea96c8d0ce
commit
940f274103
@ -3,39 +3,172 @@ package com.taobao.arthas.core.command.basic1000;
|
|||||||
import com.taobao.arthas.core.command.Constants;
|
import com.taobao.arthas.core.command.Constants;
|
||||||
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
|
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
|
||||||
import com.taobao.arthas.core.shell.command.CommandProcess;
|
import com.taobao.arthas.core.shell.command.CommandProcess;
|
||||||
|
import com.taobao.middleware.cli.annotations.Argument;
|
||||||
|
import com.taobao.middleware.cli.annotations.DefaultValue;
|
||||||
import com.taobao.middleware.cli.annotations.Description;
|
import com.taobao.middleware.cli.annotations.Description;
|
||||||
import com.taobao.middleware.cli.annotations.Name;
|
import com.taobao.middleware.cli.annotations.Name;
|
||||||
|
import com.taobao.middleware.cli.annotations.Option;
|
||||||
import com.taobao.middleware.cli.annotations.Summary;
|
import com.taobao.middleware.cli.annotations.Summary;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see com.taobao.arthas.core.shell.command.internal.GrepHandler
|
* @see com.taobao.arthas.core.shell.command.internal.GrepHandler
|
||||||
*/
|
*/
|
||||||
@Name("grep")
|
@Name("grep")
|
||||||
@Summary("grep command for pipes (-e -m -n -v -A -B -C -f )\n" )
|
@Summary("grep command for pipes.\n" )
|
||||||
@Description(Constants.EXAMPLE +
|
@Description(Constants.EXAMPLE +
|
||||||
"sysprop | grep java \n" +
|
" sysprop | grep java \n" +
|
||||||
" sysenv | grep -v JAVA -n\n" +
|
" sysprop | grep java -n\n" +
|
||||||
|
" sysenv | grep -v JAVA\n" +
|
||||||
" sysenv | grep -e \"(?i)(JAVA|sun)\" -m 3 -C 2\n" +
|
" sysenv | grep -e \"(?i)(JAVA|sun)\" -m 3 -C 2\n" +
|
||||||
" sysenv | grep -v JAVA -A2 -B3\n" +
|
" sysenv | grep JAVA -A2 -B3\n" +
|
||||||
" sysenv | grep -e JAVA -f /tmp/t.log \n" +
|
" thread | grep -m 10 -e \"TIMED_WAITING|WAITING\"\n"
|
||||||
" thread | grep -m 10 -e \"TIMED_WAITING|WAITING\"\n\n"
|
+ Constants.WIKI + Constants.WIKI_HOME + "grep")
|
||||||
+"-e, --regexp use PATTERN for matching\n"
|
|
||||||
+"-m, --max-count=NUM stop after NUM selected lines\n"
|
|
||||||
+"-n, --line-number print line number with output lines\n"
|
|
||||||
+"-v, --invert-match select non-matching lines\n"
|
|
||||||
+"-A, --after-context=NUM print NUM lines of trailing context\n"
|
|
||||||
+"-B, --before-context=NUM print NUM lines of leading context\n"
|
|
||||||
+"-C, --context=NUM print NUM lines of output context\n"
|
|
||||||
// +"-f, --output=File output result to file, filename endsWith :false for disable append mode\n"
|
|
||||||
+ Constants.WIKI + Constants.WIKI_HOME + "grep")
|
|
||||||
public class GrepCommand extends AnnotatedCommand {
|
public class GrepCommand extends AnnotatedCommand {
|
||||||
|
private String pattern;
|
||||||
|
private boolean ignoreCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* select non-matching lines
|
||||||
|
*/
|
||||||
|
private boolean invertMatch;
|
||||||
|
|
||||||
|
private boolean isRegEx = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* print line number with output lines
|
||||||
|
*/
|
||||||
|
private boolean showLineNumber = false;
|
||||||
|
|
||||||
|
private boolean trimEnd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* print NUM lines of leading context
|
||||||
|
*/
|
||||||
|
private int beforeLines;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* print NUM lines of trailing context
|
||||||
|
*/
|
||||||
|
private int afterLines;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* print NUM lines of output context
|
||||||
|
*/
|
||||||
|
private int context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stop after NUM selected lines
|
||||||
|
*/
|
||||||
|
private int maxCount;
|
||||||
|
|
||||||
|
@Argument(index = 0, argName = "pattern", required = true)
|
||||||
|
@Description("Pattern")
|
||||||
|
public void setOptionName(String pattern) {
|
||||||
|
this.pattern = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(shortName = "e", longName = "regex", flag = true)
|
||||||
|
@Description("Enable regular expression to match")
|
||||||
|
public void setRegEx(boolean regEx) {
|
||||||
|
isRegEx = regEx;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(shortName = "i", longName = "ignore-case", flag = true)
|
||||||
|
@Description("Perform case insensitive matching. By default, grep is case sensitive.")
|
||||||
|
public void setIgnoreCase(boolean ignoreCase) {
|
||||||
|
this.ignoreCase = ignoreCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(shortName = "v", longName = "invert-match", flag = true)
|
||||||
|
@Description("Select non-matching lines")
|
||||||
|
public void setInvertMatch(boolean invertMatch) {
|
||||||
|
this.invertMatch = invertMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(shortName = "n", longName = "line-number", flag = true)
|
||||||
|
@Description("Print line number with output lines")
|
||||||
|
public void setShowLineNumber(boolean showLineNumber) {
|
||||||
|
this.showLineNumber = showLineNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(longName = "trim-end", flag = true)
|
||||||
|
@DefaultValue("true")
|
||||||
|
@Description("Remove whitespaces at the end of the line")
|
||||||
|
public void setTrimEnd(boolean trimEnd) {
|
||||||
|
this.trimEnd = trimEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(shortName = "B", longName = "before-context")
|
||||||
|
@Description("Print NUM lines of leading context)")
|
||||||
|
public void setBeforeLines(int beforeLines) {
|
||||||
|
this.beforeLines = beforeLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(shortName = "A", longName = "after-context")
|
||||||
|
@Description("Print NUM lines of trailing context)")
|
||||||
|
public void setAfterLines(int afterLines) {
|
||||||
|
this.afterLines = afterLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(shortName = "C", longName = "context")
|
||||||
|
@Description("Print NUM lines of output context)")
|
||||||
|
public void setContext(int context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Option(shortName = "m", longName = "max-count")
|
||||||
|
@Description("stop after NUM selected lines)")
|
||||||
|
public void setMaxCount(int maxCount) {
|
||||||
|
this.maxCount = maxCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPattern() {
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPattern(String pattern) {
|
||||||
|
this.pattern = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isIgnoreCase() {
|
||||||
|
return ignoreCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInvertMatch() {
|
||||||
|
return invertMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRegEx() {
|
||||||
|
return isRegEx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isShowLineNumber() {
|
||||||
|
return showLineNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTrimEnd() {
|
||||||
|
return trimEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBeforeLines() {
|
||||||
|
return beforeLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAfterLines() {
|
||||||
|
return afterLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxCount() {
|
||||||
|
return maxCount;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(CommandProcess process) {
|
public void process(CommandProcess process) {
|
||||||
process.write("The grep command only for pipes ").write("\n");
|
process.write("The grep command only for pipes. See 'grep --help'").write("\n");
|
||||||
final Description ann = GrepCommand.class.getAnnotation(Description.class);
|
|
||||||
if (ann != null) {
|
|
||||||
process.write(ann.value()).write("\n");
|
|
||||||
}
|
|
||||||
process.end();
|
process.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,166 +2,169 @@ package com.taobao.arthas.core.shell.command.internal;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import com.taobao.arthas.core.command.basic1000.GrepCommand;
|
||||||
import com.taobao.arthas.core.shell.cli.CliToken;
|
import com.taobao.arthas.core.shell.cli.CliToken;
|
||||||
import com.taobao.middleware.cli.Argument;
|
import com.taobao.arthas.core.util.StringUtils;
|
||||||
import com.taobao.middleware.cli.CLIs;
|
import com.taobao.middleware.cli.CLI;
|
||||||
import com.taobao.middleware.cli.CommandLine;
|
import com.taobao.middleware.cli.CommandLine;
|
||||||
import com.taobao.middleware.cli.Option;
|
import com.taobao.middleware.cli.annotations.CLIConfigurator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author beiwei30 on 12/12/2016.
|
* @author beiwei30 on 12/12/2016.
|
||||||
*/
|
*/
|
||||||
public class GrepHandler extends StdoutHandler {
|
public class GrepHandler extends StdoutHandler {
|
||||||
public static final String NAME = "grep";
|
public static final String NAME = "grep";
|
||||||
private static final Pattern TRIM_PATTERN;
|
|
||||||
static {
|
|
||||||
//默认删除右边的空白字符是为了解决-n 因空白字符导致显示换行的问题
|
|
||||||
//ie: sysprop | grep -n java
|
|
||||||
final String p = System.getProperty("arthas_grep_trim_pattern", "[ \\f\t\\v]+$");
|
|
||||||
TRIM_PATTERN = "NOP".equals(p) ? null : Pattern.compile(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String keyword;
|
private String keyword;
|
||||||
private boolean ignoreCase;
|
private boolean ignoreCase;
|
||||||
// -v, --invert-match select non-matching lines
|
/**
|
||||||
|
* select non-matching lines
|
||||||
|
*/
|
||||||
private final boolean invertMatch;
|
private final boolean invertMatch;
|
||||||
//-e, --regexp=PATTERN use PATTERN for matching
|
|
||||||
private final Pattern pattern;
|
private final Pattern pattern;
|
||||||
// -n, --line-number print line number with output lines
|
|
||||||
|
/**
|
||||||
|
* print line number with output lines
|
||||||
|
*/
|
||||||
private final boolean showLineNumber;
|
private final boolean showLineNumber;
|
||||||
/*
|
|
||||||
-B, --before-context=NUM print NUM lines of leading context
|
private boolean trimEnd;
|
||||||
-A, --after-context=NUM print NUM lines of trailing context
|
|
||||||
-C, --context=NUM print NUM lines of output context
|
/**
|
||||||
*/
|
* print NUM lines of leading context
|
||||||
private final int beforeLines;
|
*/
|
||||||
private final int afterLines;
|
private final Integer beforeLines;
|
||||||
//-m, --max-count=NUM stop after NUM selected lines
|
/**
|
||||||
private final int maxCount;
|
* print NUM lines of trailing context
|
||||||
|
*/
|
||||||
|
private final Integer afterLines;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stop after NUM selected lines
|
||||||
|
*/
|
||||||
|
private final Integer maxCount;
|
||||||
|
|
||||||
public static StdoutHandler inject(List<CliToken> tokens) {
|
public static StdoutHandler inject(List<CliToken> tokens) {
|
||||||
List<String> args = StdoutHandler.parseArgs(tokens, NAME);
|
List<String> args = StdoutHandler.parseArgs(tokens, NAME);
|
||||||
CommandLine commandLine = CLIs.create(NAME)
|
|
||||||
.addOption(new Option().setShortName("i").setLongName("ignore-case").setFlag(true))
|
GrepCommand grepCommand = new GrepCommand();
|
||||||
.addOption(new Option().setShortName("v").setLongName("invert-match").setFlag(true))
|
CLI cli = CLIConfigurator.define(GrepCommand.class);
|
||||||
.addOption(new Option().setShortName("n").setLongName("line-number").setFlag(true))
|
CommandLine commandLine = cli.parse(args);
|
||||||
.addOption(new Option().setShortName("B").setLongName("before-context").setSingleValued(true))
|
|
||||||
.addOption(new Option().setShortName("A").setLongName("after-context").setSingleValued(true))
|
try {
|
||||||
.addOption(new Option().setShortName("C").setLongName("context").setSingleValued(true))
|
CLIConfigurator.inject(commandLine, grepCommand);
|
||||||
.addOption(new Option().setShortName("e").setLongName("regexp").setFlag(true))
|
} catch (Throwable e) {
|
||||||
.addOption(new Option().setShortName("f").setLongName("output").setSingleValued(true))
|
throw new RuntimeException(e);
|
||||||
.addOption(new Option().setShortName("m").setLongName("max-count").setSingleValued(true))
|
|
||||||
.addArgument(new Argument().setArgName("keyword").setIndex(0))
|
|
||||||
.parse(args);
|
|
||||||
Boolean ignoreCase = commandLine.isFlagEnabled("ignore-case");
|
|
||||||
String keyword = commandLine.getArgumentValue(0);
|
|
||||||
final boolean invertMatch = commandLine.isFlagEnabled("invert-match");
|
|
||||||
final boolean regexpMode = commandLine.isFlagEnabled("regexp");
|
|
||||||
final boolean showLineNumber = commandLine.isFlagEnabled("line-number");
|
|
||||||
int context = getInt(commandLine, "context", 0);
|
|
||||||
int beforeLines = getInt(commandLine, "before-context", 0);
|
|
||||||
int afterLines = getInt(commandLine, "after-context", 0);
|
|
||||||
if (context > 0) {
|
|
||||||
if (beforeLines < 1) {
|
|
||||||
beforeLines = context;
|
|
||||||
}
|
|
||||||
if (afterLines < 1 ){
|
|
||||||
afterLines = context;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
final int maxCount = getInt(commandLine, "max-count", 0);
|
|
||||||
return new GrepHandler(keyword, ignoreCase, invertMatch, regexpMode, showLineNumber
|
int context = grepCommand.getContext();
|
||||||
, beforeLines, afterLines, maxCount);
|
int beforeLines = grepCommand.getBeforeLines();
|
||||||
|
int afterLines = grepCommand.getAfterLines();
|
||||||
|
if (context > 0) {
|
||||||
|
if (beforeLines < 1) {
|
||||||
|
beforeLines = context;
|
||||||
|
}
|
||||||
|
if (afterLines < 1) {
|
||||||
|
afterLines = context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new GrepHandler(grepCommand.getPattern(), grepCommand.isIgnoreCase(), grepCommand.isInvertMatch(),
|
||||||
|
grepCommand.isRegEx(), grepCommand.isShowLineNumber(), grepCommand.isTrimEnd(), beforeLines,
|
||||||
|
afterLines, grepCommand.getMaxCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int getInt(CommandLine cmdline, String name , int defaultValue) {
|
private GrepHandler(String keyword, boolean ignoreCase, boolean invertMatch, boolean regexpMode,
|
||||||
final String v = cmdline.getOptionValue(name);
|
boolean showLineNumber, boolean trimEnd, int beforeLines, int afterLines, int maxCount) {
|
||||||
final int ret = v== null ? defaultValue : Integer.parseInt(v);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private GrepHandler(String keyword, boolean ignoreCase, boolean invertMatch, boolean regexpMode
|
|
||||||
, boolean showLineNumber, int beforeLines, int afterLines,int maxCount) {
|
|
||||||
this.ignoreCase = ignoreCase;
|
this.ignoreCase = ignoreCase;
|
||||||
this.invertMatch = invertMatch;
|
this.invertMatch = invertMatch;
|
||||||
this.showLineNumber = showLineNumber;
|
this.showLineNumber = showLineNumber;
|
||||||
|
this.trimEnd = trimEnd;
|
||||||
this.beforeLines = beforeLines > 0 ? beforeLines : 0;
|
this.beforeLines = beforeLines > 0 ? beforeLines : 0;
|
||||||
this.afterLines = afterLines > 0 ? afterLines : 0;
|
this.afterLines = afterLines > 0 ? afterLines : 0;
|
||||||
this.maxCount = maxCount > 0 ? maxCount : 0;
|
this.maxCount = maxCount > 0 ? maxCount : 0;
|
||||||
if (regexpMode) {
|
if (regexpMode) {
|
||||||
final int flags = ignoreCase ? Pattern.CASE_INSENSITIVE : 0;
|
final int flags = ignoreCase ? Pattern.CASE_INSENSITIVE : 0;
|
||||||
this.pattern = Pattern.compile(keyword, flags);
|
this.pattern = Pattern.compile(keyword, flags);
|
||||||
} else {
|
} else {
|
||||||
this.pattern = null;
|
this.pattern = null;
|
||||||
}
|
}
|
||||||
this.keyword = ignoreCase ? keyword.toLowerCase() : keyword;
|
this.keyword = ignoreCase ? keyword.toLowerCase() : keyword;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String apply(String input) {
|
public String apply(String input) {
|
||||||
StringBuilder output = new StringBuilder();
|
StringBuilder output = new StringBuilder();
|
||||||
String[] lines = input.split("\n");
|
String[] lines = input.split("\n");
|
||||||
int continueCount = 0 ;
|
int continueCount = 0;
|
||||||
int lastStartPos = 0 ;
|
int lastStartPos = 0;
|
||||||
int lastContinueLineNum = -1;
|
int lastContinueLineNum = -1;
|
||||||
int matchCount = 0;
|
int matchCount = 0;
|
||||||
for (int lineNum = 0 ; lineNum < lines.length ;) {
|
for (int lineNum = 0; lineNum < lines.length;) {
|
||||||
String line = TRIM_PATTERN.matcher(lines[lineNum++]).replaceAll("");
|
String line = null;
|
||||||
final boolean match;
|
if (this.trimEnd) {
|
||||||
if (pattern == null) {
|
line = StringUtils.stripEnd(lines[lineNum], null);
|
||||||
match = (ignoreCase ? line.toLowerCase() : line).contains(keyword);
|
} else {
|
||||||
} else {
|
line = lines[lineNum];
|
||||||
match = pattern.matcher(line).find();
|
}
|
||||||
}
|
lineNum++;
|
||||||
if (invertMatch ? !match : match) {
|
|
||||||
matchCount++;
|
final boolean match;
|
||||||
if (beforeLines > continueCount) {
|
if (pattern == null) {
|
||||||
int n = lastContinueLineNum == -1 ? ( beforeLines >= lineNum ? 1 : lineNum - beforeLines )
|
match = (ignoreCase ? line.toLowerCase() : line).contains(keyword);
|
||||||
: lineNum - beforeLines - continueCount;
|
} else {
|
||||||
if ( n >= lastContinueLineNum || lastContinueLineNum == -1 ) {
|
match = pattern.matcher(line).find();
|
||||||
StringBuilder beforeSb = new StringBuilder();
|
}
|
||||||
for (int i = n ; i < lineNum ; i++) {
|
if (invertMatch ? !match : match) {
|
||||||
appendLine(beforeSb, i, lines[i - 1]);
|
matchCount++;
|
||||||
|
if (beforeLines > continueCount) {
|
||||||
|
int n = lastContinueLineNum == -1 ? (beforeLines >= lineNum ? 1 : lineNum - beforeLines)
|
||||||
|
: lineNum - beforeLines - continueCount;
|
||||||
|
if (n >= lastContinueLineNum || lastContinueLineNum == -1) {
|
||||||
|
StringBuilder beforeSb = new StringBuilder();
|
||||||
|
for (int i = n; i < lineNum; i++) {
|
||||||
|
appendLine(beforeSb, i, lines[i - 1]);
|
||||||
|
}
|
||||||
|
output.insert(lastStartPos, beforeSb);
|
||||||
|
}
|
||||||
|
} // end handle before lines
|
||||||
|
|
||||||
|
lastStartPos = output.length();
|
||||||
|
appendLine(output, lineNum, line);
|
||||||
|
|
||||||
|
if (afterLines > continueCount) {
|
||||||
|
int last = lineNum + afterLines - continueCount;
|
||||||
|
if (last > lines.length) {
|
||||||
|
last = lines.length;
|
||||||
|
}
|
||||||
|
for (int i = lineNum; i < last; i++) {
|
||||||
|
appendLine(output, i + 1, lines[i]);
|
||||||
|
lineNum++;
|
||||||
|
continueCount++;
|
||||||
|
lastStartPos = output.length();
|
||||||
|
}
|
||||||
|
} // end handle afterLines
|
||||||
|
|
||||||
|
continueCount++;
|
||||||
|
if (maxCount > 0 && matchCount >= maxCount) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else { // not match
|
||||||
|
if (continueCount > 0) {
|
||||||
|
lastContinueLineNum = lineNum - 1;
|
||||||
|
continueCount = 0;
|
||||||
}
|
}
|
||||||
output.insert(lastStartPos, beforeSb);
|
|
||||||
}
|
|
||||||
} // end handle before lines
|
|
||||||
|
|
||||||
lastStartPos = output.length();
|
|
||||||
appendLine(output, lineNum, line);
|
|
||||||
|
|
||||||
if (afterLines > continueCount) {
|
|
||||||
int last = lineNum + afterLines - continueCount;
|
|
||||||
if (last > lines.length) {
|
|
||||||
last = lines.length;
|
|
||||||
}
|
|
||||||
for(int i = lineNum ; i < last ; i++) {
|
|
||||||
appendLine(output, i+1, lines[i]);
|
|
||||||
lineNum ++;
|
|
||||||
continueCount++;
|
|
||||||
lastStartPos = output.length();
|
|
||||||
}
|
|
||||||
} //end handle afterLines
|
|
||||||
|
|
||||||
continueCount++;
|
|
||||||
if(maxCount > 0 && matchCount >= maxCount) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else { // not match
|
|
||||||
if(continueCount > 0) {
|
|
||||||
lastContinueLineNum = lineNum -1 ;
|
|
||||||
continueCount = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
final String str = output.toString();// output.length() > 0 ? output.substring(0, output.length()-1) : "";
|
final String str = output.toString();
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void appendLine(StringBuilder output, int lineNum, String line) {
|
protected void appendLine(StringBuilder output, int lineNum, String line) {
|
||||||
if(showLineNumber) {
|
if (showLineNumber) {
|
||||||
output.append(lineNum).append(':');
|
output.append(lineNum).append(':');
|
||||||
}
|
}
|
||||||
output.append(line).append('\n');
|
output.append(line).append('\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user