mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
failed test line highlighting
This commit is contained in:
@@ -199,6 +199,12 @@
|
||||
<callHierarchyProvider language="JAVA" implementationClass="com.intellij.ide.hierarchy.call.JavaCallHierarchyProvider"/>
|
||||
<runLineMarkerContributor language="JAVA" order="after applicationRunLineMarkerProvider"
|
||||
implementationClass="com.intellij.testIntegration.TestRunLineMarkerProvider"/>
|
||||
<projectService serviceImplementation="com.intellij.testIntegration.TestFailedLineManager"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="TestFailedLine"
|
||||
enabledByDefault="true" level="ERROR"
|
||||
groupBundle="messages.InspectionsBundle" groupKey="group.names.junit.issues"
|
||||
displayName="Highlight problem line in test"
|
||||
implementationClass="com.intellij.testIntegration.TestFailedLineInspection"/>
|
||||
<testGenerator language="JAVA" implementationClass="com.intellij.testIntegration.createTest.JavaTestGenerator" />
|
||||
<testCreator language="JAVA" implementationClass="com.intellij.testIntegration.JavaTestCreator"/>
|
||||
<testFinder implementation="com.intellij.testIntegration.JavaTestFinder"/>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.testIntegration;
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.execution.TestStateStorage;
|
||||
import com.intellij.psi.JavaElementVisitor;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.PsiMethodCallExpression;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class TestFailedLineInspection extends LocalInspectionTool {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitMethodCallExpression(PsiMethodCallExpression call) {
|
||||
|
||||
TestStateStorage.Record state = TestFailedLineManager.getInstance(call.getProject()).getFailedLineState(call);
|
||||
if (state == null) return;
|
||||
holder.registerProblem(call, state.errorMessage);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.testIntegration;
|
||||
|
||||
import com.intellij.codeInsight.TestFrameworks;
|
||||
import com.intellij.execution.TestStateStorage;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.ClassUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.containers.FactoryMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class TestFailedLineManager implements FileEditorManagerListener {
|
||||
|
||||
private final TestStateStorage myStorage;
|
||||
private final Map<VirtualFile, Map<String, TestInfo>> myMap;
|
||||
|
||||
public static TestFailedLineManager getInstance(@NotNull Project project) {
|
||||
return ServiceManager.getService(project, TestFailedLineManager.class);
|
||||
}
|
||||
|
||||
public TestFailedLineManager(Project project, TestStateStorage storage) {
|
||||
myStorage = storage;
|
||||
myMap = FactoryMap.create(o -> new HashMap<>());
|
||||
project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, this);
|
||||
}
|
||||
|
||||
public TestInfo getTestInfo(@NotNull PsiMethod psiMethod) {
|
||||
PsiClass psiClass = PsiTreeUtil.getParentOfType(psiMethod, PsiClass.class);
|
||||
if (psiClass == null) return null;
|
||||
TestFramework framework = TestFrameworks.detectFramework(psiClass);
|
||||
if (framework == null || !framework.isTestMethod(psiMethod)) return null;
|
||||
|
||||
String url = "java:test://" + ClassUtil.getJVMClassName(psiClass) + "." + psiMethod.getName();
|
||||
TestStateStorage.Record state = myStorage.getState(url);
|
||||
if (state == null) return null;
|
||||
|
||||
VirtualFile file = psiMethod.getContainingFile().getVirtualFile();
|
||||
Map<String, TestInfo> map = myMap.get(file);
|
||||
TestInfo info = map.get(url);
|
||||
if (info == null || !state.date.equals(info.myRecord.date)) {
|
||||
info = new TestInfo(state);
|
||||
map.put(url, info);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
public TestStateStorage.Record getFailedLineState(PsiMethodCallExpression call) {
|
||||
PsiMethod psiMethod = PsiTreeUtil.getParentOfType(call, PsiMethod.class);
|
||||
if (psiMethod == null) return null;
|
||||
TestInfo info = getTestInfo(psiMethod);
|
||||
if (info == null) return null;
|
||||
Document document = PsiDocumentManager.getInstance(call.getProject()).getDocument(call.getContainingFile());
|
||||
if (document == null) return null;
|
||||
if (info.myPointer != null) {
|
||||
PsiElement element = info.myPointer.getElement();
|
||||
if (element != null) {
|
||||
if (call == element) {
|
||||
info.myRecord.failedLine = document.getLineNumber(call.getTextOffset()) + 1;
|
||||
return info.myRecord;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
TestStateStorage.Record state = info.myRecord;
|
||||
if (state.failedLine == -1 || StringUtil.isEmpty(state.failedMethod)) return null;
|
||||
if (!state.failedMethod.equals(call.getMethodExpression().getText())) return null;
|
||||
if (state.failedLine != document.getLineNumber(call.getTextOffset()) + 1) return null;
|
||||
info.myPointer = SmartPointerManager.createPointer(call);
|
||||
return info.myRecord;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
|
||||
Map<String, TestInfo> map = myMap.remove(file);
|
||||
if (map != null) {
|
||||
map.forEach((s, info) -> myStorage.writeState(s, info.myRecord));
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestInfo {
|
||||
public TestStateStorage.Record myRecord;
|
||||
public SmartPsiElementPointer<PsiElement> myPointer;
|
||||
|
||||
public TestInfo(TestStateStorage.Record record) {
|
||||
myRecord = record;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,15 +16,14 @@
|
||||
package com.intellij.testIntegration;
|
||||
|
||||
import com.intellij.codeInsight.TestFrameworks;
|
||||
import com.intellij.execution.TestStateStorage;
|
||||
import com.intellij.execution.lineMarker.ExecutorAction;
|
||||
import com.intellij.execution.lineMarker.RunLineMarkerContributor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiIdentifier;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.util.ClassUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -43,26 +42,21 @@ public class TestRunLineMarkerProvider extends RunLineMarkerContributor {
|
||||
TestFramework framework = TestFrameworks.detectFramework((PsiClass)element);
|
||||
if (framework != null && framework.isTestClass(element)) {
|
||||
String url = "java:suite://" + ClassUtil.getJVMClassName((PsiClass)element);
|
||||
return getInfo(url, e.getProject(), true);
|
||||
TestStateStorage.Record state = TestStateStorage.getInstance(e.getProject()).getState(url);
|
||||
return getInfo(state, true);
|
||||
}
|
||||
}
|
||||
if (element instanceof PsiMethod) {
|
||||
PsiClass psiClass = PsiTreeUtil.getParentOfType(element, PsiClass.class);
|
||||
if (psiClass != null) {
|
||||
TestFramework framework = TestFrameworks.detectFramework(psiClass);
|
||||
if (framework != null && framework.isTestMethod(element)) {
|
||||
String url = "java:test://" + ClassUtil.getJVMClassName(psiClass) + "." + ((PsiMethod)element).getName();
|
||||
return getInfo(url, e.getProject(), false);
|
||||
}
|
||||
}
|
||||
TestFailedLineManager.TestInfo testInfo = TestFailedLineManager.getInstance(e.getProject()).getTestInfo((PsiMethod)element);
|
||||
return testInfo == null ? null : getInfo(testInfo.myRecord, false);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Info getInfo(String url, Project project, boolean isClass) {
|
||||
Icon icon = getTestStateIcon(url, project, isClass);
|
||||
private static Info getInfo(TestStateStorage.Record state, boolean isClass) {
|
||||
Icon icon = getTestStateIcon(state, isClass);
|
||||
return new Info(icon, ExecutorAction.getActions(1), RunLineMarkerContributor.RUN_TEST_TOOLTIP_PROVIDER);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection highlights failed method calls or assertions in tests.
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.java.codeInsight.navigation;
|
||||
|
||||
import com.intellij.execution.TestStateStorage;
|
||||
import com.intellij.execution.testframework.sm.runner.states.TestStateInfo;
|
||||
import com.intellij.execution.testframework.sm.runner.ui.TestStackTraceParser;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiMethodCallExpression;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtilBase;
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
|
||||
import com.intellij.testIntegration.TestFailedLineInspection;
|
||||
import com.intellij.testIntegration.TestFailedLineManager;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class FailedLineTest extends LightCodeInsightFixtureTestCase {
|
||||
|
||||
public void testFailedLineManager() {
|
||||
|
||||
configure();
|
||||
|
||||
PsiElement element = PsiUtilBase.getElementAtCaret(getEditor());
|
||||
PsiMethodCallExpression callExpression = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
|
||||
PsiMethod psiMethod = PsiTreeUtil.getParentOfType(callExpression, PsiMethod.class);
|
||||
TestFailedLineManager manager = TestFailedLineManager.getInstance(getProject());
|
||||
assertNotNull(manager.getTestInfo(psiMethod));
|
||||
TestStateStorage.Record record = manager.getFailedLineState(callExpression);
|
||||
assertNotNull(record);
|
||||
}
|
||||
|
||||
public void testFailedLineInspection() {
|
||||
configure();
|
||||
myFixture.enableInspections(new TestFailedLineInspection());
|
||||
myFixture.testHighlighting();
|
||||
}
|
||||
|
||||
private void configure() {
|
||||
String url = "java:test://MainTest.testFoo";
|
||||
Pair<Integer, String> pair = TestStackTraceParser.findFailLine("\tat junit.framework.Assert.fail(Assert.java:47)\n" +
|
||||
"\tat MainTest.assertEquals(Assert.java:207)\n" +
|
||||
"\tat MainTest.testFoo(MainTest.java:3)", url);
|
||||
assertEquals(3, pair.first.intValue());
|
||||
assertEquals("assertEquals", pair.second);
|
||||
TestStateStorage.getInstance(getProject())
|
||||
.writeState(url, new TestStateStorage.Record(TestStateInfo.Magnitude.FAILED_INDEX.getValue(), new Date(),
|
||||
0, pair.first, pair.second, "oops"));
|
||||
|
||||
myFixture.addClass("package junit.framework; public class TestCase {}");
|
||||
myFixture.configureByText("MainTest.java", " public class MainTest extends junit.framework.TestCase {\n" +
|
||||
" public void testFoo() {\n" +
|
||||
" <error descr=\"oops\">assertE<caret>quals()</error>;\n" +
|
||||
" assertEquals();\n" +
|
||||
" }\n" +
|
||||
" public void assertEquals() {}\n" +
|
||||
" }");
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -82,7 +82,8 @@ public class RunLineMarkerTest extends LightCodeInsightFixtureTestCase {
|
||||
TestStateStorage stateStorage = TestStateStorage.getInstance(getProject());
|
||||
String testUrl = "java:suite://Main$MainTest";
|
||||
try {
|
||||
stateStorage.writeState(testUrl, new TestStateStorage.Record(TestStateInfo.Magnitude.FAILED_INDEX.getValue(), new Date(), 0));
|
||||
stateStorage.writeState(testUrl, new TestStateStorage.Record(TestStateInfo.Magnitude.FAILED_INDEX.getValue(), new Date(), 0, 0, "",
|
||||
""));
|
||||
myFixture.addClass("package junit.framework; public class TestCase {}");
|
||||
PsiFile file = myFixture.configureByText("MainTest.java", "public class Main {\n" +
|
||||
" public class Main<caret>Test extends junit.framework.TestCase {\n" +
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.ThrowableComputable;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.newvfs.persistent.FlushingDaemon;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
@@ -45,7 +46,7 @@ public class TestStateStorage implements Disposable {
|
||||
|
||||
private static final File TEST_HISTORY_PATH = new File(PathManager.getSystemPath(), "testHistory");
|
||||
|
||||
private static final int CURRENT_VERSION = 1;
|
||||
private static final int CURRENT_VERSION = 4;
|
||||
|
||||
private final File myFile;
|
||||
|
||||
@@ -57,11 +58,17 @@ public class TestStateStorage implements Disposable {
|
||||
public final int magnitude;
|
||||
public final long configurationHash;
|
||||
public final Date date;
|
||||
public int failedLine;
|
||||
public final String failedMethod;
|
||||
public final String errorMessage;
|
||||
|
||||
public Record(int magnitude, Date date, long configurationHash) {
|
||||
public Record(int magnitude, Date date, long configurationHash, int failLine, String method, String errorMessage) {
|
||||
this.magnitude = magnitude;
|
||||
this.date = date;
|
||||
this.configurationHash = configurationHash;
|
||||
this.failedLine = failLine;
|
||||
failedMethod = method;
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,11 +112,14 @@ public class TestStateStorage implements Disposable {
|
||||
out.writeInt(value.magnitude);
|
||||
out.writeLong(value.date.getTime());
|
||||
out.writeLong(value.configurationHash);
|
||||
out.writeInt(value.failedLine);
|
||||
out.writeUTF(StringUtil.notNullize(value.failedMethod));
|
||||
out.writeUTF(StringUtil.notNullize(value.errorMessage));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Record read(@NotNull DataInput in) throws IOException {
|
||||
return new Record(in.readInt(), new Date(in.readLong()), in.readLong());
|
||||
return new Record(in.readInt(), new Date(in.readLong()), in.readLong(), in.readInt(), in.readUTF(), in.readUTF());
|
||||
}
|
||||
}, 4096, CURRENT_VERSION);
|
||||
}
|
||||
|
||||
@@ -81,6 +81,10 @@ public abstract class RunLineMarkerContributor {
|
||||
|
||||
protected static Icon getTestStateIcon(String url, Project project, boolean isClass) {
|
||||
TestStateStorage.Record state = TestStateStorage.getInstance(project).getState(url);
|
||||
return getTestStateIcon(state, isClass);
|
||||
}
|
||||
|
||||
protected static Icon getTestStateIcon(TestStateStorage.Record state, boolean isClass) {
|
||||
if (state != null) {
|
||||
TestStateInfo.Magnitude magnitude = TestIconMapper.getMagnitude(state.magnitude);
|
||||
if (magnitude != null) {
|
||||
|
||||
@@ -59,6 +59,7 @@ public class SMTestProxy extends AbstractTestProxy {
|
||||
private boolean myHasPassedTestsCached = false;
|
||||
|
||||
private String myStacktrace;
|
||||
private String myErrorMessage;
|
||||
|
||||
private boolean myIsEmptyIsCached = false; // is used for separating unknown and unset values
|
||||
private boolean myIsEmpty = true;
|
||||
@@ -138,6 +139,15 @@ public class SMTestProxy extends AbstractTestProxy {
|
||||
if (myStacktrace == null) myStacktrace = stacktrace;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getStacktrace() {
|
||||
return myStacktrace;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return myErrorMessage;
|
||||
}
|
||||
|
||||
public boolean isLeaf() {
|
||||
return myChildren == null || myChildren.isEmpty();
|
||||
}
|
||||
@@ -412,6 +422,7 @@ public class SMTestProxy extends AbstractTestProxy {
|
||||
|
||||
public void setTestFailed(@NotNull String localizedMessage, @Nullable String stackTrace, boolean testError) {
|
||||
setStacktraceIfNotSet(stackTrace);
|
||||
myErrorMessage = localizedMessage;
|
||||
TestFailedState failedState = new TestFailedState(localizedMessage, stackTrace);
|
||||
if (myState instanceof TestComparisionFailedState) {
|
||||
CompoundTestFailedState states = new CompoundTestFailedState(localizedMessage, stackTrace);
|
||||
@@ -458,6 +469,7 @@ public class SMTestProxy extends AbstractTestProxy {
|
||||
@Nullable final String expectedFilePath,
|
||||
@Nullable final String actualFilePath) {
|
||||
setStacktraceIfNotSet(stackTrace);
|
||||
myErrorMessage = localizedMessage;
|
||||
final TestComparisionFailedState comparisionFailedState = new TestComparisionFailedState(localizedMessage, stackTrace, actualText, expectedText, expectedFilePath, actualFilePath);
|
||||
if (myState instanceof CompoundTestFailedState) {
|
||||
((CompoundTestFailedState)myState).addFailure(comparisionFailedState);
|
||||
|
||||
+7
-2
@@ -51,6 +51,7 @@ import com.intellij.openapi.progress.util.ColorProgressBar;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.Pass;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
@@ -842,12 +843,16 @@ public class SMTestRunnerResultsForm extends TestResultsPanel
|
||||
String url = proxy instanceof SMTestProxy.SMRootTestProxy ? ((SMTestProxy.SMRootTestProxy)proxy).getRootLocation() : proxy.getLocationUrl();
|
||||
if (url != null) {
|
||||
String configurationName = myConfiguration != null ? myConfiguration.getName() : null;
|
||||
storage.writeState(url, new TestStateStorage.Record(proxy.getMagnitude(), new Date(),
|
||||
configurationName == null ? 0 : configurationName.hashCode()));
|
||||
Pair<Integer, String> pair = TestStackTraceParser.findFailLine(proxy.getStacktrace(), proxy.getLocationUrl());
|
||||
storage.writeState(url, new TestStateStorage.Record(proxy.getMagnitude(), new Date(),
|
||||
configurationName == null ? 0 : configurationName.hashCode(),
|
||||
pair == null ? -1 : pair.first, pair == null ? "" : pair.second,
|
||||
proxy.getErrorMessage()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
if (myOutputFile != null && myOutputFile.exists()) {
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.execution.testframework.sm.runner.ui;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TestStackTraceParser {
|
||||
|
||||
private final static Pattern outerPattern = Pattern.compile("\tat (.*)\\.([^.]*)\\((.*)\\)");
|
||||
private final static Pattern innerPattern = Pattern.compile("(.*):(\\d*)");
|
||||
|
||||
/**
|
||||
* Return line number and called method name.
|
||||
*/
|
||||
@Nullable
|
||||
public static Pair<Integer, String> findFailLine(@Nullable String stacktrace, @Nullable String url) {
|
||||
|
||||
if (stacktrace == null || url == null) return null;
|
||||
int i = url.indexOf("//");
|
||||
if (i == -1) return null;
|
||||
String path = "\tat " + url.substring(i + 2);
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new StringReader(stacktrace))) {
|
||||
String line, previous = null;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.startsWith(path)) {
|
||||
Matcher matcher = outerPattern.matcher(line);
|
||||
if (!matcher.matches()) return null;
|
||||
Matcher matcher1 = innerPattern.matcher(matcher.group(3));
|
||||
if (!matcher1.matches()) return null;
|
||||
int lineNumber = Integer.parseInt(matcher1.group(2));
|
||||
|
||||
if (previous == null) return null;
|
||||
Matcher matcher2 = outerPattern.matcher(previous);
|
||||
if (!matcher2.matches()) return null;
|
||||
return Pair.create(lineNumber, matcher2.group(2));
|
||||
}
|
||||
previous = line;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (IOException | NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user