a2a7d2e53197a734ca37033d2e1c941da4afa346
[platform/upstream/iotivity.git] / service / simulator / java / eclipse-plugin / ClientControllerPlugin / src / oic / simulator / clientcontroller / view / LogView.java
1 /*
2  * Copyright 2015 Samsung Electronics All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package oic.simulator.clientcontroller.view;
18
19 import java.io.BufferedWriter;
20 import java.io.FileWriter;
21 import java.io.IOException;
22 import java.text.DateFormat;
23 import java.text.SimpleDateFormat;
24 import java.util.Date;
25 import java.util.HashMap;
26 import java.util.List;
27
28 import oic.simulator.clientcontroller.Activator;
29 import oic.simulator.clientcontroller.listener.ILogUIListener;
30 import oic.simulator.clientcontroller.manager.LogManager;
31 import oic.simulator.clientcontroller.utils.Constants;
32 import oic.simulator.clientcontroller.view.dialogs.FilterDialog;
33 import oic.simulator.clientcontroller.view.dialogs.LogDetailsDialog;
34 import oic.simulator.logger.LogContentProvider;
35 import oic.simulator.logger.LogEntry;
36 import oic.simulator.logger.LogLabelProvider;
37
38 import org.eclipse.jface.action.Action;
39 import org.eclipse.jface.action.IAction;
40 import org.eclipse.jface.action.IContributionItem;
41 import org.eclipse.jface.action.IMenuManager;
42 import org.eclipse.jface.action.IToolBarManager;
43 import org.eclipse.jface.action.MenuManager;
44 import org.eclipse.jface.action.Separator;
45 import org.eclipse.jface.dialogs.MessageDialog;
46 import org.eclipse.jface.resource.ImageDescriptor;
47 import org.eclipse.jface.viewers.DoubleClickEvent;
48 import org.eclipse.jface.viewers.IDoubleClickListener;
49 import org.eclipse.jface.viewers.ISelectionChangedListener;
50 import org.eclipse.jface.viewers.IStructuredSelection;
51 import org.eclipse.jface.viewers.SelectionChangedEvent;
52 import org.eclipse.jface.viewers.StructuredSelection;
53 import org.eclipse.jface.viewers.TreeViewer;
54 import org.eclipse.jface.viewers.Viewer;
55 import org.eclipse.jface.viewers.ViewerComparator;
56 import org.eclipse.jface.window.Window;
57 import org.eclipse.swt.SWT;
58 import org.eclipse.swt.events.KeyEvent;
59 import org.eclipse.swt.events.KeyListener;
60 import org.eclipse.swt.events.SelectionAdapter;
61 import org.eclipse.swt.events.SelectionEvent;
62 import org.eclipse.swt.layout.GridData;
63 import org.eclipse.swt.widgets.Composite;
64 import org.eclipse.swt.widgets.Display;
65 import org.eclipse.swt.widgets.FileDialog;
66 import org.eclipse.swt.widgets.Text;
67 import org.eclipse.swt.widgets.Tree;
68 import org.eclipse.swt.widgets.TreeColumn;
69 import org.eclipse.swt.widgets.TreeItem;
70 import org.eclipse.ui.IActionBars;
71 import org.eclipse.ui.IWorkbenchActionConstants;
72 import org.eclipse.ui.IWorkbenchPage;
73 import org.eclipse.ui.dialogs.FilteredTree;
74 import org.eclipse.ui.dialogs.PatternFilter;
75 import org.eclipse.ui.part.ViewPart;
76 import org.oic.simulator.ILogger.Level;
77
78 /**
79  * This class manages and shows the log view in the perspective.
80  */
81 public class LogView extends ViewPart {
82
83     public static final String        VIEW_ID              = "oic.simulator.clientcontroller.view.log";
84
85     private LogManager                logManager;
86     private ILogUIListener            logListener;
87
88     private LogContentProvider        treeContentProvider;
89
90     private FilteredTree              tree;
91     private TreeColumn                severityColumn;
92     private TreeColumn                dateColumn;
93     private TreeColumn                messageColumn;
94
95     private IAction                   exportLogAction;
96     private IAction                   clearLogAction;
97     private IAction                   deleteLogAction;
98     private IAction                   scrollLockAction;
99     private IAction                   logDetailsAction;
100     private IAction                   filterAction;
101     private IAction                   activateViewAction;
102     private IAction                   showTextFilter;
103     private IContributionItem         groupByAction;
104
105     private HashMap<Integer, Boolean> shownSeverities      = new HashMap<Integer, Boolean>();
106
107     private boolean                   activateOnChange     = false;
108
109     private boolean                   hideTextFilter       = false;
110
111     private boolean                   scrollLockDisabled;
112
113     public static final int           ORDER_BY_TIME        = 0;
114     public static final int           ORDER_BY_SEVERITY    = 1;
115     public static final int           ORDER_BY_MESSAGE     = 2;
116
117     int                               sortCandidate        = ORDER_BY_TIME;
118
119     SortAction                        sortByTimeAction     = new SortAction(
120                                                                    "Order by Time",
121                                                                    ORDER_BY_TIME);
122     SortAction                        sortBySeverityAction = new SortAction(
123                                                                    "Order by Severity",
124                                                                    ORDER_BY_SEVERITY);
125     SortAction                        sortByMessageAction  = new SortAction(
126                                                                    "Order by Message",
127                                                                    ORDER_BY_MESSAGE);
128
129     private ViewerComparator          dateComparator;
130     private ViewerComparator          severityComparator;
131     private ViewerComparator          messageComparator;
132
133     private TreeColumn                sortColumn           = null;
134     private static int                DOWN                 = 1;
135     private static int                UP                   = -1;
136     private int                       sortDirection        = DOWN;
137
138     public LogView() {
139
140         logListener = new ILogUIListener() {
141
142             @Override
143             public void logChanged(final List<LogEntry> entry) {
144                 Display.getDefault().asyncExec(new Runnable() {
145
146                     @Override
147                     public void run() {
148                         TreeViewer viewer = tree.getViewer();
149                         if (viewer.getControl().isDisposed()) {
150                             return;
151                         }
152                         viewer.setInput(entry);
153                         updateTree(false);
154                     }
155                 });
156             }
157
158             @Override
159             public void logAdded(final LogEntry added) {
160                 Display.getDefault().asyncExec(new Runnable() {
161
162                     @Override
163                     public void run() {
164                         TreeViewer viewer = tree.getViewer();
165                         if (viewer.getControl().isDisposed()) {
166                             return;
167                         }
168                         LogContentProvider provider = (LogContentProvider) viewer
169                                 .getContentProvider();
170                         provider.addLog(added);
171                         tree.getViewer().add(viewer.getInput(), added);
172                         @SuppressWarnings("unchecked")
173                         List<LogEntry> input = (List<LogEntry>) viewer
174                                 .getInput();
175                         if (input.size() > Constants.LOG_SIZE) {
176                             viewer.remove(viewer.getInput(), 0);
177                         }
178                         updateTree(true);
179                     }
180                 });
181             }
182
183             private void updateTree(boolean needscroll) {
184                 if (activateOnChange) {
185                     IWorkbenchPage page = Activator.getDefault().getWorkbench()
186                             .getActiveWorkbenchWindow().getActivePage();
187                     if (page != null) {
188                         page.bringToTop(LogView.this);
189                     }
190                 }
191                 if (scrollLockDisabled && needscroll) {
192                     Tree tree2 = tree.getViewer().getTree();
193                     if (tree2.getItemCount() > 0) {
194                         TreeItem item = tree2.getItem(tree2.getItemCount() - 1);
195                         tree2.setTopItem(item);
196                         deleteLogAction.setEnabled(true);
197                     }
198                 }
199             }
200         };
201
202         logManager = Activator.getDefault().getLogManager();
203
204         // Initially state of scroll lock
205         scrollLockDisabled = true;
206     }
207
208     @Override
209     public void createPartControl(Composite parent) {
210         PatternFilter filter = new PatternFilter() {
211
212             DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
213
214             @Override
215             protected boolean isLeafMatch(Viewer viewer, Object element) {
216                 if (element instanceof LogEntry) {
217                     LogEntry logEntry = (LogEntry) element;
218                     String severity = LogManager.getSeverityName(logEntry
219                             .getSeverity());
220                     String date = dateFormat.format(logEntry.getDate());
221                     String message = logEntry.getMessage();
222                     return wordMatches(severity) || wordMatches(date)
223                             || wordMatches(message);
224                 }
225                 return false;
226             }
227         };
228         filter.setIncludeLeadingWildcard(true);
229         tree = new FilteredTree(parent, SWT.SINGLE | SWT.H_SCROLL
230                 | SWT.V_SCROLL | SWT.FULL_SELECTION, filter, true);
231
232         setupFilteredTree();
233
234         createColumnComparators();
235
236         createActions();
237
238         setDefaultShownSeverities();
239
240         IActionBars actionBars = getViewSite().getActionBars();
241         IToolBarManager toolBarManager = actionBars.getToolBarManager();
242         toolBarManager.add(exportLogAction);
243         toolBarManager.add(clearLogAction);
244         toolBarManager.add(deleteLogAction);
245         toolBarManager.add(scrollLockAction);
246         toolBarManager
247                 .add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
248
249         IMenuManager mgr = actionBars.getMenuManager();
250         mgr.add(groupByAction);
251         mgr.add(new Separator());
252         mgr.add(filterAction);
253         mgr.add(new Separator());
254         mgr.add(activateViewAction);
255         mgr.add(showTextFilter);
256
257         addManagerListeners();
258
259         if (sortCandidate == ORDER_BY_TIME) {
260             sortByTimeAction.run();
261         } else if (sortCandidate == ORDER_BY_SEVERITY) {
262             sortBySeverityAction.run();
263         } else { // order_selected == ORDER_BY_NONE
264             sortByMessageAction.run();
265         }
266
267     }
268
269     private void setupFilteredTree() {
270         tree.setLayoutData(new GridData(GridData.FILL_BOTH));
271         final Tree innerTree = tree.getViewer().getTree();
272         innerTree.setLinesVisible(true);
273
274         severityColumn = new TreeColumn(innerTree, SWT.LEFT);
275         severityColumn.setText("Severity");
276         severityColumn.setWidth(110);
277         severityColumn.addSelectionListener(new SelectionAdapter() {
278
279             @Override
280             public void widgetSelected(SelectionEvent e) {
281                 sortBySeverityAction.run();
282             }
283         });
284         dateColumn = new TreeColumn(innerTree, SWT.LEFT);
285         dateColumn.setText("Time");
286         dateColumn.setWidth(110);
287         dateColumn.addSelectionListener(new SelectionAdapter() {
288
289             @Override
290             public void widgetSelected(SelectionEvent e) {
291                 sortByTimeAction.run();
292             }
293         });
294         messageColumn = new TreeColumn(innerTree, SWT.LEFT);
295         messageColumn.setText("Message");
296         messageColumn.setWidth(180);
297         messageColumn.addSelectionListener(new SelectionAdapter() {
298             @Override
299             public void widgetSelected(SelectionEvent e) {
300                 sortByMessageAction.run();
301             }
302         });
303
304         innerTree.setHeaderVisible(true);
305
306         treeContentProvider = new LogContentProvider();
307
308         tree.getViewer().setContentProvider(treeContentProvider);
309         tree.getViewer().setLabelProvider(new LogLabelProvider());
310
311         tree.getViewer().setInput(logManager.getLogEntries());
312
313         tree.getViewer().addSelectionChangedListener(
314                 new ISelectionChangedListener() {
315
316                     @Override
317                     public void selectionChanged(SelectionChangedEvent event) {
318                         deleteLogAction.setEnabled(!tree.getViewer()
319                                 .getSelection().isEmpty());
320                         logDetailsAction.setEnabled(!tree.getViewer()
321                                 .getSelection().isEmpty());
322                     }
323                 });
324
325         tree.getViewer().getTree().addKeyListener(new KeyListener() {
326
327             @Override
328             public void keyReleased(KeyEvent e) {
329             }
330
331             @Override
332             public void keyPressed(KeyEvent e) {
333                 if (e.character == (char) 127) { // If delete key is pressed
334                     if (deleteLogAction.isEnabled()) {
335                         deleteLogAction.run();
336                     }
337                 }
338             }
339         });
340
341         tree.getViewer().addDoubleClickListener(new IDoubleClickListener() {
342
343             @Override
344             public void doubleClick(DoubleClickEvent event) {
345                 logDetailsAction.run();
346             }
347         });
348     }
349
350     private void createColumnComparators() {
351         dateComparator = new ViewerComparator() {
352
353             @Override
354             public int compare(Viewer viewer, Object e1, Object e2) {
355                 LogEntry l1 = (LogEntry) e1;
356                 LogEntry l2 = (LogEntry) e2;
357                 return l1.getDate().compareTo(l2.getDate()) * sortDirection;
358             }
359         };
360
361         severityComparator = new ViewerComparator() {
362
363             @Override
364             public int compare(Viewer viewer, Object e1, Object e2) {
365                 LogEntry l1 = (LogEntry) e1;
366                 LogEntry l2 = (LogEntry) e2;
367                 if (l1.getSeverity() < l2.getSeverity()) {
368                     return -1 * sortDirection;
369                 }
370                 if (l1.getSeverity() > l2.getSeverity()) {
371                     return 1 * sortDirection;
372                 }
373                 return 0;
374             }
375         };
376
377         messageComparator = new ViewerComparator() {
378
379             @Override
380             public int compare(Viewer viewer, Object e1, Object e2) {
381                 LogEntry l1 = (LogEntry) e1;
382                 LogEntry l2 = (LogEntry) e2;
383                 return l1.getMessage().compareTo(l2.getMessage())
384                         * sortDirection;
385             }
386         };
387
388     }
389
390     private void setDefaultShownSeverities() {
391         shownSeverities.put(Level.INFO.ordinal(), true);
392         shownSeverities.put(Level.DEBUG.ordinal(), true);
393         shownSeverities.put(Level.WARNING.ordinal(), true);
394         shownSeverities.put(Level.ERROR.ordinal(), true);
395     }
396
397     private void addManagerListeners() {
398         logManager.addLogListener(logListener);
399         logManager.applyFilter(shownSeverities);
400     }
401
402     private void createActions() {
403         exportLogAction = createExportLogAction();
404         clearLogAction = createClearLogAction();
405         deleteLogAction = createDeleteLogAction();
406         scrollLockAction = createScrollLockAction();
407         logDetailsAction = createLogDetailsAction();
408
409         filterAction = createFilterAction();
410         activateViewAction = createActivateViewAction();
411         showTextFilter = createShowTextFilter();
412         groupByAction = createGroupByAction();
413     }
414
415     private IAction createExportLogAction() {
416         Action action = new Action("Export log") {
417             @Override
418             public void run() {
419                 FileDialog fd = new FileDialog(Display.getDefault()
420                         .getActiveShell(), SWT.SAVE);
421                 fd.setOverwrite(true);
422                 fd.setFileName("OIC_Simulator_ServerLog.log");
423                 fd.setFilterExtensions(Constants.SAVE_LOG_FILTER_EXTENSIONS);
424                 String name = fd.open();
425                 List<LogEntry> logEntries = logManager.getLogEntries();
426                 StringBuilder sb = new StringBuilder();
427                 for (LogEntry entry : logEntries) {
428                     sb.append(entry.toString());
429                 }
430                 String data = sb.toString();
431                 BufferedWriter out = null;
432                 try {
433                     out = new BufferedWriter(new FileWriter(name));
434                     out.write(data);
435                 } catch (IOException e) {
436                     e.printStackTrace();
437                     MessageDialog.openError(
438                             Display.getDefault().getActiveShell(),
439                             "Export error",
440                             "Could not export log. IO exception: "
441                                     + e.getMessage());
442                 } finally {
443                     try {
444                         if (null != out) {
445                             out.close();
446                         }
447                     } catch (IOException e) {
448                         Activator
449                                 .getDefault()
450                                 .getLogManager()
451                                 .log(Level.ERROR.ordinal(),
452                                         new Date(),
453                                         "[" + e.getClass().getSimpleName()
454                                                 + "]" + e.getMessage());
455                     }
456                 }
457             }
458         };
459         action.setToolTipText("Export log");
460
461         action.setImageDescriptor(ImageDescriptor.createFromFile(
462                 this.getClass(), "/icons/export_log_e.gif"));
463         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
464                 this.getClass(), "/icons/export_log_d.gif"));
465         action.setEnabled(true);
466
467         return action;
468     }
469
470     private IAction createClearLogAction() {
471         Action action = new Action("Clear log") {
472
473             @Override
474             public void run() {
475                 logManager.clearLog();
476             }
477         };
478         action.setToolTipText("Clear log");
479
480         action.setImageDescriptor(ImageDescriptor.createFromFile(
481                 this.getClass(), "/icons/clear_e.gif"));
482         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
483                 this.getClass(), "/icons/clear_d.gif"));
484         action.setEnabled(true);
485         return action;
486     }
487
488     private IAction createDeleteLogAction() {
489         Action action = new Action("Delete log entry") {
490
491             @Override
492             @SuppressWarnings("unchecked")
493             public void run() {
494                 IStructuredSelection selection = (IStructuredSelection) tree
495                         .getViewer().getSelection();
496                 List<LogEntry> entries = (List<LogEntry>) tree.getViewer()
497                         .getInput();
498                 LogEntry selectedEntry = (LogEntry) selection.getFirstElement();
499                 if (null != selectedEntry) {
500                     LogEntry toBeShownEntry = null;
501                     for (LogEntry entry : entries) {
502                         if (entry.equals(selectedEntry)) {
503                             int size = entries.size();
504                             int index = entries.indexOf(selectedEntry);
505                             if (index + 1 < size) {
506                                 toBeShownEntry = entries.get(index + 1);
507                             } else if (index > 0) {
508                                 toBeShownEntry = entries.get(index - 1);
509                             }
510                             break;
511                         }
512                     }
513                     logManager.removeEntry(selectedEntry);
514                     if (null != toBeShownEntry) {
515                         tree.getViewer().setSelection(
516                                 new StructuredSelection(toBeShownEntry));
517                     }
518                 }
519             }
520         };
521         action.setToolTipText("Delete log entry");
522         action.setImageDescriptor(ImageDescriptor.createFromFile(
523                 this.getClass(), "/icons/delete_e.gif"));
524         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
525                 this.getClass(), "/icons/delete_d.gif"));
526         action.setEnabled(false);
527         return action;
528     }
529
530     private IAction createScrollLockAction() {
531         Action action = new Action("Scroll lock") {
532
533             @Override
534             public void run() {
535                 scrollLockDisabled = !this.isChecked();
536             };
537
538             @Override
539             public int getStyle() {
540                 return IAction.AS_CHECK_BOX;
541             }
542         };
543         action.setToolTipText("Scroll lock");
544         action.setImageDescriptor(ImageDescriptor.createFromFile(
545                 this.getClass(), "/icons/lock_e.gif"));
546         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
547                 this.getClass(), "/icons/lock_d.gif"));
548         action.setEnabled(true);
549         return action;
550     }
551
552     private IAction createLogDetailsAction() {
553         Action action = new Action("Details...") {
554
555             @Override
556             public void run() {
557                 Display.getDefault().asyncExec(new Runnable() {
558
559                     @Override
560                     public void run() {
561                         LogEntry x = (LogEntry) ((IStructuredSelection) tree
562                                 .getViewer().getSelection()).getFirstElement();
563
564                         new LogDetailsDialog(Display.getDefault()
565                                 .getActiveShell(), LogManager.getSeverityName(x
566                                 .getSeverity()), LogManager.getSeverityIcon(x
567                                 .getSeverity()), x.getDate(), x.getMessage())
568                                 .open();
569                     }
570                 });
571             }
572         };
573         action.setToolTipText("Details...");
574         action.setImageDescriptor(ImageDescriptor.createFromFile(
575                 this.getClass(), "/icons/log_details_e.gif"));
576         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
577                 this.getClass(), "/icons/log_details_e.gif"));
578         action.setEnabled(false);
579         return action;
580     }
581
582     private IAction createFilterAction() {
583         Action action = new Action("Filters ...") {
584
585             @Override
586             public void run() {
587                 FilterDialog fd = new FilterDialog(Display.getDefault()
588                         .getActiveShell(), shownSeverities);
589                 if (fd.open() == Window.OK) {
590                     logManager.applyFilter(shownSeverities);
591                 }
592                 tree.getViewer().refresh();
593             }
594         };
595         action.setToolTipText("Filters ...");
596
597         action.setImageDescriptor(ImageDescriptor.createFromFile(
598                 this.getClass(), "/icons/filter_e.gif"));
599         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
600                 this.getClass(), "/icons/filter_d.gif"));
601         action.setEnabled(true);
602         return action;
603     }
604
605     private IAction createActivateViewAction() {
606         Action action = new Action("Activate view on new events",
607                 IAction.AS_CHECK_BOX) {
608
609             @Override
610             public void run() {
611                 activateOnChange = this.isChecked();
612             }
613         };
614         action.setChecked(activateOnChange);
615         action.setToolTipText("Activate view on new events");
616
617         action.setImageDescriptor(ImageDescriptor.createFromFile(
618                 this.getClass(), "/icons/prop_e.gif"));
619         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
620                 this.getClass(), "/icons/prop_d.gif"));
621         action.setEnabled(true);
622         return action;
623     }
624
625     private IAction createShowTextFilter() {
626         Action action = new Action("Show text filter", IAction.AS_CHECK_BOX) {
627
628             @Override
629             public void run() {
630                 Text filterControl = tree.getFilterControl();
631                 Composite filterComposite = filterControl.getParent();
632                 GridData gd = (GridData) filterComposite.getLayoutData();
633                 boolean visible = isChecked();
634                 gd.exclude = !visible;
635                 filterComposite.setVisible(visible);
636                 filterControl.setText("");
637                 if (visible) {
638                     filterControl.selectAll();
639                     setFocus();
640                 }
641                 tree.layout(false);
642                 hideTextFilter = !visible;
643             }
644         };
645         action.setToolTipText("Show text filter");
646
647         action.setImageDescriptor(ImageDescriptor.createFromFile(
648                 this.getClass(), "/icons/tree_mode_e.gif"));
649         action.setDisabledImageDescriptor(ImageDescriptor.createFromFile(
650                 this.getClass(), "/icons/tree_mode_d.gif"));
651         action.setEnabled(true);
652         action.setChecked(!hideTextFilter);
653         if (hideTextFilter) {
654             action.run();
655         }
656         return action;
657     }
658
659     private IContributionItem createGroupByAction() {
660         IMenuManager manager = new MenuManager("Order by");
661         manager.add(sortByTimeAction);
662         manager.add(sortBySeverityAction);
663         manager.add(sortByMessageAction);
664         return manager;
665     }
666
667     class SortAction extends Action {
668
669         private final int sortBy;
670
671         public SortAction(String text, int sortBy) {
672             super(text, IAction.AS_RADIO_BUTTON);
673             this.sortBy = sortBy;
674
675             if (sortCandidate == sortBy) {
676                 setChecked(true);
677             }
678         }
679
680         @Override
681         public void run() {
682             sortBySeverityAction.setChecked(false);
683             sortByTimeAction.setChecked(false);
684             sortCandidate = sortBy;
685             setChecked(true);
686
687             ViewerComparator comparator;
688             TreeColumn column;
689             if (sortBy == ORDER_BY_SEVERITY) {
690                 comparator = severityComparator;
691                 column = severityColumn;
692             } else if (sortBy == ORDER_BY_TIME) {
693                 comparator = dateComparator;
694                 column = dateColumn;
695             } else { // Order by message
696                 comparator = messageComparator;
697                 column = messageColumn;
698             }
699             TreeViewer viewer = tree.getViewer();
700             viewer.setComparator(comparator);
701             viewer.getTree().setSortColumn(column);
702             if (column.equals(sortColumn)) { // reverse sorting order
703                 sortDirection = viewer.getTree().getSortDirection() == SWT.UP ? DOWN
704                         : UP;
705                 viewer.getTree().setSortDirection(
706                         sortDirection == UP ? SWT.UP : SWT.DOWN);
707                 viewer.refresh();
708             } else { // set this column as the one to sort by
709                 sortDirection = DOWN;
710                 viewer.getTree().setSortDirection(SWT.DOWN);
711             }
712             sortColumn = column;
713             refresh();
714         }
715     }
716
717     private void refresh() {
718         tree.getViewer().refresh();
719     }
720
721     @Override
722     public void setFocus() {
723         tree.setFocus();
724     }
725
726     @Override
727     public void dispose() {
728         logManager.removeLogListener(logListener);
729         super.dispose();
730     }
731 }