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>");
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)) {
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>");
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("... [truncated ");
431 truncated.append(msg.length() - (Short.MAX_VALUE - 100));
432 truncated.append(" out of ");
433 truncated.append(msg.length());
434 truncated.append(" characters]");
435 msg = msg.substring(0, Short.MAX_VALUE - 100) + truncated;
438 //fMessageDescription.setText(FormTextUtil.form(msg), true, false);
439 //fMessageDescription.setText(HtmlUtil.html(msg));
440 fMessageDescription.setText(msg);
441 } catch (SWTException e) {
442 // Failed to parse markup, fall back to not parsing the input.
443 //fMessageDescription.setText(FormTextUtil.form(msg), false, false);
444 fMessageDescription.setText(msg);
446 } else if (entry instanceof Group) {
447 if (entry instanceof LogSession) {
448 LogSession logSession = (LogSession) entry;
449 //fMessageDescription.setText(logSession.getSessionData(), false, false);
450 fMessageDescription.setText(logSession.getSessionData());
452 Group grp = (Group) entry;
453 //fMessageDescription.setText(grp.toString(), false, false);
454 fMessageDescription.setText(grp.toString());
458 //fMessageDescription.refresh();
459 //composite.layout(true);
463 MessageService.getDefault().addLogListener(this);
464 // PlatformUI.getWorkbench().getHelpSystem().setHelp(fTree, IHelpContextIds.LOG_VIEW);
465 getSite().getWorkbenchWindow().addPerspectiveListener(new IPerspectiveListener2() {
467 public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective, IWorkbenchPartReference partRef, String changeId) {
468 if (!(partRef instanceof IViewReference))
471 IWorkbenchPart part = partRef.getPart(false);
476 if (part.equals(LogView.this)) {
477 if (changeId.equals(IWorkbenchPage.CHANGE_VIEW_SHOW)) {
478 if (!batchedEntries.isEmpty()) {
479 pushBatchedEntries();
482 batchEntries = false;
483 } else if (changeId.equals(IWorkbenchPage.CHANGE_VIEW_HIDE)) {
489 public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspective) {
493 public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective, String changeId) {
501 * Creates the actions for the viewsite action bars
503 private void createActions() {
504 IActionBars bars = getViewSite().getActionBars();
506 fCopyAction = createCopyAction();
507 bars.setGlobalActionHandler(ActionFactory.COPY.getId(), fCopyAction);
509 IToolBarManager toolBarManager = bars.getToolBarManager();
511 fExportAction = createExportAction();
512 toolBarManager.add(fExportAction);
514 final Action importLogAction = createImportLogAction();
515 toolBarManager.add(importLogAction);
517 toolBarManager.add(new Separator());
519 final Action clearAction = createClearAction();
520 toolBarManager.add(clearAction);
522 fDeleteLogAction = createDeleteLogAction();
523 toolBarManager.add(fDeleteLogAction);
525 fOpenLogAction = createOpenLogAction();
526 toolBarManager.add(fOpenLogAction);
528 fReadLogAction = createReadLogAction();
529 toolBarManager.add(fReadLogAction);
531 toolBarManager.add(new Separator());
532 toolBarManager.add(createShowTextFilter());
535 // toolBarManager.add(new Separator());
536 // final Action testAction = createTestAction();
537 // toolBarManager.add(testAction);
540 IMenuManager mgr = bars.getMenuManager();
542 mgr.add(createGroupByAction());
544 mgr.add(new Separator());
546 mgr.add(createFilterAction());
548 mgr.add(new Separator());
550 fActivateViewAction = createActivateViewAction();
551 mgr.add(fActivateViewAction);
553 // mgr.add(createShowTextFilter());
555 createPropertiesAction();
557 MenuManager popupMenuManager = new MenuManager("#PopupMenu"); //$NON-NLS-1$
558 IMenuListener listener = new IMenuListener() {
559 public void menuAboutToShow(IMenuManager manager) {
560 manager.add(fCopyAction);
561 manager.add(new Separator());
562 manager.add(clearAction);
563 manager.add(fDeleteLogAction);
564 manager.add(fOpenLogAction);
565 manager.add(fReadLogAction);
566 manager.add(new Separator());
567 manager.add(fExportAction);
568 manager.add(createImportLogAction());
569 manager.add(new Separator());
571 ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
572 TreeItem[] selection = fTree.getSelection();
573 if ((selection.length > 0) && (selection[0].getData() instanceof LogEntry)) {
574 manager.add(fPropertiesAction);
577 manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
580 popupMenuManager.addMenuListener(listener);
581 popupMenuManager.setRemoveAllWhenShown(true);
582 getSite().registerContextMenu(popupMenuManager, getSite().getSelectionProvider());
583 Menu menu = popupMenuManager.createContextMenu(fTree);
587 private Action createActivateViewAction() {
588 Action action = new Action(Messages.LogView_activate) { //
590 fMemento.putString(P_ACTIVATE, isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
593 action.setChecked(fMemento.getString(P_ACTIVATE).equals("true")); //$NON-NLS-1$
597 @SuppressWarnings("unused")
598 private Action createTestAction() {
599 Action action = new Action("Test") {
601 IStatus s1 = new Status(IStatus.INFO, Activator.PLUGIN_ID, "Test message 1", null);
602 IStatus s2 = new Status(IStatus.WARNING, Activator.PLUGIN_ID, "Test message 2", null);
603 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 " + HtmlUtil.a("http://www.simantics.org", "simantics.org")), null);
604 MessageService.defaultLog(s1);
605 MessageService.defaultLog(s2);
606 MessageService.defaultLog(s3);
607 // Activator.getDefault().getLog().log(s1);
608 // Activator.getDefault().getLog().log(s2);
609 // Activator.getDefault().getLog().log(s3);
611 MultiStatus s4 = new MultiStatus(Activator.PLUGIN_ID, 0, "Test message 4", new Exception());
612 s4.merge(new Status(IStatus.INFO, Activator.PLUGIN_ID, "MultiStatus Test 1", null));
613 s4.merge(new Status(IStatus.WARNING, Activator.PLUGIN_ID, "MultiStatus Test 2", null));
614 s4.merge(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "MultiStatus Test 3", null));
615 MessageService.defaultLog(s4);
616 // Activator.getDefault().getLog().log(s4);
619 action.setImageDescriptor(ImageDescriptor.getMissingImageDescriptor());
620 action.setToolTipText("Produce test log entries");
624 private Action createClearAction() {
625 Action action = new Action(Messages.LogView_clear) {
630 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_CLEAR));
631 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_CLEAR_DISABLED));
632 action.setToolTipText(Messages.LogView_clear_tooltip);
633 action.setText(Messages.LogView_clear);
637 private Action createCopyAction() {
638 Action action = new Action(Messages.LogView_copy) {
640 copyToClipboard(fFilteredTree.getViewer().getSelection());
643 action.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_COPY));
647 private Action createDeleteLogAction() {
648 Action action = new Action(Messages.LogView_delete) {
653 action.setToolTipText(Messages.LogView_delete_tooltip);
654 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_REMOVE_LOG));
655 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_REMOVE_LOG_DISABLED));
656 action.setEnabled(fInputFile.exists() && isPlatformLog(fInputFile));
660 private Action createExportAction() {
661 Action action = new Action(Messages.LogView_export) {
666 action.setToolTipText(Messages.LogView_export_tooltip);
667 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_EXPORT));
668 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_EXPORT_DISABLED));
669 action.setEnabled(fInputFile.exists());
673 private Action createFilterAction() {
674 Action action = new Action(Messages.LogView_filter) {
679 action.setToolTipText(Messages.LogView_filter);
680 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_FILTER));
681 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_FILTER_DISABLED));
685 private Action createImportLogAction() {
686 Action action = new ImportLogAction(this, Messages.LogView_import, fMemento);
687 action.setToolTipText(Messages.LogView_import_tooltip);
688 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_IMPORT));
689 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_IMPORT_DISABLED));
693 private Action createOpenLogAction() {
694 Action action = null;
697 // TODO this isn't the best way to check... we should be smarter and use package admin
698 // check to see if org.eclipse.ui.ide is available
699 Class.forName("org.eclipse.ui.ide.IDE"); //$NON-NLS-1$
700 // check to see if org.eclipse.core.filesystem is available
701 Class.forName("org.eclipse.core.filesystem.IFileStore"); //$NON-NLS-1$
702 action = new OpenIDELogFileAction(this);
703 } catch (ClassNotFoundException e) {
705 action = new Action() {
707 if (fInputFile.exists()) {
708 Job job = getOpenLogFileJob();
710 job.setPriority(Job.SHORT);
716 action.setText(Messages.LogView_view_currentLog);
717 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_OPEN_LOG));
718 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_OPEN_LOG_DISABLED));
719 action.setEnabled(fInputFile.exists());
720 action.setToolTipText(Messages.LogView_view_currentLog_tooltip);
724 private void createPropertiesAction() {
725 fPropertiesAction = new EventDetailsDialogAction(fTree.getShell(), fFilteredTree.getViewer(), fMemento);
726 fPropertiesAction.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_PROPERTIES));
727 fPropertiesAction.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_PROPERTIES_DISABLED));
728 fPropertiesAction.setToolTipText(Messages.LogView_properties_tooltip);
729 fPropertiesAction.setEnabled(false);
732 private Action createReadLogAction() {
733 Action action = new Action(Messages.LogView_readLog_restore) {
735 fInputFile = getPlatformLogFile();
739 action.setToolTipText(Messages.LogView_readLog_restore_tooltip);
740 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_READ_LOG));
741 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_READ_LOG_DISABLED));
746 * Creates the Show Text Filter view menu action
747 * @return the new action for the Show Text Filter
749 private Action createShowTextFilter() {
750 Action action = new Action(Messages.LogView_show_filter_text, Action.AS_CHECK_BOX) {
752 showFilterText(isChecked());
755 action.setToolTipText(Messages.LogView_show_filter_tooltip);
756 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_SHOW_TEXT_FILTER));
757 boolean visible = fMemento.getBoolean(P_SHOW_FILTER_TEXT).booleanValue();
758 action.setChecked(visible);
759 showFilterText(visible);
764 * Shows/hides the filter text control from the filtered tree. This method also sets the
765 * P_SHOW_FILTER_TEXT preference to the visible state
767 * @param visible if the filter text control should be shown or not
769 private void showFilterText(boolean visible) {
770 fMemento.putBoolean(P_SHOW_FILTER_TEXT, visible);
771 Composite ctrl = fFilteredTree.getFilterControl().getParent();
772 GridData gd = (GridData) ctrl.getLayoutData();
773 gd.exclude = !visible;
774 ctrl.setVisible(visible);
775 gd.verticalIndent = 8;
776 gd.horizontalIndent = 4;
777 if (!visible) // reset control if we aren't visible
778 fFilteredTree.getFilterControl().setText(Messages.LogView_show_filter_initialText);
779 fFilteredTree.layout(false);
782 private IContributionItem createGroupByAction() {
783 IMenuManager manager = new MenuManager(Messages.LogView_GroupBy);
784 manager.add(new GroupByAction(Messages.LogView_GroupBySession, LogView.GROUP_BY_SESSION));
785 manager.add(new GroupByAction(Messages.LogView_GroupByPlugin, LogView.GROUP_BY_PLUGIN));
786 manager.add(new GroupByAction(Messages.LogView_GroupByNone, LogView.GROUP_BY_NONE));
790 private void createViewer(Composite parent) {
792 fFilteredTree = new FilteredTree(parent, SWT.FULL_SELECTION, new PatternFilter() {
793 protected boolean isLeafMatch(Viewer viewer, Object element) {
794 if (element instanceof LogEntry) {
795 LogEntry logEntry = (LogEntry) element;
796 String message = logEntry.getMessage();
797 String plugin = logEntry.getPluginId();
798 String date = logEntry.getFormattedDate();
799 return wordMatches(message) || wordMatches(plugin) || wordMatches(date);
804 fFilteredTree.setInitialText(Messages.LogView_show_filter_initialText);
805 fTree = fFilteredTree.getViewer().getTree();
806 createColumns(fTree);
807 fTree.setLinesVisible(true);
808 fFilteredTree.getViewer().setAutoExpandLevel(fMemento.getInteger(P_EXPAND_LEVEL).intValue());
809 fFilteredTree.getViewer().setContentProvider(new LogViewContentProvider(this));
810 fFilteredTree.getViewer().setLabelProvider(fLabelProvider = new LogViewLabelProvider(this));
811 fLabelProvider.connect(this);
812 fFilteredTree.getViewer().addSelectionChangedListener(new ISelectionChangedListener() {
813 public void selectionChanged(SelectionChangedEvent e) {
814 handleSelectionChanged(e.getSelection());
815 if (fPropertiesAction.isEnabled())
816 ((EventDetailsDialogAction) fPropertiesAction).resetSelection();
819 fFilteredTree.getViewer().addDoubleClickListener(new IDoubleClickListener() {
820 public void doubleClick(DoubleClickEvent event) {
821 ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
822 fPropertiesAction.run();
825 fFilteredTree.getViewer().setInput(this);
830 private void createColumns(final Tree tree) {
831 fColumn1 = new TreeColumn(tree, SWT.LEFT);
832 fColumn1.setText(Messages.LogView_column_message);
833 fColumn1.setWidth(fMemento.getInteger(P_COLUMN_1).intValue());
834 fColumn1.addSelectionListener(new SelectionAdapter() {
835 public void widgetSelected(SelectionEvent e) {
837 ViewerComparator comparator = getViewerComparator(MESSAGE);
838 fFilteredTree.getViewer().setComparator(comparator);
839 boolean isComparatorSet = ((EventDetailsDialogAction) fPropertiesAction).resetSelection(MESSAGE, MESSAGE_ORDER);
840 setComparator(MESSAGE);
841 if (!isComparatorSet)
842 ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
843 fMemento.putInteger(P_ORDER_VALUE, MESSAGE_ORDER);
844 fMemento.putInteger(P_ORDER_TYPE, MESSAGE);
845 setColumnSorting(fColumn1, MESSAGE_ORDER);
848 tree.addControlListener(new ControlAdapter() {
851 public void controlResized(ControlEvent e) {
852 fColumn1.setWidth(tree.getSize().x - 20);
857 // fColumn2 = new TreeColumn(tree, SWT.LEFT);
858 // fColumn2.setText(Messages.LogView_column_plugin);
859 // fColumn2.setWidth(fMemento.getInteger(P_COLUMN_2).intValue());
860 // fColumn2.addSelectionListener(new SelectionAdapter() {
861 // public void widgetSelected(SelectionEvent e) {
862 // PLUGIN_ORDER *= -1;
863 // ViewerComparator comparator = getViewerComparator(PLUGIN);
864 // fFilteredTree.getViewer().setComparator(comparator);
865 // boolean isComparatorSet = ((EventDetailsDialogAction) fPropertiesAction).resetSelection(PLUGIN, PLUGIN_ORDER);
866 // setComparator(PLUGIN);
867 // if (!isComparatorSet)
868 // ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
869 // fMemento.putInteger(P_ORDER_VALUE, PLUGIN_ORDER);
870 // fMemento.putInteger(P_ORDER_TYPE, PLUGIN);
871 // setColumnSorting(fColumn2, PLUGIN_ORDER);
875 // fColumn3 = new TreeColumn(tree, SWT.LEFT);
876 // fColumn3.setText(Messages.LogView_column_date);
877 // fColumn3.setWidth(fMemento.getInteger(P_COLUMN_3).intValue());
878 // fColumn3.addSelectionListener(new SelectionAdapter() {
879 // public void widgetSelected(SelectionEvent e) {
881 // ViewerComparator comparator = getViewerComparator(DATE);
882 // fFilteredTree.getViewer().setComparator(comparator);
883 // setComparator(DATE);
884 // ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
885 // fMemento.putInteger(P_ORDER_VALUE, DATE_ORDER);
886 // fMemento.putInteger(P_ORDER_TYPE, DATE);
887 // setColumnSorting(fColumn3, DATE_ORDER);
891 tree.setHeaderVisible(true);
894 private void initializeViewerSorter() {
895 byte orderType = fMemento.getInteger(P_ORDER_TYPE).byteValue();
896 ViewerComparator comparator = getViewerComparator(orderType);
897 fFilteredTree.getViewer().setComparator(comparator);
898 if (orderType == MESSAGE)
899 setColumnSorting(fColumn1, MESSAGE_ORDER);
900 // else if (orderType == PLUGIN)
901 // setColumnSorting(fColumn2, PLUGIN_ORDER);
902 // else if (orderType == DATE)
903 // setColumnSorting(fColumn3, DATE_ORDER);
906 private void setColumnSorting(TreeColumn column, int order) {
907 fTree.setSortColumn(column);
908 fTree.setSortDirection(order == ASCENDING ? SWT.UP : SWT.DOWN);
911 public void dispose() {
913 MessageService.getDefault().removeLogListener(this);
914 fClipboard.dispose();
915 if (fTextShell != null)
916 fTextShell.dispose();
917 fLabelProvider.disconnect(this);
918 fFilteredTree.dispose();
919 resourceManager.dispose();
923 void handleImport() {
924 FileDialog dialog = new FileDialog(getViewSite().getShell());
925 dialog.setFilterExtensions(new String[] {"*.log"}); //$NON-NLS-1$
926 if (fDirectory != null)
927 dialog.setFilterPath(fDirectory);
928 String path = dialog.open();
929 if (path == null) { // cancel
933 File file = new Path(path).toFile();
935 handleImportPath(path);
937 String msg = NLS.bind(Messages.LogView_FileCouldNotBeFound, file.getName());
938 MessageDialog.openError(getViewSite().getShell(), Messages.LogView_OpenFile, msg);
942 public void handleImportPath(String path) {
943 if (path != null && new Path(path).toFile().exists()) {
944 fInputFile = new Path(path).toFile();
945 fDirectory = fInputFile.getParent();
946 IRunnableWithProgress op = new IRunnableWithProgress() {
947 public void run(IProgressMonitor monitor) {
948 monitor.beginTask(Messages.LogView_operation_importing, IProgressMonitor.UNKNOWN);
952 ProgressMonitorDialog pmd = new ProgressMonitorDialog(getViewSite().getShell());
954 pmd.run(true, true, op);
955 } catch (InvocationTargetException e) { // do nothing
956 } catch (InterruptedException e) { // do nothing
958 fReadLogAction.setText(Messages.LogView_readLog_reload);
959 fReadLogAction.setToolTipText(Messages.LogView_readLog_reload);
961 resetDialogButtons();
966 private void handleExport() {
967 FileDialog dialog = new FileDialog(getViewSite().getShell(), SWT.SAVE);
968 dialog.setFilterExtensions(new String[] {"*.log"}); //$NON-NLS-1$
969 if (fDirectory != null)
970 dialog.setFilterPath(fDirectory);
971 String path = dialog.open();
973 if (path.indexOf('.') == -1 && !path.endsWith(".log")) //$NON-NLS-1$
974 path += ".log"; //$NON-NLS-1$
975 File outputFile = new Path(path).toFile();
976 fDirectory = outputFile.getParent();
977 if (outputFile.exists()) {
978 String message = NLS.bind(Messages.LogView_confirmOverwrite_message, outputFile.toString());
979 if (!MessageDialog.openQuestion(getViewSite().getShell(), Messages.LogView_exportLog, message))
982 copy(fInputFile, outputFile);
986 private void copy(File inputFile, File outputFile) {
987 BufferedReader reader = null;
988 BufferedWriter writer = null;
990 reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile), "UTF-8")); //$NON-NLS-1$
991 writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), "UTF-8")); //$NON-NLS-1$
992 while (reader.ready()) {
993 writer.write(reader.readLine());
994 writer.write(System.getProperty("line.separator")); //$NON-NLS-1$
996 } catch (IOException e) { // do nothing
1003 } catch (IOException e1) { // do nothing
1008 private void handleFilter() {
1009 FilterDialog dialog = new FilterDialog(Activator.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell(), fMemento);
1011 dialog.getShell().setText(Messages.LogView_FilterDialog_title);
1012 if (dialog.open() == Window.OK)
1016 private void doDeleteLog() {
1017 String title = Messages.LogView_confirmDelete_title;
1018 String message = Messages.LogView_confirmDelete_message;
1019 if (!MessageDialog.openConfirm(fTree.getShell(), title, message))
1021 if (fInputFile.delete() || elements.size() > 0) {
1024 currentSession.removeAllChildren();
1025 asyncRefresh(false);
1026 resetDialogButtons();
1030 public void fillContextMenu(IMenuManager manager) { // nothing
1033 public AbstractEntry[] getElements() {
1034 return (AbstractEntry[]) elements.toArray(new AbstractEntry[elements.size()]);
1037 protected void handleClear() {
1038 BusyIndicator.showWhile(fTree.getDisplay(), new Runnable() {
1042 currentSession.removeAllChildren();
1043 asyncRefresh(false);
1044 resetDialogButtons();
1049 protected void reloadLog() {
1050 IRunnableWithProgress op = new IRunnableWithProgress() {
1051 public void run(IProgressMonitor monitor) {
1052 monitor.beginTask(Messages.LogView_operation_reloading, IProgressMonitor.UNKNOWN);
1056 ProgressMonitorDialog pmd = new ProgressMonitorDialog(getViewSite().getShell());
1058 pmd.run(true, true, op);
1059 } catch (InvocationTargetException e) { // do nothing
1060 } catch (InterruptedException e) { // do nothing
1062 fReadLogAction.setText(Messages.LogView_readLog_restore);
1063 fReadLogAction.setToolTipText(Messages.LogView_readLog_restore);
1064 asyncRefresh(false);
1065 resetDialogButtons();
1069 private void readLogFile() {
1070 if (!fInputFile.exists())
1076 List<Object> result = new ArrayList<Object>();
1077 currentSession = LogReader.parseLogFile(fInputFile, result, fMemento);
1079 limitEntriesCount();
1081 getSite().getShell().getDisplay().asyncExec(new Runnable() {
1083 setContentDescription(getTitleSummary());
1089 private String getTitleSummary() {
1090 String path = ""; //$NON-NLS-1$
1092 path = fInputFile.getCanonicalPath();
1093 } catch (IOException e) { // log nothing
1096 if (isPlatformLogOpen()) {
1097 // Remove the content description in this case
1098 // to save vertical space from the view.
1099 //return Messages.LogView_WorkspaceLogFile;
1103 Map<String, File> sources = LogFilesManager.getLogSources();
1104 if (sources.containsValue(path)) {
1105 for (Iterator<String> i = sources.keySet().iterator(); i.hasNext();) {
1106 String key = i.next();
1107 if (sources.get(key).equals(path)) {
1108 return NLS.bind(Messages.LogView_LogFileTitle, new String[] {key, path});
1117 * Add new entries to correct groups in the view.
1118 * @param entries new entries to show up in groups in the view.
1120 private void group(List<?> entries) {
1121 if (fMemento.getInteger(P_GROUP_BY).intValue() == GROUP_BY_NONE) {
1122 elements.addAll(entries);
1124 for (Iterator<?> i = entries.iterator(); i.hasNext();) {
1125 LogEntry entry = (LogEntry) i.next();
1126 Group group = getGroup(entry);
1127 group.addChild(entry);
1133 * Limits the number of entries according to the max entries limit set in
1136 private void limitEntriesCount() {
1137 int limit = Integer.MAX_VALUE;
1138 if (fMemento.getString(LogView.P_USE_LIMIT).equals("true")) {//$NON-NLS-1$
1139 limit = fMemento.getInteger(LogView.P_LOG_LIMIT).intValue();
1142 int entriesCount = getEntriesCount();
1144 if (entriesCount <= limit) {
1147 Comparator<Object> dateComparator = new Comparator<Object>() {
1148 public int compare(Object o1, Object o2) {
1149 Date l1 = ((LogEntry) o1).getDate();
1150 Date l2 = ((LogEntry) o2).getDate();
1151 if ((l1 != null) && (l2 != null)) {
1152 return l1.before(l2) ? -1 : 1;
1153 } else if ((l1 == null) && (l2 == null)) {
1156 return (l1 == null) ? -1 : 1;
1160 if (fMemento.getInteger(P_GROUP_BY).intValue() == GROUP_BY_NONE) {
1161 elements.subList(0, elements.size() - limit).clear();
1163 List<Object> copy = new ArrayList<Object>(entriesCount);
1164 for (Iterator<?> i = elements.iterator(); i.hasNext();) {
1165 AbstractEntry group = (AbstractEntry) i.next();
1166 copy.addAll(Arrays.asList(group.getChildren(group)));
1169 Collections.sort(copy, dateComparator);
1170 List<?> toRemove = copy.subList(0, copy.size() - limit);
1172 for (Iterator<?> i = elements.iterator(); i.hasNext();) {
1173 AbstractEntry group = (AbstractEntry) i.next();
1174 group.removeChildren(toRemove);
1180 private int getEntriesCount() {
1181 if (fMemento.getInteger(P_GROUP_BY).intValue() == GROUP_BY_NONE) {
1182 return elements.size();
1185 for (Iterator<?> i = elements.iterator(); i.hasNext();) {
1186 AbstractEntry group = (AbstractEntry) i.next();
1187 size += group.size();
1193 * Returns group appropriate for the entry. Group depends on P_GROUP_BY
1194 * preference, or is null if grouping is disabled (GROUP_BY_NONE), or group
1195 * could not be determined. May create group if it haven't existed before.
1197 * @param entry entry to be grouped
1198 * @return group or null if grouping is disabled
1200 protected Group getGroup(LogEntry entry) {
1201 int groupBy = fMemento.getInteger(P_GROUP_BY).intValue();
1202 Object elementGroupId = null;
1203 String groupName = null;
1206 case GROUP_BY_PLUGIN :
1207 groupName = entry.getPluginId();
1208 elementGroupId = groupName;
1211 case GROUP_BY_SESSION :
1212 elementGroupId = entry.getSession();
1215 default : // grouping is disabled
1219 if (elementGroupId == null) { // could not determine group
1223 Group group = (Group) groups.get(elementGroupId);
1224 if (group == null) {
1225 if (groupBy == GROUP_BY_SESSION) {
1226 group = entry.getSession();
1228 group = new Group(groupName);
1230 groups.put(elementGroupId, group);
1231 elements.add(group);
1237 public void logging(IStatus status, String plugin) {
1238 if (!isPlatformLog(fInputFile))
1242 // create LogEntry immediately to don't loose IStatus creation date.
1243 LogEntry entry = createLogEntry(status);
1244 batchedEntries.add(entry);
1251 fFirstEvent = false;
1253 LogEntry entry = createLogEntry(status);
1255 if (!batchedEntries.isEmpty()) {
1256 // batch new entry as well, to have only one asyncRefresh()
1257 batchedEntries.add(entry);
1258 pushBatchedEntries();
1267 * Push batched entries to log view.
1269 private void pushBatchedEntries() {
1270 Job job = new Job(Messages.LogView_AddingBatchedEvents) {
1271 protected IStatus run(IProgressMonitor monitor) {
1272 for (int i = 0; i < batchedEntries.size(); i++) {
1273 if (!monitor.isCanceled()) {
1274 LogEntry entry = (LogEntry) batchedEntries.get(i);
1276 batchedEntries.remove(i);
1280 return Status.OK_STATUS;
1286 private LogEntry createLogEntry(IStatus status) {
1287 LogEntry entry = new LogEntry(status);
1288 entry.setSession(currentSession);
1292 private synchronized void pushEntry(LogEntry entry) {
1293 if (LogReader.isLogged(entry, fMemento)) {
1294 group(Collections.singletonList(entry));
1295 limitEntriesCount();
1300 private void asyncRefresh(final boolean activate) {
1301 if (fTree.isDisposed())
1303 Display display = fTree.getDisplay();
1304 final ViewPart view = this;
1305 if (display != null) {
1306 display.asyncExec(new Runnable() {
1308 if (!fTree.isDisposed()) {
1309 fFilteredTree.getViewer().refresh();
1310 fFilteredTree.getViewer().expandToLevel(fMemento.getInteger(P_EXPAND_LEVEL).intValue());
1311 fDeleteLogAction.setEnabled(fInputFile.exists() && isPlatformLog(fInputFile));
1312 fOpenLogAction.setEnabled(fInputFile.exists());
1313 fExportAction.setEnabled(fInputFile.exists());
1314 if (activate && fActivateViewAction.isChecked()) {
1315 IWorkbenchPage page = Activator.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage();
1317 page.bringToTop(view);
1325 public void setFocus() {
1326 if (fFilteredTree != null && !fFilteredTree.isDisposed())
1327 fFilteredTree.setFocus();
1330 private void handleSelectionChanged(ISelection selection) {
1331 updateStatus(selection);
1332 fCopyAction.setEnabled((!selection.isEmpty()) && ((IStructuredSelection) selection).getFirstElement() instanceof LogEntry);
1333 fPropertiesAction.setEnabled(!selection.isEmpty());
1336 private void updateStatus(ISelection selection) {
1337 IStatusLineManager status = getViewSite().getActionBars().getStatusLineManager();
1338 if (selection.isEmpty())
1339 status.setMessage(null);
1341 Object element = ((IStructuredSelection) selection).getFirstElement();
1342 status.setMessage(((LogViewLabelProvider) fFilteredTree.getViewer().getLabelProvider()).getColumnText(element, 0));
1347 * Converts selected log view element to string.
1348 * @return textual log entry representation or null if selection doesn't contain log entry
1350 private static String selectionToString(ISelection selection) {
1351 StringWriter writer = new StringWriter();
1352 PrintWriter pwriter = new PrintWriter(writer);
1353 if (selection.isEmpty())
1355 LogEntry entry = (LogEntry) ((IStructuredSelection) selection).getFirstElement();
1356 entry.write(pwriter);
1358 String textVersion = writer.toString();
1362 } catch (IOException e) {
1370 * Copies selected element to clipboard.
1372 private void copyToClipboard(ISelection selection) {
1373 String textVersion = selectionToString(selection);
1374 if ((textVersion != null) && (textVersion.trim().length() > 0)) {
1375 // set the clipboard contents
1376 fClipboard.setContents(new Object[] {textVersion}, new Transfer[] {TextTransfer.getInstance()});
1380 public void init(IViewSite site, IMemento memento) throws PartInitException {
1381 super.init(site, memento);
1382 if (memento == null)
1383 this.fMemento = XMLMemento.createWriteRoot("LOGVIEW"); //$NON-NLS-1$
1385 this.fMemento = memento;
1388 // initialize column ordering
1389 final byte type = this.fMemento.getInteger(P_ORDER_TYPE).byteValue();
1392 DATE_ORDER = this.fMemento.getInteger(P_ORDER_VALUE).intValue();
1393 MESSAGE_ORDER = DESCENDING;
1394 PLUGIN_ORDER = DESCENDING;
1397 MESSAGE_ORDER = this.fMemento.getInteger(P_ORDER_VALUE).intValue();
1398 DATE_ORDER = DESCENDING;
1399 PLUGIN_ORDER = DESCENDING;
1402 PLUGIN_ORDER = this.fMemento.getInteger(P_ORDER_VALUE).intValue();
1403 MESSAGE_ORDER = DESCENDING;
1404 DATE_ORDER = DESCENDING;
1407 DATE_ORDER = DESCENDING;
1408 MESSAGE_ORDER = DESCENDING;
1409 PLUGIN_ORDER = DESCENDING;
1411 setComparator(fMemento.getInteger(P_ORDER_TYPE).byteValue());
1414 private void initializeMemento() {
1415 if (fMemento.getInteger(P_EXPAND_LEVEL) == null) {
1416 fMemento.putInteger(P_EXPAND_LEVEL, DEFAULT_EXPAND_LEVEL);
1418 if (fMemento.getString(P_USE_LIMIT) == null) {
1419 fMemento.putString(P_USE_LIMIT, "true"); //$NON-NLS-1$
1421 if (fMemento.getInteger(P_LOG_LIMIT) == null) {
1422 fMemento.putInteger(P_LOG_LIMIT, 50);
1424 if (fMemento.getString(P_LOG_INFO) == null) {
1425 fMemento.putString(P_LOG_INFO, "true"); //$NON-NLS-1$
1427 if (fMemento.getString(P_LOG_WARNING) == null) {
1428 fMemento.putString(P_LOG_WARNING, "true"); //$NON-NLS-1$
1430 if (fMemento.getString(P_LOG_ERROR) == null) {
1431 fMemento.putString(P_LOG_ERROR, "true"); //$NON-NLS-1$
1433 if (fMemento.getString(P_LOG_DEBUG) == null) {
1434 fMemento.putString(P_LOG_DEBUG, "true"); //$NON-NLS-1$
1436 if (fMemento.getString(P_SHOW_ALL_SESSIONS) == null) {
1437 fMemento.putString(P_SHOW_ALL_SESSIONS, "false"); //$NON-NLS-1$
1439 Integer width = fMemento.getInteger(P_COLUMN_1);
1440 if (width == null || width.intValue() == 0) {
1441 fMemento.putInteger(P_COLUMN_1, 300);
1443 width = fMemento.getInteger(P_COLUMN_2);
1444 if (width == null || width.intValue() == 0) {
1445 fMemento.putInteger(P_COLUMN_2, 150);
1447 width = fMemento.getInteger(P_COLUMN_3);
1448 if (width == null || width.intValue() == 0) {
1449 fMemento.putInteger(P_COLUMN_3, 150);
1451 if (fMemento.getString(P_ACTIVATE) == null) {
1452 fMemento.putString(P_ACTIVATE, "false"); //$NON-NLS-1$
1454 if (fMemento.getBoolean(P_SHOW_FILTER_TEXT) == null) {
1455 fMemento.putBoolean(P_SHOW_FILTER_TEXT, false);
1457 fMemento.putInteger(P_ORDER_VALUE, DESCENDING);
1458 fMemento.putInteger(P_ORDER_TYPE, DATE);
1459 if (fMemento.getInteger(P_GROUP_BY) == null) {
1460 fMemento.putInteger(P_GROUP_BY, GROUP_BY_NONE);
1464 public void saveState(IMemento memento) {
1465 if (this.fMemento == null || memento == null)
1467 this.fMemento.putInteger(P_COLUMN_1, fColumn1.getWidth());
1468 // this.fMemento.putInteger(P_COLUMN_2, fColumn2.getWidth());
1469 // this.fMemento.putInteger(P_COLUMN_3, fColumn3.getWidth());
1470 this.fMemento.putString(P_ACTIVATE, fActivateViewAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1471 memento.putMemento(this.fMemento);
1475 private void addMouseListeners() {
1476 Listener tableListener = new Listener() {
1477 public void handleEvent(Event e) {
1479 case SWT.MouseMove :
1482 case SWT.MouseHover :
1485 case SWT.MouseDown :
1491 int[] tableEvents = new int[] {SWT.MouseDown, SWT.MouseMove, SWT.MouseHover};
1492 for (int i = 0; i < tableEvents.length; i++) {
1493 fTree.addListener(tableEvents[i], tableListener);
1498 * Adds drag source support to error log tree.
1500 private void addDragSource() {
1501 DragSource source = new DragSource(fTree, DND.DROP_COPY);
1502 Transfer[] types = new Transfer[] {TextTransfer.getInstance()};
1503 source.setTransfer(types);
1505 source.addDragListener(new DragSourceAdapter() {
1507 public void dragStart(DragSourceEvent event) {
1508 ISelection selection = fFilteredTree.getViewer().getSelection();
1509 if (selection.isEmpty()) {
1514 AbstractEntry entry = (AbstractEntry) ((TreeSelection) selection).getFirstElement();
1515 if (!(entry instanceof LogEntry)) {
1521 public void dragSetData(DragSourceEvent event) {
1522 if (!TextTransfer.getInstance().isSupportedType(event.dataType)) {
1526 ISelection selection = fFilteredTree.getViewer().getSelection();
1527 String textVersion = selectionToString(selection);
1528 event.data = textVersion;
1533 private void makeHoverShell() {
1534 fTextShell = new Shell(fTree.getShell(), SWT.NO_FOCUS | SWT.ON_TOP | SWT.TOOL);
1535 Display display = fTextShell.getDisplay();
1536 fTextShell.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
1537 GridLayout layout = new GridLayout(1, false);
1538 int border = ((fTree.getShell().getStyle() & SWT.NO_TRIM) == 0) ? 0 : 1;
1539 layout.marginHeight = border;
1540 layout.marginWidth = border;
1541 fTextShell.setLayout(layout);
1542 fTextShell.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1543 Composite shellComposite = new Composite(fTextShell, SWT.NONE);
1544 layout = new GridLayout();
1545 layout.marginHeight = 0;
1546 layout.marginWidth = 0;
1547 shellComposite.setLayout(layout);
1548 shellComposite.setLayoutData(new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_BEGINNING));
1549 fTextLabel = new Text(shellComposite, SWT.WRAP | SWT.MULTI | SWT.READ_ONLY);
1550 GridData gd = new GridData(GridData.FILL_BOTH);
1552 gd.grabExcessHorizontalSpace = true;
1553 fTextLabel.setLayoutData(gd);
1554 Color c = fTree.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
1555 fTextLabel.setBackground(c);
1556 c = fTree.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
1557 fTextLabel.setForeground(c);
1558 fTextLabel.setEditable(false);
1559 fTextShell.addDisposeListener(new DisposeListener() {
1560 public void widgetDisposed(DisposeEvent e) {
1561 onTextShellDispose(e);
1566 void onTextShellDispose(DisposeEvent e) {
1567 fCanOpenTextShell = true;
1571 void onMouseDown(Event e) {
1572 if (fTextShell != null && !fTextShell.isDisposed() && !fTextShell.isFocusControl()) {
1573 fTextShell.setVisible(false);
1574 fCanOpenTextShell = true;
1578 void onMouseHover(Event e) {
1579 if (!fCanOpenTextShell || fTextShell == null || fTextShell.isDisposed())
1581 fCanOpenTextShell = false;
1582 Point point = new Point(e.x, e.y);
1583 TreeItem item = fTree.getItem(point);
1587 String message = null;
1588 if (item.getData() instanceof LogEntry) {
1589 message = ((LogEntry) item.getData()).getStack();
1590 } else if (item.getData() instanceof LogSession) {
1591 LogSession session = ((LogSession) item.getData());
1592 message = Messages.LogView_SessionStarted;
1593 if (session.getDate() != null) {
1594 DateFormat formatter = new SimpleDateFormat(LogEntry.F_DATE_FORMAT);
1595 message += formatter.format(session.getDate());
1599 if (message == null)
1602 fTextLabel.setText(message);
1603 Rectangle bounds = fTree.getDisplay().getBounds();
1604 Point cursorPoint = fTree.getDisplay().getCursorLocation();
1606 int y = point.y + 25;
1607 int width = fTree.getColumn(0).getWidth();
1609 if (cursorPoint.x + width > bounds.width)
1611 if (cursorPoint.y + height + 25 > bounds.height)
1614 fTextShell.setLocation(fTree.toDisplay(x, y));
1615 fTextShell.setSize(width, height);
1616 fTextShell.setVisible(true);
1619 void onMouseMove(Event e) {
1620 if (fTextShell != null && !fTextShell.isDisposed() && fTextShell.isVisible())
1621 fTextShell.setVisible(false);
1623 Point point = new Point(e.x, e.y);
1624 TreeItem item = fTree.getItem(point);
1627 Image image = item.getImage();
1628 Object data = item.getData();
1629 if (data instanceof LogEntry) {
1630 LogEntry entry = (LogEntry) data;
1631 int parentCount = getNumberOfParents(entry);
1632 int startRange = 20 + Math.max(image.getBounds().width + 2, 7 + 2) * parentCount;
1633 int endRange = startRange + 16;
1634 fCanOpenTextShell = e.x >= startRange && e.x <= endRange;
1638 private int getNumberOfParents(AbstractEntry entry) {
1639 AbstractEntry parent = (AbstractEntry) entry.getParent(entry);
1642 return 1 + getNumberOfParents(parent);
1645 public Comparator<Object> getComparator() {
1649 private void setComparator(byte sortType) {
1650 if (sortType == DATE) {
1651 fComparator = new Comparator<Object>() {
1652 public int compare(Object e1, Object e2) {
1655 if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1656 date1 = ((LogEntry) e1).getDate().getTime();
1657 date2 = ((LogEntry) e2).getDate().getTime();
1658 } else if ((e1 instanceof LogSession) && (e2 instanceof LogSession)) {
1659 date1 = ((LogSession) e1).getDate() == null ? 0 : ((LogSession) e1).getDate().getTime();
1660 date2 = ((LogSession) e2).getDate() == null ? 0 : ((LogSession) e2).getDate().getTime();
1662 if (date1 == date2) {
1663 int result = elements.indexOf(e2) - elements.indexOf(e1);
1664 if (DATE_ORDER == DESCENDING)
1665 result *= DESCENDING;
1668 if (DATE_ORDER == DESCENDING)
1669 return date1 > date2 ? DESCENDING : ASCENDING;
1670 return date1 < date2 ? DESCENDING : ASCENDING;
1673 } else if (sortType == PLUGIN) {
1674 fComparator = new Comparator<Object>() {
1675 public int compare(Object e1, Object e2) {
1676 if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1677 LogEntry entry1 = (LogEntry) e1;
1678 LogEntry entry2 = (LogEntry) e2;
1679 return getDefaultComparator().compare(entry1.getPluginId(), entry2.getPluginId()) * PLUGIN_ORDER;
1685 fComparator = new Comparator<Object>() {
1686 public int compare(Object e1, Object e2) {
1687 if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1688 LogEntry entry1 = (LogEntry) e1;
1689 LogEntry entry2 = (LogEntry) e2;
1690 return getDefaultComparator().compare(entry1.getMessage(), entry2.getMessage()) * MESSAGE_ORDER;
1698 @SuppressWarnings("unchecked")
1699 private Comparator<Object> getDefaultComparator() {
1700 return Policy.getComparator();
1703 private ViewerComparator getViewerComparator(byte sortType) {
1704 if (sortType == PLUGIN) {
1705 return new ViewerComparator() {
1706 @SuppressWarnings("unchecked")
1707 public int compare(Viewer viewer, Object e1, Object e2) {
1708 if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1709 LogEntry entry1 = (LogEntry) e1;
1710 LogEntry entry2 = (LogEntry) e2;
1711 return getComparator().compare(entry1.getPluginId(), entry2.getPluginId()) * PLUGIN_ORDER;
1716 } else if (sortType == MESSAGE) {
1717 return new ViewerComparator() {
1718 @SuppressWarnings("unchecked")
1719 public int compare(Viewer viewer, Object e1, Object e2) {
1720 if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1721 LogEntry entry1 = (LogEntry) e1;
1722 LogEntry entry2 = (LogEntry) e2;
1723 return getComparator().compare(entry1.getMessage(), entry2.getMessage()) * MESSAGE_ORDER;
1729 return new ViewerComparator() {
1730 private int indexOf(Object[] array, Object o) {
1733 for (int i = 0; i < array.length; ++i)
1734 if (o.equals(array[i]))
1739 public int compare(Viewer viewer, Object e1, Object e2) {
1742 if ((e1 instanceof LogEntry) && (e2 instanceof LogEntry)) {
1743 date1 = ((LogEntry) e1).getDate().getTime();
1744 date2 = ((LogEntry) e2).getDate().getTime();
1745 } else if ((e1 instanceof LogSession) && (e2 instanceof LogSession)) {
1746 date1 = ((LogSession) e1).getDate() == null ? 0 : ((LogSession) e1).getDate().getTime();
1747 date2 = ((LogSession) e2).getDate() == null ? 0 : ((LogSession) e2).getDate().getTime();
1750 if (date1 == date2) {
1751 // Everything that appears in logview should be an AbstractEntry.
1752 AbstractEntry parent = (AbstractEntry) ((AbstractEntry) e1).getParent(null);
1753 Object[] children = null;
1755 children = parent.getChildren(parent);
1758 if (children != null) {
1759 // The elements in children seem to be in reverse order,
1760 // i.e. latest log message first, therefore index(e2)-index(e1)
1761 result = indexOf(children, e2) - indexOf(children, e1);
1763 result = elements.indexOf(e1) - elements.indexOf(e2);
1765 if (DATE_ORDER == DESCENDING)
1766 result *= DESCENDING;
1769 if (DATE_ORDER == DESCENDING)
1770 return date1 > date2 ? DESCENDING : ASCENDING;
1771 return date1 < date2 ? DESCENDING : ASCENDING;
1777 private void resetDialogButtons() {
1778 ((EventDetailsDialogAction) fPropertiesAction).resetDialogButtons();
1782 * Returns the filter dialog settings object used to maintain
1783 * state between filter dialogs
1784 * @return the dialog settings to be used
1786 private IDialogSettings getLogSettings() {
1787 IDialogSettings settings = Activator.getDefault().getDialogSettings();
1788 return settings.getSection(getClass().getName());
1792 * Returns the plugin preferences used to maintain
1794 * @return the plugin preferences
1796 private Preferences getLogPreferences() {
1797 return Activator.getDefault().getPluginPreferences();
1800 private void readSettings() {
1801 IDialogSettings s = getLogSettings();
1802 Preferences p = getLogPreferences();
1803 if (s == null || p == null) {
1804 initializeMemento();
1808 fMemento.putString(P_USE_LIMIT, s.getBoolean(P_USE_LIMIT) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1809 fMemento.putString(P_LOG_INFO, s.getBoolean(P_LOG_INFO) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1810 fMemento.putString(P_LOG_WARNING, s.getBoolean(P_LOG_WARNING) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1811 fMemento.putString(P_LOG_ERROR, s.getBoolean(P_LOG_ERROR) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1812 fMemento.putString(P_LOG_DEBUG, s.getBoolean(P_LOG_DEBUG) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1813 fMemento.putString(P_SHOW_ALL_SESSIONS, s.getBoolean(P_SHOW_ALL_SESSIONS) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1814 fMemento.putInteger(P_LOG_LIMIT, s.getInt(P_LOG_LIMIT));
1815 fMemento.putInteger(P_COLUMN_1, p.getInt(P_COLUMN_1) > 0 ? p.getInt(P_COLUMN_1) : 300);
1816 fMemento.putInteger(P_COLUMN_2, p.getInt(P_COLUMN_2) > 0 ? p.getInt(P_COLUMN_2) : 150);
1817 fMemento.putInteger(P_COLUMN_3, p.getInt(P_COLUMN_3) > 0 ? p.getInt(P_COLUMN_3) : 150);
1818 fMemento.putString(P_ACTIVATE, p.getBoolean(P_ACTIVATE) ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
1819 int order = p.getInt(P_ORDER_VALUE);
1820 fMemento.putInteger(P_ORDER_VALUE, order == 0 ? DESCENDING : order);
1821 fMemento.putInteger(P_ORDER_TYPE, p.getInt(P_ORDER_TYPE));
1822 fMemento.putBoolean(P_SHOW_FILTER_TEXT, p.getBoolean(P_SHOW_FILTER_TEXT));
1823 fMemento.putInteger(P_GROUP_BY, p.getInt(P_GROUP_BY));
1824 fMemento.putInteger(P_EXPAND_LEVEL, s.getInt(P_EXPAND_LEVEL));
1825 } catch (NumberFormatException e) {
1826 fMemento.putInteger(P_EXPAND_LEVEL, DEFAULT_EXPAND_LEVEL);
1827 fMemento.putInteger(P_LOG_LIMIT, 50);
1828 fMemento.putInteger(P_COLUMN_1, 300);
1829 fMemento.putInteger(P_COLUMN_2, 150);
1830 fMemento.putInteger(P_COLUMN_3, 150);
1831 fMemento.putInteger(P_ORDER_TYPE, DATE);
1832 fMemento.putInteger(P_ORDER_VALUE, DESCENDING);
1833 fMemento.putInteger(P_GROUP_BY, GROUP_BY_NONE);
1837 private void writeSettings() {
1838 writeViewSettings();
1839 writeFilterSettings();
1842 private void writeFilterSettings() {
1843 IDialogSettings settings = getLogSettings();
1844 if (settings == null)
1845 settings = Activator.getDefault().getDialogSettings().addNewSection(getClass().getName());
1846 settings.put(P_USE_LIMIT, fMemento.getString(P_USE_LIMIT).equals("true")); //$NON-NLS-1$
1847 settings.put(P_LOG_LIMIT, fMemento.getInteger(P_LOG_LIMIT).intValue());
1848 settings.put(P_LOG_INFO, fMemento.getString(P_LOG_INFO).equals("true")); //$NON-NLS-1$
1849 settings.put(P_LOG_WARNING, fMemento.getString(P_LOG_WARNING).equals("true")); //$NON-NLS-1$
1850 settings.put(P_LOG_ERROR, fMemento.getString(P_LOG_ERROR).equals("true")); //$NON-NLS-1$
1851 settings.put(P_LOG_DEBUG, fMemento.getString(P_LOG_DEBUG).equals("true")); //$NON-NLS-1$
1852 settings.put(P_SHOW_ALL_SESSIONS, fMemento.getString(P_SHOW_ALL_SESSIONS).equals("true")); //$NON-NLS-1$
1855 private void writeViewSettings() {
1856 Preferences preferences = getLogPreferences();
1857 preferences.setValue(P_COLUMN_1, fMemento.getInteger(P_COLUMN_1).intValue());
1858 preferences.setValue(P_COLUMN_2, fMemento.getInteger(P_COLUMN_2).intValue());
1859 preferences.setValue(P_COLUMN_3, fMemento.getInteger(P_COLUMN_3).intValue());
1860 preferences.setValue(P_ACTIVATE, fMemento.getString(P_ACTIVATE).equals("true")); //$NON-NLS-1$
1861 int order = fMemento.getInteger(P_ORDER_VALUE).intValue();
1862 preferences.setValue(P_ORDER_VALUE, order == 0 ? DESCENDING : order);
1863 preferences.setValue(P_ORDER_TYPE, fMemento.getInteger(P_ORDER_TYPE).intValue());
1864 preferences.setValue(P_SHOW_FILTER_TEXT, fMemento.getBoolean(P_SHOW_FILTER_TEXT).booleanValue());
1865 preferences.setValue(P_GROUP_BY, fMemento.getInteger(P_GROUP_BY).intValue());
1866 preferences.setValue(P_EXPAND_LEVEL, fMemento.getInteger(P_EXPAND_LEVEL).intValue());
1869 public void sortByDateDescending() {
1870 setColumnSorting(fColumn3, DESCENDING);
1873 protected Job getOpenLogFileJob() {
1874 final Shell shell = getViewSite().getShell();
1875 return new Job(Messages.OpenLogDialog_message) {
1876 protected IStatus run(IProgressMonitor monitor) {
1877 boolean failed = false;
1878 if (fInputFile.length() <= LogReader.MAX_FILE_LENGTH) {
1879 failed = !Program.launch(fInputFile.getAbsolutePath());
1881 Program p = Program.findProgram(".txt"); //$NON-NLS-1$
1883 p.execute(fInputFile.getAbsolutePath());
1884 return Status.OK_STATUS;
1889 final OpenLogDialog openDialog = new OpenLogDialog(shell, fInputFile);
1890 Display.getDefault().asyncExec(new Runnable() {
1892 openDialog.create();
1897 return Status.OK_STATUS;
1902 protected File getLogFile() {
1907 * Returns whether given session equals to currently displayed in LogView.
1908 * @param session LogSession
1909 * @return true if given session equals to currently displayed in LogView
1911 public boolean isCurrentLogSession(LogSession session) {
1912 return isPlatformLogOpen() && (currentSession != null) && (currentSession.equals(session));
1916 * Returns whether currently open log is platform log or imported file.
1917 * @return true if currently open log is platform log, false otherwise
1919 public boolean isPlatformLogOpen() {
1920 return isPlatformLog(fInputFile);
1924 * Returns whether currently open log is platform log or imported file.
1925 * @return true if currently open log is platform log, false otherwise
1927 public boolean isPlatformLog(File file) {
1928 return (file.equals(getPlatformLogFile()));
1931 public File getPlatformLogFile() {
1932 return org.simantics.message.internal.Activator.getLogFile();