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.rule;
19  
20  import org.apache.log4j.spi.LoggingEvent;
21  
22  import java.util.Stack;
23  import java.util.StringTokenizer;
24  
25  
26  /***
27   * A Rule class supporting both infix and postfix expressions,
28   * accepting any rule which
29   * is supported by the <code>RuleFactory</code>.
30   *
31   * NOTE: parsing is supported through the use of
32   * <code>StringTokenizer</code>, which
33   * implies two limitations:
34   * 1: all tokens in the expression must be separated by spaces,
35   * including parenthesis
36   * 2: operands which contain spaces MUST be wrapped in single quotes.
37   *    For example, the expression:
38   *      msg == 'some msg'
39   *    is a valid expression.
40   * 3: To group expressions, use parentheses.
41   *    For example, the expression:
42   *      level >= INFO || ( msg == 'some msg' || logger == 'test' )
43   *    is a valid expression.
44   * See org.apache.log4j.rule.InFixToPostFix for a
45   * description of supported operators.
46   * See org.apache.log4j.spi.LoggingEventFieldResolver for field keywords.
47   *
48   * @author Scott Deboy (sdeboy@apache.org)
49   */
50  public class ExpressionRule extends AbstractRule {
51      /***
52       * Serialization ID.
53       */
54    static final long serialVersionUID = 5809121703146893729L;
55      /***
56       * Converter.
57       */
58    private static final InFixToPostFix CONVERTER = new InFixToPostFix();
59      /***
60       * Compiler.
61       */
62    private static final PostFixExpressionCompiler COMPILER =
63            new PostFixExpressionCompiler();
64      /***
65       * Rule.
66       */
67    private final Rule rule;
68  
69      /***
70       * Create new instance.
71       * @param r rule
72       */
73    private ExpressionRule(final Rule r) {
74      super();
75      this.rule = r;
76    }
77  
78      /***
79       * Get rule.
80       * @param expression expression.
81       * @return rule.
82       */
83    public static Rule getRule(final String expression) {
84        return getRule(expression, false);
85    }
86  
87      /***
88       * Get rule.
89       * @param expression expression.
90       * @param isPostFix If post-fix.
91       * @return rule
92       */
93    public static Rule getRule(final String expression,
94                               final boolean isPostFix) {
95      String postFix = expression;
96      if (!isPostFix) {
97        postFix = CONVERTER.convert(expression);
98      }
99  
100     return new ExpressionRule(COMPILER.compileExpression(postFix));
101   }
102 
103     /***
104      * {@inheritDoc}
105      */
106   public boolean evaluate(final LoggingEvent event) {
107     return rule.evaluate(event);
108   }
109 
110     /***
111      * {@inheritDoc}
112      */
113   public String toString() {
114       return rule.toString();
115   }
116 
117   /***
118    * Evaluate a boolean postfix expression.
119    *
120    */
121   static final class PostFixExpressionCompiler {
122       /***
123        * Compile expression.
124        * @param expression expression.
125        * @return rule.
126        */
127     public Rule compileExpression(final String expression) {
128       RuleFactory factory = RuleFactory.getInstance();
129 
130       Stack stack = new Stack();
131       StringTokenizer tokenizer = new StringTokenizer(expression);
132 
133       while (tokenizer.hasMoreTokens()) {
134         //examine each token
135         String token = tokenizer.nextToken();
136         if ((token.startsWith("'"))
137                 && (token.endsWith("'")
138                 && (token.length() > 2))) {
139             token = token.substring(1, token.length() - 1);
140         }
141         if ((token.startsWith("'"))
142                 && (token.endsWith("'")
143                 && (token.length() == 2))) {
144             token = "";
145         }
146 
147         boolean inText = token.startsWith("'");
148         if (inText) {
149             token = token.substring(1);
150             while (inText && tokenizer.hasMoreTokens()) {
151               token = token + " " + tokenizer.nextToken();
152               inText = !(token.endsWith("'"));
153           }
154           if (token.length() > 0) {
155               token = token.substring(0, token.length() - 1);
156           }
157         }
158 
159         //if a symbol is found, pop 2 off the stack,
160           // evaluate and push the result
161         if (factory.isRule(token)) {
162           Rule r = factory.getRule(token, stack);
163           stack.push(r);
164         } else {
165           //variables or constants are pushed onto the stack
166           if (token.length() > 0) {
167               stack.push(token);
168           }
169         }
170       }
171 
172       if ((stack.size() == 1) && (!(stack.peek() instanceof Rule))) {
173         //while this may be an attempt at creating an expression,
174         //for ease of use, convert this single entry to a partial-text
175         //match on the MSG field
176         Object o = stack.pop();
177         stack.push("MSG");
178         stack.push(o);
179         return factory.getRule("~=", stack);
180       }
181 
182       //stack should contain a single rule if the expression is valid
183       if ((stack.size() != 1) || (!(stack.peek() instanceof Rule))) {
184         throw new IllegalArgumentException("invalid expression: " + expression);
185       } else {
186         return (Rule) stack.pop();
187       }
188     }
189   }
190 }
191 
192