1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.ide.eclipse.ddms.views; 17 18 import com.android.ddmlib.IDevice; 19 import com.android.ddmuilib.logcat.ILogCatMessageSelectionListener; 20 import com.android.ddmuilib.logcat.LogCatMessage; 21 import com.android.ddmuilib.logcat.LogCatPanel; 22 import com.android.ddmuilib.logcat.LogCatStackTraceParser; 23 import com.android.ide.eclipse.ddms.DdmsPlugin; 24 import com.android.ide.eclipse.ddms.i18n.Messages; 25 import com.android.ide.eclipse.ddms.preferences.PreferenceInitializer; 26 27 import org.eclipse.core.resources.IFile; 28 import org.eclipse.core.resources.IMarker; 29 import org.eclipse.core.runtime.CoreException; 30 import org.eclipse.core.runtime.NullProgressMonitor; 31 import org.eclipse.core.runtime.Status; 32 import org.eclipse.jdt.core.search.IJavaSearchConstants; 33 import org.eclipse.jdt.core.search.SearchEngine; 34 import org.eclipse.jdt.core.search.SearchMatch; 35 import org.eclipse.jdt.core.search.SearchParticipant; 36 import org.eclipse.jdt.core.search.SearchPattern; 37 import org.eclipse.jdt.core.search.SearchRequestor; 38 import org.eclipse.jface.action.Action; 39 import org.eclipse.jface.preference.IPreferenceStore; 40 import org.eclipse.swt.dnd.Clipboard; 41 import org.eclipse.swt.layout.FillLayout; 42 import org.eclipse.swt.widgets.Composite; 43 import org.eclipse.ui.IActionBars; 44 import org.eclipse.ui.IPerspectiveRegistry; 45 import org.eclipse.ui.IWorkbench; 46 import org.eclipse.ui.IWorkbenchPage; 47 import org.eclipse.ui.IWorkbenchWindow; 48 import org.eclipse.ui.PlatformUI; 49 import org.eclipse.ui.WorkbenchException; 50 import org.eclipse.ui.actions.ActionFactory; 51 import org.eclipse.ui.ide.IDE; 52 53 import java.util.HashMap; 54 import java.util.Map; 55 56 public class LogCatView extends SelectionDependentViewPart { 57 /** LogCatView ID as defined in plugin.xml. */ 58 public static final String ID = "com.android.ide.eclipse.ddms.views.LogCatView"; //$NON-NLS-1$ 59 60 /** Constant indicating that double clicking on a stack trace should 61 * open the method declaration. */ 62 public static final String CHOICE_METHOD_DECLARATION = 63 DdmsPlugin.PLUGIN_ID + ".logcat.MethodDeclaration"; //$NON-NLS-1$ 64 65 /** Constant indicating that double clicking on a stack trace should 66 * open the line at which error occurred. */ 67 public static final String CHOICE_ERROR_LINE = 68 DdmsPlugin.PLUGIN_ID + ".logcat.ErrorLine"; //$NON-NLS-1$ 69 70 /** Switch perspective when a Java file is opened from logcat view. */ 71 public static final boolean DEFAULT_SWITCH_PERSPECTIVE = true; 72 73 /** Target perspective to open when a Java file is opened from logcat view. */ 74 public static final String DEFAULT_PERSPECTIVE_ID = 75 "org.eclipse.jdt.ui.JavaPerspective"; //$NON-NLS-1$ 76 77 private LogCatPanel mLogCatPanel; 78 private LogCatStackTraceParser mStackTraceParser = new LogCatStackTraceParser(); 79 80 private Clipboard mClipboard; 81 82 @Override 83 public void createPartControl(Composite parent) { 84 parent.setLayout(new FillLayout()); 85 86 IPreferenceStore prefStore = DdmsPlugin.getDefault().getPreferenceStore(); 87 mLogCatPanel = new LogCatPanel(prefStore); 88 mLogCatPanel.createPanel(parent); 89 setSelectionDependentPanel(mLogCatPanel); 90 91 mLogCatPanel.addLogCatMessageSelectionListener(new ILogCatMessageSelectionListener() { 92 public void messageDoubleClicked(LogCatMessage m) { 93 onDoubleClick(m); 94 } 95 }); 96 97 mClipboard = new Clipboard(parent.getDisplay()); 98 IActionBars actionBars = getViewSite().getActionBars(); 99 actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), 100 new Action(Messages.LogCatView_Copy) { 101 @Override 102 public void run() { 103 mLogCatPanel.copySelectionToClipboard(mClipboard); 104 } 105 }); 106 107 actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), 108 new Action(Messages.LogCatView_Select_All) { 109 @Override 110 public void run() { 111 mLogCatPanel.selectAll(); 112 } 113 }); 114 } 115 116 @Override 117 public void setFocus() { 118 } 119 120 /** 121 * This class defines what to do with the search match returned by a 122 * double-click or by the Go to Problem action. 123 */ 124 private class LogCatViewSearchRequestor extends SearchRequestor { 125 private boolean mFoundFirstMatch = false; 126 private String mChoice; 127 private int mLineNumber; 128 129 public LogCatViewSearchRequestor(String choice, int lineNumber) { 130 super(); 131 mChoice = choice; 132 mLineNumber = lineNumber; 133 } 134 135 IMarker createMarkerFromSearchMatch(IFile file, SearchMatch match) { 136 IMarker marker = null; 137 try { 138 if (CHOICE_METHOD_DECLARATION.equals(mChoice)) { 139 Map<String, Object> attrs = new HashMap<String, Object>(); 140 attrs.put(IMarker.CHAR_START, Integer.valueOf(match.getOffset())); 141 attrs.put(IMarker.CHAR_END, Integer.valueOf(match.getOffset() 142 + match.getLength())); 143 marker = file.createMarker(IMarker.TEXT); 144 marker.setAttributes(attrs); 145 } else if (CHOICE_ERROR_LINE.equals(mChoice)) { 146 marker = file.createMarker(IMarker.TEXT); 147 marker.setAttribute(IMarker.LINE_NUMBER, mLineNumber); 148 } 149 } catch (CoreException e) { 150 Status s = new Status(Status.ERROR, DdmsPlugin.PLUGIN_ID, e.getMessage(), e); 151 DdmsPlugin.getDefault().getLog().log(s); 152 } 153 return marker; 154 } 155 156 @Override 157 public void acceptSearchMatch(SearchMatch match) throws CoreException { 158 if (match.getResource() instanceof IFile && !mFoundFirstMatch) { 159 mFoundFirstMatch = true; 160 IFile matchedFile = (IFile) match.getResource(); 161 IMarker marker = createMarkerFromSearchMatch(matchedFile, match); 162 // There should only be one exact match, 163 // so we go immediately to that one. 164 if (marker != null) { 165 switchPerspective(); 166 showMarker(marker); 167 } 168 } 169 } 170 } 171 172 /** 173 * Switch to perspective specified by user when opening a source file. 174 * User preferences control whether the perspective should be switched, 175 * and if so, what the target perspective is. 176 */ 177 private void switchPerspective() { 178 IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore(); 179 if (store.getBoolean(PreferenceInitializer.ATTR_SWITCH_PERSPECTIVE)) { 180 IWorkbench workbench = PlatformUI.getWorkbench(); 181 IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); 182 IPerspectiveRegistry perspectiveRegistry = workbench.getPerspectiveRegistry(); 183 String perspectiveId = store.getString(PreferenceInitializer.ATTR_PERSPECTIVE_ID); 184 if (perspectiveId != null 185 && perspectiveId.length() > 0 186 && perspectiveRegistry.findPerspectiveWithId(perspectiveId) != null) { 187 try { 188 workbench.showPerspective(perspectiveId, window); 189 } catch (WorkbenchException e) { 190 Status s = new Status(Status.ERROR, DdmsPlugin.PLUGIN_ID, e.getMessage(), e); 191 DdmsPlugin.getDefault().getLog().log(s); 192 } 193 } 194 } 195 } 196 197 private void showMarker(IMarker marker) { 198 try { 199 IWorkbenchPage page = getViewSite().getWorkbenchWindow() 200 .getActivePage(); 201 if (page != null) { 202 IDE.openEditor(page, marker); 203 marker.delete(); 204 } 205 } catch (CoreException e) { 206 Status s = new Status(Status.ERROR, DdmsPlugin.PLUGIN_ID, e.getMessage(), e); 207 DdmsPlugin.getDefault().getLog().log(s); 208 } 209 } 210 211 private void onDoubleClick(LogCatMessage m) { 212 String msg = m.getMessage(); 213 if (!mStackTraceParser.isValidExceptionTrace(msg)) { 214 return; 215 } 216 217 String methodName = mStackTraceParser.getMethodName(msg); 218 String fileName = mStackTraceParser.getFileName(msg); 219 int lineNumber = mStackTraceParser.getLineNumber(msg); 220 221 IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore(); 222 String jumpToLocation = store.getString(PreferenceInitializer.ATTR_LOGCAT_GOTO_PROBLEM); 223 224 String stringPattern = methodName; 225 LogCatViewSearchRequestor requestor = 226 new LogCatViewSearchRequestor(CHOICE_METHOD_DECLARATION, 0); 227 int searchFor = IJavaSearchConstants.METHOD; 228 if (jumpToLocation.equals(CHOICE_ERROR_LINE)) { 229 searchFor = IJavaSearchConstants.CLASS; 230 stringPattern = fileName; 231 requestor = new LogCatViewSearchRequestor(CHOICE_ERROR_LINE, lineNumber); 232 } 233 234 SearchEngine se = new SearchEngine(); 235 SearchPattern searchPattern = SearchPattern.createPattern(stringPattern, 236 searchFor, 237 IJavaSearchConstants.DECLARATIONS, 238 SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE); 239 try { 240 se.search(searchPattern, 241 new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()}, 242 SearchEngine.createWorkspaceScope(), 243 requestor, 244 new NullProgressMonitor()); 245 } catch (CoreException e) { 246 Status s = new Status(Status.ERROR, DdmsPlugin.PLUGIN_ID, e.getMessage(), e); 247 DdmsPlugin.getDefault().getLog().log(s); 248 } 249 } 250 251 public void selectTransientAppFilter(String appName) { 252 mLogCatPanel.selectTransientAppFilter(appName); 253 } 254 } 255