View Javadoc

1   /***
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Exoffice Technologies.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Exoffice Technologies. Exolab is a registered
23   *    trademark of Exoffice Technologies.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 2000-2001,2003 (C) Exoffice Technologies Inc. All Rights Reserved.
42   */
43  
44  package org.exolab.jms.selector;
45  
46  import java.io.StringReader;
47  
48  import javax.jms.InvalidSelectorException;
49  import javax.jms.Message;
50  
51  import org.exolab.jms.selector.parser.SelectorLexer;
52  import org.exolab.jms.selector.parser.SelectorParser;
53  import org.exolab.jms.selector.parser.SelectorTreeParser;
54  
55  
56  /***
57   * This class enables messages to be filtered using a message selector.
58   * This is a String whose syntax is based on a subset of the SQL92
59   * conditional expression syntax.
60   *
61   * A selector can contain:
62   * <ul>
63   * <li>Literals:</li>
64   * <ul>
65   *   <li>A string literal is enclosed in single quotes with an included
66   *       single quote represented by doubled single quote such as 'literal'
67   *       and 'literal''s'; like Java <i>String</i> literals these use the
68   *       unicode character encoding.
69   *   </li>
70   *   <li>An exact numeric literal is a numeric value without a decimal point
71   *       such as 57, -957, +62; numbers in the range of Java <i>long</i> are
72   *       supported. Exact numeric literals use the Java integer literal syntax.
73   *   </li>
74   *   <li>An approximate numeric literal is a numeric value in scientific
75   *       notation such as 7E3, -57.9E2 or a numeric value with a decimal such
76   *       as 7., -95.7, +6.2; numbers in the range of Java <i>double</i>
77   *       are supported. Approximate literals use the Java floating point
78   *       literal syntax.
79   *   </li>
80   *   <li>The boolean literals <i>TRUE </i>and <i>FALSE</i>.</li>
81   * </ul>
82   * <li>Identifiers:</li>
83   * <ul>
84   *   <li>Identifiers use the Java identifier syntax. They are case sensitive.
85   *   </li>
86   *   <li>Identifiers cannot be the names <i>NULL</i>, <i>TRUE</i>, or
87   *       <i>FALSE</i>.
88   *   </li>
89   *   <li>Identifiers cannot be <i>NOT, AND, OR, BETWEEN, LIKE, IN</i>, and
90   *       <i>IS</i>.
91   *   </li>
92   *   <li>Identifiers are either header field references or property references.
93   *   </li>
94   *   <br>Message header field references are restricted to
95   *       <i>JMSDeliveryMode</i>, <i>JMSPriority</i>, <i>JMSMessageID</i>,
96   *       <i>JMSTimestamp</i>, <i>JMSCorrelationID</i>, and <i>JMSType</i>.
97   *       <i>JMSMessageID</i>, <i>JMSCorrelationID</i>, and <i>JMSType</i>
98   *       values may be <i>null</i> and if so are treated as a NULL value.
99   *   <li>Any name beginning with 'JMSX' is a JMS defined property name.</li>
100  *   <li>Any name beginning with 'JMS_' is a provider-specific property name.
101  *   </li>
102  *   <li>Any name that does not begin with 'JMS' is an application-specific
103  *       property name. If a property is referenced that does not exist in a
104  *       message its value is NULL. If it does exist, its value is the
105  *       corresponding property value.
106  *   </li>
107  * </ul>
108  * <li>Expressions:</li>
109  * <ul>
110  *   <li>A selector is a conditional expression; a selector that evaluates to
111  *       true matches; a selector that evaluates to false or unknown does not
112  *       match.
113  *   </li>
114  *   <li>Arithmetic expressions are composed of themselves, arithmetic
115  *       operations, identifiers with numeric values and numeric literals.
116  *   </li>
117  *   <li>Conditional expressions are composed of themselves, comparison
118  *       operations, logical operations, identifiers with boolean values and
119  *       boolean literals.
120  *   </li>
121  *   <li>Standard bracketing () for ordering expression evaluation is
122  *       supported.
123  *   </li>
124  *   <li>Logical operators in precedence order: NOT, AND, OR.</li>
125  *   <li>Comparison operators: =, >, >=, &lt;, &lt;=, &lt;> (not equal).
126  *   </li>
127  *   <li>Only <i>like </i>type values can be compared. One exception is that it
128  *       is valid to compare exact numeric values and approximate numeric
129  *       values (the type conversion required is defined by the rules of Java
130  *       numeric promotion). If the comparison of non-like type values is
131  *       attempted, the selector is always false.
132  *   </li>
133  *   <li><i>String</i> and <i>Boolean</i> comparison is restricted to = and
134  *       &lt;>. Two strings are equal if and only if they contain the same
135  *       sequence of characters.
136  *   </li>
137  * </ul>
138  * <li>Arithmetic operators in precedence order:</li>
139  * <ul>
140  *   <li>+, - unary</li>
141  *   <li>*, / multiplication and division</li>
142  *   <li>+, - addition and subtraction</li>
143  *   <li>Arithmetic operations use Java numeric promotion.</li>
144  * </ul>
145  *
146  * <li><i>arithmetic-expr1 </i>[NOT] BETWEEN <i>arithmetic-expr2 </i>AND<i>
147  *     arithmetic-expr3</i> comparison operator
148  * </li>
149  *   <ul>
150  *     <li>age BETWEEN 15 and 19 is equivalent to age >= 15 AND age &lt;= 19
151  *     </li>
152  *     <li>age NOT BETWEEN 15 and 19 is equivalent to age &lt; 15 OR age > 19
153  *     </li>
154  *   </ul>
155  *   <li><i>identifier </i>[NOT] IN (<i>string-literal1, string-literal2,...
156  *       </i>)
157  *   </li>
158  *   <br>comparison operator where identifier has a <i>String</i> or NULL
159  *       value.
160  *   <ul>
161  *     <li>Country IN ('UK', 'US', 'France') is true for 'UK' and false for
162  *         'Peru'. It is equivalent to the expression (Country = ' UK') OR
163  *         (Country = ' US') OR (Country = ' France')
164  *     </li>
165  *     <li>Country NOT IN (' UK', 'US', 'France') is false for 'UK' and true
166  *         for 'Peru'. It is equivalent to the expression NOT ((Country = 'UK')
167  *         OR (Country = 'US') OR (Country = 'France'))
168  *     </li>
169  *     <li>If <i>identifier </i>of an IN or NOT IN operation is NULL the value
170  *         of the operation is unknown.
171  *     </li>
172  *   </ul>
173  *   <li><i>identifier </i>[NOT] LIKE <i>pattern-value</i> [ESCAPE
174  *       <i>escape-character</i>]
175  *   </li>
176  *   <br>comparison operator, where <i>identifier</i> has a <i>String</i>
177  *       value; <i>pattern-value</i> is a string literal where '_' stands for
178  *       any single character; '%' stands for any sequence of characters
179  *       (including the empty sequence); and all other characters stand for
180  *       themselves. The optional <i>escape-character</i> is a single character
181  *       string literal whose character is used to escape the special meaning
182  *       of the '_' and '%' in <i>pattern-value</i>.
183  *   <ul>
184  *     <li><i>phone LIKE '12%3'</i> is true for '123', '12993' and false for
185  *         '1234'
186  *     </li>
187  *     <li><i>word LIKE 'l_se'</i> is true for 'lose' and false for 'loose'
188  *     </li>
189  *     <li><i>underscored LIKE '\_%' ESCAPE '\'</i> is true for '_foo' and
190  *         false for 'bar'
191  *     </li>
192  *     <li><i>phone NOT LIKE '12%3'</i> is false for '123' and '12993' and
193  *         true for '1234'
194  *     </li>
195  *     <li>If <i>identifier</i> of a LIKE or NOT LIKE operation is NULL the
196  *         value of the operation is unknown.
197  *     </li>
198  *   </ul>
199  *   <li><i>identifier</i> IS NULL</li>
200  *   <br>comparison operator tests for a null header field value, or a
201  *       missing property value.
202  *   <ul>
203  *     <li><i>prop_name</i> IS NULL</li>
204  *     <li><i>identifier</i> IS NOT NULL comparison operator tests for the
205  *         existence of a non null header field value or property value.
206  *     </li>
207  *     <li><i>prop_name</i> IS NOT NULL</li>
208  *   </ul>
209  * </ul></ul>
210  *
211  * @version     $Revision: 1.1 $ $Date: 2004/11/26 01:50:44 $
212  * @author      <a href="mailto:tma@netspace.net.au">Tim Anderson</a>
213  */
214 public class Selector {
215 
216     /***
217      * The 'compiled' expression
218      */
219     private final Expression _evaluator;
220 
221 
222     /***
223      * Construct a message selector that selects messages based on the
224      * supplied expression.
225      *
226      * @param       expression      the conditional expression
227      * @throws      InvalidSelectorException if expression is invalid
228      */
229     public Selector(final String expression) throws InvalidSelectorException {
230         try {
231             if (expression == null || expression.length() == 0) {
232                 // always return true for null or empty expressions
233                 _evaluator = Literal.booleanLiteral(true);
234             } else {
235                 SelectorLexer lexer = new SelectorLexer(
236                     new StringReader(expression));
237                 lexer.initialise();
238 
239                 SelectorParser parser = new SelectorParser(lexer);
240                 parser.initialise();
241                 parser.selector(); // start parsing at the selector rule
242 
243                 SelectorTreeParser builder = new SelectorTreeParser();
244                 builder.initialise(new DefaultExpressionFactory());
245                 _evaluator = builder.selector(parser.getAST());
246             }
247         } catch (Exception exception) {
248             throw new InvalidSelectorException(exception.toString());
249         }
250     }
251 
252     /***
253      * Return if message is selected by the expression
254      *
255      * @param message the message
256      * @return <code>true</code> if the message is selected, otherwise
257      * <code>false</code>
258      */
259     public boolean selects(final Message message) {
260         boolean result = false;
261         try {
262             SObject value = _evaluator.evaluate(message);
263             if (value instanceof SBool) {
264                 result = ((SBool) value).value();
265             }
266         } catch (TypeMismatchException ignore) {
267         }
268         return result;
269     }
270 
271 } //-- Selector