1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.message.ui;
14 import java.io.BufferedReader;
15 import java.io.BufferedWriter;
17 import java.io.FileInputStream;
18 import java.io.FileOutputStream;
19 import java.io.IOException;
20 import java.io.InputStreamReader;
21 import java.io.OutputStreamWriter;
22 import java.io.PrintWriter;
23 import java.io.StringWriter;
24 import java.lang.reflect.InvocationTargetException;
26 import java.net.URISyntaxException;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collections;
30 import java.util.Comparator;
31 import java.util.Date;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.List;
37 import org.eclipse.core.runtime.IProgressMonitor;
38 import org.eclipse.core.runtime.IStatus;
39 import org.eclipse.core.runtime.MultiStatus;
40 import org.eclipse.core.runtime.Path;
41 import org.eclipse.core.runtime.Preferences;
42 import org.eclipse.core.runtime.Status;
43 import org.eclipse.core.runtime.jobs.Job;
44 import org.eclipse.jface.action.Action;
45 import org.eclipse.jface.action.IAction;
46 import org.eclipse.jface.action.IContributionItem;
47 import org.eclipse.jface.action.IMenuListener;
48 import org.eclipse.jface.action.IMenuManager;
49 import org.eclipse.jface.action.IStatusLineManager;
50 import org.eclipse.jface.action.IToolBarManager;
51 import org.eclipse.jface.action.MenuManager;
52 import org.eclipse.jface.action.Separator;
53 import org.eclipse.jface.dialogs.IDialogSettings;
54 import org.eclipse.jface.dialogs.MessageDialog;
55 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
56 import org.eclipse.jface.operation.IRunnableWithProgress;
57 import org.eclipse.jface.resource.ImageDescriptor;
58 import org.eclipse.jface.resource.JFaceResources;
59 import org.eclipse.jface.resource.LocalResourceManager;
60 import org.eclipse.jface.util.Policy;
61 import org.eclipse.jface.viewers.DoubleClickEvent;
62 import org.eclipse.jface.viewers.IDoubleClickListener;
63 import org.eclipse.jface.viewers.ISelection;
64 import org.eclipse.jface.viewers.ISelectionChangedListener;
65 import org.eclipse.jface.viewers.IStructuredSelection;
66 import org.eclipse.jface.viewers.SelectionChangedEvent;
67 import org.eclipse.jface.viewers.TreeSelection;
68 import org.eclipse.jface.viewers.Viewer;
69 import org.eclipse.jface.viewers.ViewerComparator;
70 import org.eclipse.jface.window.Window;
71 import org.eclipse.osgi.util.NLS;
72 import org.eclipse.swt.SWT;
73 import org.eclipse.swt.SWTException;
74 import org.eclipse.swt.browser.Browser;
75 import org.eclipse.swt.browser.LocationEvent;
76 import org.eclipse.swt.browser.LocationListener;
77 import org.eclipse.swt.custom.BusyIndicator;
78 import org.eclipse.swt.custom.SashForm;
79 import org.eclipse.swt.dnd.Clipboard;
80 import org.eclipse.swt.dnd.DND;
81 import org.eclipse.swt.dnd.DragSource;
82 import org.eclipse.swt.dnd.DragSourceAdapter;
83 import org.eclipse.swt.dnd.DragSourceEvent;
84 import org.eclipse.swt.dnd.TextTransfer;
85 import org.eclipse.swt.dnd.Transfer;
86 import org.eclipse.swt.events.ControlAdapter;
87 import org.eclipse.swt.events.ControlEvent;
88 import org.eclipse.swt.events.DisposeEvent;
89 import org.eclipse.swt.events.DisposeListener;
90 import org.eclipse.swt.events.SelectionAdapter;
91 import org.eclipse.swt.events.SelectionEvent;
92 import org.eclipse.swt.graphics.Color;
93 import org.eclipse.swt.graphics.Image;
94 import org.eclipse.swt.graphics.Point;
95 import org.eclipse.swt.graphics.Rectangle;
96 import org.eclipse.swt.layout.GridData;
97 import org.eclipse.swt.layout.GridLayout;
98 import org.eclipse.swt.program.Program;
99 import org.eclipse.swt.widgets.Composite;
100 import org.eclipse.swt.widgets.Display;
101 import org.eclipse.swt.widgets.Event;
102 import org.eclipse.swt.widgets.FileDialog;
103 import org.eclipse.swt.widgets.Listener;
104 import org.eclipse.swt.widgets.Menu;
105 import org.eclipse.swt.widgets.Shell;
106 import org.eclipse.swt.widgets.Text;
107 import org.eclipse.swt.widgets.Tree;
108 import org.eclipse.swt.widgets.TreeColumn;
109 import org.eclipse.swt.widgets.TreeItem;
110 import org.eclipse.ui.IActionBars;
111 import org.eclipse.ui.IMemento;
112 import org.eclipse.ui.IPerspectiveDescriptor;
113 import org.eclipse.ui.IPerspectiveListener2;
114 import org.eclipse.ui.ISharedImages;
115 import org.eclipse.ui.IViewReference;
116 import org.eclipse.ui.IViewSite;
117 import org.eclipse.ui.IWorkbenchActionConstants;
118 import org.eclipse.ui.IWorkbenchPage;
119 import org.eclipse.ui.IWorkbenchPart;
120 import org.eclipse.ui.IWorkbenchPartReference;
121 import org.eclipse.ui.PartInitException;
122 import org.eclipse.ui.PlatformUI;
123 import org.eclipse.ui.XMLMemento;
124 import org.eclipse.ui.actions.ActionFactory;
125 import org.eclipse.ui.dialogs.FilteredTree;
126 import org.eclipse.ui.dialogs.PatternFilter;
127 import org.eclipse.ui.part.ViewPart;
128 import org.simantics.message.DetailStatus;
129 import org.simantics.message.ILogListener;
130 import org.simantics.message.IMessageDataSchemeExtension;
131 import org.simantics.message.IMessageSchemeManager;
132 import org.simantics.message.MessageSchemeException;
133 import org.simantics.message.MessageService;
134 import org.simantics.message.ReferenceSerializationException;
135 import org.simantics.message.util.HtmlUtil;
136 import org.simantics.utils.ui.ErrorLogger;
138 import com.ibm.icu.text.DateFormat;
139 import com.ibm.icu.text.SimpleDateFormat;
142 * A direct rip of Eclipse's own <code>LogView</code> for our own purposes.
145 * The only thing that has changed is the the dependence on
146 * <code>Activator.getLogFile()</code> instead of
147 * <code>Platform.getLogFileLocation()</code>.
149 * @author Tuukka Lehtonen
150 * @see org.eclipse.ui.internal.views.LogView
152 @SuppressWarnings("deprecation")
153 public class LogView extends ViewPart implements ILogListener {
155 public static final int DEFAULT_EXPAND_LEVEL = 1;
157 public static final String P_LOG_WARNING = "warning"; //$NON-NLS-1$
158 public static final String P_LOG_ERROR = "error"; //$NON-NLS-1$
159 public static final String P_LOG_INFO = "info"; //$NON-NLS-1$
160 public static final String P_LOG_DEBUG = "debug"; //$NON-NLS-1$
161 public static final String P_LOG_LIMIT = "limit"; //$NON-NLS-1$
162 public static final String P_EXPAND_LEVEL = "expandLevel"; //$NON-NLS-1$
163 public static final String P_USE_LIMIT = "useLimit"; //$NON-NLS-1$
164 public static final String P_SHOW_ALL_SESSIONS = "allSessions"; //$NON-NLS-1$
165 private static final String P_COLUMN_1 = "column2"; //$NON-NLS-1$
166 private static final String P_COLUMN_2 = "column3"; //$NON-NLS-1$
167 private static final String P_COLUMN_3 = "column4"; //$NON-NLS-1$
168 public static final String P_ACTIVATE = "activate"; //$NON-NLS-1$
169 public static final String P_SHOW_FILTER_TEXT = "show_filter_text"; //$NON-NLS-1$
170 public static final String P_ORDER_TYPE = "orderType"; //$NON-NLS-1$
171 public static final String P_ORDER_VALUE = "orderValue"; //$NON-NLS-1$
172 public static final String P_IMPORT_LOG = "importLog"; //$NON-NLS-1$
173 public static final String P_GROUP_BY = "groupBy"; //$NON-NLS-1$
175 private int MESSAGE_ORDER;
176 private int PLUGIN_ORDER;
177 private int DATE_ORDER;
179 public final static byte MESSAGE = 0x0;
180 public final static byte PLUGIN = 0x1;
181 public final static byte DATE = 0x2;
182 public static int ASCENDING = 1;
183 public static int DESCENDING = -1;
185 public static final int GROUP_BY_NONE = 0;
186 public static final int GROUP_BY_SESSION = 1;
187 public static final int GROUP_BY_PLUGIN = 2;
189 private List<Object> elements;
190 private Map<Object, Object> groups;
191 private LogSession currentSession;
193 private List<Object> batchedEntries;
194 private boolean batchEntries;
196 private Clipboard fClipboard;
198 private IMemento fMemento;
199 private File fInputFile;
200 private String fDirectory;
202 private Comparator<Object> fComparator;
205 private boolean fCanOpenTextShell;
206 private Text fTextLabel;
207 private Shell fTextShell;
209 private boolean fFirstEvent = true;
211 private TreeColumn fColumn1;
212 @SuppressWarnings("unused")
213 private TreeColumn fColumn2;
214 private TreeColumn fColumn3;
217 private FilteredTree fFilteredTree;
218 private LogViewLabelProvider fLabelProvider;
219 //private ScrolledComposite fMessageDescriptorComposite;
220 //private FormText fMessageDescription;
221 private Browser fMessageDescription;
223 private Action fPropertiesAction;
224 private Action fDeleteLogAction;
225 private Action fReadLogAction;
226 private Action fCopyAction;
227 private Action fActivateViewAction;
228 private Action fOpenLogAction;
229 private Action fExportAction;
231 private LocalResourceManager resourceManager;
234 * Action called when user selects "Group by -> ..." from menu.
236 class GroupByAction extends Action {
239 public GroupByAction(String text, int groupBy) {
240 super(text, IAction.AS_RADIO_BUTTON);
242 this.groupBy = groupBy;
244 if (fMemento.getInteger(LogView.P_GROUP_BY).intValue() == groupBy) {
250 if (fMemento.getInteger(LogView.P_GROUP_BY).intValue() != groupBy) {
251 fMemento.putInteger(LogView.P_GROUP_BY, groupBy);
261 elements = new ArrayList<Object>();
262 groups = new HashMap<Object, Object>();
263 batchedEntries = new ArrayList<Object>();
264 fInputFile = getPlatformLogFile();
268 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
270 public void createPartControl(Composite parent) {
271 resourceManager = new LocalResourceManager(JFaceResources.getResources());
273 SashForm sashForm = new SashForm(parent, SWT.VERTICAL | SWT.SMOOTH);
275 final Composite composite = new Composite(sashForm, SWT.NONE);
276 GridLayout layout = new GridLayout();
277 layout.horizontalSpacing = 0;
278 layout.verticalSpacing = 0;
279 layout.marginWidth = 0;
280 layout.marginHeight = 0;
281 layout.marginBottom = 0;
282 layout.marginTop = 0;
283 layout.marginLeft = 0;
284 layout.marginRight = 0;
285 composite.setLayout(layout);
288 createViewer(composite);
289 getSite().setSelectionProvider(fFilteredTree.getViewer());
291 fClipboard = new Clipboard(fTree.getDisplay());
292 fTree.setToolTipText(""); //$NON-NLS-1$
293 initializeViewerSorter();
298 fMessageDescriptorComposite = new ScrolledComposite(sashForm, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
299 GridDataFactory.fillDefaults().grab(true, false).applyTo(fMessageDescriptorComposite);
300 TableWrapLayout layout2 = new TableWrapLayout();
301 layout2.bottomMargin = 0;
302 layout2.topMargin = 0;
303 layout2.leftMargin = 0;
304 layout2.rightMargin = 0;
305 fMessageDescriptorComposite.setLayout(layout2);
307 fMessageDescription = new FormText(fMessageDescriptorComposite, SWT.NONE);
308 fMessageDescription.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
309 TableWrapData data = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.FILL_GRAB);
310 fMessageDescription.setLayoutData(data);
311 TextColors.bindTo(resourceManager, fMessageDescription);
312 fMessageDescription.addHyperlinkListener(new IHyperlinkListener() {
314 public void linkActivated(HyperlinkEvent event) {
315 String s = (String) event.data;
316 //System.out.println("link activated: " + s);
318 URI uri = new URI(s);
319 String scheme = uri.getScheme();
320 String schemeSpecificPart = uri.getSchemeSpecificPart();
322 IMessageSchemeManager msm = org.simantics.message.internal.Activator.getDefault().getMessageSchemeManager();
323 IMessageDataSchemeExtension[] exts = msm.getByScheme(scheme);
325 for (IMessageDataSchemeExtension ext : exts) {
326 Object data = ext.getSerializer().deserialize(schemeSpecificPart);
327 ext.getHandler().perform(data);
330 } catch (URISyntaxException e) {
331 // TODO Auto-generated catch block
333 } catch (ReferenceSerializationException e) {
334 // TODO Auto-generated catch block
339 public void linkEntered(HyperlinkEvent e) {
340 //System.out.println("link entered: " + e.data);
343 public void linkExited(HyperlinkEvent e) {
344 //System.out.println("link exited: " + e.data);
347 fMessageDescription.setText("Select a message to show its description here.", false, false);
349 fMessageDescriptorComposite.setContent(fMessageDescription);
350 fMessageDescriptorComposite.setExpandVertical(true);
351 fMessageDescriptorComposite.setExpandHorizontal(true);
352 fMessageDescriptorComposite.addControlListener(new ControlAdapter() {
353 public void controlResized(ControlEvent e) {
354 Rectangle r = fMessageDescriptorComposite.getClientArea();
355 //System.out.println("scrolled composite resized: " + e + ", client area: " + r);
356 Point contentSize = fMessageDescription.computeSize(r.width, SWT.DEFAULT);
357 //System.out.println("computed content size: " + contentSize);
358 fMessageDescriptorComposite.setMinSize(contentSize);
362 fMessageDescription = new Browser(sashForm, SWT.NONE);
363 fMessageDescription.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
364 fMessageDescription.setText("<html><head></head><body><p>Select a message to show its description here.</p></body></html>"); //$NON-NLS-1$
365 fMessageDescription.addLocationListener(new LocationListener() {
367 public void changed(LocationEvent event) {
368 //System.out.println("changed: " + event);
371 public void changing(LocationEvent event) {
372 //System.out.println("changing: " + event);
373 String location = event.location;
374 if ("about:blank".equals(location)) { //$NON-NLS-1$
378 //System.out.println("link activated: " + location);
380 URI uri = new URI(location);
381 String scheme = uri.getScheme();
382 //String schemeSpecificPart = uri.getSchemeSpecificPart();
384 IMessageSchemeManager msm = org.simantics.message.internal.Activator.getDefault().getMessageSchemeManager();
385 IMessageDataSchemeExtension[] exts = msm.getByScheme(scheme);
387 for (IMessageDataSchemeExtension ext : exts) {
388 Object data = ext.getSerializer().deserialize(uri);
390 ext.getHandler().perform(data);
392 } catch (MessageSchemeException e) {
393 ErrorLogger.defaultLogError(e);
397 } catch (URISyntaxException e) {
398 // TODO Auto-generated catch block
400 } catch (ReferenceSerializationException e) {
401 // TODO Auto-generated catch block
408 sashForm.setWeights(new int[] { 5, 2 });
410 fFilteredTree.getViewer().addSelectionChangedListener(new ISelectionChangedListener() {
412 public void selectionChanged(SelectionChangedEvent event) {
413 IStructuredSelection s = (IStructuredSelection) event.getSelection();
415 //fMessageDescription.setText("Select a message to show its description here.", false, false);
416 fMessageDescription.setText("<html><head></head><body><pre>Select a message to show its description here.</pre></body></html>"); //$NON-NLS-1$
418 AbstractEntry entry = (AbstractEntry) s.getFirstElement();
419 if (entry instanceof LogEntry) {
420 LogEntry logEntry = (LogEntry) entry;
421 String msg = logEntry.getDetailedDescription();
422 if (msg == null || msg.trim().isEmpty())
423 msg = logEntry.getMessage();
425 // FormText only supports texts of Short.MAX_VALUE length
426 // since TextSegment.TextFragment uses shorts. This message
427 // truncation enables us to show even lengthy messages.
428 if (msg.length() > Short.MAX_VALUE) {
429 StringBuilder truncated = new StringBuilder();
430 truncated.append( NLS.bind(Messages.LogView_Truncated, msg.length() - (Short.MAX_VALUE - 100), msg.length()));
431 msg = msg.substring(0, Short.MAX_VALUE - 100) + truncated;
434 //fMessageDescription.setText(FormTextUtil.form(msg), true, false);
435 //fMessageDescription.setText(HtmlUtil.html(msg));
436 fMessageDescription.setText(msg);
437 } catch (SWTException e) {
438 // Failed to parse markup, fall back to not parsing the input.
439 //fMessageDescription.setText(FormTextUtil.form(msg), false, false);
440 fMessageDescription.setText(msg);
442 } else if (entry instanceof Group) {
443 if (entry instanceof LogSession) {
444 LogSession logSession = (LogSession) entry;
445 //fMessageDescription.setText(logSession.getSessionData(), false, false);
446 fMessageDescription.setText(logSession.getSessionData());
448 Group grp = (Group) entry;
449 //fMessageDescription.setText(grp.toString(), false, false);
450 fMessageDescription.setText(grp.toString());
454 //fMessageDescription.refresh();
455 //composite.layout(true);
459 MessageService.getDefault().addLogListener(this);
460 // PlatformUI.getWorkbench().getHelpSystem().setHelp(fTree, IHelpContextIds.LOG_VIEW);
461 getSite().getWorkbenchWindow().addPerspectiveListener(new IPerspectiveListener2() {
463 public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective, IWorkbenchPartReference partRef, String changeId) {
464 if (!(partRef instanceof IViewReference))
467 IWorkbenchPart part = partRef.getPart(false);
472 if (part.equals(LogView.this)) {
473 if (changeId.equals(IWorkbenchPage.CHANGE_VIEW_SHOW)) {
474 if (!batchedEntries.isEmpty()) {
475 pushBatchedEntries();
478 batchEntries = false;
479 } else if (changeId.equals(IWorkbenchPage.CHANGE_VIEW_HIDE)) {
485 public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspective) {
489 public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective, String changeId) {
497 * Creates the actions for the viewsite action bars
499 private void createActions() {
500 IActionBars bars = getViewSite().getActionBars();
502 fCopyAction = createCopyAction();
503 bars.setGlobalActionHandler(ActionFactory.COPY.getId(), fCopyAction);
505 IToolBarManager toolBarManager = bars.getToolBarManager();
507 fExportAction = createExportAction();
508 toolBarManager.add(fExportAction);
510 final Action importLogAction = createImportLogAction();
511 toolBarManager.add(importLogAction);
513 toolBarManager.add(new Separator());
515 final Action clearAction = createClearAction();
516 toolBarManager.add(clearAction);
518 fDeleteLogAction = createDeleteLogAction();
519 toolBarManager.add(fDeleteLogAction);
521 fOpenLogAction = createOpenLogAction();
522 toolBarManager.add(fOpenLogAction);
524 fReadLogAction = createReadLogAction();
525 toolBarManager.add(fReadLogAction);
527 toolBarManager.add(new Separator());
528 toolBarManager.add(createShowTextFilter());
531 // toolBarManager.add(new Separator());
532 // final Action testAction = createTestAction();
533 // toolBarManager.add(testAction);
536 IMenuManager mgr = bars.getMenuManager();
538 mgr.add(createGroupByAction());
540 mgr.add(new Separator());
542 mgr.add(createFilterAction());
544 mgr.add(new Separator());
546 fActivateViewAction = createActivateViewAction();
547 mgr.add(fActivateViewAction);
549 // mgr.add(createShowTextFilter());
551 createPropertiesAction();
553 MenuManager popupMenuManager = new MenuManager("#PopupMenu"); //$NON-NLS-1$
554 IMenuListener listener = new IMenuListener() {
555 public void menuAboutToShow(IMenuManager manager) {
556 manager.add(fCopyAction);
557 manager.add(new Separator());
558 manager.add(clearAction);
559 manager.add(fDeleteLogAction);
560 manager.add(fOpenLogAction);
561 manager.add(fReadLogAction);
562 manager.add(new Separator());
563 manager.add(fExportAction);
564 manager.add(createImportLogAction());
565 manager.add(new Separator());
567 ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
568 TreeItem[] selection = fTree.getSelection();
569 if ((selection.length > 0) && (selection[0].getData() instanceof LogEntry)) {
570 manager.add(fPropertiesAction);
573 manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
576 popupMenuManager.addMenuListener(listener);
577 popupMenuManager.setRemoveAllWhenShown(true);
578 getSite().registerContextMenu(popupMenuManager, getSite().getSelectionProvider());
579 Menu menu = popupMenuManager.createContextMenu(fTree);
583 private Action createActivateViewAction() {
584 Action action = new Action(Messages.LogView_activate) { //
586 fMemento.putString(P_ACTIVATE, isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
589 action.setChecked(fMemento.getString(P_ACTIVATE).equals("true")); //$NON-NLS-1$
593 @SuppressWarnings("unused")
594 private Action createTestAction() {
595 Action action = new Action("Test") { //$NON-NLS-1$
597 IStatus s1 = new Status(IStatus.INFO, Activator.PLUGIN_ID, "Test message 1", null); //$NON-NLS-1$
598 IStatus s2 = new Status(IStatus.WARNING, Activator.PLUGIN_ID, "Test message 2", null); //$NON-NLS-1$
599 IStatus s3 = new DetailStatus(IStatus.ERROR, Activator.PLUGIN_ID, "This is the short message.", HtmlUtil.p("A multi-lined message...\n<br/>continuing...<br/><br/>still...<br/>Error occurred, report at {0}" + HtmlUtil.a("http://www.simantics.org", "simantics.org")), null); //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-1$
600 MessageService.defaultLog(s1);
601 MessageService.defaultLog(s2);
602 MessageService.defaultLog(s3);
603 // Activator.getDefault().getLog().log(s1);
604 // Activator.getDefault().getLog().log(s2);
605 // Activator.getDefault().getLog().log(s3);
607 MultiStatus s4 = new MultiStatus(Activator.PLUGIN_ID, 0, "Test message 4", new Exception()); //$NON-NLS-1$
608 s4.merge(new Status(IStatus.INFO, Activator.PLUGIN_ID, "MultiStatus Test 1", null)); //$NON-NLS-1$
609 s4.merge(new Status(IStatus.WARNING, Activator.PLUGIN_ID, "MultiStatus Test 2", null)); //$NON-NLS-1$
610 s4.merge(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "MultiStatus Test 3", null)); //$NON-NLS-1$
611 MessageService.defaultLog(s4);
612 // Activator.getDefault().getLog().log(s4);
615 action.setImageDescriptor(ImageDescriptor.getMissingImageDescriptor());
616 action.setToolTipText("Produce test log entries"); //$NON-NLS-1$
620 private Action createClearAction() {
621 Action action = new Action(Messages.LogView_clear) {
626 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_CLEAR));
627 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_CLEAR_DISABLED));
628 action.setToolTipText(Messages.LogView_clear_tooltip);
629 action.setText(Messages.LogView_clear);
633 private Action createCopyAction() {
634 Action action = new Action(Messages.LogView_copy) {
636 copyToClipboard(fFilteredTree.getViewer().getSelection());
639 action.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_COPY));
643 private Action createDeleteLogAction() {
644 Action action = new Action(Messages.LogView_delete) {
649 action.setToolTipText(Messages.LogView_delete_tooltip);
650 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_REMOVE_LOG));
651 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_REMOVE_LOG_DISABLED));
652 action.setEnabled(fInputFile.exists() && isPlatformLog(fInputFile));
656 private Action createExportAction() {
657 Action action = new Action(Messages.LogView_export) {
662 action.setToolTipText(Messages.LogView_export_tooltip);
663 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_EXPORT));
664 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_EXPORT_DISABLED));
665 action.setEnabled(fInputFile.exists());
669 private Action createFilterAction() {
670 Action action = new Action(Messages.LogView_filter) {
675 action.setToolTipText(Messages.LogView_filter);
676 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_FILTER));
677 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_FILTER_DISABLED));
681 private Action createImportLogAction() {
682 Action action = new ImportLogAction(this, Messages.LogView_import, fMemento);
683 action.setToolTipText(Messages.LogView_import_tooltip);
684 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_IMPORT));
685 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_IMPORT_DISABLED));
689 private Action createOpenLogAction() {
690 Action action = null;
693 // TODO this isn't the best way to check... we should be smarter and use package admin
694 // check to see if org.eclipse.ui.ide is available
695 Class.forName("org.eclipse.ui.ide.IDE"); //$NON-NLS-1$
696 // check to see if org.eclipse.core.filesystem is available
697 Class.forName("org.eclipse.core.filesystem.IFileStore"); //$NON-NLS-1$
698 action = new OpenIDELogFileAction(this);
699 } catch (ClassNotFoundException e) {
701 action = new Action() {
703 if (fInputFile.exists()) {
704 Job job = getOpenLogFileJob();
706 job.setPriority(Job.SHORT);
712 action.setText(Messages.LogView_view_currentLog);
713 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_OPEN_LOG));
714 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_OPEN_LOG_DISABLED));
715 action.setEnabled(fInputFile.exists());
716 action.setToolTipText(Messages.LogView_view_currentLog_tooltip);
720 private void createPropertiesAction() {
721 fPropertiesAction = new EventDetailsDialogAction(fTree.getShell(), fFilteredTree.getViewer(), fMemento);
722 fPropertiesAction.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_PROPERTIES));
723 fPropertiesAction.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_PROPERTIES_DISABLED));
724 fPropertiesAction.setToolTipText(Messages.LogView_properties_tooltip);
725 fPropertiesAction.setEnabled(false);
728 private Action createReadLogAction() {
729 Action action = new Action(Messages.LogView_readLog_restore) {
731 fInputFile = getPlatformLogFile();
735 action.setToolTipText(Messages.LogView_readLog_restore_tooltip);
736 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_READ_LOG));
737 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_READ_LOG_DISABLED));
742 * Creates the Show Text Filter view menu action
743 * @return the new action for the Show Text Filter
745 private Action createShowTextFilter() {
746 Action action = new Action(Messages.LogView_show_filter_text, Action.AS_CHECK_BOX) {
748 showFilterText(isChecked());
751 action.setToolTipText(Messages.LogView_show_filter_tooltip);
752 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_SHOW_TEXT_FILTER));
753 boolean visible = fMemento.getBoolean(P_SHOW_FILTER_TEXT).booleanValue();
754 action.setChecked(visible);
755 showFilterText(visible);
760 * Shows/hides the filter text control from the filtered tree. This method also sets the
761 * P_SHOW_FILTER_TEXT preference to the visible state
763 * @param visible if the filter text control should be shown or not
765 private void showFilterText(boolean visible) {
766 fMemento.putBoolean(P_SHOW_FILTER_TEXT, visible);
767 Composite ctrl = fFilteredTree.getFilterControl().getParent();
768 GridData gd = (GridData) ctrl.getLayoutData();
769 gd.exclude = !visible;
770 ctrl.setVisible(visible);
771 gd.verticalIndent = 8;
772 gd.horizontalIndent = 4;
773 if (!visible) // reset control if we aren't visible
774 fFilteredTree.getFilterControl().setText(Messages.LogView_show_filter_initialText);
775 fFilteredTree.layout(false);
778 private IContributionItem createGroupByAction() {
779 IMenuManager manager = new MenuManager(Messages.LogView_GroupBy);
780 manager.add(new GroupByAction(Messages.LogView_GroupBySession, LogView.GROUP_BY_SESSION));
781 manager.add(new GroupByAction(Messages.LogView_GroupByPlugin, LogView.GROUP_BY_PLUGIN));
782 manager.add(new GroupByAction(Messages.LogView_GroupByNone, LogView.GROUP_BY_NONE));
786 private void createViewer(Composite parent) {
788 fFilteredTree = new FilteredTree(parent, SWT.FULL_SELECTION, new PatternFilter() {
789 protected boolean isLeafMatch(Viewer viewer, Object element) {
790 if (element instanceof LogEntry) {
791 LogEntry logEntry = (LogEntry) element;
792 String message = logEntry.getMessage();
793 String plugin = logEntry.getPluginId();
794 String date = logEntry.getFormattedDate();
795 return wordMatches(message) || wordMatches(plugin) || wordMatches(date);
800 fFilteredTree.setInitialText(Messages.LogView_show_filter_initialText);
801 fTree = fFilteredTree.getViewer().getTree();
802 createColumns(fTree);
803 fTree.setLinesVisible(true);
804 fFilteredTree.getViewer().setAutoExpandLevel(fMemento.getInteger(P_EXPAND_LEVEL).intValue());
805 fFilteredTree.getViewer().setContentProvider(new LogViewContentProvider(this));
806 fFilteredTree.getViewer().setLabelProvider(fLabelProvider = new LogViewLabelProvider(this));
807 fLabelProvider.connect(this);
808 fFilteredTree.getViewer().addSelectionChangedListener(new ISelectionChangedListener() {
809 public void selectionChanged(SelectionChangedEvent e) {
810 handleSelectionChanged(e.getSelection());
811 if (fPropertiesAction.isEnabled())
812 ((EventDetailsDialogAction) fPropertiesAction).resetSelection();
815 fFilteredTree.getViewer().addDoubleClickListener(new IDoubleClickListener() {
816 public void doubleClick(DoubleClickEvent event) {
817 ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
818 fPropertiesAction.run();
821 fFilteredTree.getViewer().setInput(this);
826 private void createColumns(final Tree tree) {
827 fColumn1 = new TreeColumn(tree, SWT.LEFT);
828 fColumn1.setText(Messages.LogView_column_message);
829 fColumn1.setWidth(fMemento.getInteger(P_COLUMN_1).intValue());
830 fColumn1.addSelectionListener(new SelectionAdapter() {
831 public void widgetSelected(SelectionEvent e) {
833 ViewerComparator comparator = getViewerComparator(MESSAGE);
834 fFilteredTree.getViewer().setComparator(comparator);
835 boolean isComparatorSet = ((EventDetailsDialogAction) fPropertiesAction).resetSelection(MESSAGE, MESSAGE_ORDER);
836 setComparator(MESSAGE);
837 if (!isComparatorSet)
838 ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
839 fMemento.putInteger(P_ORDER_VALUE, MESSAGE_ORDER);
840 fMemento.putInteger(P_ORDER_TYPE, MESSAGE);
841 setColumnSorting(fColumn1, MESSAGE_ORDER);
844 tree.addControlListener(new ControlAdapter() {
847 public void controlResized(ControlEvent e) {
848 fColumn1.setWidth(tree.getSize().x - 20);
853 // fColumn2 = new TreeColumn(tree, SWT.LEFT);
854 // fColumn2.setText(Messages.LogView_column_plugin);
855 // fColumn2.setWidth(fMemento.getInteger(P_COLUMN_2).intValue());
856 // fColumn2.addSelectionListener(new SelectionAdapter() {
857 // public void widgetSelected(SelectionEvent e) {
858 // PLUGIN_ORDER *= -1;
859 // ViewerComparator comparator = getViewerComparator(PLUGIN);
860 // fFilteredTree.getViewer().setComparator(comparator);
861 // boolean isComparatorSet = ((EventDetailsDialogAction) fPropertiesAction).resetSelection(PLUGIN, PLUGIN_ORDER);
862 // setComparator(PLUGIN);
863 // if (!isComparatorSet)
864 // ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
865 // fMemento.putInteger(P_ORDER_VALUE, PLUGIN_ORDER);
866 // fMemento.putInteger(P_ORDER_TYPE, PLUGIN);
867 // setColumnSorting(fColumn2, PLUGIN_ORDER);
871 // fColumn3 = new TreeColumn(tree, SWT.LEFT);
872 // fColumn3.setText(Messages.LogView_column_date);
873 // fColumn3.setWidth(fMemento.getInteger(P_COLUMN_3).intValue());
874 // fColumn3.addSelectionListener(new SelectionAdapter() {
875 // public void widgetSelected(SelectionEvent e) {
877 // ViewerComparator comparator = getViewerComparator(DATE);
878 // fFilteredTree.getViewer().setComparator(comparator);
879 // setComparator(DATE);
880 // ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
881 // fMemento.putInteger(P_ORDER_VALUE, DATE_ORDER);
882 // fMemento.putInteger(P_ORDER_TYPE, DATE);
883 // setColumnSorting(fColumn3, DATE_ORDER);
887 tree.setHeaderVisible(true);
890 private void initializeViewerSorter() {
891 byte orderType = fMemento.getInteger(P_ORDER_TYPE).byteValue();
892 ViewerComparator comparator = getViewerComparator(orderType);
893 fFilteredTree.getViewer().setComparator(comparator);
894 if (orderType == MESSAGE)
895 setColumnSorting(fColumn1, MESSAGE_ORDER);
896 // else if (orderType == PLUGIN)
897 // setColumnSorting(fColumn2, PLUGIN_ORDER);
898 // else if (orderType == DATE)
899 // setColumnSorting(fColumn3, DATE_ORDER);
902 private void setColumnSorting(TreeColumn column, int order) {
903 fTree.setSortColumn(column);
904 fTree.setSortDirection(order == ASCENDING ? SWT.UP : SWT.DOWN);
907 public void dispose() {
909 MessageService.getDefault().removeLogListener(this);
910 fClipboard.dispose();
911 if (fTextShell != null)
912 fTextShell.dispose();
913 fLabelProvider.disconnect(this);
914 fFilteredTree.dispose();
915 resourceManager.dispose();
919 void handleImport() {
920 FileDialog dialog = new FileDialog(getViewSite().getShell());
921 dialog.setFilterExtensions(new String[] {"*.log"}); //$NON-NLS-1$
922 if (fDirectory != null)
923 dialog.setFilterPath(fDirectory);
924 String path = dialog.open();
925 if (path == null) { // cancel
929 File file = new Path(path).toFile();
931 handleImportPath(path);
933 String msg = NLS.bind(Messages.LogView_FileCouldNotBeFound, file.getName());
934 MessageDialog.openError(getViewSite().getShell(), Messages.LogView_OpenFile, msg);
938 public void handleImportPath(String path) {
939 if (path != null && new Path(path).toFile().exists()) {
940 fInputFile = new Path(path).toFile();
941 fDirectory = fInputFile.getParent();
942 IRunnableWithProgress op = new IRunnableWithProgress() {
943 public void run(IProgressMonitor monitor) {
944 monitor.beginTask(Messages.LogView_operation_importing, IProgressMonitor.UNKNOWN);
948 ProgressMonitorDialog pmd = new ProgressMonitorDialog(getViewSite().getShell());
950 pmd.run(true, true, op);
951 } catch (InvocationTargetException e) { // do nothing
952 } catch (InterruptedException e) { // do nothing
954 fReadLogAction.setText(Messages.LogView_readLog_reload);
955 fReadLogAction.setToolTipText(Messages.LogView_readLog_reload);
957 resetDialogButtons();
962 private void handleExport() {
963 FileDialog dialog = new FileDialog(getViewSite().getShell(), SWT.SAVE);
964 dialog.setFilterExtensions(new String[] {"*.log"}); //$NON-NLS-1$
965 if (fDirectory != null)
966 dialog.setFilterPath(fDirectory);
967 String path = dialog.open();
969 if (path.indexOf('.') == -1 && !path.endsWith(".log")) //$NON-NLS-1$
970 path += ".log"; //$NON-NLS-1$
971 File outputFile = new Path(path).toFile();
972 fDirectory = outputFile.getParent();
973 if (outputFile.exists()) {
974 String message = NLS.bind(Messages.LogView_confirmOverwrite_message, outputFile.toString());
975 if (!MessageDialog.openQuestion(getViewSite().getShell(), Messages.LogView_exportLog, message))
978 copy(fInputFile, outputFile);
982 private void copy(File inputFile, File outputFile) {
983 BufferedReader reader = null;
984 BufferedWriter writer = null;
986 reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile), "UTF-8")); //$NON-NLS-1$
987 writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), "UTF-8")); //$NON-NLS-1$
988 while (reader.ready()) {
989 writer.write(reader.readLine());
990 writer.write(System.getProperty("line.separator")); //$NON-NLS-1$
992 } catch (IOException e) { // do nothing
999 } catch (IOException e1) { // do nothing
1004 private void handleFilter() {
1005 FilterDialog dialog = new FilterDialog(Activator.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell(), fMemento);
1007 dialog.getShell().setText(Messages.LogView_FilterDialog_title);
1008 if (dialog.open() == Window.OK)
1012 private void doDeleteLog() {
1013 String title = Messages.LogView_confirmDelete_title;
1014 String message = Messages.LogView_confirmDelete_message;
1015 if (!MessageDialog.openConfirm(fTree.getShell(), title, message))
1017 if (fInputFile.delete() || elements.size() > 0) {
1020 currentSession.removeAllChildren();
1021 asyncRefresh(false);
1022 resetDialogButtons();
1026 public void fillContextMenu(IMenuManager manager) { // nothing
1029 public AbstractEntry[] getElements() {
1030 return (AbstractEntry[]) elements.toArray(new AbstractEntry[elements.size()]);
1033 protected void handleClear() {
1034 BusyIndicator.showWhile(fTree.getDisplay(), new Runnable() {
1038 currentSession.removeAllChildren();
1039 asyncRefresh(false);
1040 resetDialogButtons();
1045 protected void reloadLog() {
1046 IRunnableWithProgress op = new IRunnableWithProgress() {
1047 public void run(IProgressMonitor monitor) {
1048 monitor.beginTask(Messages.LogView_operation_reloading, IProgressMonitor.UNKNOWN);
1052 ProgressMonitorDialog pmd = new ProgressMonitorDialog(getViewSite().getShell());
1054 pmd.run(true, true, op);
1055 } catch (InvocationTargetException e) { // do nothing
1056 } catch (InterruptedException e) { // do nothing
1058 fReadLogAction.setText(Messages.LogView_readLog_restore);
1059 fReadLogAction.setToolTipText(Messages.LogView_readLog_restore);
1060 asyncRefresh(false);
1061 resetDialogButtons();
1065 private void readLogFile() {
1066 if (!fInputFile.exists())
1072 List<Object> result = new ArrayList<Object>();
1073 currentSession = LogReader.parseLogFile(fInputFile, result, fMemento);
1075 limitEntriesCount();
1077 getSite().getShell().getDisplay().asyncExec(new Runnable() {
1079 setContentDescription(getTitleSummary());
1085 private String getTitleSummary() {
1086 String path = ""; //$NON-NLS-1$
1088 path = fInputFile.getCanonicalPath();
1089 } catch (IOException e) { // log nothing
1092 if (isPlatformLogOpen()) {
1093 // Remove the content description in this case
1094 // to save vertical space from the view.
1095 //return Messages.LogView_WorkspaceLogFile;
1096 return ""; //$NON-NLS-1$
1099 Map<String, File> sources = LogFilesManager.getLogSources();
1100 if (sources.containsValue(path)) {
1101 for (Iterator<String> i = sources.keySet().iterator(); i.hasNext();) {
1102 String key = i.next();
1103 if (sources.get(key).equals(path)) {
1104 return NLS.bind(Messages.LogView_LogFileTitle, new String[] {key, path});
1113 * Add new entries to correct groups in the view.
1114 * @param entries new entries to show up in groups in the view.
1116 private void group(List<?> entries) {
1117 if (fMemento.getInteger(P_GROUP_BY).intValue() == GROUP_BY_NONE) {
1118 elements.addAll(entries);
1120 for (Iterator<?> i = entries.iterator(); i.hasNext();) {
1121 LogEntry entry = (LogEntry) i.next();
1122 Group group = getGroup(entry);
1123 group.addChild(entry);
1129 * Limits the number of entries according to the max entries limit set in
1132 private void limitEntriesCount() {
1133 int limit = Integer.MAX_VALUE;
1134 if (fMemento.getString(LogView.P_USE_LIMIT).equals("true")) {//$NON-NLS-1$
1135 limit = fMemento.getInteger(LogView.P_LOG_LIMIT).intValue();
1138 int entriesCount = getEntriesCount();
1140 if (entriesCount <= limit) {
1143 Comparator<Object> dateComparator = new Comparator<Object>() {
1144 public int compare(Object o1, Object o2) {
1145 Date l1 = ((LogEntry) o1).getDate();
1146 Date l2 = ((LogEntry) o2).getDate();
1147 if ((l1 != null) && (l2 != null)) {
1148 return l1.before(l2) ? -1 : 1;
1149 } else if ((l1 == null) && (l2 == null)) {
1152 return (l1 == null) ? -1 : 1;
1156 if (fMemento.getInteger(P_GROUP_BY).intValue() == GROUP_BY_NONE) {
1157 elements.subList(0, elements.size() - limit).clear();
1159 List<Object> copy = new ArrayList<Object>(entriesCount);
1160 for (Iterator<?> i = elements.iterator(); i.hasNext();) {
1161 AbstractEntry group = (AbstractEntry) i.next();
1162 copy.addAll(Arrays.asList(group.getChildren(group)));
1165 Collections.sort(copy, dateComparator);
1166 List<?> toRemove = copy.subList(0, copy.size() - limit);
1168 for (Iterator<?> i = elements.iterator(); i.hasNext();) {
1169 AbstractEntry group = (AbstractEntry) i.next();
1170 group.removeChildren(toRemove);
1176 private int getEntriesCount() {
1177 if (fMemento.getInteger(P_GROUP_BY).intValue() == GROUP_BY_NONE) {
1178 return elements.size();
1181 for (Iterator<?> i = elements.iterator(); i.hasNext();) {
1182 AbstractEntry group = (AbstractEntry) i.next();
1183 size += group.size();
1189 * Returns group appropriate for the entry. Group depends on P_GROUP_BY
1190 * preference, or is null if grouping is disabled (GROUP_BY_NONE), or group
1191 * could not be determined. May create group if it haven't existed before.
1193 * @param entry entry to be grouped
1194 * @return group or null if grouping is disabled
1196 protected Group getGroup(LogEntry entry) {
1197 int groupBy = fMemento.getInteger(P_GROUP_BY).intValue();
1198 Object elementGroupId = null;
1199 String groupName = null;
1202 case GROUP_BY_PLUGIN :
1203 groupName = entry.getPluginId();
1204 elementGroupId = groupName;
1207 case GROUP_BY_SESSION :
1208 elementGroupId = entry.getSession();
1211 default : // grouping is disabled
1215 if (elementGroupId == null) { // could not determine group
1219 Group group = (Group) groups.get(elementGroupId);
1220 if (group == null) {
1221 if (groupBy == GROUP_BY_SESSION) {
1222 group = entry.getSession();
1224 group = new Group(groupName);
1226 groups.put(elementGroupId, group);
1227 elements.add(group);
1233 public void logging(IStatus status, String plugin) {
1234 if (!isPlatformLog(fInputFile))
1238 // create LogEntry immediately to don't loose IStatus creation date.
1239 LogEntry entry = createLogEntry(status);
1240 batchedEntries.add(entry);
1247 fFirstEvent = false;
1249 LogEntry entry = createLogEntry(status);
1251 if (!batchedEntries.isEmpty()) {
1252 // batch new entry as well, to have only one asyncRefresh()
1253 batchedEntries.add(entry);
1254 pushBatchedEntries();
1263 * Push batched entries to log view.
1265 private void pushBatchedEntries() {
1266 Job job = new Job(Messages.LogView_AddingBatchedEvents) {
1267 protected IStatus run(IProgressMonitor monitor) {
1268 for (int i = 0; i < batchedEntries.size(); i++) {
1269 if (!monitor.isCanceled()) {
1270 LogEntry entry = (LogEntry) batchedEntries.get(i);
1272 batchedEntries.remove(i);
1276 return Status.OK_STATUS;
1282 private LogEntry createLogEntry(IStatus status) {
1283 LogEntry entry = new LogEntry(status);
1284 entry.setSession(currentSession);
1288 private synchronized void pushEntry(LogEntry entry) {
1289 if (LogReader.isLogged(entry, fMemento)) {
1290 group(Collections.singletonList(entry));
1291 limitEntriesCount();
1296 private void asyncRefresh(final boolean activate) {
1297 if (fTree.isDisposed())
1299 Display display = fTree.getDisplay();
1300 final ViewPart view = this;
1301 if (display != null) {
1302 display.asyncExec(new Runnable() {
1304 if (!fTree.isDisposed()) {
1305 fFilteredTree.getViewer().refresh();
1306 fFilteredTree.getViewer().expandToLevel(fMemento.getInteger(P_EXPAND_LEVEL).intValue());
1307 fDeleteLogAction.setEnabled(fInputFile.exists() && isPlatformLog(fInputFile));
1308 fOpenLogAction.setEnabled(fInputFile.exists());
1309 fExportAction.setEnabled(fInputFile.exists());
1310 if (activate && fActivateViewAction.isChecked()) {
1311 IWorkbenchPage page = Activator.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage();
1313 page.bringToTop(view);
1321 public void setFocus() {
1322 if (fFilteredTree != null && !fFilteredTree.isDisposed())
1323 fFilteredTree.setFocus();
1326 private void handleSelectionChanged(ISelection selection) {
1327 updateStatus(selection);
1328 fCopyAction.setEnabled((!selection.isEmpty()) && ((IStructuredSelection) selection).getFirstElement() instanceof LogEntry);
1329 fPropertiesAction.setEnabled(!selection.isEmpty());
1332 private void updateStatus(ISelection selection) {
1333 IStatusLineManager status = getViewSite().getActionBars().getStatusLineManager();
1334 if (selection.isEmpty())
1335 status.setMessage(null);
1337 Object element = ((IStructuredSelection) selection).getFirstElement();
1338 status.setMessage(((LogViewLabelProvider) fFilteredTree.getViewer().getLabelProvider()).getColumnText(element, 0));
1343 * Converts selected log view element to string.
1344 * @return textual log entry representation or null if selection doesn't contain log entry
1346 private static String selectionToString(ISelection selection) {
1347 StringWriter writer = new StringWriter();
1348 PrintWriter pwriter = new PrintWriter(writer);
1349 if (selection.isEmpty())
1351 LogEntry entry = (LogEntry) ((IStructuredSelection) selection).getFirstElement();
1352 entry.write(pwriter);
1354 String textVersion = writer.toString();
1358 } catch (IOException e) {
1366 * Copies selected element to clipboard.
1368 private void copyToClipboard(ISelection selection) {
1369 String textVersion = selectionToString(selection);
1370 if ((textVersion != null) && (textVersion.trim().length() > 0)) {
1371 // set the clipboard contents
1372 fClipboard.setContents(new Object[] {textVersion}, new Transfer[] {TextTransfer.getInstance()});
1376 public void init(IViewSite site, IMemento memento) throws PartInitException {
1377 super.init(site, memento);
1378 if (memento == null)
1379 this.fMemento = XMLMemento.createWriteRoot("LOGVIEW"); //$NON-NLS-1$
1381 this.fMemento = memento;
1384 // initialize column ordering
1385 final byte type = this.fMemento.getInteger(P_ORDER_TYPE).byteValue();
1388 DATE_ORDER = this.fMemento.getInteger(P_ORDER_VALUE).intValue();
1389 MESSAGE_ORDER = DESCENDING;
1390 PLUGIN_ORDER = DESCENDING;
1393 MESSAGE_ORDER = this.fMemento.getInteger(P_ORDER_VALUE).intValue();
1394 DATE_ORDER = DESCENDING;
1395 PLUGIN_ORDER = DESCENDING;
1398 PLUGIN_ORDER = this.fMemento.getInteger(P_ORDER_VALUE).intValue();
1399 MESSAGE_ORDER = DESCENDING;
1400 DATE_ORDER = DESCENDING;
1403 DATE_ORDER = DESCENDING;
1404 MESSAGE_ORDER = DESCENDING;
1405 PLUGIN_ORDER = DESCENDING;
1407 setComparator(fMemento.getInteger(P_ORDER_TYPE).byteValue());
1410 private void initializeMemento() {
1411 if (fMemento.getInteger(P_EXPAND_LEVEL) == null) {
1412 fMemento.putInteger(P_EXPAND_LEVEL, DEFAULT_EXPAND_LEVEL);
1414 if (fMemento.getString(P_USE_LIMIT) == null) {
1415 fMemento.putString(P_USE_LIMIT, "true"); //$NON-NLS-1$
1417 if (fMemento.getInteger(P_LOG_LIMIT) == null) {
1418 fMemento.putInteger(P_LOG_LIMIT, 50);
1420 if (fMemento.getString(P_LOG_INFO) == null) {
1421 fMemento.putString(P_LOG_INFO, "true"); //$NON-NLS-1$
1423 if (fMemento.getString(P_LOG_WARNING) == null) {
1424 fMemento.putString(P_LOG_WARNING, "true"); //$NON-NLS-1$
1426 if (fMemento.getString(P_LOG_ERROR) == null) {
1427 fMemento.putString(P_LOG_ERROR, "true"); //$NON-NLS-1$
1429 if (fMemento.getString(P_LOG_DEBUG) == null) {
1430 fMemento.putString(P_LOG_DEBUG, "true"); //$NON-NLS-1$
1432 if (fMemento.getString(P_SHOW_ALL_SESSIONS) == null) {
1433 fMemento.putString(P_SHOW_ALL_SESSIONS, "false"); //$NON-NLS-1$
1435 Integer width = fMemento.getInteger(P_COLUMN_1);
1436 if (width == null || width.intValue() == 0) {
1437 fMemento.putInteger(P_COLUMN_1, 300);
1439 width = fMemento.getInteger(P_COLUMN_2);
1440 if (width == null || width.intValue() == 0) {
1441 fMemento.putInteger(P_COLUMN_2, 150);
1443 width = fMemento.getInteger(P_COLUMN_3);
1444 if (width == null || width.intValue() == 0) {
1445 fMemento.putInteger(P_COLUMN_3, 150);
1447 if (fMemento.getString(P_ACTIVATE) == null) {
1448 fMemento.putString(P_ACTIVATE, "false"); //$NON-NLS-1$
1450 if (fMemento.getBoolean(P_SHOW_FILTER_TEXT) == null) {
1451 fMemento.putBoolean(P_SHOW_FILTER_TEXT, false);
1453 fMemento.putInteger(P_ORDER_VALUE, DESCENDING);
1454 fMemento.putInteger(P_ORDER_TYPE, DATE);
1455 if (fMemento.getInteger(P_GROUP_BY) == null) {
1456 fMemento.putInteger(P_GROUP_BY, GROUP_BY_NONE);
1460 public void saveState(IMemento memento) {
1461 if (this.fMemento == null || memento == null)
1463 this.fMemento.putInteger(P_COLUMN_1, fColumn1.getWidth());
1464 // this.fMemento.putInteger(P_COLUMN_2, fColumn2.getWidth());
1465 // this.fMemento.putInteger(P_COLUMN_3, fColumn3.getWidth());
1466 this.fMemento.putString(P_ACTIVATE, fActivateViewAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1467 memento.putMemento(this.fMemento);
1471 private void addMouseListeners() {
1472 Listener tableListener = new Listener() {
1473 public void handleEvent(Event e) {
1475 case SWT.MouseMove :
1478 case SWT.MouseHover :
1481 case SWT.MouseDown :
1487 int[] tableEvents = new int[] {SWT.MouseDown, SWT.MouseMove, SWT.MouseHover};
1488 for (int i = 0; i < tableEvents.length; i++) {
1489 fTree.addListener(tableEvents[i], tableListener);
1494 * Adds drag source support to error log tree.
1496 private void addDragSource() {
1497 DragSource source = new DragSource(fTree, DND.DROP_COPY);
1498 Transfer[] types = new Transfer[] {TextTransfer.getInstance()};
1499 source.setTransfer(types);
1501 source.addDragListener(new DragSourceAdapter() {
1503 public void dragStart(DragSourceEvent event) {
1504 ISelection selection = fFilteredTree.getViewer().getSelection();
1505 if (selection.isEmpty()) {
1510 AbstractEntry entry = (AbstractEntry) ((TreeSelection) selection).getFirstElement();
1511 if (!(entry instanceof LogEntry)) {
1517 public void dragSetData(DragSourceEvent event) {
1518 if (!TextTransfer.getInstance().isSupportedType(event.dataType)) {
1522 ISelection selection = fFilteredTree.getViewer().getSelection();
1523 String textVersion = selectionToString(selection);
1524 event.data = textVersion;
1529 private void makeHoverShell() {
1530 fTextShell = new Shell(fTree.getShell(), SWT.NO_FOCUS | SWT.ON_TOP | SWT.TOOL);
1531 Display display = fTextShell.getDisplay();
1532 fTextShell.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
1533 GridLayout layout = new GridLayout(1, false);
1534 int border = ((fTree.getShell().getStyle() & SWT.NO_TRIM) == 0) ? 0 : 1;
1535 layout.marginHeight = border;
1536 layout.marginWidth = border;
1537 fTextShell.setLayout(layout);
1538 fTextShell.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1539 Composite shellComposite = new Composite(fTextShell, SWT.NONE);
1540 layout = new GridLayout();
1541 layout.marginHeight = 0;
1542 layout.marginWidth = 0;
1543 shellComposite.setLayout(layout);
1544 shellComposite.setLayoutData(new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_BEGINNING));
1545 fTextLabel = new Text(shellComposite, SWT.WRAP | SWT.MULTI | SWT.READ_ONLY);
1546 GridData gd = new GridData(GridData.FILL_BOTH);
1548 gd.grabExcessHorizontalSpace = true;
1549 fTextLabel.setLayoutData(gd);
1550 Color c = fTree.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
1551 fTextLabel.setBackground(c);
1552 c = fTree.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
1553 fTextLabel.setForeground(c);
1554 fTextLabel.setEditable(false);
1555 fTextShell.addDisposeListener(new DisposeListener() {
1556 public void widgetDisposed(DisposeEvent e) {
1557 onTextShellDispose(e);
1562 void onTextShellDispose(DisposeEvent e) {
1563 fCanOpenTextShell = true;
1567 void onMouseDown(Event e) {
1568 if (fTextShell != null && !fTextShell.isDisposed() && !fTextShell.isFocusControl()) {
1569 fTextShell.setVisible(false);
1570 fCanOpenTextShell = true;
1574 void onMouseHover(Event e) {
1575 if (!fCanOpenTextShell || fTextShell == null || fTextShell.isDisposed())
1577 fCanOpenTextShell = false;
1578 Point point = new Point(e.x, e.y);
1579 TreeItem item = fTree.getItem(point);
1583 String message = null;
1584 if (item.getData() instanceof LogEntry) {
1585 message = ((LogEntry) item.getData()).getStack();
1586 } else if (item.getData() instanceof LogSession) {
1587 LogSession session = ((LogSession) item.getData());
1588 message = Messages.LogView_SessionStarted;
1589 if (session.getDate() != null) {
1590 DateFormat formatter = new SimpleDateFormat(LogEntry.F_DATE_FORMAT);
1591 message += formatter.format(session.getDate());
1595 if (message == null)
1598 fTextLabel.setText(message);
1599 Rectangle bounds = fTree.getDisplay().getBounds();
1600 Point cursorPoint = fTree.getDisplay().getCursorLocation();
1602 int y = point.y + 25;
1603 int width = fTree.getColumn(0).getWidth();
1605 if (cursorPoint.x + width > bounds.width)
1607 if (cursorPoint.y + height + 25 > bounds.height)
1610 fTextShell.setLocation(fTree.toDisplay(x, y));
1611 fTextShell.setSize(width, height);
1612 fTextShell.setVisible(true);
1615 void onMouseMove(Event e) {
1616 if (fTextShell != null && !fTextShell.isDisposed() && fTextShell.isVisible())
1617 fTextShell.setVisible(false);
1619 Point point = new Point(e.x, e.y);
1620 TreeItem item = fTree.getItem(point);
1623 Image image = item.getImage();
1624 Object data = item.getData();
1625 if (data instanceof LogEntry) {
1626 LogEntry entry = (LogEntry) data;
1627 int parentCount = getNumberOfParents(entry);
1628 int startRange = 20 + Math.max(image.getBounds().width + 2, 7 + 2) * parentCount;
1629 int endRange = startRange + 16;
1630 fCanOpenTextShell = e.x >= startRange && e.x <= endRange;
1634 private int getNumberOfParents(AbstractEntry entry) {
1635 AbstractEntry parent = (AbstractEntry) entry.getParent(entry);
1638 return 1 + getNumberOfParents(parent);
1641 public Comparator<Object> getComparator() {
1645 private void setComparator(byte sortType) {
1646 if (sortType == DATE) {
1647 fComparator = new Comparator<Object>() {
1648 public int compare(Object e1, Object e2) {
1651 if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1652 date1 = ((LogEntry) e1).getDate().getTime();
1653 date2 = ((LogEntry) e2).getDate().getTime();
1654 } else if ((e1 instanceof LogSession) && (e2 instanceof LogSession)) {
1655 date1 = ((LogSession) e1).getDate() == null ? 0 : ((LogSession) e1).getDate().getTime();
1656 date2 = ((LogSession) e2).getDate() == null ? 0 : ((LogSession) e2).getDate().getTime();
1658 if (date1 == date2) {
1659 int result = elements.indexOf(e2) - elements.indexOf(e1);
1660 if (DATE_ORDER == DESCENDING)
1661 result *= DESCENDING;
1664 if (DATE_ORDER == DESCENDING)
1665 return date1 > date2 ? DESCENDING : ASCENDING;
1666 return date1 < date2 ? DESCENDING : ASCENDING;
1669 } else if (sortType == PLUGIN) {
1670 fComparator = new Comparator<Object>() {
1671 public int compare(Object e1, Object e2) {
1672 if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1673 LogEntry entry1 = (LogEntry) e1;
1674 LogEntry entry2 = (LogEntry) e2;
1675 return getDefaultComparator().compare(entry1.getPluginId(), entry2.getPluginId()) * PLUGIN_ORDER;
1681 fComparator = new Comparator<Object>() {
1682 public int compare(Object e1, Object e2) {
1683 if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1684 LogEntry entry1 = (LogEntry) e1;
1685 LogEntry entry2 = (LogEntry) e2;
1686 return getDefaultComparator().compare(entry1.getMessage(), entry2.getMessage()) * MESSAGE_ORDER;
1694 @SuppressWarnings("unchecked")
1695 private Comparator<Object> getDefaultComparator() {
1696 return Policy.getComparator();
1699 private ViewerComparator getViewerComparator(byte sortType) {
1700 if (sortType == PLUGIN) {
1701 return new ViewerComparator() {
1702 @SuppressWarnings("unchecked")
1703 public int compare(Viewer viewer, Object e1, Object e2) {
1704 if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1705 LogEntry entry1 = (LogEntry) e1;
1706 LogEntry entry2 = (LogEntry) e2;
1707 return getComparator().compare(entry1.getPluginId(), entry2.getPluginId()) * PLUGIN_ORDER;
1712 } else if (sortType == MESSAGE) {
1713 return new ViewerComparator() {
1714 @SuppressWarnings("unchecked")
1715 public int compare(Viewer viewer, Object e1, Object e2) {
1716 if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1717 LogEntry entry1 = (LogEntry) e1;
1718 LogEntry entry2 = (LogEntry) e2;
1719 return getComparator().compare(entry1.getMessage(), entry2.getMessage()) * MESSAGE_ORDER;
1725 return new ViewerComparator() {
1726 private int indexOf(Object[] array, Object o) {
1729 for (int i = 0; i < array.length; ++i)
1730 if (o.equals(array[i]))
1735 public int compare(Viewer viewer, Object e1, Object e2) {
1738 if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1739 date1 = ((LogEntry) e1).getDate().getTime();
1740 date2 = ((LogEntry) e2).getDate().getTime();
1741 } else if ((e1 instanceof LogSession) && (e2 instanceof LogSession)) {
1742 date1 = ((LogSession) e1).getDate() == null ? 0 : ((LogSession) e1).getDate().getTime();
1743 date2 = ((LogSession) e2).getDate() == null ? 0 : ((LogSession) e2).getDate().getTime();
1746 if (date1 == date2) {
1747 // Everything that appears in logview should be an AbstractEntry.
1748 AbstractEntry parent = (AbstractEntry) ((AbstractEntry) e1).getParent(null);
1749 Object[] children = null;
1751 children = parent.getChildren(parent);
1754 if (children != null) {
1755 // The elements in children seem to be in reverse order,
1756 // i.e. latest log message first, therefore index(e2)-index(e1)
1757 result = indexOf(children, e2) - indexOf(children, e1);
1759 result = elements.indexOf(e1) - elements.indexOf(e2);
1761 if (DATE_ORDER == DESCENDING)
1762 result *= DESCENDING;
1765 if (DATE_ORDER == DESCENDING)
1766 return date1 > date2 ? DESCENDING : ASCENDING;
1767 return date1 < date2 ? DESCENDING : ASCENDING;
1773 private void resetDialogButtons() {
1774 ((EventDetailsDialogAction) fPropertiesAction).resetDialogButtons();
1778 * Returns the filter dialog settings object used to maintain
1779 * state between filter dialogs
1780 * @return the dialog settings to be used
1782 private IDialogSettings getLogSettings() {
1783 IDialogSettings settings = Activator.getDefault().getDialogSettings();
1784 return settings.getSection(getClass().getName());
1788 * Returns the plugin preferences used to maintain
1790 * @return the plugin preferences
1792 private Preferences getLogPreferences() {
1793 return Activator.getDefault().getPluginPreferences();
1796 private void readSettings() {
1797 IDialogSettings s = getLogSettings();
1798 Preferences p = getLogPreferences();
1799 if (s == null || p == null) {
1800 initializeMemento();
1804 fMemento.putString(P_USE_LIMIT, s.getBoolean(P_USE_LIMIT) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1805 fMemento.putString(P_LOG_INFO, s.getBoolean(P_LOG_INFO) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1806 fMemento.putString(P_LOG_WARNING, s.getBoolean(P_LOG_WARNING) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1807 fMemento.putString(P_LOG_ERROR, s.getBoolean(P_LOG_ERROR) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1808 fMemento.putString(P_LOG_DEBUG, s.getBoolean(P_LOG_DEBUG) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1809 fMemento.putString(P_SHOW_ALL_SESSIONS, s.getBoolean(P_SHOW_ALL_SESSIONS) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1810 fMemento.putInteger(P_LOG_LIMIT, s.getInt(P_LOG_LIMIT));
1811 fMemento.putInteger(P_COLUMN_1, p.getInt(P_COLUMN_1) > 0 ? p.getInt(P_COLUMN_1) : 300);
1812 fMemento.putInteger(P_COLUMN_2, p.getInt(P_COLUMN_2) > 0 ? p.getInt(P_COLUMN_2) : 150);
1813 fMemento.putInteger(P_COLUMN_3, p.getInt(P_COLUMN_3) > 0 ? p.getInt(P_COLUMN_3) : 150);
1814 fMemento.putString(P_ACTIVATE, p.getBoolean(P_ACTIVATE) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1815 int order = p.getInt(P_ORDER_VALUE);
1816 fMemento.putInteger(P_ORDER_VALUE, order == 0 ? DESCENDING : order);
1817 fMemento.putInteger(P_ORDER_TYPE, p.getInt(P_ORDER_TYPE));
1818 fMemento.putBoolean(P_SHOW_FILTER_TEXT, p.getBoolean(P_SHOW_FILTER_TEXT));
1819 fMemento.putInteger(P_GROUP_BY, p.getInt(P_GROUP_BY));
1820 fMemento.putInteger(P_EXPAND_LEVEL, s.getInt(P_EXPAND_LEVEL));
1821 } catch (NumberFormatException e) {
1822 fMemento.putInteger(P_EXPAND_LEVEL, DEFAULT_EXPAND_LEVEL);
1823 fMemento.putInteger(P_LOG_LIMIT, 50);
1824 fMemento.putInteger(P_COLUMN_1, 300);
1825 fMemento.putInteger(P_COLUMN_2, 150);
1826 fMemento.putInteger(P_COLUMN_3, 150);
1827 fMemento.putInteger(P_ORDER_TYPE, DATE);
1828 fMemento.putInteger(P_ORDER_VALUE, DESCENDING);
1829 fMemento.putInteger(P_GROUP_BY, GROUP_BY_NONE);
1833 private void writeSettings() {
1834 writeViewSettings();
1835 writeFilterSettings();
1838 private void writeFilterSettings() {
1839 IDialogSettings settings = getLogSettings();
1840 if (settings == null)
1841 settings = Activator.getDefault().getDialogSettings().addNewSection(getClass().getName());
1842 settings.put(P_USE_LIMIT, fMemento.getString(P_USE_LIMIT).equals("true")); //$NON-NLS-1$
1843 settings.put(P_LOG_LIMIT, fMemento.getInteger(P_LOG_LIMIT).intValue());
1844 settings.put(P_LOG_INFO, fMemento.getString(P_LOG_INFO).equals("true")); //$NON-NLS-1$
1845 settings.put(P_LOG_WARNING, fMemento.getString(P_LOG_WARNING).equals("true")); //$NON-NLS-1$
1846 settings.put(P_LOG_ERROR, fMemento.getString(P_LOG_ERROR).equals("true")); //$NON-NLS-1$
1847 settings.put(P_LOG_DEBUG, fMemento.getString(P_LOG_DEBUG).equals("true")); //$NON-NLS-1$
1848 settings.put(P_SHOW_ALL_SESSIONS, fMemento.getString(P_SHOW_ALL_SESSIONS).equals("true")); //$NON-NLS-1$
1851 private void writeViewSettings() {
1852 Preferences preferences = getLogPreferences();
1853 preferences.setValue(P_COLUMN_1, fMemento.getInteger(P_COLUMN_1).intValue());
1854 preferences.setValue(P_COLUMN_2, fMemento.getInteger(P_COLUMN_2).intValue());
1855 preferences.setValue(P_COLUMN_3, fMemento.getInteger(P_COLUMN_3).intValue());
1856 preferences.setValue(P_ACTIVATE, fMemento.getString(P_ACTIVATE).equals("true")); //$NON-NLS-1$
1857 int order = fMemento.getInteger(P_ORDER_VALUE).intValue();
1858 preferences.setValue(P_ORDER_VALUE, order == 0 ? DESCENDING : order);
1859 preferences.setValue(P_ORDER_TYPE, fMemento.getInteger(P_ORDER_TYPE).intValue());
1860 preferences.setValue(P_SHOW_FILTER_TEXT, fMemento.getBoolean(P_SHOW_FILTER_TEXT).booleanValue());
1861 preferences.setValue(P_GROUP_BY, fMemento.getInteger(P_GROUP_BY).intValue());
1862 preferences.setValue(P_EXPAND_LEVEL, fMemento.getInteger(P_EXPAND_LEVEL).intValue());
1865 public void sortByDateDescending() {
1866 setColumnSorting(fColumn3, DESCENDING);
1869 protected Job getOpenLogFileJob() {
1870 final Shell shell = getViewSite().getShell();
1871 return new Job(Messages.OpenLogDialog_message) {
1872 protected IStatus run(IProgressMonitor monitor) {
1873 boolean failed = false;
1874 if (fInputFile.length() <= LogReader.MAX_FILE_LENGTH) {
1875 failed = !Program.launch(fInputFile.getAbsolutePath());
1877 Program p = Program.findProgram(".txt"); //$NON-NLS-1$
1879 p.execute(fInputFile.getAbsolutePath());
1880 return Status.OK_STATUS;
1885 final OpenLogDialog openDialog = new OpenLogDialog(shell, fInputFile);
1886 Display.getDefault().asyncExec(new Runnable() {
1888 openDialog.create();
1893 return Status.OK_STATUS;
1898 protected File getLogFile() {
1903 * Returns whether given session equals to currently displayed in LogView.
1904 * @param session LogSession
1905 * @return true if given session equals to currently displayed in LogView
1907 public boolean isCurrentLogSession(LogSession session) {
1908 return isPlatformLogOpen() && (currentSession != null) && (currentSession.equals(session));
1912 * Returns whether currently open log is platform log or imported file.
1913 * @return true if currently open log is platform log, false otherwise
1915 public boolean isPlatformLogOpen() {
1916 return isPlatformLog(fInputFile);
1920 * Returns whether currently open log is platform log or imported file.
1921 * @return true if currently open log is platform log, false otherwise
1923 public boolean isPlatformLog(File file) {
1924 return (file.equals(getPlatformLogFile()));
1927 public File getPlatformLogFile() {
1928 return org.simantics.message.internal.Activator.getLogFile();