From 50192fe103eacf358a399358f9945944fc142e44 Mon Sep 17 00:00:00 2001 From: Siva Velusamy Date: Tue, 16 Aug 2011 17:33:17 -0700 Subject: Goto source when user double clicks on stack trace. Change-Id: Ia2693424f0106fb3aa1724c211a8e1273f3a0c28 --- .../logcat/ILogCatMessageSelectionListener.java | 24 +++++++ .../com/android/ddmuilib/logcat/LogCatPanel.java | 29 ++++++++ .../ddmuilib/logcat/LogCatStackTraceParser.java | 81 ++++++++++++++++++++++ .../logcat/LogCatStackTraceParserTest.java | 54 +++++++++++++++ 4 files changed, 188 insertions(+) create mode 100644 ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/ILogCatMessageSelectionListener.java create mode 100644 ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatStackTraceParser.java create mode 100644 ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatStackTraceParserTest.java (limited to 'ddms') diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/ILogCatMessageSelectionListener.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/ILogCatMessageSelectionListener.java new file mode 100644 index 0000000..6e814b0 --- /dev/null +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/ILogCatMessageSelectionListener.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed 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 com.android.ddmuilib.logcat; + +/** + * Classes interested in listening to user selection of logcat + * messages should implement this interface. + */ +public interface ILogCatMessageSelectionListener { + void messageDoubleClicked(LogCatMessage m); +} diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java index 9a598c0..83a0e2f 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatPanel.java @@ -548,6 +548,8 @@ public final class LogCatPanel extends SelectionDependentPanel mViewer.setLabelProvider(mLogCatMessageLabelProvider); mViewer.setContentProvider(new LogCatMessageContentProvider()); mViewer.setInput(mReceiver.getMessages()); + + initDoubleClickListener(); } private String getColPreferenceKey(String field) { @@ -660,6 +662,9 @@ public final class LogCatPanel extends SelectionDependentPanel private void refreshFiltersTable() { Display.getDefault().asyncExec(new Runnable() { public void run() { + if (mFiltersTableViewer.getTable().isDisposed()) { + return; + } mFiltersTableViewer.refresh(); } }); @@ -704,4 +709,28 @@ public final class LogCatPanel extends SelectionDependentPanel return sb.getSelection() + sb.getThumb() == sb.getMaximum(); } + + private List mMessageSelectionListeners; + + private void initDoubleClickListener() { + mMessageSelectionListeners = new ArrayList(1); + + mViewer.getTable().addSelectionListener(new SelectionAdapter() { + @Override + public void widgetDefaultSelected(SelectionEvent arg0) { + List selectedMessages = getSelectedLogCatMessages(); + if (selectedMessages.size() == 0) { + return; + } + + for (ILogCatMessageSelectionListener l : mMessageSelectionListeners) { + l.messageDoubleClicked(selectedMessages.get(0)); + } + } + }); + } + + public void addLogCatMessageSelectionListener(ILogCatMessageSelectionListener l) { + mMessageSelectionListeners.add(l); + } } diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatStackTraceParser.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatStackTraceParser.java new file mode 100644 index 0000000..3da9fd0 --- /dev/null +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/logcat/LogCatStackTraceParser.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed 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 com.android.ddmuilib.logcat; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Helper class that can determine if a string matches the exception + * stack trace pattern, and if so, can provide the java source file + * and line where the exception occured. + */ +public final class LogCatStackTraceParser { + /** Regex to match a stack trace line. E.g.: + * at com.foo.Class.method(FileName.extension:10) + * extension is typically java, but can be anything (java/groovy/scala/..). + */ + private static final String EXCEPTION_LINE_REGEX = + "\\s*at\\ (.*)\\((.*)\\..*\\:(\\d+)\\)"; //$NON-NLS-1$ + + private static final Pattern EXCEPTION_LINE_PATTERN = + Pattern.compile(EXCEPTION_LINE_REGEX); + + /** + * Identify if a input line matches the expected pattern + * for a stack trace from an exception. + */ + public boolean isValidExceptionTrace(String line) { + return EXCEPTION_LINE_PATTERN.matcher(line).find(); + } + + /** + * Get fully qualified method name that threw the exception. + * @param line line from the stack trace, must have been validated with + * {@link LogCatStackTraceParser#isValidExceptionTrace(String)} before calling this method. + * @return fully qualified method name + */ + public String getMethodName(String line) { + Matcher m = EXCEPTION_LINE_PATTERN.matcher(line); + m.find(); + return m.group(1); + } + + /** + * Get source file name where exception was generated. Input line must be first validated with + * {@link LogCatStackTraceParser#isValidExceptionTrace(String)}. + */ + public String getFileName(String line) { + Matcher m = EXCEPTION_LINE_PATTERN.matcher(line); + m.find(); + return m.group(2); + } + + /** + * Get line number where exception was generated. Input line must be first validated with + * {@link LogCatStackTraceParser#isValidExceptionTrace(String)}. + */ + public int getLineNumber(String line) { + Matcher m = EXCEPTION_LINE_PATTERN.matcher(line); + m.find(); + try { + return Integer.parseInt(m.group(3)); + } catch (NumberFormatException e) { + return 0; + } + } + +} diff --git a/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatStackTraceParserTest.java b/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatStackTraceParserTest.java new file mode 100644 index 0000000..7d9869a --- /dev/null +++ b/ddms/libs/ddmuilib/tests/src/com/android/ddmuilib/logcat/LogCatStackTraceParserTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed 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 com.android.ddmuilib.logcat; + +import junit.framework.TestCase; + +public class LogCatStackTraceParserTest extends TestCase { + private LogCatStackTraceParser mTranslator; + + private static final String SAMPLE_METHOD = "com.foo.Class.method"; //$NON-NLS-1$ + private static final String SAMPLE_FNAME = "FileName"; //$NON-NLS-1$ + private static final int SAMPLE_LINENUM = 20; + private static final String SAMPLE_TRACE = + String.format(" at %s(%s.groovy:%d)", //$NON-NLS-1$ + SAMPLE_METHOD, SAMPLE_FNAME, SAMPLE_LINENUM); + + @Override + protected void setUp() throws Exception { + mTranslator = new LogCatStackTraceParser(); + } + + public void testIsValidExceptionTrace() { + assertTrue(mTranslator.isValidExceptionTrace(SAMPLE_TRACE)); + assertFalse(mTranslator.isValidExceptionTrace( + "java.lang.RuntimeException: message")); //$NON-NLS-1$ + assertFalse(mTranslator.isValidExceptionTrace( + "at com.foo.test(Ins.java:unknown)")); //$NON-NLS-1$ + } + + public void testGetMethodName() { + assertEquals(SAMPLE_METHOD, mTranslator.getMethodName(SAMPLE_TRACE)); + } + + public void testGetFileName() { + assertEquals(SAMPLE_FNAME, mTranslator.getFileName(SAMPLE_TRACE)); + } + + public void testGetLineNumber() { + assertEquals(SAMPLE_LINENUM, mTranslator.getLineNumber(SAMPLE_TRACE)); + } +} -- cgit v1.1