1 /* 2 * Copyright (C) 2007 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 17 package com.android.ide.eclipse.ddms.views; 18 19 import com.android.ddmlib.Log.LogLevel; 20 import com.android.ddmuilib.logcat.LogColors; 21 import com.android.ddmuilib.logcat.LogFilter; 22 import com.android.ddmuilib.logcat.LogPanel; 23 import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager; 24 import com.android.ide.eclipse.ddms.CommonAction; 25 import com.android.ide.eclipse.ddms.DdmsPlugin; 26 import com.android.ide.eclipse.ddms.ImageLoader; 27 import com.android.ide.eclipse.ddms.preferences.PreferenceInitializer; 28 29 import org.eclipse.core.resources.IFile; 30 import org.eclipse.core.resources.IMarker; 31 import org.eclipse.core.runtime.CoreException; 32 import org.eclipse.core.runtime.NullProgressMonitor; 33 import org.eclipse.core.runtime.Status; 34 import org.eclipse.jdt.core.search.IJavaSearchConstants; 35 import org.eclipse.jdt.core.search.SearchEngine; 36 import org.eclipse.jdt.core.search.SearchMatch; 37 import org.eclipse.jdt.core.search.SearchParticipant; 38 import org.eclipse.jdt.core.search.SearchPattern; 39 import org.eclipse.jdt.core.search.SearchRequestor; 40 import org.eclipse.jface.action.Action; 41 import org.eclipse.jface.action.IAction; 42 import org.eclipse.jface.action.IMenuManager; 43 import org.eclipse.jface.action.IToolBarManager; 44 import org.eclipse.jface.action.Separator; 45 import org.eclipse.swt.dnd.Clipboard; 46 import org.eclipse.swt.graphics.Color; 47 import org.eclipse.swt.graphics.Font; 48 import org.eclipse.swt.graphics.FontData; 49 import org.eclipse.swt.widgets.Composite; 50 import org.eclipse.swt.widgets.Display; 51 import org.eclipse.ui.IActionBars; 52 import org.eclipse.ui.IPerspectiveRegistry; 53 import org.eclipse.ui.IWorkbenchPage; 54 import org.eclipse.ui.IWorkbenchWindow; 55 import org.eclipse.ui.actions.ActionFactory; 56 import org.eclipse.ui.ide.IDE; 57 import org.eclipse.ui.internal.WorkbenchPlugin; 58 import org.eclipse.ui.internal.registry.PerspectiveDescriptor; 59 60 import java.util.ArrayList; 61 import java.util.HashMap; 62 import java.util.regex.Matcher; 63 import java.util.regex.Pattern; 64 65 /** 66 * The log cat view displays log output from the current device selection. 67 * 68 */ 69 public final class LogCatView extends SelectionDependentViewPart { 70 71 public static final String ID = 72 "com.android.ide.eclipse.ddms.views.LogCatView"; // $NON-NLS-1$ 73 74 private static final String PREFS_COL_TIME = 75 DdmsPlugin.PLUGIN_ID + ".logcat.time"; // $NON-NLS-1$ 76 private static final String PREFS_COL_LEVEL = 77 DdmsPlugin.PLUGIN_ID + ".logcat.level"; // $NON-NLS-1$ 78 private static final String PREFS_COL_PID = 79 DdmsPlugin.PLUGIN_ID + ".logcat.pid"; // $NON-NLS-1$ 80 private static final String PREFS_COL_TAG = 81 DdmsPlugin.PLUGIN_ID + ".logcat.tag"; // $NON-NLS-1$ 82 private static final String PREFS_COL_MESSAGE = 83 DdmsPlugin.PLUGIN_ID + ".logcat.message"; // $NON-NLS-1$ 84 85 private static final String PREFS_FILTERS = 86 DdmsPlugin.PLUGIN_ID + ".logcat.filters"; // $NON-NLS-1$ 87 88 private static LogCatView sThis; 89 private LogPanel mLogPanel; 90 91 private CommonAction mCreateFilterAction; 92 private CommonAction mDeleteFilterAction; 93 private CommonAction mEditFilterAction; 94 private CommonAction mExportAction; 95 private CommonAction gotoLineAction; 96 97 private CommonAction[] mLogLevelActions; 98 private String[] mLogLevelIcons = { 99 "v.png", //$NON-NLS-1S 100 "d.png", //$NON-NLS-1S 101 "i.png", //$NON-NLS-1S 102 "w.png", //$NON-NLS-1S 103 "e.png", //$NON-NLS-1S 104 }; 105 106 private Action mClearAction; 107 108 private Clipboard mClipboard; 109 110 /** 111 * An implementation of {@link ILogFilterStorageManager} to bridge to the eclipse preference 112 * store, and saves the log filters. 113 */ 114 private final class FilterStorage implements ILogFilterStorageManager { 115 116 public LogFilter[] getFilterFromStore() { 117 String filterPrefs = DdmsPlugin.getDefault().getPreferenceStore().getString( 118 PREFS_FILTERS); 119 120 // split in a string per filter 121 String[] filters = filterPrefs.split("\\|"); // $NON-NLS-1$ 122 123 ArrayList<LogFilter> list = 124 new ArrayList<LogFilter>(filters.length); 125 126 for (String f : filters) { 127 if (f.length() > 0) { 128 LogFilter logFilter = new LogFilter(); 129 if (logFilter.loadFromString(f)) { 130 list.add(logFilter); 131 } 132 } 133 } 134 135 return list.toArray(new LogFilter[list.size()]); 136 } 137 138 public void saveFilters(LogFilter[] filters) { 139 StringBuilder sb = new StringBuilder(); 140 for (LogFilter f : filters) { 141 String filterString = f.toString(); 142 sb.append(filterString); 143 sb.append('|'); 144 } 145 146 DdmsPlugin.getDefault().getPreferenceStore().setValue(PREFS_FILTERS, sb.toString()); 147 } 148 149 public boolean requiresDefaultFilter() { 150 return true; 151 } 152 } 153 154 public LogCatView() { 155 sThis = this; 156 LogPanel.PREFS_TIME = PREFS_COL_TIME; 157 LogPanel.PREFS_LEVEL = PREFS_COL_LEVEL; 158 LogPanel.PREFS_PID = PREFS_COL_PID; 159 LogPanel.PREFS_TAG = PREFS_COL_TAG; 160 LogPanel.PREFS_MESSAGE = PREFS_COL_MESSAGE; 161 } 162 163 /** 164 * Returns the singleton instance. 165 */ 166 public static LogCatView getInstance() { 167 return sThis; 168 } 169 170 /** 171 * Sets the display font. 172 * @param font The font. 173 */ 174 public static void setFont(Font font) { 175 if (sThis != null && sThis.mLogPanel != null) { 176 sThis.mLogPanel.setFont(font); 177 } 178 } 179 180 @Override 181 public void createPartControl(Composite parent) { 182 Display d = parent.getDisplay(); 183 LogColors colors = new LogColors(); 184 185 ImageLoader loader = DdmsPlugin.getImageLoader(); 186 187 colors.infoColor = new Color(d, 0, 127, 0); 188 colors.debugColor = new Color(d, 0, 0, 127); 189 colors.errorColor = new Color(d, 255, 0, 0); 190 colors.warningColor = new Color(d, 255, 127, 0); 191 colors.verboseColor = new Color(d, 0, 0, 0); 192 193 mCreateFilterAction = new CommonAction("Create Filter") { 194 @Override 195 public void run() { 196 mLogPanel.addFilter(); 197 } 198 }; 199 mCreateFilterAction.setToolTipText("Create Filter"); 200 mCreateFilterAction.setImageDescriptor(loader 201 .loadDescriptor("add.png")); // $NON-NLS-1$ 202 203 mEditFilterAction = new CommonAction("Edit Filter") { 204 @Override 205 public void run() { 206 mLogPanel.editFilter(); 207 } 208 }; 209 mEditFilterAction.setToolTipText("Edit Filter"); 210 mEditFilterAction.setImageDescriptor(loader 211 .loadDescriptor("edit.png")); // $NON-NLS-1$ 212 213 mDeleteFilterAction = new CommonAction("Delete Filter") { 214 @Override 215 public void run() { 216 mLogPanel.deleteFilter(); 217 } 218 }; 219 mDeleteFilterAction.setToolTipText("Delete Filter"); 220 mDeleteFilterAction.setImageDescriptor(loader 221 .loadDescriptor("delete.png")); // $NON-NLS-1$ 222 223 mExportAction = new CommonAction("Export Selection As Text...") { 224 @Override 225 public void run() { 226 mLogPanel.save(); 227 } 228 }; 229 mExportAction.setToolTipText("Export Selection As Text..."); 230 mExportAction.setImageDescriptor(loader.loadDescriptor("save.png")); // $NON-NLS-1$ 231 232 gotoLineAction = new CommonAction("Go to Problem") { 233 @Override 234 public void run() { 235 goToErrorLine(); 236 } 237 }; 238 239 LogLevel[] levels = LogLevel.values(); 240 mLogLevelActions = new CommonAction[mLogLevelIcons.length]; 241 for (int i = 0 ; i < mLogLevelActions.length; i++) { 242 String name = levels[i].getStringValue(); 243 mLogLevelActions[i] = new CommonAction(name, IAction.AS_CHECK_BOX) { 244 @Override 245 public void run() { 246 // disable the other actions and record current index 247 for (int i = 0 ; i < mLogLevelActions.length; i++) { 248 Action a = mLogLevelActions[i]; 249 if (a == this) { 250 a.setChecked(true); 251 252 // set the log level 253 mLogPanel.setCurrentFilterLogLevel(i+2); 254 } else { 255 a.setChecked(false); 256 } 257 } 258 } 259 }; 260 261 mLogLevelActions[i].setToolTipText(name); 262 mLogLevelActions[i].setImageDescriptor(loader.loadDescriptor(mLogLevelIcons[i])); 263 } 264 265 mClearAction = new Action("Clear Log") { 266 @Override 267 public void run() { 268 mLogPanel.clear(); 269 } 270 }; 271 mClearAction.setImageDescriptor(loader 272 .loadDescriptor("clear.png")); // $NON-NLS-1$ 273 274 275 // now create the log view 276 mLogPanel = new LogPanel(loader, colors, new FilterStorage(), LogPanel.FILTER_MANUAL); 277 mLogPanel.setActions(mDeleteFilterAction, mEditFilterAction, mLogLevelActions); 278 279 // get the font 280 String fontStr = DdmsPlugin.getDefault().getPreferenceStore().getString( 281 PreferenceInitializer.ATTR_LOGCAT_FONT); 282 if (fontStr != null) { 283 FontData data = new FontData(fontStr); 284 285 if (fontStr != null) { 286 mLogPanel.setFont(new Font(parent.getDisplay(), data)); 287 } 288 } 289 290 mLogPanel.createPanel(parent); 291 setSelectionDependentPanel(mLogPanel); 292 293 // place the actions. 294 placeActions(); 295 296 // setup the copy action 297 mClipboard = new Clipboard(d); 298 IActionBars actionBars = getViewSite().getActionBars(); 299 actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), new Action("Copy") { 300 @Override 301 public void run() { 302 mLogPanel.copy(mClipboard); 303 } 304 }); 305 306 // setup the select all action 307 actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), 308 new Action("Select All") { 309 @Override 310 public void run() { 311 mLogPanel.selectAll(); 312 } 313 }); 314 } 315 316 @Override 317 public void dispose() { 318 mLogPanel.stopLogCat(true); 319 mClipboard.dispose(); 320 } 321 322 @Override 323 public void setFocus() { 324 mLogPanel.setFocus(); 325 } 326 327 /** 328 * Place the actions in the ui. 329 */ 330 private void placeActions() { 331 IActionBars actionBars = getViewSite().getActionBars(); 332 333 // first in the menu 334 IMenuManager menuManager = actionBars.getMenuManager(); 335 menuManager.add(mCreateFilterAction); 336 menuManager.add(mEditFilterAction); 337 menuManager.add(mDeleteFilterAction); 338 menuManager.add(new Separator()); 339 menuManager.add(mClearAction); 340 menuManager.add(new Separator()); 341 menuManager.add(mExportAction); 342 menuManager.add(gotoLineAction); 343 344 // and then in the toolbar 345 IToolBarManager toolBarManager = actionBars.getToolBarManager(); 346 for (CommonAction a : mLogLevelActions) { 347 toolBarManager.add(a); 348 } 349 toolBarManager.add(new Separator()); 350 toolBarManager.add(mCreateFilterAction); 351 toolBarManager.add(mEditFilterAction); 352 toolBarManager.add(mDeleteFilterAction); 353 toolBarManager.add(new Separator()); 354 toolBarManager.add(mClearAction); 355 } 356 357 IMarker createMarkerFromSearchMatch(IFile file, SearchMatch match) { 358 HashMap<String, Object> map = new HashMap<String, Object>(); 359 map.put(IMarker.CHAR_START, new Integer(match.getOffset())); 360 map.put(IMarker.CHAR_END, new Integer(match.getOffset() 361 + match.getLength())); 362 IMarker marker = null; 363 try { 364 marker = file.createMarker(IMarker.TEXT); 365 marker.setAttributes(map); 366 } catch (CoreException e) { 367 Status s = new Status(Status.ERROR, DdmsPlugin.PLUGIN_ID, e.getMessage(), e); 368 DdmsPlugin.getDefault().getLog().log(s); 369 } 370 return marker; 371 } 372 373 void openFile(IFile file, IMarker marker) { 374 try { 375 IWorkbenchPage page = getViewSite().getWorkbenchWindow() 376 .getActivePage(); 377 if (page != null) { 378 IDE.openEditor(page, marker); 379 marker.delete(); 380 } 381 } catch (CoreException e) { 382 Status s = new Status(Status.ERROR, DdmsPlugin.PLUGIN_ID, e.getMessage(), e); 383 DdmsPlugin.getDefault().getLog().log(s); 384 } 385 } 386 387 void switchPerspective() { 388 389 IWorkbenchWindow window = getViewSite().getWorkbenchWindow() 390 .getWorkbench().getActiveWorkbenchWindow(); 391 String rtPerspectiveId = "org.eclipse.jdt.ui.JavaPerspective"; 392 IPerspectiveRegistry reg = WorkbenchPlugin.getDefault() 393 .getPerspectiveRegistry(); 394 PerspectiveDescriptor rtPerspectiveDesc = (PerspectiveDescriptor) reg 395 .findPerspectiveWithId(rtPerspectiveId); 396 if (window != null) { 397 IWorkbenchPage page = window.getActivePage(); 398 page.setPerspective(rtPerspectiveDesc); 399 } 400 } 401 402 void goToErrorLine() { 403 try { 404 String msg = mLogPanel.getSelectedErrorLineMessage(); 405 if (msg != null) { 406 String error_line_matcher_string = "\\s*at\\ (.*)\\((.*\\.java)\\:(\\d+)\\)"; 407 Matcher error_line_matcher = Pattern.compile( 408 error_line_matcher_string).matcher(msg); 409 410 if (error_line_matcher.find()) { 411 String class_name = error_line_matcher.group(1); 412 413 // TODO: Search currently only matches the class declaration (using 414 // IJavaSearchConstants.DECLARATIONS). We may want to jump to the 415 // "reference" of the class instead (IJavaSearchConstants.REFERENCES) 416 // using the filename and line number to disambiguate the search results. 417 // String filename = error_line_matcher.group(2); 418 // int line_number = Integer.parseInt(error_line_matcher.group(3)); 419 420 SearchEngine se = new SearchEngine(); 421 se.search(SearchPattern.createPattern(class_name, 422 IJavaSearchConstants.METHOD, 423 IJavaSearchConstants.DECLARATIONS, 424 SearchPattern.R_EXACT_MATCH 425 | SearchPattern.R_CASE_SENSITIVE), 426 new SearchParticipant[] { SearchEngine 427 .getDefaultSearchParticipant() }, 428 SearchEngine.createWorkspaceScope(), 429 new SearchRequestor() { 430 boolean found_first_match = false; 431 432 @Override 433 public void acceptSearchMatch( 434 SearchMatch match) 435 throws CoreException { 436 437 if (match.getResource() instanceof IFile 438 && !found_first_match) { 439 found_first_match = true; 440 441 IFile matched_file = (IFile) match 442 .getResource(); 443 IMarker marker = createMarkerFromSearchMatch( 444 matched_file, match); 445 446 // There should only be one exact match, 447 // so we go immediately to that one. 448 switchPerspective(); 449 openFile(matched_file, marker); 450 } 451 } 452 }, new NullProgressMonitor()); 453 454 } 455 } 456 } catch (Exception e) { 457 Status s = new Status(Status.ERROR, DdmsPlugin.PLUGIN_ID, e.getMessage(), e); 458 DdmsPlugin.getDefault().getLog().log(s); 459 } 460 } 461 } 462 463