Workaround to fix performance problems when opening log view
[simantics/platform.git] / bundles / org.simantics.message.ui / src / org / simantics / message / ui / LogView.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.message.ui;
13
14 import java.io.BufferedReader;
15 import java.io.BufferedWriter;
16 import java.io.File;
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;
25 import java.net.URI;
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;
35 import java.util.Map;
36
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;
137
138 import com.ibm.icu.text.DateFormat;
139 import com.ibm.icu.text.SimpleDateFormat;
140
141 /**
142  * A direct rip of Eclipse's own <code>LogView</code> for our own purposes.
143  * 
144  * <p>
145  * The only thing that has changed is the the dependence on
146  * <code>Activator.getLogFile()</code> instead of
147  * <code>Platform.getLogFileLocation()</code>.
148  * 
149  * @author Tuukka Lehtonen
150  * @see org.eclipse.ui.internal.views.LogView
151  */
152 @SuppressWarnings("deprecation")
153 public class LogView extends ViewPart implements ILogListener {
154
155     public static final int      DEFAULT_EXPAND_LEVEL = 1;
156     
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$
174
175         private int                  MESSAGE_ORDER;
176     private int                  PLUGIN_ORDER;
177     private int                  DATE_ORDER;
178
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;
184
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;
188
189         private List<Object>         elements;
190     private Map<Object, Object>  groups;
191     private LogSession           currentSession;
192
193         private List<Object>         batchedEntries;
194     private boolean              batchEntries;
195
196     private Clipboard            fClipboard;
197
198         private IMemento             fMemento;
199     private File                 fInputFile;
200     private String               fDirectory;
201
202         private Comparator<Object>   fComparator;
203
204         // hover text
205     private boolean              fCanOpenTextShell;
206     private Text                 fTextLabel;
207     private Shell                fTextShell;
208
209         private boolean              fFirstEvent         = true;
210
211         private TreeColumn           fColumn1;
212     @SuppressWarnings("unused")
213     private TreeColumn           fColumn2;
214     private TreeColumn           fColumn3;
215
216         private Tree                 fTree;
217         private FilteredTree         fFilteredTree;
218     private LogViewLabelProvider fLabelProvider;
219     //private ScrolledComposite    fMessageDescriptorComposite;
220     //private FormText             fMessageDescription;
221     private Browser              fMessageDescription;
222
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;
230
231         private LocalResourceManager resourceManager;
232
233         /**
234          * Action called when user selects "Group by -> ..." from menu.
235          */
236         class GroupByAction extends Action {
237                 private int groupBy;
238
239                 public GroupByAction(String text, int groupBy) {
240                         super(text, IAction.AS_RADIO_BUTTON);
241
242                         this.groupBy = groupBy;
243
244                         if (fMemento.getInteger(LogView.P_GROUP_BY).intValue() == groupBy) {
245                                 setChecked(true);
246                         }
247                 }
248
249                 public void run() {
250                         if (fMemento.getInteger(LogView.P_GROUP_BY).intValue() != groupBy) {
251                                 fMemento.putInteger(LogView.P_GROUP_BY, groupBy);
252                                 reloadLog();
253                         }
254                 }
255         }
256
257         /**
258          * Constructor
259          */
260         public LogView() {
261                 elements = new ArrayList<Object>();
262                 groups = new HashMap<Object, Object>();
263                 batchedEntries = new ArrayList<Object>();
264                 fInputFile = getPlatformLogFile();
265         }
266
267         /* (non-Javadoc)
268          * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
269          */
270         public void createPartControl(Composite parent) {
271             resourceManager = new LocalResourceManager(JFaceResources.getResources());
272             
273             SashForm sashForm = new SashForm(parent, SWT.VERTICAL | SWT.SMOOTH);
274             
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);
286
287                 readLogFile();
288                 createViewer(composite);
289                 getSite().setSelectionProvider(fFilteredTree.getViewer());
290                 createActions();
291                 fClipboard = new Clipboard(fTree.getDisplay());
292                 fTree.setToolTipText(""); //$NON-NLS-1$
293                 initializeViewerSorter();
294
295                 makeHoverShell();
296
297                 /*
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);
306
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() {
313                     @Override
314                     public void linkActivated(HyperlinkEvent event) {
315                         String s = (String) event.data;
316                 //System.out.println("link activated: " + s);
317                 try {
318                     URI uri = new URI(s);
319                     String scheme = uri.getScheme();
320                     String schemeSpecificPart = uri.getSchemeSpecificPart();
321
322                     IMessageSchemeManager msm = org.simantics.message.internal.Activator.getDefault().getMessageSchemeManager();
323                     IMessageDataSchemeExtension[] exts = msm.getByScheme(scheme);
324
325                     for (IMessageDataSchemeExtension ext : exts) {
326                         Object data = ext.getSerializer().deserialize(schemeSpecificPart);
327                         ext.getHandler().perform(data);
328                         return;
329                     }
330                 } catch (URISyntaxException e) {
331                     // TODO Auto-generated catch block
332                     e.printStackTrace();
333                 } catch (ReferenceSerializationException e) {
334                     // TODO Auto-generated catch block
335                     e.printStackTrace();
336                 }
337                     }
338                     @Override
339                     public void linkEntered(HyperlinkEvent e) {
340                         //System.out.println("link entered: " + e.data);
341                     }
342                     @Override
343                     public void linkExited(HyperlinkEvent e) {
344                         //System.out.println("link exited: " + e.data);
345                     }
346                 });
347                 fMessageDescription.setText("Select a message to show its description here.", false, false);
348
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);
359                     }
360                 });
361                 */
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() {
366                     @Override
367                     public void changed(LocationEvent event) {
368                         //System.out.println("changed: " + event);
369                     }
370                     @Override
371                     public void changing(LocationEvent event) {
372                         //System.out.println("changing: " + event);
373                         String location = event.location;
374                         if ("about:blank".equals(location)) {
375                             event.doit = true;
376                         } else {
377                             event.doit = false;
378                             //System.out.println("link activated: " + location);
379                             try {
380                                 URI uri = new URI(location);
381                                 String scheme = uri.getScheme();
382                                 //String schemeSpecificPart = uri.getSchemeSpecificPart();
383
384                                 IMessageSchemeManager msm = org.simantics.message.internal.Activator.getDefault().getMessageSchemeManager();
385                                 IMessageDataSchemeExtension[] exts = msm.getByScheme(scheme);
386
387                                 for (IMessageDataSchemeExtension ext : exts) {
388                                     Object data = ext.getSerializer().deserialize(uri);
389                                     try {
390                                         ext.getHandler().perform(data);
391                                         return;
392                                     } catch (MessageSchemeException e) {
393                                         ErrorLogger.defaultLogError(e);
394                                     }
395                                 }
396                                 return;
397                             } catch (URISyntaxException e) {
398                                 // TODO Auto-generated catch block
399                                 e.printStackTrace();
400                             } catch (ReferenceSerializationException e) {
401                                 // TODO Auto-generated catch block
402                                 e.printStackTrace();
403                             }
404                         }
405                     }
406                 });
407
408                 sashForm.setWeights(new int[] { 5, 2 });
409                 
410                 fFilteredTree.getViewer().addSelectionChangedListener(new ISelectionChangedListener() {
411             @Override
412             public void selectionChanged(SelectionChangedEvent event) {
413                 IStructuredSelection s = (IStructuredSelection) event.getSelection();
414                 if (s.isEmpty()) {
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>");
417                 } else {
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();
424                         
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;
436                         }
437                         try {
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);
445                         }
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());
451                         } else {
452                             Group grp = (Group) entry;
453                             //fMessageDescription.setText(grp.toString(), false, false);
454                             fMessageDescription.setText(grp.toString());
455                         }
456                     }
457                 }
458                 //fMessageDescription.refresh();
459                 //composite.layout(true);
460             }
461                 });
462
463                 MessageService.getDefault().addLogListener(this);
464 //              PlatformUI.getWorkbench().getHelpSystem().setHelp(fTree, IHelpContextIds.LOG_VIEW);
465                 getSite().getWorkbenchWindow().addPerspectiveListener(new IPerspectiveListener2() {
466
467                         public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective, IWorkbenchPartReference partRef, String changeId) {
468                                 if (!(partRef instanceof IViewReference))
469                                         return;
470
471                                 IWorkbenchPart part = partRef.getPart(false);
472                                 if (part == null) {
473                                         return;
474                                 }
475
476                                 if (part.equals(LogView.this)) {
477                                         if (changeId.equals(IWorkbenchPage.CHANGE_VIEW_SHOW)) {
478                                                 if (!batchedEntries.isEmpty()) {
479                                                         pushBatchedEntries();
480                                                 }
481
482                                                 batchEntries = false;
483                                         } else if (changeId.equals(IWorkbenchPage.CHANGE_VIEW_HIDE)) {
484                                                 batchEntries = true;
485                                         }
486                                 }
487                         }
488
489                         public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspective) {
490                                 // empty
491                         }
492
493                         public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective, String changeId) {
494                                 // empty
495                         }
496
497                 });
498         }
499
500         /**
501          * Creates the actions for the viewsite action bars
502          */
503         private void createActions() {
504                 IActionBars bars = getViewSite().getActionBars();
505
506                 fCopyAction = createCopyAction();
507                 bars.setGlobalActionHandler(ActionFactory.COPY.getId(), fCopyAction);
508
509                 IToolBarManager toolBarManager = bars.getToolBarManager();
510
511                 fExportAction = createExportAction();
512                 toolBarManager.add(fExportAction);
513
514                 final Action importLogAction = createImportLogAction();
515                 toolBarManager.add(importLogAction);
516
517         toolBarManager.add(new Separator());
518
519                 final Action clearAction = createClearAction();
520                 toolBarManager.add(clearAction);
521
522                 fDeleteLogAction = createDeleteLogAction();
523                 toolBarManager.add(fDeleteLogAction);
524
525                 fOpenLogAction = createOpenLogAction();
526                 toolBarManager.add(fOpenLogAction);
527
528                 fReadLogAction = createReadLogAction();
529                 toolBarManager.add(fReadLogAction);
530
531                 toolBarManager.add(new Separator());
532                 toolBarManager.add(createShowTextFilter());
533
534         // FOR TESTING
535 //        toolBarManager.add(new Separator());
536 //        final Action testAction = createTestAction();
537 //        toolBarManager.add(testAction);
538         // FOR TESTING ENDS
539                 
540                 IMenuManager mgr = bars.getMenuManager();
541
542                 mgr.add(createGroupByAction());
543
544                 mgr.add(new Separator());
545
546                 mgr.add(createFilterAction());
547
548                 mgr.add(new Separator());
549
550                 fActivateViewAction = createActivateViewAction();
551                 mgr.add(fActivateViewAction);
552
553 //              mgr.add(createShowTextFilter());
554
555                 createPropertiesAction();
556
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());
570
571                                 ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
572                                 TreeItem[] selection = fTree.getSelection();
573                                 if ((selection.length > 0) && (selection[0].getData() instanceof LogEntry)) {
574                                         manager.add(fPropertiesAction);
575                                 }
576
577                                 manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
578                         }
579                 };
580                 popupMenuManager.addMenuListener(listener);
581                 popupMenuManager.setRemoveAllWhenShown(true);
582                 getSite().registerContextMenu(popupMenuManager, getSite().getSelectionProvider());
583                 Menu menu = popupMenuManager.createContextMenu(fTree);
584                 fTree.setMenu(menu);
585         }
586
587         private Action createActivateViewAction() {
588                 Action action = new Action(Messages.LogView_activate) { //              
589                         public void run() {
590                                 fMemento.putString(P_ACTIVATE, isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$
591                         }
592                 };
593                 action.setChecked(fMemento.getString(P_ACTIVATE).equals("true")); //$NON-NLS-1$
594                 return action;
595         }
596
597     @SuppressWarnings("unused")
598     private Action createTestAction() {
599         Action action = new Action("Test") {
600             public void run() {
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);
610
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);
617             }
618         };
619         action.setImageDescriptor(ImageDescriptor.getMissingImageDescriptor());
620         action.setToolTipText("Produce test log entries");
621         return action;
622     }
623
624     private Action createClearAction() {
625         Action action = new Action(Messages.LogView_clear) {
626             public void run() {
627                 handleClear();
628             }
629         };
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);
634         return action;
635     }
636
637         private Action createCopyAction() {
638                 Action action = new Action(Messages.LogView_copy) {
639                         public void run() {
640                                 copyToClipboard(fFilteredTree.getViewer().getSelection());
641                         }
642                 };
643                 action.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_COPY));
644                 return action;
645         }
646
647         private Action createDeleteLogAction() {
648                 Action action = new Action(Messages.LogView_delete) {
649                         public void run() {
650                                 doDeleteLog();
651                         }
652                 };
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));
657                 return action;
658         }
659
660         private Action createExportAction() {
661                 Action action = new Action(Messages.LogView_export) {
662                         public void run() {
663                                 handleExport();
664                         }
665                 };
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());
670                 return action;
671         }
672
673         private Action createFilterAction() {
674                 Action action = new Action(Messages.LogView_filter) {
675                         public void run() {
676                                 handleFilter();
677                         }
678                 };
679                 action.setToolTipText(Messages.LogView_filter);
680                 action.setImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_FILTER));
681                 action.setDisabledImageDescriptor(SharedImages.getImageDescriptor(SharedImages.DESC_FILTER_DISABLED));
682                 return action;
683         }
684
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));
690                 return action;
691         }
692
693         private Action createOpenLogAction() {
694                 Action action = null;
695                 /*
696                 try {
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) {
704                 */
705                         action = new Action() {
706                                 public void run() {
707                                         if (fInputFile.exists()) {
708                                                 Job job = getOpenLogFileJob();
709                                                 job.setUser(false);
710                                                 job.setPriority(Job.SHORT);
711                                                 job.schedule();
712                                         }
713                                 }
714                         };
715                 //}
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);
721                 return action;
722         }
723
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);
730         }
731
732         private Action createReadLogAction() {
733                 Action action = new Action(Messages.LogView_readLog_restore) {
734                         public void run() {
735                                 fInputFile = getPlatformLogFile();
736                                 reloadLog();
737                         }
738                 };
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));
742                 return action;
743         }
744
745         /**
746          * Creates the Show Text Filter view menu action 
747          * @return the new action for the Show Text Filter 
748          */
749         private Action createShowTextFilter() {
750                 Action action = new Action(Messages.LogView_show_filter_text, Action.AS_CHECK_BOX) {
751                         public void run() {
752                                 showFilterText(isChecked());
753                         }
754                 };
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);
760                 return action;
761         }
762
763         /**
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
766          * 
767          * @param visible if the filter text control should be shown or not
768          */
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);
780         }
781
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));
787                 return manager;
788         }
789
790         private void createViewer(Composite parent) {
791
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);
800                                 }
801                                 return false;
802                         }
803                 });
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();
817                         }
818                 });
819                 fFilteredTree.getViewer().addDoubleClickListener(new IDoubleClickListener() {
820                         public void doubleClick(DoubleClickEvent event) {
821                                 ((EventDetailsDialogAction) fPropertiesAction).setComparator(fComparator);
822                                 fPropertiesAction.run();
823                         }
824                 });
825                 fFilteredTree.getViewer().setInput(this);
826                 addMouseListeners();
827                 addDragSource();
828         }
829
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) {
836                                 MESSAGE_ORDER *= -1;
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);
846                         }
847                 });
848                 tree.addControlListener(new ControlAdapter() {
849
850                         @Override
851                         public void controlResized(ControlEvent e) {
852                                 fColumn1.setWidth(tree.getSize().x - 20);
853                         }
854                         
855                 });
856
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);
872 //                      }
873 //              });
874 //
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) {
880 //                              DATE_ORDER *= -1;
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);
888 //                      }
889 //              });
890
891                 tree.setHeaderVisible(true);
892         }
893
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);
904         }
905
906         private void setColumnSorting(TreeColumn column, int order) {
907                 fTree.setSortColumn(column);
908                 fTree.setSortDirection(order == ASCENDING ? SWT.UP : SWT.DOWN);
909         }
910
911         public void dispose() {
912                 writeSettings();
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();
920                 super.dispose();
921         }
922
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
930                         return;
931                 }
932
933                 File file = new Path(path).toFile();
934                 if (file.exists()) {
935                         handleImportPath(path);
936                 } else {
937                         String msg = NLS.bind(Messages.LogView_FileCouldNotBeFound, file.getName());
938                         MessageDialog.openError(getViewSite().getShell(), Messages.LogView_OpenFile, msg);
939                 }
940         }
941
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);
949                                         readLogFile();
950                                 }
951                         };
952                         ProgressMonitorDialog pmd = new ProgressMonitorDialog(getViewSite().getShell());
953                         try {
954                                 pmd.run(true, true, op);
955                         } catch (InvocationTargetException e) { // do nothing
956                         } catch (InterruptedException e) { // do nothing
957                         } finally {
958                                 fReadLogAction.setText(Messages.LogView_readLog_reload);
959                                 fReadLogAction.setToolTipText(Messages.LogView_readLog_reload);
960                                 asyncRefresh(false);
961                                 resetDialogButtons();
962                         }
963                 }
964         }
965
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();
972                 if (path != null) {
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))
980                                         return;
981                         }
982                         copy(fInputFile, outputFile);
983                 }
984         }
985
986         private void copy(File inputFile, File outputFile) {
987                 BufferedReader reader = null;
988                 BufferedWriter writer = null;
989                 try {
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$
995                         }
996                 } catch (IOException e) { // do nothing
997                 } finally {
998                         try {
999                                 if (reader != null)
1000                                         reader.close();
1001                                 if (writer != null)
1002                                         writer.close();
1003                         } catch (IOException e1) { // do nothing
1004                         }
1005                 }
1006         }
1007
1008         private void handleFilter() {
1009                 FilterDialog dialog = new FilterDialog(Activator.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell(), fMemento);
1010                 dialog.create();
1011                 dialog.getShell().setText(Messages.LogView_FilterDialog_title);
1012                 if (dialog.open() == Window.OK)
1013                         reloadLog();
1014         }
1015
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))
1020                         return;
1021                 if (fInputFile.delete() || elements.size() > 0) {
1022                         elements.clear();
1023                         groups.clear();
1024                         currentSession.removeAllChildren();
1025                         asyncRefresh(false);
1026                         resetDialogButtons();
1027                 }
1028         }
1029
1030         public void fillContextMenu(IMenuManager manager) { // nothing
1031         }
1032
1033         public AbstractEntry[] getElements() {
1034                 return (AbstractEntry[]) elements.toArray(new AbstractEntry[elements.size()]);
1035         }
1036
1037         protected void handleClear() {
1038                 BusyIndicator.showWhile(fTree.getDisplay(), new Runnable() {
1039                         public void run() {
1040                                 elements.clear();
1041                                 groups.clear();
1042                                 currentSession.removeAllChildren();
1043                                 asyncRefresh(false);
1044                                 resetDialogButtons();
1045                         }
1046                 });
1047         }
1048
1049         protected void reloadLog() {
1050                 IRunnableWithProgress op = new IRunnableWithProgress() {
1051                         public void run(IProgressMonitor monitor) {
1052                                 monitor.beginTask(Messages.LogView_operation_reloading, IProgressMonitor.UNKNOWN);
1053                                 readLogFile();
1054                         }
1055                 };
1056                 ProgressMonitorDialog pmd = new ProgressMonitorDialog(getViewSite().getShell());
1057                 try {
1058                         pmd.run(true, true, op);
1059                 } catch (InvocationTargetException e) { // do nothing
1060                 } catch (InterruptedException e) { // do nothing
1061                 } finally {
1062                         fReadLogAction.setText(Messages.LogView_readLog_restore);
1063                         fReadLogAction.setToolTipText(Messages.LogView_readLog_restore);
1064                         asyncRefresh(false);
1065                         resetDialogButtons();
1066                 }
1067         }
1068
1069         private void readLogFile() {
1070                 if (!fInputFile.exists())
1071                         return;
1072
1073                 elements.clear();
1074                 groups.clear();
1075
1076                 List<Object> result = new ArrayList<Object>();
1077                 currentSession = LogReader.parseLogFile(fInputFile, result, fMemento);
1078                 group(result);
1079                 limitEntriesCount();
1080
1081                 getSite().getShell().getDisplay().asyncExec(new Runnable() {
1082                         public void run() {
1083                                 setContentDescription(getTitleSummary());
1084                         }
1085                 });
1086
1087         }
1088
1089         private String getTitleSummary() {
1090                 String path = ""; //$NON-NLS-1$
1091                 try {
1092                         path = fInputFile.getCanonicalPath();
1093                 } catch (IOException e) { // log nothing
1094                 }
1095
1096                 if (isPlatformLogOpen()) {
1097                     // Remove the content description in this case
1098                     // to save vertical space from the view.
1099                         //return Messages.LogView_WorkspaceLogFile;
1100                     return "";
1101                 }
1102
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});
1109                                 }
1110                         }
1111                 }
1112
1113                 return path;
1114         }
1115
1116         /**
1117          * Add new entries to correct groups in the view.
1118          * @param entries new entries to show up in groups in the view.
1119          */
1120         private void group(List<?> entries) {
1121                 if (fMemento.getInteger(P_GROUP_BY).intValue() == GROUP_BY_NONE) {
1122                         elements.addAll(entries);
1123                 } else {
1124                         for (Iterator<?> i = entries.iterator(); i.hasNext();) {
1125                                 LogEntry entry = (LogEntry) i.next();
1126                                 Group group = getGroup(entry);
1127                                 group.addChild(entry);
1128                         }
1129                 }
1130         }
1131
1132         /**
1133          * Limits the number of entries according to the max entries limit set in
1134          * memento.
1135          */
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();
1140                 }
1141
1142                 int entriesCount = getEntriesCount();
1143
1144                 if (entriesCount <= limit) {
1145                         return;
1146                 }
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)) {
1154                                         return 0;
1155                                 } else
1156                                         return (l1 == null) ? -1 : 1;
1157                         }
1158                 };
1159
1160                 if (fMemento.getInteger(P_GROUP_BY).intValue() == GROUP_BY_NONE) {
1161                         elements.subList(0, elements.size() - limit).clear();
1162                 } else {
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)));
1167                         }
1168
1169                         Collections.sort(copy, dateComparator);
1170                         List<?> toRemove = copy.subList(0, copy.size() - limit);
1171
1172                         for (Iterator<?> i = elements.iterator(); i.hasNext();) {
1173                                 AbstractEntry group = (AbstractEntry) i.next();
1174                                 group.removeChildren(toRemove);
1175                         }
1176                 }
1177
1178         }
1179
1180         private int getEntriesCount() {
1181                 if (fMemento.getInteger(P_GROUP_BY).intValue() == GROUP_BY_NONE) {
1182                         return elements.size();
1183                 }
1184                 int size = 0;
1185                 for (Iterator<?> i = elements.iterator(); i.hasNext();) {
1186                         AbstractEntry group = (AbstractEntry) i.next();
1187                         size += group.size();
1188                 }
1189                 return size;
1190         }
1191
1192         /**
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.
1196          * 
1197          * @param entry entry to be grouped
1198          * @return group or null if grouping is disabled
1199          */
1200         protected Group getGroup(LogEntry entry) {
1201                 int groupBy = fMemento.getInteger(P_GROUP_BY).intValue();
1202                 Object elementGroupId = null;
1203                 String groupName = null;
1204
1205                 switch (groupBy) {
1206                         case GROUP_BY_PLUGIN :
1207                                 groupName = entry.getPluginId();
1208                                 elementGroupId = groupName;
1209                                 break;
1210
1211                         case GROUP_BY_SESSION :
1212                                 elementGroupId = entry.getSession();
1213                                 break;
1214
1215                         default : // grouping is disabled
1216                                 return null;
1217                 }
1218
1219                 if (elementGroupId == null) { // could not determine group
1220                         return null;
1221                 }
1222
1223                 Group group = (Group) groups.get(elementGroupId);
1224                 if (group == null) {
1225                         if (groupBy == GROUP_BY_SESSION) {
1226                                 group = entry.getSession();
1227                         } else {
1228                                 group = new Group(groupName);
1229                         }
1230                         groups.put(elementGroupId, group);
1231                         elements.add(group);
1232                 }
1233
1234                 return group;
1235         }
1236
1237         public void logging(IStatus status, String plugin) {
1238                 if (!isPlatformLog(fInputFile))
1239                         return;
1240
1241                 if (batchEntries) {
1242                         // create LogEntry immediately to don't loose IStatus creation date.
1243                         LogEntry entry = createLogEntry(status);
1244                         batchedEntries.add(entry);
1245                         return;
1246                 }
1247
1248                 if (fFirstEvent) {
1249                         readLogFile();
1250                         asyncRefresh(true);
1251                         fFirstEvent = false;
1252                 } else {
1253                         LogEntry entry = createLogEntry(status);
1254
1255                         if (!batchedEntries.isEmpty()) {
1256                                 // batch new entry as well, to have only one asyncRefresh()
1257                                 batchedEntries.add(entry);
1258                                 pushBatchedEntries();
1259                         } else {
1260                                 pushEntry(entry);
1261                                 asyncRefresh(true);
1262                         }
1263                 }
1264         }
1265
1266         /**
1267          * Push batched entries to log view.
1268          */
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);
1275                                                 pushEntry(entry);
1276                                                 batchedEntries.remove(i);
1277                                         }
1278                                 }
1279                                 asyncRefresh(true);
1280                                 return Status.OK_STATUS;
1281                         }
1282                 };
1283                 job.schedule();
1284         }
1285
1286         private LogEntry createLogEntry(IStatus status) {
1287                 LogEntry entry = new LogEntry(status);
1288                 entry.setSession(currentSession);
1289                 return entry;
1290         }
1291
1292         private synchronized void pushEntry(LogEntry entry) {
1293                 if (LogReader.isLogged(entry, fMemento)) {
1294                         group(Collections.singletonList(entry));
1295                         limitEntriesCount();
1296                 }
1297                 asyncRefresh(true);
1298         }
1299
1300         private void asyncRefresh(final boolean activate) {
1301                 if (fTree.isDisposed())
1302                         return;
1303                 Display display = fTree.getDisplay();
1304                 final ViewPart view = this;
1305                 if (display != null) {
1306                         display.asyncExec(new Runnable() {
1307                                 public void run() {
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();
1316                                                         if (page != null)
1317                                                                 page.bringToTop(view);
1318                                                 }
1319                                         }
1320                                 }
1321                         });
1322                 }
1323         }
1324
1325         public void setFocus() {
1326                 if (fFilteredTree != null && !fFilteredTree.isDisposed())
1327                         fFilteredTree.setFocus();
1328         }
1329
1330         private void handleSelectionChanged(ISelection selection) {
1331                 updateStatus(selection);
1332                 fCopyAction.setEnabled((!selection.isEmpty()) && ((IStructuredSelection) selection).getFirstElement() instanceof LogEntry);
1333                 fPropertiesAction.setEnabled(!selection.isEmpty());
1334         }
1335
1336         private void updateStatus(ISelection selection) {
1337                 IStatusLineManager status = getViewSite().getActionBars().getStatusLineManager();
1338                 if (selection.isEmpty())
1339                         status.setMessage(null);
1340                 else {
1341                         Object element = ((IStructuredSelection) selection).getFirstElement();
1342                         status.setMessage(((LogViewLabelProvider) fFilteredTree.getViewer().getLabelProvider()).getColumnText(element, 0));
1343                 }
1344         }
1345
1346         /**
1347          * Converts selected log view element to string.
1348          * @return textual log entry representation or null if selection doesn't contain log entry
1349          */
1350         private static String selectionToString(ISelection selection) {
1351                 StringWriter writer = new StringWriter();
1352                 PrintWriter pwriter = new PrintWriter(writer);
1353                 if (selection.isEmpty())
1354                         return null;
1355                 LogEntry entry = (LogEntry) ((IStructuredSelection) selection).getFirstElement();
1356                 entry.write(pwriter);
1357                 pwriter.flush();
1358                 String textVersion = writer.toString();
1359                 try {
1360                         pwriter.close();
1361                         writer.close();
1362                 } catch (IOException e) {
1363                         // empty
1364                 }
1365
1366                 return textVersion;
1367         }
1368
1369         /**
1370          * Copies selected element to clipboard.
1371          */
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()});
1377                 }
1378         }
1379
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$
1384                 else
1385                         this.fMemento = memento;
1386                 readSettings();
1387
1388                 // initialize column ordering 
1389                 final byte type = this.fMemento.getInteger(P_ORDER_TYPE).byteValue();
1390                 switch (type) {
1391                         case DATE :
1392                                 DATE_ORDER = this.fMemento.getInteger(P_ORDER_VALUE).intValue();
1393                                 MESSAGE_ORDER = DESCENDING;
1394                                 PLUGIN_ORDER = DESCENDING;
1395                                 break;
1396                         case MESSAGE :
1397                                 MESSAGE_ORDER = this.fMemento.getInteger(P_ORDER_VALUE).intValue();
1398                                 DATE_ORDER = DESCENDING;
1399                                 PLUGIN_ORDER = DESCENDING;
1400                                 break;
1401                         case PLUGIN :
1402                                 PLUGIN_ORDER = this.fMemento.getInteger(P_ORDER_VALUE).intValue();
1403                                 MESSAGE_ORDER = DESCENDING;
1404                                 DATE_ORDER = DESCENDING;
1405                                 break;
1406                         default :
1407                                 DATE_ORDER = DESCENDING;
1408                                 MESSAGE_ORDER = DESCENDING;
1409                                 PLUGIN_ORDER = DESCENDING;
1410                 }
1411                 setComparator(fMemento.getInteger(P_ORDER_TYPE).byteValue());
1412         }
1413
1414         private void initializeMemento() {
1415         if (fMemento.getInteger(P_EXPAND_LEVEL) == null) {
1416             fMemento.putInteger(P_EXPAND_LEVEL, DEFAULT_EXPAND_LEVEL);
1417         }
1418                 if (fMemento.getString(P_USE_LIMIT) == null) {
1419                         fMemento.putString(P_USE_LIMIT, "true"); //$NON-NLS-1$
1420                 }
1421                 if (fMemento.getInteger(P_LOG_LIMIT) == null) {
1422                         fMemento.putInteger(P_LOG_LIMIT, 50);
1423                 }
1424                 if (fMemento.getString(P_LOG_INFO) == null) {
1425                         fMemento.putString(P_LOG_INFO, "true"); //$NON-NLS-1$
1426                 }
1427                 if (fMemento.getString(P_LOG_WARNING) == null) {
1428                         fMemento.putString(P_LOG_WARNING, "true"); //$NON-NLS-1$
1429                 }
1430         if (fMemento.getString(P_LOG_ERROR) == null) {
1431             fMemento.putString(P_LOG_ERROR, "true"); //$NON-NLS-1$
1432         }
1433         if (fMemento.getString(P_LOG_DEBUG) == null) {
1434             fMemento.putString(P_LOG_DEBUG, "true"); //$NON-NLS-1$
1435         }
1436                 if (fMemento.getString(P_SHOW_ALL_SESSIONS) == null) {
1437                         fMemento.putString(P_SHOW_ALL_SESSIONS, "false"); //$NON-NLS-1$
1438                 }
1439                 Integer width = fMemento.getInteger(P_COLUMN_1);
1440                 if (width == null || width.intValue() == 0) {
1441                         fMemento.putInteger(P_COLUMN_1, 300);
1442                 }
1443                 width = fMemento.getInteger(P_COLUMN_2);
1444                 if (width == null || width.intValue() == 0) {
1445                         fMemento.putInteger(P_COLUMN_2, 150);
1446                 }
1447                 width = fMemento.getInteger(P_COLUMN_3);
1448                 if (width == null || width.intValue() == 0) {
1449                         fMemento.putInteger(P_COLUMN_3, 150);
1450                 }
1451                 if (fMemento.getString(P_ACTIVATE) == null) {
1452                         fMemento.putString(P_ACTIVATE, "false"); //$NON-NLS-1$
1453                 }
1454                 if (fMemento.getBoolean(P_SHOW_FILTER_TEXT) == null) {
1455                         fMemento.putBoolean(P_SHOW_FILTER_TEXT, false);
1456                 }
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);
1461                 }
1462         }
1463
1464         public void saveState(IMemento memento) {
1465                 if (this.fMemento == null || memento == null)
1466                         return;
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);
1472                 writeSettings();
1473         }
1474
1475         private void addMouseListeners() {
1476                 Listener tableListener = new Listener() {
1477                         public void handleEvent(Event e) {
1478                                 switch (e.type) {
1479                                         case SWT.MouseMove :
1480                                                 onMouseMove(e);
1481                                                 break;
1482                                         case SWT.MouseHover :
1483                                                 onMouseHover(e);
1484                                                 break;
1485                                         case SWT.MouseDown :
1486                                                 onMouseDown(e);
1487                                                 break;
1488                                 }
1489                         }
1490                 };
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);
1494                 }
1495         }
1496
1497         /**
1498          * Adds drag source support to error log tree.
1499          */
1500         private void addDragSource() {
1501                 DragSource source = new DragSource(fTree, DND.DROP_COPY);
1502                 Transfer[] types = new Transfer[] {TextTransfer.getInstance()};
1503                 source.setTransfer(types);
1504
1505                 source.addDragListener(new DragSourceAdapter() {
1506
1507                         public void dragStart(DragSourceEvent event) {
1508                                 ISelection selection = fFilteredTree.getViewer().getSelection();
1509                                 if (selection.isEmpty()) {
1510                                         event.doit = false;
1511                                         return;
1512                                 }
1513
1514                                 AbstractEntry entry = (AbstractEntry) ((TreeSelection) selection).getFirstElement();
1515                                 if (!(entry instanceof LogEntry)) {
1516                                         event.doit = false;
1517                                         return;
1518                                 }
1519                         }
1520
1521                         public void dragSetData(DragSourceEvent event) {
1522                                 if (!TextTransfer.getInstance().isSupportedType(event.dataType)) {
1523                                         return;
1524                                 }
1525
1526                                 ISelection selection = fFilteredTree.getViewer().getSelection();
1527                                 String textVersion = selectionToString(selection);
1528                                 event.data = textVersion;
1529                         }
1530                 });
1531         }
1532
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);
1551                 gd.widthHint = 100;
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);
1562                         }
1563                 });
1564         }
1565
1566         void onTextShellDispose(DisposeEvent e) {
1567                 fCanOpenTextShell = true;
1568                 setFocus();
1569         }
1570
1571         void onMouseDown(Event e) {
1572                 if (fTextShell != null && !fTextShell.isDisposed() && !fTextShell.isFocusControl()) {
1573                         fTextShell.setVisible(false);
1574                         fCanOpenTextShell = true;
1575                 }
1576         }
1577
1578         void onMouseHover(Event e) {
1579                 if (!fCanOpenTextShell || fTextShell == null || fTextShell.isDisposed())
1580                         return;
1581                 fCanOpenTextShell = false;
1582                 Point point = new Point(e.x, e.y);
1583                 TreeItem item = fTree.getItem(point);
1584                 if (item == null)
1585                         return;
1586
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());
1596                         }
1597                 }
1598
1599                 if (message == null)
1600                         return;
1601
1602                 fTextLabel.setText(message);
1603                 Rectangle bounds = fTree.getDisplay().getBounds();
1604                 Point cursorPoint = fTree.getDisplay().getCursorLocation();
1605                 int x = point.x;
1606                 int y = point.y + 25;
1607                 int width = fTree.getColumn(0).getWidth();
1608                 int height = 125;
1609                 if (cursorPoint.x + width > bounds.width)
1610                         x -= width;
1611                 if (cursorPoint.y + height + 25 > bounds.height)
1612                         y -= height + 27;
1613
1614                 fTextShell.setLocation(fTree.toDisplay(x, y));
1615                 fTextShell.setSize(width, height);
1616                 fTextShell.setVisible(true);
1617         }
1618
1619         void onMouseMove(Event e) {
1620                 if (fTextShell != null && !fTextShell.isDisposed() && fTextShell.isVisible())
1621                         fTextShell.setVisible(false);
1622
1623                 Point point = new Point(e.x, e.y);
1624                 TreeItem item = fTree.getItem(point);
1625                 if (item == null)
1626                         return;
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;
1635                 }
1636         }
1637
1638         private int getNumberOfParents(AbstractEntry entry) {
1639                 AbstractEntry parent = (AbstractEntry) entry.getParent(entry);
1640                 if (parent == null)
1641                         return 0;
1642                 return 1 + getNumberOfParents(parent);
1643         }
1644
1645         public Comparator<Object> getComparator() {
1646                 return fComparator;
1647         }
1648
1649         private void setComparator(byte sortType) {
1650                 if (sortType == DATE) {
1651                         fComparator = new Comparator<Object>() {
1652                                 public int compare(Object e1, Object e2) {
1653                                         long date1 = 0;
1654                                         long date2 = 0;
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();
1661                                         }
1662                                         if (date1 == date2) {
1663                                                 int result = elements.indexOf(e2) - elements.indexOf(e1);
1664                                                 if (DATE_ORDER == DESCENDING)
1665                                                         result *= DESCENDING;
1666                                                 return result;
1667                                         }
1668                                         if (DATE_ORDER == DESCENDING)
1669                                                 return date1 > date2 ? DESCENDING : ASCENDING;
1670                                         return date1 < date2 ? DESCENDING : ASCENDING;
1671                                 }
1672                         };
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;
1680                                         }
1681                                         return 0;
1682                                 }
1683                         };
1684                 } else {
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;
1691                                         }
1692                                         return 0;
1693                                 }
1694                         };
1695                 }
1696         }
1697
1698         @SuppressWarnings("unchecked")
1699     private Comparator<Object> getDefaultComparator() {
1700                 return Policy.getComparator();
1701         }
1702
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;
1712                                         }
1713                                         return 0;
1714                                 }
1715                         };
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;
1724                                         }
1725                                         return 0;
1726                                 }
1727                         };
1728                 } else {
1729                         return new ViewerComparator() {
1730                             private int indexOf(Object[] array, Object o) {
1731                                 if (o == null)
1732                                     return -1;
1733                                 for (int i = 0; i < array.length; ++i)
1734                                     if (o.equals(array[i]))
1735                                         return i;
1736                                 return -1;
1737                             }
1738                             
1739                                 public int compare(Viewer viewer, Object e1, Object e2) {
1740                                         long date1 = 0;
1741                                         long date2 = 0;
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();
1748                                         }
1749
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;
1754                             if (parent != null)
1755                                 children = parent.getChildren(parent);
1756                                             
1757                                             int result = 0;
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);
1762                                             } else {
1763                                 result = elements.indexOf(e1) - elements.indexOf(e2);
1764                                             }
1765                                                 if (DATE_ORDER == DESCENDING)
1766                                                         result *= DESCENDING;
1767                                                 return result;
1768                                         }
1769                                         if (DATE_ORDER == DESCENDING)
1770                                                 return date1 > date2 ? DESCENDING : ASCENDING;
1771                                         return date1 < date2 ? DESCENDING : ASCENDING;
1772                                 }
1773                         };
1774                 }
1775         }
1776
1777         private void resetDialogButtons() {
1778                 ((EventDetailsDialogAction) fPropertiesAction).resetDialogButtons();
1779         }
1780
1781         /**
1782          * Returns the filter dialog settings object used to maintain
1783          * state between filter dialogs
1784          * @return the dialog settings to be used
1785          */
1786         private IDialogSettings getLogSettings() {
1787                 IDialogSettings settings = Activator.getDefault().getDialogSettings();
1788                 return settings.getSection(getClass().getName());
1789         }
1790
1791         /**
1792          * Returns the plugin preferences used to maintain
1793          * state of log view
1794          * @return the plugin preferences
1795          */
1796         private Preferences getLogPreferences() {
1797                 return Activator.getDefault().getPluginPreferences();
1798         }
1799
1800         private void readSettings() {
1801                 IDialogSettings s = getLogSettings();
1802                 Preferences p = getLogPreferences();
1803                 if (s == null || p == null) {
1804                         initializeMemento();
1805                         return;
1806                 }
1807                 try {
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);
1834                 }
1835         }
1836
1837         private void writeSettings() {
1838                 writeViewSettings();
1839                 writeFilterSettings();
1840         }
1841
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$
1853         }
1854
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());
1867         }
1868
1869         public void sortByDateDescending() {
1870                 setColumnSorting(fColumn3, DESCENDING);
1871         }
1872
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());
1880                                         if (failed) {
1881                                                 Program p = Program.findProgram(".txt"); //$NON-NLS-1$
1882                                                 if (p != null) {
1883                                                         p.execute(fInputFile.getAbsolutePath());
1884                                                         return Status.OK_STATUS;
1885                                                 }
1886                                         }
1887                                 }
1888                                 if (failed) {
1889                                         final OpenLogDialog openDialog = new OpenLogDialog(shell, fInputFile);
1890                                         Display.getDefault().asyncExec(new Runnable() {
1891                                                 public void run() {
1892                                                         openDialog.create();
1893                                                         openDialog.open();
1894                                                 }
1895                                         });
1896                                 }
1897                                 return Status.OK_STATUS;
1898                         }
1899                 };
1900         }
1901
1902         protected File getLogFile() {
1903                 return fInputFile;
1904         }
1905
1906         /**
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
1910          */
1911         public boolean isCurrentLogSession(LogSession session) {
1912                 return isPlatformLogOpen() && (currentSession != null) && (currentSession.equals(session));
1913         }
1914
1915     /**
1916      * Returns whether currently open log is platform log or imported file.
1917      * @return true if currently open log is platform log, false otherwise
1918      */
1919     public boolean isPlatformLogOpen() {
1920         return isPlatformLog(fInputFile);
1921     }
1922
1923     /**
1924      * Returns whether currently open log is platform log or imported file.
1925      * @return true if currently open log is platform log, false otherwise
1926      */
1927     public boolean isPlatformLog(File file) {
1928         return (file.equals(getPlatformLogFile()));
1929     }
1930     
1931     public File getPlatformLogFile() {
1932         return org.simantics.message.internal.Activator.getLogFile();
1933     }
1934 }