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
    18  package org.apache.log4j.chainsaw;
    19
    20  import java.awt.Point;
    21  import java.awt.event.InputEvent;
    22  import java.awt.event.KeyAdapter;
    23  import java.awt.event.KeyEvent;
    24  import java.awt.event.MouseAdapter;
    25  import java.awt.event.MouseEvent;
    26
    27  import javax.swing.DefaultListModel;
    28  import javax.swing.JList;
    29  import javax.swing.JPopupMenu;
    30  import javax.swing.JScrollPane;
    31  import javax.swing.ListModel;
    32  import javax.swing.text.JTextComponent;
    33
    34  import org.apache.log4j.chainsaw.filter.FilterModel;
    35  import org.apache.log4j.rule.RuleFactory;
    36  import org.apache.log4j.spi.LoggingEventFieldResolver;
    37
    38  /**
    39   * A popup menu which assists in building expression rules.  Completes event keywords, operators and
    40   * context if available.
    41   *
    42   * @author Scott Deboy &lt;sdeboy@apache.org&gt;
    43   */
    44  public class ExpressionRuleContext extends KeyAdapter {
    45    RuleFactory factory = RuleFactory.getInstance();
    46    LoggingEventFieldResolver resolver = LoggingEventFieldResolver.getInstance();
    47    JPopupMenu contextMenu = new JPopupMenu();
    48    JList list = new JList();
    49    FilterModel filterModel;
    50    JScrollPane scrollPane = new JScrollPane(list);
    51    final JTextComponent textComponent;
    52    private DefaultListModel fieldModel = new DefaultListModel();
    53    private DefaultListModel operatorModel = new DefaultListModel();
    54
    55    public ExpressionRuleContext(
    56      final FilterModel filterModel, final JTextComponent textComponent) {
    57      this.filterModel = filterModel;
    58      this.textComponent = textComponent;
    59      fieldModel.addElement("LOGGER");
    60      fieldModel.addElement("LEVEL");
    61      fieldModel.addElement("CLASS");
    62      fieldModel.addElement("FILE");
    63      fieldModel.addElement("LINE");
    64      fieldModel.addElement("METHOD");
    65      fieldModel.addElement("MSG");
    66      fieldModel.addElement("NDC");
    67      fieldModel.addElement("EXCEPTION");
    68      fieldModel.addElement("TIMESTAMP");
    69      fieldModel.addElement("THREAD");
    70      fieldModel.addElement("PROP.");
    71
    72      operatorModel.addElement("&&");
    73      operatorModel.addElement("||");
    74      operatorModel.addElement("!");
    75      operatorModel.addElement("!=");
    76      operatorModel.addElement("==");
    77      operatorModel.addElement("~=");
    78      operatorModel.addElement("LIKE");
    79      operatorModel.addElement("EXISTS");
    80      operatorModel.addElement("<");
    81      operatorModel.addElement(">");
    82      operatorModel.addElement("<=");
    83      operatorModel.addElement(">=");
    84
    85      //make long to avoid scrollbar
    86      list.setVisibleRowCount(13);
    87
    88      PopupListener popupListener = new PopupListener();
    89      textComponent.addMouseListener(popupListener);
    90
    91      list.addKeyListener(
    92        new KeyAdapter() {
    93          public void keyPressed(KeyEvent e) {
    94            if (e.getKeyCode() == KeyEvent.VK_ENTER) {
    95              String value = list.getSelectedValue().toString();
    96              String contextKey = getContextKey();
    97              if (contextKey != null && (!(contextKey.endsWith(".")))) {
    98                value = "'"+value+"'";
    99              }
    100
    101             updateField(value);
    102             contextMenu.setVisible(false);
    103           }
    104         }
    105       });
    106
    107     list.addMouseListener(
    108       new MouseAdapter() {
    109         public void mouseClicked(MouseEvent e) {
    110           if (e.getClickCount() == 2) {
    111             String value = list.getSelectedValue().toString();
    112             String contextKey = getContextKey();
    113             if (contextKey != null && (!(contextKey.endsWith(".")))) {
    114               value = "'"+value+"'";
    115             }
    116
    117             updateField(value);
    118             contextMenu.setVisible(false);
    119           }
    120         }
    121       });
    122
    123     contextMenu.insert(scrollPane, 0);
    124   }
    125
    126   private void updateField(String value) {
    127     if (textComponent.getSelectedText() == null) {
    128         if (!(value.endsWith("."))) {
    129             value = value + " ";
    130         }
    131     }
    132
    133     textComponent.replaceSelection(value);
    134   }
    135
    136   public void keyPressed(KeyEvent e) {
    137     if (
    138       (e.getKeyCode() == KeyEvent.VK_SPACE)
    139         && (e.getModifiers() == InputEvent.CTRL_MASK)) {
    140       displayContext();
    141     }
    142   }
    143
    144   public void displayContext() {
    145     String lastField = getContextKey();
    146
    147     if (lastField != null) {
    148       ListModel model = filterModel.getContainer().getModel(lastField);
    149       if (model == null) {
    150         return;
    151       }
    152       list.setModel(model);
    153       list.setSelectedIndex(0);
    154
    155       Point p = textComponent.getCaret().getMagicCaretPosition();
    156       contextMenu.doLayout();
    157       contextMenu.show(textComponent, p.x, (p.y + (textComponent.getHeight() - 5)));
    158       list.requestFocus();
    159     } else {
    160       if (isOperatorContextValid()) {
    161         list.setModel(operatorModel);
    162         list.setSelectedIndex(0);
    163
    164         Point p = textComponent.getCaret().getMagicCaretPosition();
    165         contextMenu.doLayout();
    166         contextMenu.show(textComponent, p.x, (p.y + (textComponent.getHeight() - 5)));
    167         list.requestFocus();
    168       } else if (isFieldContextValid()) {
    169         list.setModel(fieldModel);
    170         list.setSelectedIndex(0);
    171
    172         Point p = textComponent.getCaret().getMagicCaretPosition();
    173
    174         if (p == null) {
    175           p = new Point(
    176               textComponent.getLocation().x,
    177               (textComponent.getLocation().y - textComponent.getHeight() + 5));
    178         }
    179         contextMenu.doLayout();
    180         contextMenu.show(textComponent, p.x, (p.y + (textComponent.getHeight() - 5)));
    181         list.requestFocus();
    182       }
    183     }
    184   }
    185
    186   private boolean isFieldContextValid() {
    187     String text = textComponent.getText();
    188     int currentPosition = textComponent.getSelectionStart();
    189
    190     return ((currentPosition == 0)
    191     || (text.charAt(currentPosition - 1) == ' '));
    192   }
    193
    194   private String getContextKey() {
    195     String field = getField();
    196
    197     if (field == null) {
    198       field = getSubField();
    199     }
    200
    201     return field;
    202   }
    203
    204   private boolean isOperatorContextValid() {
    205     String text = textComponent.getText();
    206
    207     int currentPosition = textComponent.getSelectionStart();
    208
    209     if ((currentPosition < 1) || (text.charAt(currentPosition - 1) != ' ')) {
    210       return false;
    211     }
    212
    213     int lastFieldPosition = text.lastIndexOf(" ", currentPosition - 1);
    214
    215     if (lastFieldPosition == -1) {
    216       return false;
    217     }
    218
    219     int lastFieldStartPosition =
    220       Math.max(0, text.lastIndexOf(" ", lastFieldPosition - 1));
    221     String field =
    222       text.substring(lastFieldStartPosition, lastFieldPosition).toUpperCase()
    223           .trim();
    224
    225     if (resolver.isField(field)) {
    226       return true;
    227     }
    228
    229     return false;
    230   }
    231
    232   //returns the currently active field which can be used to display a context menu
    233   //the field returned is the left hand portion of an expression (for example, logger == )
    234   //logger is the field that is returned
    235   private String getField() {
    236     String text = textComponent.getText();
    237
    238     int currentPosition = textComponent.getSelectionStart();
    239
    240     if ((currentPosition < 1) || (text.charAt(currentPosition - 1) != ' ')) {
    241       return null;
    242     }
    243
    244     int symbolPosition = text.lastIndexOf(" ", currentPosition - 1);
    245
    246     if (symbolPosition < 0) {
    247       return null;
    248     }
    249
    250     int lastFieldPosition = text.lastIndexOf(" ", symbolPosition - 1);
    251
    252     if (lastFieldPosition < 0) {
    253       return null;
    254     }
    255
    256     int lastFieldStartPosition =
    257       Math.max(0, text.lastIndexOf(" ", lastFieldPosition - 1));
    258     String lastSymbol =
    259       text.substring(lastFieldPosition + 1, symbolPosition).trim();
    260
    261     String lastField =
    262       text.substring(lastFieldStartPosition, lastFieldPosition).trim();
    263
    264     if (
    265       factory.isRule(lastSymbol)
    266         && filterModel.getContainer().modelExists(lastField)) {
    267       return lastField;
    268     }
    269
    270     return null;
    271   }
    272
    273   //subfields allow the key portion of a field to provide context menu support
    274   //and are available after the fieldname and a . (for example, PROP.)
    275   private String getSubField() {
    276     int currentPosition = textComponent.getSelectionStart();
    277     String text = textComponent.getText();
    278
    279     if (text.substring(0, currentPosition).toUpperCase().endsWith("PROP.")) {
    280       return "PROP.";
    281     }
    282     return null;
    283   }
    284
    285   class PopupListener extends MouseAdapter {
    286     PopupListener() {
    287     }
    288
    289     public void mousePressed(MouseEvent e) {
    290       checkPopup(e);
    291     }
    292
    293     public void mouseReleased(MouseEvent e) {
    294       checkPopup(e);
    295     }
    296
    297     private void checkPopup(MouseEvent e) {
    298       if (e.isPopupTrigger()) {
    299         displayContext();
    300       }
    301     }
    302   }
    303 }