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 &lt;psmith@apache.org&gt;
    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 }