]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.logging.ui/src/org/simantics/logging/ui/LoggerManagementDialog.java
Add UI dialog for dynamic reconfiguration of Logback loggers
[simantics/platform.git] / bundles / org.simantics.logging.ui / src / org / simantics / logging / ui / LoggerManagementDialog.java
1 package org.simantics.logging.ui;
2
3 import java.util.HashMap;
4 import java.util.List;
5 import java.util.Map;
6 import java.util.concurrent.atomic.AtomicReference;
7 import java.util.function.Consumer;
8 import java.util.function.Function;
9 import java.util.function.Predicate;
10
11 import org.eclipse.jface.dialogs.IDialogConstants;
12 import org.eclipse.jface.dialogs.IDialogSettings;
13 import org.eclipse.jface.dialogs.TrayDialog;
14 import org.eclipse.jface.layout.GridDataFactory;
15 import org.eclipse.jface.layout.GridLayoutFactory;
16 import org.eclipse.jface.layout.TableColumnLayout;
17 import org.eclipse.jface.resource.FontDescriptor;
18 import org.eclipse.jface.resource.JFaceResources;
19 import org.eclipse.jface.viewers.ArrayContentProvider;
20 import org.eclipse.jface.viewers.CellEditor;
21 import org.eclipse.jface.viewers.ColumnLabelProvider;
22 import org.eclipse.jface.viewers.ColumnWeightData;
23 import org.eclipse.jface.viewers.ComboBoxCellEditor;
24 import org.eclipse.jface.viewers.EditingSupport;
25 import org.eclipse.jface.viewers.TableViewer;
26 import org.eclipse.jface.viewers.TableViewerColumn;
27 import org.eclipse.jface.viewers.TextCellEditor;
28 import org.eclipse.jface.viewers.Viewer;
29 import org.eclipse.jface.viewers.ViewerCell;
30 import org.eclipse.jface.viewers.ViewerFilter;
31 import org.eclipse.swt.SWT;
32 import org.eclipse.swt.custom.CCombo;
33 import org.eclipse.swt.graphics.Font;
34 import org.eclipse.swt.widgets.Composite;
35 import org.eclipse.swt.widgets.Control;
36 import org.eclipse.swt.widgets.Display;
37 import org.eclipse.swt.widgets.Shell;
38 import org.eclipse.swt.widgets.Text;
39 import org.simantics.logging.LogConfigurator;
40 import org.simantics.logging.LoggerLevel;
41
42 public class LoggerManagementDialog extends TrayDialog {
43
44         private static final String DIALOG = LoggerManagementDialog.class.getSimpleName(); //$NON-NLS-1$
45
46         private static final String[] LEVELS = {
47                         "ERROR",
48                         "WARN",
49                         "INFO",
50                         "DEBUG",
51                         "TRACE",
52         };
53
54         private static final Map<String, Integer> LEVEL_TO_INDEX = new HashMap<>();
55
56         static {
57                 for (int i = 0; i < LEVELS.length; i++) {
58                         LEVEL_TO_INDEX.put(LEVELS[i], i);
59                 }
60         }
61
62         /**
63          * ID for Apply buttons.
64          */
65         private int APPLY_ID = IDialogConstants.CLIENT_ID + 1;
66
67         /**
68          * The label for Apply buttons.
69          */
70         private String APPLY_LABEL = JFaceResources.getString("apply"); //$NON-NLS-1$
71
72         private TableViewer tableViewer;
73         private List<LoggerLevel> configuration;
74         private IDialogSettings dialogBoundsSettings;
75
76         private Consumer<List<LoggerLevel>> applyFunction;
77
78         public LoggerManagementDialog(Shell shell, Consumer<List<LoggerLevel>> applyFunction) {
79                 super(shell);
80                 reloadConfiguration();
81                 this.applyFunction = applyFunction;
82
83                 IDialogSettings settings = Activator.getDefault().getDialogSettings();
84                 dialogBoundsSettings = settings.getSection(DIALOG);
85                 if (dialogBoundsSettings == null)
86                         dialogBoundsSettings = settings.addNewSection(DIALOG);
87         }
88
89         private void reloadConfiguration() {
90                 this.configuration = LogConfigurator.listConfiguredLoggers();
91                 addEmptyRow();
92         }
93
94         private void addEmptyRow() {
95                 this.configuration.add(new LoggerLevel("", LEVELS[2]));
96         }
97
98         @Override
99         protected IDialogSettings getDialogBoundsSettings() {
100                 return dialogBoundsSettings;
101         }
102
103         @Override
104         protected void configureShell(Shell newShell) {
105                 super.configureShell(newShell);
106                 newShell.setText("Manage Loggers");
107                 newShell.setMinimumSize(800, 600);
108         }
109
110         @Override
111         protected boolean isResizable() {
112                 return true;
113         }
114
115         @Override
116         protected void createButtonsForButtonBar(Composite parent) {
117                 createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
118                 createButton(parent, APPLY_ID, APPLY_LABEL, false);
119                 createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
120         }
121
122         @Override
123         protected void buttonPressed(int buttonId) {
124                 if (buttonId == IDialogConstants.CLOSE_ID) {
125                         super.buttonPressed(IDialogConstants.CANCEL_ID);
126                 } else if (buttonId == APPLY_ID) {
127                         if (applyFunction != null) {
128                                 applyFunction.accept(configuration);
129                                 reloadConfiguration();
130                                 setInput();
131                         }
132                 } else {
133                         super.buttonPressed(buttonId);
134                 }
135         }
136
137         @Override
138         protected Control createDialogArea(Composite parent) {
139                 final Composite composite = (Composite) super.createDialogArea(parent);
140                 GridLayoutFactory.fillDefaults().margins(10, 10).numColumns(1).applyTo(composite);
141                 GridDataFactory.fillDefaults().grab(true, true).applyTo(composite);
142
143                 Composite tableComposite = new Composite(composite, SWT.NONE);
144                 TableColumnLayout tcl = new TableColumnLayout();
145                 tableComposite.setLayout(tcl);
146                 GridDataFactory.fillDefaults().grab(true, true).applyTo(tableComposite);
147
148                 tableViewer = new TableViewer(tableComposite, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI);
149
150                 Display display = getShell().getDisplay(); 
151
152                 // Fonts
153                 final Font systemFont = display.getSystemFont();
154                 final Font italic = FontDescriptor.createFrom(systemFont).setStyle(SWT.ITALIC).createFont(display);
155                 final Font bold = FontDescriptor.createFrom(systemFont).setStyle(SWT.BOLD).createFont(display);
156                 tableViewer.getTable().addDisposeListener(e -> {
157                         italic.dispose();
158                         bold.dispose();
159                 });
160
161                 Function<LoggerLevel, Font> fontFunction = l -> { 
162                         if (l.isLoggerDefined()) {
163                                 if (l.levelChanged()) {
164                                         return bold;
165                                 }
166                                 return systemFont;
167                         }
168                         return italic;
169                 };
170
171                 // Column 1: logger name
172                 TableViewerColumn column1 = new TableViewerColumn(tableViewer, SWT.NONE);
173                 column1.getColumn().setWidth(300);
174                 column1.getColumn().setText("Logger");
175                 column1.getColumn().setToolTipText("Package Name of Logger");
176                 column1.setLabelProvider(new ColumnLabelProvider() {
177                         @Override
178                         public void update(ViewerCell cell) {
179                                 LoggerLevel l = (LoggerLevel) cell.getElement();
180                                 cell.setText(l.getName());
181                                 cell.setFont(fontFunction.apply(l));
182                         }
183                 });
184                 column1.setEditingSupport(new EditingSupport(tableViewer) {
185                         CellEditor editor = new TextCellEditor(tableViewer.getTable());
186                         @Override
187                         protected CellEditor getCellEditor(Object element) {
188                                 return editor;
189                         }
190                         @Override
191                         protected boolean canEdit(Object element) {
192                                 LoggerLevel l = (LoggerLevel) element;
193                                 return !l.isLoggerDefined();
194                         }
195                         @Override
196                         protected Object getValue(Object element) {
197                                 return ((LoggerLevel)element).getName();
198                         }
199                         @Override
200                         protected void setValue(Object element, Object value) {
201                                 LoggerLevel l = (LoggerLevel) element;
202                                 String s = ((String) value).trim();
203
204                                 boolean sameNameExists = !s.isEmpty() && configuration.stream().anyMatch(ll -> ll != l && ll.getName().equals(s));
205
206                                 // Prevent providing the same package twice
207                                 if (sameNameExists)
208                                         return;
209
210                                 String previousName = l.getName();
211                                 l.setName(s);
212                                 if (s.isEmpty()) {
213                                         int index = configuration.indexOf(l);
214                                         if (index >= 0 && index < (configuration.size()-1)) {
215                                                 configuration.remove(index);
216                                         }
217                                 } else if (previousName.isEmpty()) {
218                                         addEmptyRow();
219                                 }
220                                 setInput();
221                         }
222                 });
223
224                 // Column 2: logging level of logger
225                 TableViewerColumn column2 = new TableViewerColumn(tableViewer, SWT.NONE);
226                 column2.getColumn().setWidth(100);
227                 column2.getColumn().setText("Log Level");
228                 column2.getColumn().setToolTipText("Logging Level for Package and Subpackages");
229                 column2.setLabelProvider(new ColumnLabelProvider() {
230                         @Override
231                         public void update(ViewerCell cell) {
232                                 LoggerLevel l = (LoggerLevel) cell.getElement();
233                                 cell.setText(l.getLevel());
234                                 cell.setFont(fontFunction.apply(l));
235                         }
236                 });
237                 column2.setEditingSupport(new EditingSupport(tableViewer) {
238                         @Override
239                         protected boolean canEdit(Object element) {
240                                 return true;
241                         }
242                         @Override
243                         protected CellEditor getCellEditor(Object element) {
244                                 return new ComboBoxCellEditor(tableViewer.getTable(), LEVELS, SWT.READ_ONLY) {
245                                         @Override
246                                         protected Control createControl(Composite parent) {
247                                                 CCombo combo = (CCombo) super.createControl(parent);
248                                                 // The only way found to actually open the combo box list
249                                                 // right away when starting to edit.
250                                                 combo.getDisplay().asyncExec(() -> {
251                                                         if (!combo.isDisposed())
252                                                                 combo.setListVisible(true);
253                                                 });
254                                                 return combo;
255                                         }
256                                 };
257                         }
258                         @Override
259                         protected Object getValue(Object element) {
260                                 LoggerLevel l = (LoggerLevel) element;
261                                 return LEVEL_TO_INDEX.get(l.getLevel());
262                         }
263                         @Override
264                         protected void setValue(Object element, Object value) {
265                                 LoggerLevel l = (LoggerLevel)element;
266                                 l.setLevel(LEVELS[(Integer) value]);
267                                 getViewer().update(element, null);
268                         }
269                 });
270
271                 tcl.setColumnData(column1.getColumn(), new ColumnWeightData(5, 300));
272                 tcl.setColumnData(column2.getColumn(), new ColumnWeightData(1, 150));
273
274                 // Decorations
275                 tableViewer.getTable().setHeaderVisible(true);
276                 tableViewer.getTable().setLinesVisible(true);
277
278                 // Table content
279                 tableViewer.setContentProvider(ArrayContentProvider.getInstance());
280
281                 tableViewer.getTable().addListener(SWT.KeyDown, e -> {
282                         if (e.keyCode == SWT.DEL) {
283                                 @SuppressWarnings("unchecked")
284                                 List<Object> s = tableViewer.getStructuredSelection().toList();
285                                 Predicate<Object> removable = l -> {
286                                         LoggerLevel ll = (LoggerLevel) l;
287                                         return ll.isLoggerDefined() && !ll.getName().isEmpty();
288                                 };
289                                 if (s.stream().allMatch(removable)) {
290                                         s.forEach(configuration::remove);
291                                         setInput();
292                                 }
293                         }
294                 });
295
296                 Text filterText = new Text(composite, SWT.FLAT | SWT.BORDER);
297                 GridDataFactory.fillDefaults().grab(true, false).applyTo(filterText);
298                 filterText.setToolTipText("Package Name Filter");
299                 filterText.moveAbove(tableComposite);
300                 filterText.addModifyListener(e -> {
301                         String filter = filterText.getText().trim().toLowerCase();
302                         ViewerFilter[] filters = {};
303                         if (!filter.isEmpty()) {
304                                 filters = new ViewerFilter[] {
305                                                 new ViewerFilter() {
306                                                         @Override
307                                                         public boolean select(Viewer viewer, Object parentElement, Object element) {
308                                                                 LoggerLevel l = (LoggerLevel) element;
309                                                                 return !l.isLoggerDefined() || l.levelChanged()
310                                                                                 ? true
311                                                                                 : l.getName().toLowerCase().contains(filter);
312                                                         }
313                                                 }
314                                 };
315                         };
316                         scheduleSetFilters(filters);
317                 });
318
319                 setInput();
320
321                 return composite;
322         }
323
324         private void setInput() {
325                 tableViewer.setInput(configuration.toArray(new LoggerLevel[configuration.size()]));
326         }
327
328         public List<LoggerLevel> getConfiguration() {
329                 return configuration;
330         }
331
332         private final AtomicReference<ViewerFilter[]> filtersToSet = new AtomicReference<>();
333         private final Runnable setFilters = () -> {
334                 if (!tableViewer.getTable().isDisposed()) {
335                         ViewerFilter[] fs = filtersToSet.getAndSet(null);
336                         if (fs != null) {
337                                 tableViewer.getTable().setRedraw(false);
338                                 tableViewer.setFilters(fs);
339                                 tableViewer.getTable().setRedraw(true);
340                         }
341                 }
342         };
343
344         protected void scheduleSetFilters(ViewerFilter[] array) {
345                 filtersToSet.set(array);
346                 getShell().getDisplay().timerExec(250, setFilters);
347         }
348
349 }