View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.log4j.chainsaw.receivers;
18  
19  import org.apache.log4j.Level;
20  import org.apache.log4j.LogManager;
21  import org.apache.log4j.Logger;
22  import org.apache.log4j.chainsaw.ChainsawConstants;
23  import org.apache.log4j.chainsaw.Generator;
24  import org.apache.log4j.chainsaw.helper.TableCellEditorFactory;
25  import org.apache.log4j.plugins.Plugin;
26  
27  import javax.swing.*;
28  import javax.swing.table.AbstractTableModel;
29  import javax.swing.table.DefaultTableModel;
30  import javax.swing.table.TableCellEditor;
31  import javax.swing.table.TableModel;
32  import java.awt.*;
33  import java.awt.event.WindowAdapter;
34  import java.awt.event.WindowEvent;
35  import java.beans.BeanInfo;
36  import java.beans.IntrospectionException;
37  import java.beans.Introspector;
38  import java.beans.PropertyDescriptor;
39  import java.util.*;
40  import java.util.List;
41  import javax.swing.event.CellEditorListener;
42  import javax.swing.table.TableCellRenderer;
43  
44  
45  /**
46   * A panel that allows the user to edit a particular Plugin, by using introspection
47   * this class discovers the modifiable properties of the Plugin
48   *
49   * @author Paul Smith <psmith@apache.org>
50   */
51  public class PluginPropertyEditorPanel extends JPanel {
52  
53      private final JScrollPane scrollPane = new JScrollPane();
54      private final JTable propertyTable = new JTable();
55  
56      private Plugin plugin;
57      private TableModel defaultModel = new DefaultTableModel(
58          new String[]{"Property", "Value"}, 1);
59  
60      private static final Logger logger = LogManager.getLogger(PluginPropertyEditorPanel.class);
61  
62      /**
63       *
64       */
65      public PluginPropertyEditorPanel() {
66          super();
67          initComponents();
68          setupListeners();
69      }
70  
71      /**
72       *
73       */
74      private void initComponents() {
75          propertyTable.setRowHeight(ChainsawConstants.DEFAULT_ROW_HEIGHT);
76          setLayout(new BorderLayout());
77          scrollPane.setViewportView(propertyTable);
78  
79          add(scrollPane, BorderLayout.CENTER);
80  
81          propertyTable.setModel(
82              defaultModel = new DefaultTableModel(
83                  new String[]{"Property", "Value"}, 1));
84  
85      }
86  
87      /**
88       *
89       */
90      private void setupListeners() {
91          addPropertyChangeListener("plugin", evt -> {
92  
93              final Plugin p = (Plugin) evt.getNewValue();
94  
95              if (p != null) {
96  
97                  try {
98  
99                      PluginPropertyTableModel model =
100                         new PluginPropertyTableModel(p);
101                     propertyTable.setModel(model);
102                     propertyTable.getColumnModel().getColumn(1)
103                         .setCellEditor(new PluginTableCellEditor());
104                     propertyTable.setEnabled(true);
105                 } catch (Throwable e) {
106                     logger.error("Failed to introspect the Plugin", e);
107                 }
108             } else {
109                 propertyTable.setModel(defaultModel);
110                 propertyTable.setEnabled(false);
111             }
112 
113         });
114     }
115 
116     /**
117      * @return Returns the plugin.
118      */
119     public final Plugin getPlugin() {
120 
121         return plugin;
122     }
123 
124     /**
125      * @param plugin The plugin to set.
126      */
127     public final void setPlugin(Plugin plugin) {
128 
129         Plugin oldValue = this.plugin;
130         this.plugin = plugin;
131         firePropertyChange("plugin", oldValue, this.plugin);
132     }
133 
134     /**
135      * @author psmith
136      */
137     private class PluginTableCellEditor extends AbstractCellEditor
138         implements TableCellEditor {
139 
140         private Map editorMap = new HashMap();
141         private DefaultCellEditor defaultEditor = new DefaultCellEditor(
142             new JTextField());
143         private DefaultCellEditor currentEditor = defaultEditor;
144 
145         private PluginTableCellEditor() {
146 
147             editorMap.put(Boolean.class,
148                 TableCellEditorFactory.createBooleanTableCellEditor());
149             editorMap.put(Level.class,
150                 TableCellEditorFactory.createLevelTableCellEditor());
151             //support primitive boolean parameters with the appropriate editor
152             editorMap.put(boolean.class, TableCellEditorFactory.createBooleanTableCellEditor());
153         }
154 
155         /* (non-Javadoc)
156          * @see javax.swing.table.TableCellEditor#getTableCellEditorComponent(javax.swing.JTable, java.lang.Object, boolean, int, int)
157          */
158         public Component getTableCellEditorComponent(JTable table, Object value,
159                                                      boolean isSelected, int row, int column) {
160 
161             PluginPropertyTableModel model = (PluginPropertyTableModel) table.getModel();
162             PropertyDescriptor descriptor = model.getDescriptors()[row];
163             Class valueClass = descriptor.getPropertyType();
164 
165             if (editorMap.containsKey(valueClass)) {
166 
167                 DefaultCellEditor editor =
168                     (DefaultCellEditor) editorMap.get(valueClass);
169                 logger.debug("Located CellEditor for " + valueClass);
170                 currentEditor = editor;
171 
172                 return currentEditor.getTableCellEditorComponent(table, value,
173                     isSelected, row, column);
174             }
175 
176             currentEditor = defaultEditor;
177             logger.debug("Cell value class " + valueClass +
178                 " not know, using default editor");
179 
180             Component c = defaultEditor.getTableCellEditorComponent(table, value,
181                 isSelected, row, column);
182             table.setRowHeight( row, c.getPreferredSize().height );
183             return c;
184         }
185 
186         /* (non-Javadoc)
187          * @see javax.swing.CellEditor#getCellEditorValue()
188          */
189         public Object getCellEditorValue() {
190 
191             return currentEditor.getCellEditorValue();
192         }
193 
194     }
195 
196     private class PluginPropertyTableModel extends AbstractTableModel {
197 
198         private final PropertyDescriptor[] descriptors;
199         private final Plugin plugin;
200 
201         private PluginPropertyTableModel(Plugin p)
202             throws IntrospectionException {
203             super();
204 
205             BeanInfo beanInfo = Introspector.getBeanInfo(p.getClass());
206 
207             List list = new ArrayList(Arrays.asList(
208                 beanInfo.getPropertyDescriptors()));
209 
210             list.sort((o1, o2) -> {
211 
212                 PropertyDescriptor d1 = (PropertyDescriptor) o1;
213                 PropertyDescriptor d2 = (PropertyDescriptor) o2;
214 
215                 return d1.getDisplayName().compareToIgnoreCase(
216                     d2.getDisplayName());
217             });
218             this.plugin = p;
219             this.descriptors = (PropertyDescriptor[]) list.toArray(
220                 new PropertyDescriptor[0]);
221         }
222 
223         /* (non-Javadoc)
224          * @see javax.swing.table.AbstractTableModel#getValueAt(int, int)
225          */
226         public Object getValueAt(int row, int col) {
227 
228             PropertyDescriptor d = descriptors[row];
229 
230             switch (col) {
231 
232                 case 1:
233 
234                     try {
235 
236                         Object object = d.getReadMethod().invoke(plugin);
237 
238                         if (object != null) {
239 
240                             return object;
241                         }
242                     } catch (Exception e) {
243                         logger.error(
244                             "Error reading value for PropertyDescriptor " + d);
245                     }
246 
247                     return "";
248 
249                 case 0:
250                     return d.getName();
251             }
252 
253             return null;
254         }
255 
256         /* (non-Javadoc)
257          * @see javax.swing.table.AbstractTableModel#getColumnCount()
258          */
259         public int getColumnCount() {
260 
261             return 2;
262         }
263 
264         /* (non-Javadoc)
265          * @see javax.swing.table.AbstractTableModel#getRowCount()
266          */
267         public int getRowCount() {
268 
269             return descriptors.length;
270         }
271 
272         /* (non-Javadoc)
273          * @see javax.swing.table.TableModel#isCellEditable(int, int)
274          */
275         public boolean isCellEditable(int rowIndex, int columnIndex) {
276 
277 //        TODO Determine if the property is one of the ones a User could edit
278             return columnIndex == 1 && descriptors[rowIndex].getWriteMethod() != null;
279 
280         }
281 
282         /* (non-Javadoc)
283          * @see javax.swing.table.TableModel#getColumnName(int)
284          */
285         public String getColumnName(int column) {
286 
287             return (column == 0) ? "Property" : "Value";
288         }
289 
290         /* (non-Javadoc)
291          * @see javax.swing.table.TableModel#setValueAt(java.lang.Object, int, int)
292          */
293         public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
294 
295 
296             if (columnIndex == 1) {
297                 //ensure name is set
298                 if (descriptors[rowIndex].getName().equalsIgnoreCase("name") && (aValue == null || aValue.toString().trim().equals(""))) {
299                     logger.error("Name required");
300                     return;
301                 }
302                 aValue = translateValueIfNeeded(rowIndex, aValue);
303                 logger.debug(
304                     "setValueAt, " + rowIndex + ", " + columnIndex +
305                         ", value=" + aValue + ", valueClass" + aValue.getClass());
306 
307                 try {
308                     descriptors[rowIndex].getWriteMethod().invoke(plugin,
309                         aValue);
310                     fireTableCellUpdated(rowIndex, columnIndex);
311                 } catch (IllegalArgumentException e) {
312                     // ignore
313                 } catch (Exception e) {
314                     logger.error(
315                         "Failed to modify the Plugin because of Exception", e);
316                 }
317 
318             } else {
319                 super.setValueAt(aValue, rowIndex, columnIndex);
320             }
321 
322             // Since the value has been set, resize all of the rows(if required)
323             propertyTable.setRowHeight(ChainsawConstants.DEFAULT_ROW_HEIGHT);
324         }
325 
326         /**
327          * @param row
328          * @param value
329          * @return
330          */
331         private Object translateValueIfNeeded(int row, Object value) {
332 
333             if ((descriptors[row].getPropertyType() == int.class) ||
334                 (descriptors[row].getPropertyType() == Integer.class)) {
335 
336                 try {
337 
338                     return Integer.valueOf(value.toString());
339                 } catch (Exception e) {
340                     logger.error("Failed to convert to Integer type");
341                 }
342             }
343 
344             return value;
345         }
346 
347         /**
348          * @return Returns the descriptors.
349          */
350         public final PropertyDescriptor[] getDescriptors() {
351             return descriptors;
352         }
353     }
354 }