PageRenderTime 63ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/logredirector/core/src/java/org/glassfish/openesb/addons/logredirector/selector/Selector.java

https://bitbucket.org/openesb/openesb-glassfish-addons
Java | 2346 lines | 1618 code | 256 blank | 472 comment | 500 complexity | cc1f2f11f8dfda8f36f67708461946ca MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 2000-2008 Sun Microsystems, Inc. All rights reserved.
  5. *
  6. * The contents of this file are subject to the terms of either the GNU
  7. * General Public License Version 2 only ("GPL") or the Common Development
  8. * and Distribution License ("CDDL") (collectively, the "License"). You may
  9. * not use this file except in compliance with the License. You can obtain
  10. * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
  11. * or mq/legal/LICENSE.txt. See the License for the specific language
  12. * governing permissions and limitations under the License.
  13. *
  14. * When distributing the software, include this License Header Notice in each
  15. * file and include the License file at mq/legal/LICENSE.txt. Sun designates
  16. * this particular file as subject to the "Classpath" exception as provided by
  17. * Sun in the GPL Version 2 section of the License file that accompanied this
  18. * code. If applicable, add the following below the License Header, with the
  19. * fields enclosed by brackets [] replaced by your own identifying information:
  20. * "Portions Copyrighted [year] [name of copyright owner]"
  21. *
  22. * Contributor(s):
  23. *
  24. * If you wish your version of this file to be governed by only the CDDL or
  25. * only the GPL Version 2, indicate your decision by adding "[Contributor]
  26. * elects to include this software in this distribution under the [CDDL or GPL
  27. * Version 2] license." If you don't indicate a single choice of license, a
  28. * recipient has the option to distribute your version of this file under
  29. * either the CDDL, the GPL Version 2 or to extend the choice of license to
  30. * its licensees as provided above. However, if you add GPL Version 2 code
  31. * and therefore, elected the GPL Version 2 license, then the option applies
  32. * only if the new code is made subject to such option by the copyright holder.
  33. */
  34. /*
  35. * @(#)Selector.java 1.11 06/29/07
  36. */
  37. //========================================================================================
  38. // NOTE: THIS FILE IS A COPY OF THE FILE
  39. // mq/src/share/java/com/sun/messaging/jmq/util/selector/Selector.java
  40. // FROM THE MQ REPOSITORY. SEE http://mq.dev.java.net.
  41. //========================================================================================
  42. package org.glassfish.openesb.addons.logredirector.selector;
  43. import org.glassfish.openesb.addons.logredirector.lists.WeakValueHashMap;
  44. import java.util.*;
  45. /**
  46. * A class that implements JMS selectors. See section 3.8 of the JMS 1.1 spec.
  47. *
  48. * Attribution: this was copied from JMQ (http://mq.dev.java.net)
  49. */
  50. @SuppressWarnings("unchecked")
  51. public class Selector {
  52. public static boolean DEBUG = false;
  53. public static boolean VERBOSE_DEBUG = false;
  54. // Tokens that can appear in a selector string. Note that some
  55. // tokens are considered "compound". I.e. they are built from
  56. // one or more primitive tokens.
  57. static final int INVALID = 500; // Illegal token
  58. static final int STARTING = 0;
  59. // Operators
  60. static final int OR = 1; // OR, or
  61. static final int AND = 2; // AND, and
  62. static final int NOT = 3; // NOT, not
  63. static final int NOT_EQUALS = 4; // <>
  64. static final int LTE = 5; // <=
  65. static final int LT = 6; // <
  66. static final int GTE = 7; // >=
  67. static final int GT = 8; // >
  68. static final int EQUALS = 9; // =
  69. static final int UNARY_PLUS = 10; // +
  70. static final int UNARY_MINUS = 11; // -
  71. static final int MULTIPLY = 12; // *
  72. static final int DIVIDE = 13; // /
  73. static final int PLUS = 14; // +
  74. static final int MINUS = 15; // -
  75. static final int BETWEEN = 16; // BETWEEN, between
  76. static final int NOT_BETWEEN = 17; // Compound: "NOT BETWEEN"
  77. static final int IN = 18; // IN
  78. static final int NOT_IN = 19; // Compound: "NOT IN"
  79. static final int LIKE = 20; // LIKE
  80. static final int ESCAPE = 21; // ESCAPE
  81. static final int NOT_LIKE = 22; // Compound: "NOT LIKE"
  82. static final int IS_NULL = 23; // Compound: "IS NULL"
  83. static final int IS_NOT_NULL = 24; // Compound: "IS NOT NULL"
  84. static final int IS = 25; // IS
  85. static final int IS_NOT = 26; // Compound "IS NOT"
  86. static final int LEFT_PAREN = 27; // (
  87. static final int RIGHT_PAREN = 28; // )
  88. static final int COMMA = 29; // ,
  89. // Operands
  90. static final int IDENTIFIER = 101; // Java identifier
  91. static final int STRING = 102; // '...'
  92. static final int DOUBLE = 103; // -57.92
  93. static final int LONG = 104; // +347
  94. static final int TRUE = 105; // TRUE, true
  95. static final int FALSE = 106; // FALSE, false
  96. static final int JMS_FIELD = 107; // JMS*
  97. static final int RANGE = 108; // Compound: "15 AND 19"
  98. static final int LIST = 109; // Compound: "('US', 'UK', 'Peru')"
  99. static final int WHITESPACE = 110; // Whitespace
  100. static final int NULL = 111; // NULL token
  101. static final int UNKNOWN = 112; // Unknown result
  102. static final int RE = 113; // LIKE regular expression
  103. // Markers
  104. static final int AND_MARKER = 200;
  105. static final int OR_MARKER = 201;
  106. /*
  107. * The JMS specification specifically states that type conversions
  108. * between Strings and numeric values should not be performed.
  109. * See JMS 1.1 section 3.8.1.1. For example if you set a string property:
  110. * msg.setStringProperty("count", "2")
  111. * then the following should evaluate to false because a string cannot be
  112. * used in an arithmetic expression:
  113. * "count = 2"
  114. * The above expression should be "count = '2'"
  115. * The older selector implementation supported this type conversion
  116. * for some expressions. This introduces the possibility that some
  117. * applications may be relying on this bug, and will break now that
  118. * it is fixed. This boolean let's use switch to the old behavior.
  119. *
  120. * If convertTypes is true, then the selector evaluator will convert
  121. * string values to numeric values. Currently for only = and <>
  122. */
  123. private static boolean convertTypes = false;
  124. /*
  125. * True to short circuit boolean evaluation. For example
  126. * if you have "e1 AND e2", you do not need to evaluate e2 if e1 is
  127. * false. This boolean is just a safetyvalve in case there is a flaw
  128. * in the shortCircuit algorithm
  129. */
  130. private static boolean shortCircuit = true;
  131. private boolean usesProperties = false;
  132. private boolean usesFields = false;
  133. private static HashMap keywords = null;
  134. private static HashSet headers = null;
  135. // Original selector string
  136. private String selector = null;
  137. // Compiled selector string. An array of SelectorTokens in RPN
  138. private Object[] compiledSelector = null;
  139. // Stack used for evaluation
  140. private Stack stack = new Stack();
  141. // The selector cache is used to cache selectors. This way we can
  142. // return the same Selector instance for identical selector strings.
  143. // The selectors are cached in a WeakValueHashMap. This means once
  144. // the Selector is no longer referenced it is garbage collected and
  145. // removed from the HashMap.
  146. private static WeakValueHashMap selectorCache = null;
  147. static {
  148. keywords = new HashMap();
  149. keywords.put("NOT", new Integer(NOT));
  150. keywords.put("AND", new Integer(AND));
  151. keywords.put("OR", new Integer(OR));
  152. keywords.put("BETWEEN", new Integer(BETWEEN));
  153. keywords.put("LIKE", new Integer(LIKE));
  154. keywords.put("IN", new Integer(IN));
  155. keywords.put("IS", new Integer(IS));
  156. keywords.put("ESCAPE", new Integer(ESCAPE));
  157. keywords.put("NULL", new Integer(NULL));
  158. keywords.put("TRUE", new Integer(TRUE));
  159. keywords.put("FALSE", new Integer(FALSE));
  160. headers = new HashSet(6);
  161. headers.add("JMSDeliveryMode");
  162. headers.add("JMSPriority");
  163. headers.add("JMSMessageID");
  164. headers.add("JMSTimestamp");
  165. headers.add("JMSCorrelationID");
  166. headers.add("JMSType");
  167. selectorCache = new WeakValueHashMap("SelectorCache");
  168. }
  169. public static void setConvertTypes(boolean b) {
  170. convertTypes = b;
  171. }
  172. public static boolean getConvertTypes() {
  173. return convertTypes;
  174. }
  175. public static void setShortCircuit(boolean b) {
  176. shortCircuit = b;
  177. }
  178. public static boolean getShortCircuit() {
  179. return shortCircuit;
  180. }
  181. /**
  182. * Compiles a selector string into a Selector object.
  183. * This also checks to ensure that the passed selector string
  184. * is a valid expression.
  185. *
  186. * @param selector Selector string as specified in JMS 1.1
  187. */
  188. public static Selector compile(String selector)
  189. throws SelectorFormatException{
  190. if (selector == null || selector.length() == 0) {
  191. return null;
  192. }
  193. Selector o = null;
  194. synchronized (selectorCache) {
  195. // First check if selector is already in cache.
  196. o = (Selector)selectorCache.get(selector);
  197. if (o == null) {
  198. // Selector not in cache. Create a new one and stick it in the
  199. // cache.
  200. o = new Selector(selector);
  201. o.compile();
  202. selectorCache.put(selector, o);
  203. }
  204. }
  205. return o;
  206. }
  207. /**
  208. * Create a Selector.
  209. *
  210. * @param selector Selector string as specified in JMS 1.1
  211. */
  212. private Selector(String selector) {
  213. this.selector = selector;
  214. }
  215. /**
  216. * Compile the Selector
  217. * <p>
  218. * Compiles the selector into its binary form. This must
  219. * be called before match(). A call to compile also performs
  220. * an evaluation to make sure the selector is a valid expression.
  221. */
  222. public synchronized void compile() throws SelectorFormatException {
  223. /*
  224. * This isn't the most efficient implementation possible,
  225. * but compilation doesn't need to be as fast as evaluation.
  226. * Note that we do some extra work (mainly by tracking more
  227. * token values than we need to) to make debugging easier.
  228. * Also, we do this in multiple passes simply because it is easier
  229. * to understand, and optimizing compile speed is not a priority.
  230. *
  231. * A compiled selector consists of a token stream. This steam
  232. * is held in a LinkedList initially, but is an Object[] in its
  233. * final compiled form. Each token is ecapsulated by a SelectorToken
  234. * object. This object contains the token constant (OR, STRING, etc)
  235. * as well as an optional associated value (like the actual
  236. * string value for a STRING token).
  237. *
  238. * By the time the compilation is done we want everything boiled
  239. * down to operators and operands.
  240. *
  241. * The final compiled form is in Reverse Polish Notation (RPN).
  242. */
  243. /* First pass: tokenize into primatives.
  244. * Add a trailing space to the selector cleanly terminate parsing
  245. */
  246. LinkedList l = tokenize(selector + " ");
  247. if (VERBOSE_DEBUG) {
  248. dumpTokens(l);
  249. }
  250. /* Second pass: aggregate primatives into compound tokens (if any)
  251. * For example this converts the 3 primative tokens: IS NOT NULL
  252. * to the single token IS_NOT_NULL
  253. */
  254. l = aggregate(l);
  255. if (VERBOSE_DEBUG) {
  256. dumpTokens(l);
  257. System.out.println();
  258. }
  259. /* Third pass: prepare
  260. * This pass prepares some of the more funky operations
  261. * (LIKE, BETWEEN, IN, etc) for evaluation. For example it
  262. * takes all the strings in the IN list and puts them in
  263. * a hash table and making that a single operand.
  264. */
  265. l = prepare(l);
  266. if (VERBOSE_DEBUG) {
  267. dumpTokens(l);
  268. System.out.println();
  269. }
  270. /* Fourth pass: Perform any additional validation
  271. */
  272. validate(l);
  273. /* Fith pass: convert to RPN. This removes parens and
  274. * prepares the stream for simple stack based execution.
  275. * The output from this is an Object[] of SelectorTokens
  276. */
  277. compiledSelector = convertToRPN(l);
  278. if (DEBUG) {
  279. System.out.println(toDebugString());
  280. }
  281. // At this point compiledSelector has a token stream that is all
  282. // ready for evaluation! We perform one evaluation to catch any
  283. // errors that may occur at runtime. We do this with empty
  284. // property hashtables
  285. this.match(new HashMap(0), new HashMap(0));
  286. }
  287. /**
  288. * Parse selector string into token primatives. This uses
  289. * a state machine to track state. Each state has a number.
  290. */
  291. private LinkedList tokenize(String selector)
  292. throws SelectorFormatException {
  293. LinkedList buf = new LinkedList();
  294. int len = selector.length();
  295. int state = 0;
  296. // A buffer to hold the token string.
  297. StringBuffer tokenBuf = new StringBuffer(80);
  298. int token = STARTING;
  299. int lastToken = STARTING;
  300. int radix = 10;
  301. int i = 0;
  302. for (i = 0; i < len; i++) {
  303. char c = selector.charAt(i);
  304. Object value = null;
  305. switch (state) {
  306. case 0:
  307. tokenBuf.delete(0, tokenBuf.length());
  308. switch (c) {
  309. case ',':
  310. token = Selector.COMMA;
  311. tokenBuf.append(c);
  312. value = tokenBuf.toString();
  313. break;
  314. case '=':
  315. if (lastToken == Selector.EQUALS) {
  316. // We do an explicit check for == since this may be
  317. // a common error.
  318. throw new SelectorFormatException(
  319. "Invalid operator ==, use =",
  320. selector, i);
  321. }
  322. token = Selector.EQUALS;
  323. tokenBuf.append(c);
  324. value = tokenBuf.toString();
  325. break;
  326. case '/':
  327. token = Selector.DIVIDE;
  328. tokenBuf.append(c);
  329. value = tokenBuf.toString();
  330. break;
  331. case '*':
  332. token = Selector.MULTIPLY;
  333. tokenBuf.append(c);
  334. value = tokenBuf.toString();
  335. break;
  336. case '(':
  337. token = Selector.LEFT_PAREN;
  338. tokenBuf.append(c);
  339. value = tokenBuf.toString();
  340. break;
  341. case ')':
  342. token = Selector.RIGHT_PAREN;
  343. tokenBuf.append(c);
  344. value = tokenBuf.toString();
  345. break;
  346. case '-':
  347. // If last token was an operator then this is unary
  348. if (lastToken == STARTING ||
  349. (isOperator(lastToken) && lastToken != RIGHT_PAREN ) ) {
  350. token = Selector.UNARY_MINUS;
  351. tokenBuf.append(c);
  352. value = tokenBuf.toString();
  353. } else {
  354. token = Selector.MINUS;
  355. tokenBuf.append(c);
  356. value = tokenBuf.toString();
  357. }
  358. break;
  359. case '+':
  360. // If last token was an operator then this is unary
  361. if (lastToken == STARTING ||
  362. (isOperator(lastToken) && lastToken != RIGHT_PAREN ) ) {
  363. token = Selector.UNARY_PLUS;
  364. tokenBuf.append(c);
  365. value = tokenBuf.toString();
  366. } else {
  367. token = Selector.PLUS;
  368. tokenBuf.append(c);
  369. value = tokenBuf.toString();
  370. }
  371. break;
  372. case '>':
  373. // GT or GTE.
  374. tokenBuf.append(c);
  375. state = 1;
  376. break;
  377. case '<':
  378. // LT, LTE, or NOT_EQUALS
  379. tokenBuf.append(c);
  380. state = 2;
  381. break;
  382. case '\'':
  383. // Start of a string literal
  384. state = 9;
  385. break;
  386. case '.':
  387. // Start of a float
  388. tokenBuf.append(c);
  389. state = 6;
  390. break;
  391. case '0':
  392. // Start of octal or hex numeric constant
  393. tokenBuf.append(c);
  394. state = 3;
  395. break;
  396. default:
  397. if (Character.isJavaIdentifierStart(c)) {
  398. // Start of an identifier
  399. tokenBuf.append(c);
  400. state = 11;
  401. } else if (Character.isDigit(c)) {
  402. // Start of a number
  403. tokenBuf.append(c);
  404. state = 5;
  405. } else if (Character.isWhitespace(c)) {
  406. // Whitespace. Ignore.
  407. token = Selector.WHITESPACE;
  408. } else {
  409. // Invalid character
  410. throw new SelectorFormatException(
  411. "Invalid character " + c,
  412. selector, i);
  413. }
  414. }
  415. break;
  416. // Saw a >
  417. case 1:
  418. switch (c) {
  419. case '=':
  420. tokenBuf.append(c);
  421. token = Selector.GTE;
  422. value = tokenBuf.toString();
  423. state = 0;
  424. break;
  425. default:
  426. token = Selector.GT;
  427. value = tokenBuf.toString();
  428. state = 0;
  429. i--; // pushback delimiter
  430. break;
  431. }
  432. break;
  433. // Saw a <
  434. case 2:
  435. switch (c) {
  436. case '=':
  437. tokenBuf.append(c);
  438. token = Selector.LTE;
  439. value = tokenBuf.toString();
  440. state = 0;
  441. break;
  442. case '>':
  443. tokenBuf.append(c);
  444. token = Selector.NOT_EQUALS;
  445. value = tokenBuf.toString();
  446. state = 0;
  447. break;
  448. default:
  449. token = Selector.LT;
  450. value = tokenBuf.toString();
  451. state = 0;
  452. i--; // pushback delimiter
  453. break;
  454. }
  455. break;
  456. // Either an octal or hex numeric constant
  457. case 3:
  458. // We go to state 5 whether it's a hex or an octal constant.
  459. // This means we may get something like 049h which is invalid.
  460. // But when we go to construct the java.lang number object
  461. // we'll catch this.
  462. if (c == 'x' || c == 'X') {
  463. // Hex. Don't remember X, just that we're in base 16
  464. radix = 16;
  465. state = 5;
  466. } else if (Character.isDigit(c)) {
  467. // Octal
  468. radix = 8;
  469. tokenBuf.append(c);
  470. state = 5;
  471. } else {
  472. // Hit a delimeter. Back up and make state 5 handle this 0
  473. i--;
  474. state = 5;
  475. }
  476. break;
  477. // Working on a number!
  478. case 5:
  479. if ( (radix == 16 && isHexDigit(c)) ||
  480. Character.isDigit(c) ) {
  481. tokenBuf.append(c);
  482. } else if (c == '.') {
  483. // It's a float. Go get decimal portion
  484. tokenBuf.append(c);
  485. state = 6;
  486. } else if (c == 'E' || c == 'e') {
  487. // It's a float. Go get exponential
  488. tokenBuf.append(c);
  489. state = 7;
  490. } else {
  491. // Hit delimeter. It's just an integer
  492. token = Selector.LONG;
  493. // Handle this here, cause if the value is MIN_LONG
  494. // we can't create the absoute value of it!
  495. if (lastToken == UNARY_MINUS) {
  496. tokenBuf.insert(0, '-');
  497. // Remove UNARY_MINUS from token stream
  498. buf.removeLast();
  499. }
  500. try {
  501. value = Long.valueOf(tokenBuf.toString(), radix);
  502. radix = 10;
  503. } catch (NumberFormatException e) {
  504. throw new SelectorFormatException(
  505. "Invalid numeric constant: " + e.getMessage(),
  506. selector, i);
  507. }
  508. state = 0;
  509. if (c == 'l' || c == 'L') {
  510. // If it is a trailing L then we skip it.
  511. // We always use longs
  512. } else {
  513. i--; // pushback delimiter
  514. }
  515. }
  516. break;
  517. // Working on decimal portion of a float
  518. case 6:
  519. if (Character.isDigit(c)) {
  520. tokenBuf.append(c);
  521. } else if (c == 'E' || c == 'e') {
  522. // Go get exponential
  523. tokenBuf.append(c);
  524. state = 7;
  525. } else {
  526. // Hit delimeter.
  527. token = Selector.DOUBLE;
  528. try {
  529. value = Double.valueOf(tokenBuf.toString());
  530. } catch (NumberFormatException e) {
  531. throw new SelectorFormatException(
  532. "Invalid numeric constant: " + e.getMessage(),
  533. selector, i);
  534. }
  535. state = 0;
  536. if (c == 'd' || c == 'D' || c == 'f' || c == 'F') {
  537. // Trailing qualifier. Just skip it. Everything is a D
  538. } else {
  539. i--; // pushback delimiter
  540. }
  541. }
  542. break;
  543. // Starting to work on exponential portion of a float
  544. case 7:
  545. if (Character.isDigit(c)) {
  546. tokenBuf.append(c);
  547. state = 8;
  548. } else if (c == '-') {
  549. tokenBuf.append(c);
  550. state = 8;
  551. } else {
  552. // Hit delimeter. Nothing after the E
  553. token = Selector.DOUBLE;
  554. try {
  555. value = Double.valueOf(tokenBuf.toString());
  556. } catch (NumberFormatException e) {
  557. throw new SelectorFormatException(
  558. "Invalid numeric constant: " + e.getMessage(),
  559. selector, i);
  560. }
  561. state = 0;
  562. if (c == 'd' || c == 'D' || c == 'f' || c == 'F') {
  563. // Trailing qualifier. Just skip it. Everything is a D
  564. } else {
  565. i--; // pushback delimiter
  566. }
  567. }
  568. break;
  569. // Finishing work on exponential portion of a float
  570. case 8:
  571. if (Character.isDigit(c)) {
  572. tokenBuf.append(c);
  573. } else {
  574. // Hit delimeter.
  575. token = Selector.DOUBLE;
  576. try {
  577. value = Double.valueOf(tokenBuf.toString());
  578. } catch (NumberFormatException e) {
  579. throw new SelectorFormatException(
  580. "Invalid numeric constant: " + e.getMessage(),
  581. selector, i);
  582. }
  583. state = 0;
  584. if (c == 'd' || c == 'D' || c == 'f' || c == 'F') {
  585. // Trailing qualifier. Just skip it. Everything is a D
  586. } else {
  587. i--; // pushback delimiter
  588. }
  589. }
  590. break;
  591. // Working on a string literal
  592. case 9:
  593. if (c == '\'') {
  594. state = 10;
  595. } else {
  596. tokenBuf.append(c);
  597. }
  598. break;
  599. // Is this the end of a string? Or an escaped single quote
  600. case 10:
  601. if (c == '\'') {
  602. // Escaped single quote. Put it in token and continue
  603. state = 9;
  604. tokenBuf.append(c);
  605. } else {
  606. // Hit delimeter.
  607. token = Selector.STRING;
  608. value = tokenBuf.toString();
  609. state = 0;
  610. i--; // pushback delimiter
  611. }
  612. break;
  613. // Working on an identifier
  614. case 11:
  615. if (Character.isJavaIdentifierPart(c)) {
  616. tokenBuf.append(c);
  617. } else {
  618. value = tokenBuf.toString();
  619. // OK, we either have an identifier, or a keyword.
  620. // this method handles figuring that out.
  621. token = identifierToKeyWord((String)value);
  622. state = 0;
  623. i--; // pushback delimiter
  624. }
  625. break;
  626. default:
  627. // This should never happen.
  628. throw new SelectorFormatException(
  629. "Selector tokenizer in bad state: " + state +
  630. " tokenBuf=" + tokenBuf + " char=" + c,
  631. selector, i);
  632. }
  633. // We detect if the Selector uses message properties
  634. // (as opposed to just JMS fields).
  635. if (token == Selector.IDENTIFIER) {
  636. usesProperties = true;
  637. } else if (token == Selector.JMS_FIELD) {
  638. usesFields = true;
  639. }
  640. if (state == 0 && token == Selector.INVALID) {
  641. // This should never happen.
  642. throw new SelectorFormatException(
  643. "Unknown token: " + token +
  644. " tokenBuf=" + tokenBuf,
  645. selector, i);
  646. }
  647. if (state == 0 && token != Selector.WHITESPACE) {
  648. buf.add(SelectorToken.getInstance(token, value));
  649. lastToken = token;
  650. radix = 10;
  651. }
  652. }
  653. if (state == 9) {
  654. // Missing closing quote
  655. throw new SelectorFormatException(
  656. "Missing closing quote", selector, i);
  657. } else if (state != 0) {
  658. throw new SelectorFormatException(
  659. "Invalid Expression", selector, i);
  660. }
  661. return buf;
  662. }
  663. // Check if s is a keyword, JMS field, or generic identifier
  664. private int identifierToKeyWord(String s) {
  665. Integer n = (Integer)keywords.get(s.toUpperCase());
  666. if (n != null) {
  667. return n.intValue();
  668. } else if (s.startsWith("JMS")) {
  669. if(headers.contains(s))
  670. return JMS_FIELD;
  671. else
  672. return IDENTIFIER;
  673. } else {
  674. return IDENTIFIER;
  675. }
  676. }
  677. private boolean isHexDigit(char c) {
  678. return (Character.isDigit(c) ||
  679. c == 'a' || c == 'A' ||
  680. c == 'b' || c == 'B' ||
  681. c == 'c' || c == 'C' ||
  682. c == 'd' || c == 'D' ||
  683. c == 'e' || c == 'E' ||
  684. c == 'f' || c == 'F');
  685. }
  686. /**
  687. * Aggregate primatives into compound tokens (if any).
  688. * This performs the following conversions:
  689. * NOT BETWEEN => NOT_BETWEEN
  690. * NOT IN => NOT_IN
  691. * NOT LIKE => NOT_LIKE
  692. * IS NULL => IS_NULL
  693. * IS NOT NULL => IS_NOT_NULL
  694. */
  695. private LinkedList aggregate(LinkedList in)
  696. throws SelectorFormatException {
  697. LinkedList out = new LinkedList();
  698. SelectorToken token0;
  699. SelectorToken token1;
  700. SelectorToken token2;
  701. int len = in.size();
  702. for (int i = 0; i < len; i++) {
  703. token0 = (SelectorToken)in.get(i);
  704. token1 = null;
  705. token2 = null;
  706. if (i + 1 < len) {
  707. token1 = (SelectorToken)in.get(i + 1);
  708. }
  709. if (i + 2 < len) {
  710. token2 = (SelectorToken)in.get(i + 2);
  711. }
  712. switch (token0.getToken()) {
  713. case Selector.NOT:
  714. if (token1 == null) {
  715. // NOT
  716. out.add(token0);
  717. } else if (token1.getToken() == Selector.BETWEEN) {
  718. // NOT BETWEEN
  719. out.add(SelectorToken.getInstance(Selector.NOT_BETWEEN,
  720. (String)token0.getValue() + " " +
  721. (String)token1.getValue() ));
  722. // Skip BETWEEN
  723. i++;
  724. } else if (token1.getToken() == Selector.IN) {
  725. // NOT IN
  726. out.add(SelectorToken.getInstance(Selector.NOT_IN,
  727. (String)token0.getValue() + " " +
  728. (String)token1.getValue() ));
  729. // Skip IN
  730. i++;
  731. } else if (token1.getToken() == Selector.LIKE) {
  732. // NOT LIKE
  733. out.add(SelectorToken.getInstance(Selector.NOT_LIKE,
  734. (String)token0.getValue() + " " +
  735. (String)token1.getValue() ));
  736. // Skip LIKE
  737. i++;
  738. } else {
  739. // NOT
  740. out.add(token0);
  741. }
  742. break;
  743. case Selector.IS:
  744. if (token1 == null) {
  745. // just IS
  746. out.add(token0);
  747. } else if (token1.getToken() == Selector.NULL) {
  748. // IS NULL
  749. out.add(SelectorToken.getInstance(Selector.IS_NULL,
  750. (String)token0.getValue() + " " +
  751. (String)token1.getValue()));
  752. // Skip NULL
  753. i++;
  754. } else if (token1.getToken() == Selector.NOT) {
  755. // IS NOT
  756. if (token2 == null) {
  757. // just IS NOT
  758. out.add(SelectorToken.getInstance(Selector.IS_NOT,
  759. (String)token0.getValue() + " " +
  760. (String)token1.getValue()));
  761. // Skip NOT
  762. i++;
  763. } else if (token2.getToken() == Selector.NULL) {
  764. // IS NOT NULL
  765. out.add(SelectorToken.getInstance(Selector.IS_NOT_NULL,
  766. (String)token0.getValue() + " " +
  767. (String)token1.getValue() + " " +
  768. (String)token2.getValue()));
  769. // Skip NOT NULL
  770. i++;
  771. i++;
  772. } else {
  773. // just IS NOT
  774. out.add(SelectorToken.getInstance(Selector.IS_NOT,
  775. (String)token0.getValue() + " " +
  776. (String)token1.getValue()));
  777. // Skip NOT
  778. i++;
  779. }
  780. } else {
  781. // Just IS
  782. out.add(token0);
  783. }
  784. break;
  785. default:
  786. // Simple token
  787. out.add(token0);
  788. break;
  789. }
  790. }
  791. return out;
  792. }
  793. /**
  794. * Prepare list for conversion to RPN.
  795. * This step prepares some of the more funky operations into
  796. * a format that can be more easily evaluated using a simple RPN
  797. * expression evaluator.
  798. * It performs the following:
  799. *
  800. * Replaces the AND in the BETWEEN and NOT_BETWEEN constructs with
  801. * a comma. The comma ensures we correctly convert the arithmetic
  802. * expressions in the BETWEEN ranges to RPN. This is especially true
  803. * when you take into account unary minus (ie BETWEEN - 1 and 5).
  804. * Then BETWEEN is just treated as an operator that requires 3 operands
  805. * and the COMMA is ignmored.
  806. *
  807. * Converts the list construct in the IN and NOT_IN operations into
  808. * a single token (operand) that has a HashMap for it's value.
  809. *
  810. * Detects the ESCAPE keyword and converts the LIKE regular expression
  811. * string into a simple object that continas the string and the
  812. * escape character, so when we go to evaluate it we can do the
  813. * right thing based on the RE package we use.
  814. */
  815. private LinkedList prepare(LinkedList in)
  816. throws SelectorFormatException {
  817. LinkedList out = new LinkedList();
  818. SelectorToken token0;
  819. int len = in.size();
  820. for (int i = 0; i < len; i++) {
  821. token0 = (SelectorToken)in.get(i);
  822. switch (token0.getToken()) {
  823. case Selector.BETWEEN:
  824. case Selector.NOT_BETWEEN:
  825. out.add(token0);
  826. i++;
  827. // OKAY we saw a BETWEEN. Scan forward until we hit an AND
  828. // and convert it to a COMMA
  829. while (i < len) {
  830. token0 = (SelectorToken)in.get(i);
  831. if (token0.getToken() == Selector.AND) {
  832. out.add(SelectorToken.getInstance(Selector.COMMA, ","));
  833. break;
  834. }
  835. out.add(token0);
  836. i++;
  837. }
  838. break;
  839. case Selector.IN:
  840. case Selector.NOT_IN:
  841. out.add(token0);
  842. i++;
  843. token0 = (SelectorToken)in.get(i);
  844. if (token0.getToken() != Selector.LEFT_PAREN) {
  845. throw new SelectorFormatException(
  846. "Missing ( in IN statement", selector);
  847. }
  848. // Skip open paren
  849. i++;
  850. // OK convert list of strings into a HashSet
  851. HashSet set = new HashSet();
  852. while (i < len) {
  853. token0 = (SelectorToken)in.get(i);
  854. if (token0.getToken() == Selector.RIGHT_PAREN) {
  855. // skip close paren and terminate
  856. break;
  857. }
  858. if (token0.getToken() == Selector.COMMA) {
  859. // skip commas
  860. i++;
  861. continue;
  862. }
  863. if (token0.getToken() != Selector.STRING) {
  864. throw new SelectorFormatException(
  865. "IN requires string literal: " +
  866. token0.getValue(), selector);
  867. }
  868. // Put string in HashMap
  869. set.add(token0.getValue());
  870. i++;
  871. }
  872. // Put list token with HashSet as value. This now becomes
  873. // the right operand for IN and NOT_IN
  874. out.add(SelectorToken.getInstance(Selector.LIST, set));
  875. break;
  876. case Selector.LIKE:
  877. case Selector.NOT_LIKE:
  878. out.add(token0);
  879. i++;
  880. // String literal should be next token
  881. token0 = (SelectorToken)in.get(i);
  882. if (token0.getToken() != Selector.STRING) {
  883. throw new SelectorFormatException(
  884. "LIKE requires string literal: " +
  885. token0.getValue(), selector);
  886. }
  887. // String literal is the regular expression
  888. String re = (String)token0.getValue();
  889. String escape = null;
  890. i++;
  891. if (i < len) {
  892. token0 = (SelectorToken)in.get(i);
  893. if (token0.getToken() == Selector.ESCAPE) {
  894. // Get escape string
  895. i++;
  896. token0 = (SelectorToken)in.get(i);
  897. if (token0.getToken() != Selector.STRING) {
  898. throw new SelectorFormatException(
  899. "ESCAPE requires string literal: " +
  900. token0.getValue(), selector);
  901. } else {
  902. escape = (String)token0.getValue();
  903. }
  904. } else {
  905. i--; // push back token since it wasn't ESCAPE
  906. }
  907. }
  908. out.add(SelectorToken.getInstance(Selector.RE,
  909. new RegularExpression(re, escape)));
  910. break;
  911. default:
  912. // Simple token
  913. out.add(token0);
  914. break;
  915. }
  916. }
  917. return out;
  918. }
  919. /**
  920. * Validate expression
  921. * This does a simple, final syntax check before conversion to RPN.
  922. * It detects invalid expressions such as "= red 'color'"
  923. */
  924. private void validate(LinkedList in)
  925. throws SelectorFormatException {
  926. SelectorToken token;
  927. int len = in.size();
  928. int prevToken = STARTING;
  929. for (int i = 0; i < len; i++) {
  930. token = (SelectorToken)in.get(i);
  931. // If the current token is an operand, then the previous
  932. // token must be an operator (or STARTING)
  933. if (!isOperator(token)) {
  934. if (prevToken != STARTING &&
  935. !isOperator(prevToken)) {
  936. throw new SelectorFormatException(
  937. "Missing operator", selector);
  938. }
  939. }
  940. prevToken = token.getToken();
  941. }
  942. return;
  943. }
  944. /**
  945. * Convert the token stream into Reverse Polish Notation (aka RPN
  946. * or postfix notation). This helps detect syntax errors and
  947. * prepares the expression for evaluation. Here is the procedure
  948. * for converting infix to postfix:
  949. *
  950. * Scan infix expression from left to right.
  951. * A. When an operand is encountered move it immediately to the
  952. * RPN expression.
  953. * B. When an operator is encountered:
  954. * 1. First pop operators from the stack and place them into the
  955. * RPN expression until either the stack is empty or the precedence
  956. * level of the top operator in the stack is LESS than the
  957. * precedence of the operator encountered in the scan.
  958. * 2. Then push the operator encountered onto the stack
  959. * C. When a left paren is encountered push it onto the stack
  960. * (it creates a "sub-stack").
  961. * D. When unstacking operators stop when a left paren comes to the top
  962. * of the stack.
  963. * E. When a right paren is encountered when scanning the expression unstack
  964. * operators until a matching left paren is found in the stack.
  965. * Pop left paren and disgard. Disgard right paren.
  966. * F. When the entire expression has been scanned pop any remaining
  967. * operators from the stack and place into the RPN expression.
  968. *
  969. * The following is done to support evaluation short circuit:
  970. *
  971. * After you have pushed an AND (OR) operator onto the stack, insert the
  972. * AND_MARKER (OR_MARKER) into the RPN expression.
  973. */
  974. private Object[] convertToRPN(LinkedList in)
  975. throws SelectorFormatException {
  976. Stack stack = new Stack();
  977. // For this final pass we convert to a fixed size array to
  978. // make final evaluation faster. We make the array larger to
  979. // handle markers if we have any.
  980. Object[] out = new Object[(int)(in.size() * 1.5)];
  981. int i = 0;
  982. Iterator iter = in.iterator();
  983. while (iter.hasNext()) {
  984. SelectorToken token = (SelectorToken)iter.next();
  985. if (!isOperator(token)) {
  986. // Operand. Move directly to RPN
  987. out[i++] = token;
  988. continue;
  989. }
  990. if (token.getToken() == LEFT_PAREN) {
  991. // Push ( immediately on stack
  992. stack.push(token);
  993. continue;
  994. }
  995. SelectorToken t = null;
  996. if (token.getToken() == RIGHT_PAREN) {
  997. // Pop operators until we encounter a left paren
  998. do {
  999. if (stack.empty()) {
  1000. throw new SelectorFormatException(
  1001. "Missing (", selector);
  1002. }
  1003. t = (SelectorToken)stack.pop();
  1004. if (t.getToken() != LEFT_PAREN) {
  1005. out[i++] = t;
  1006. }
  1007. } while (t.getToken() != LEFT_PAREN);
  1008. continue;
  1009. }
  1010. // Operator is not a paren. Copy operators off of stack
  1011. // until we hit one with a lower priority than the one
  1012. // from the scanned expression.
  1013. while (!stack.empty()) {
  1014. t = (SelectorToken)stack.peek();
  1015. if (t.getToken() == LEFT_PAREN) {
  1016. break;
  1017. }
  1018. if (getPrecedence(t) < getPrecedence(token)) {
  1019. // Stop if precedence of top operator is less than
  1020. // operator from expression scan.
  1021. break;
  1022. }
  1023. // Copy higher precedence operators to RPN expression
  1024. out[i++] = (SelectorToken)stack.pop();
  1025. }
  1026. // Push operator from scanned expression onto stack
  1027. stack.push(token);
  1028. if (shortCircuit) {
  1029. // Markers are used to short circuit expression evaluation
  1030. // If we just pushed an AND or OR onto the stack, put the
  1031. // corresponding marker into the expression
  1032. if (token.getToken() == Selector.AND) {
  1033. out[i++] = SelectorToken.getInstance(Selector.AND_MARKER);
  1034. } else if (token.getToken() == Selector.OR) {
  1035. out[i++] = SelectorToken.getInstance(Selector.OR_MARKER);
  1036. }
  1037. }
  1038. }
  1039. // Expression has been scanned. Pop all remaining operators
  1040. // off of stack and put in expression.
  1041. while (!stack.empty()) {
  1042. out[i] = (SelectorToken)stack.pop();
  1043. if ( ((SelectorToken)out[i]).getToken() == LEFT_PAREN ) {
  1044. throw new SelectorFormatException(
  1045. "Missing )", selector);
  1046. }
  1047. i++;
  1048. }
  1049. return out;
  1050. }
  1051. /**
  1052. * Evaluate the selector using the passed properties and message fields.
  1053. * compile() must have been called before calling match().
  1054. *
  1055. * @param properties HashMap containing message properties. These
  1056. * should be String/Object pairs.
  1057. * If usesProperties() returns 'false' then message
  1058. * properties are not needed to evaluate the expression
  1059. * and this parameter may be null.
  1060. * @param fields HashMap containg JMS Message fields. These
  1061. * should be String/Object pairs.
  1062. * If usesFields() returns 'false' then JMS fields
  1063. * are not needed to evaluate the expression
  1064. * and this parameter may be null.
  1065. *
  1066. * @return true if expression evaluates to true, else false.
  1067. *
  1068. * @throws SelectorFormatException if the selector syntax is invalid
  1069. */
  1070. public synchronized boolean match(Map properties, Map fields)
  1071. throws SelectorFormatException {
  1072. /*
  1073. * This method is synchronized primarily because of the runtime
  1074. * stack. If the stack was local then we wouldn't need to synchronize,
  1075. * but we'd be creating a new stack on every match() call.
  1076. */
  1077. /*
  1078. * You evaluate an RPN using a stack. It goes like this:
  1079. * A. Scan RPN expression from left to right
  1080. * B. When you encounter an operand push it onto the stack.
  1081. * C. When you encounter an operator pop off as many operands
  1082. * as you need (in our case 1, 2 or 3), and apply operator
  1083. * to the operands.
  1084. * D. Push result onto the stack
  1085. * E. When scan is complete the final result is on top of the stack
  1086. *
  1087. * The following is performed when supporting evaluation short circuit:
  1088. *
  1089. * If an AND_MARKER is encountered during scanning:
  1090. * If the top of the evaluation stack is FALSE then scan the
  1091. * RPN expression until the AND operator is encountered. Skip the
  1092. * AND operator. If during this scan additional AND_MARKERS are
  1093. * encountered then continue scanning expression until you have
  1094. * skipped as man AND operators as AND_MARKERS encountered.
  1095. * Then continue evaluation.
  1096. * Else skip the AND_MARKER and continue evaluation.
  1097. *
  1098. * If an OR_MARKER is encountered during scanning:
  1099. * If the top of the evaluation stack is TRUE then scan the
  1100. * RPN expression until the OR operator is encountered. Skip the
  1101. * OR operator. If during this scan additional OR_MARKERS are
  1102. * encountered then continue scanning expression until you have
  1103. * skipped as man OR operators as OR_MARKERS encountered.
  1104. * Then continue evaluation.
  1105. * Else skip the OR_MARKER and continue evaluation.
  1106. */
  1107. stack.clear();
  1108. SelectorToken token, operand1, operand2;
  1109. int markers = 0;
  1110. try {
  1111. for (int i = 0; i < compiledSelector.length; i++) {
  1112. token = (SelectorToken)compiledSelector[i];
  1113. if (token == null) {
  1114. // RPN may be shorter than the original since we
  1115. // remove parens.
  1116. break;
  1117. }
  1118. if (shortCircuit) {
  1119. // Short circuit boolean expressions
  1120. if (token.getToken() == Selector.AND_MARKER) {
  1121. // We hit an AND_MARKER.
  1122. int t = ((SelectorToken)stack.peek()).getToken();
  1123. if (t == Selector.FALSE) {
  1124. // Top of eval stack is FALSE. Short circuit by scanning
  1125. // until we hit an AND operator. If we see other AND_MARKERS
  1126. // we must skip as many operators as markers.
  1127. markers = 1;
  1128. while (markers > 0) {
  1129. token = (SelectorToken)compiledSelector[++i];
  1130. if (token.getToken() == Selector.AND_MARKER) {
  1131. markers++;
  1132. } else if (token.getToken() == Selector.AND) {
  1133. markers--;
  1134. } else {
  1135. }
  1136. }
  1137. // Completed short circuit. Continue evaluation
  1138. continue;
  1139. } else {
  1140. // Not a short circuit. Skip marker and continue
  1141. continue;
  1142. }
  1143. } else if (token.getToken() == Selector.OR_MARKER) {
  1144. // We hit an OR_MARKER.
  1145. int t = ((SelectorToken)stack.peek()).getToken();
  1146. if (t == Selector.TRUE) {
  1147. // Top of eval stack is TRUE. Short circuit by scanning
  1148. // until we hit an OR operator. If we see other OR_MARKERS
  1149. // we must skip as many operators as markers.
  1150. markers = 1;
  1151. while (markers > 0) {
  1152. token = (SelectorToken)compiledSelector[++i];
  1153. if (token.getToken() == Selector.OR_MARKER) {
  1154. markers++;
  1155. } else if (token.getToken() == Selector.OR) {
  1156. markers--;
  1157. } else {
  1158. }
  1159. }
  1160. // Completed short circuit. Continue evaluation
  1161. continue;
  1162. } else {
  1163. // Not a short circuit. Skip marker and continue
  1164. continue;
  1165. }
  1166. }
  1167. } // if shortCircuit
  1168. // Push operands onto stack
  1169. if (!isOperator(token)) {
  1170. if (token.getToken() == IDENTIFIER) {
  1171. // Expand identifier
  1172. Object value;
  1173. if (properties == null) {
  1174. value = null;
  1175. } else {
  1176. value = properties.get((String)token.getValue());
  1177. }
  1178. if (value == null) {
  1179. stack.push(SelectorToken.getInstance(UNKNOWN, null));
  1180. } else {
  1181. stack.push(propertyToToken(value));
  1182. }
  1183. } else if (token.getToken() == JMS_FIELD) {
  1184. // Expand identifier
  1185. Object value;
  1186. if (fields == null) {
  1187. value = null;
  1188. } else {
  1189. value = fields.get((String)token.getValue());
  1190. }
  1191. if (value == null) {
  1192. stack.push(SelectorToken.getInstance(UNKNOWN, null));
  1193. } else {
  1194. stack.push(propertyToToken(value));
  1195. }
  1196. } else {
  1197. // A literal operand
  1198. stack.push(token);
  1199. }
  1200. continue;
  1201. }
  1202. if (token.getToken() == COMMA) {
  1203. // Comma's are no-ops. They were there to ensure conversion
  1204. // from infix to RPN went correctly for BETWEEN ranges.
  1205. continue;
  1206. }
  1207. // Handle operator. We know we'll need at least one operand
  1208. // so get it now.
  1209. operand1 = (SelectorToken)stack.pop();
  1210. // Process operator
  1211. switch (token.getToken()) {
  1212. // For OR, AND, and NOT we have to handle UNKNOWN.
  1213. // See Section 3.8.1.2 of the JMS 1.1 spec
  1214. case OR:
  1215. operand2 = (SelectorToken)stack.pop();
  1216. if (operand1.getToken() == TRUE ||
  1217. operand2.getToken() == TRUE) {
  1218. stack.push(SelectorToken.getInstance(TRUE));
  1219. } else if (operand1.getToken() == FALSE &&
  1220. operand2.getToken() == FALSE) {
  1221. stack.push(SelectorToken.getInstance(FALSE));
  1222. } else {
  1223. stack.push(SelectorToken.getInstance(UNKNOWN));
  1224. }
  1225. break;
  1226. case AND:
  1227. operand2 = (SelectorToken)stack.pop();
  1228. if (operand1.getToken() == TRUE &&
  1229. operand2.getToken() == TRUE) {
  1230. stack.push(SelectorToken.getInstance(TRUE));
  1231. } else if (operand1.getToken() == FALSE ||
  1232. operand2.getToken() == FALSE) {
  1233. stack.push(SelectorToken.getInstance(FALSE));
  1234. } else {
  1235. stack.push(SelectorToken.getInstance(UNKNOWN));
  1236. }
  1237. break;
  1238. case NOT:
  1239. if (operand1.getToken() == TRUE) {
  1240. stack.push(SelectorToken.getInstance(FALSE));
  1241. } else if (operand1.getToken() == FALSE) {
  1242. stack.push(SelectorToken.getInstance(TRUE));
  1243. } else {
  1244. stack.push(SelectorToken.getInstance(UNKNOWN));
  1245. }
  1246. break;
  1247. case EQUALS:
  1248. operand2 = (SelectorToken)stack.pop();
  1249. if (isNumeric(operand1) || isNumeric(operand2)) {
  1250. stack.push(doNumericOperation(
  1251. token, operand2, operand1));
  1252. } else if (operand1.equals(operand2)) {
  1253. stack.push(SelectorToken.getInstance(TRUE));
  1254. } else {
  1255. stack.push(SelectorToken.getInstance(FALSE));
  1256. }
  1257. break;
  1258. case NOT_EQUALS:
  1259. operand2 = (SelectorToken)stack.pop();
  1260. if (isNumeric(operand1) || isNumeric(operand2)) {
  1261. stack.push(doNumericOperation(
  1262. token, operand2, operand1));
  1263. } else if (operand1.equals(operand2)) {
  1264. stack.push(SelectorToken.getInstance(FALSE));
  1265. } else {
  1266. stack.push(SelectorToken.getInstance(TRUE));
  1267. }
  1268. break;
  1269. case LT:
  1270. case LTE:
  1271. case GT:
  1272. case GTE:
  1273. operand2 = (SelectorToken)stack.pop();
  1274. // operand2 is first. It is actually the first
  1275. // operation. They are reversed on the stack
  1276. stack.push(doNumericOperation(
  1277. token, operand2, operand1));
  1278. break;
  1279. case PLUS:
  1280. case MINUS:
  1281. case MULTIPLY:
  1282. case DIVIDE:
  1283. operand2 = (SelectorToken)stack.pop();
  1284. stack.push(doNumericOperation(
  1285. token, operand2, operand1));
  1286. break;
  1287. case UNARY_MINUS:
  1288. stack.push(doNumericOperation(token, operand1, null));
  1289. break;
  1290. case UNARY_PLUS:
  1291. stack.push(doNumericOperation(token, operand1, null));
  1292. break;
  1293. case BETWEEN:
  1294. case NOT_BETWEEN:
  1295. // Operand 1 is the second range value
  1296. SelectorToken max = operand1;
  1297. // Operand 2 is the first range value
  1298. SelectorToken min = (SelectorToken)stack.pop();
  1299. // Operand 3 is the operand on the left side of BETWEEN
  1300. SelectorToken operand = (SelectorToken)stack.pop();
  1301. boolean between = false;
  1302. // The operands may be floats or longs. We use
  1303. // doNumericOperation to handle this for us
  1304. if (doNumericOperation(SelectorToken.getInstance(GTE),
  1305. operand, min).getToken() == TRUE &&
  1306. doNumericOperation(SelectorToken.getInstance(LTE),
  1307. operand, max).getToken() == TRUE) {
  1308. between = true;
  1309. }
  1310. if (token.getToken() == BETWEEN) {
  1311. if (between) {
  1312. stack.push(SelectorToken.getInstance(TRUE));
  1313. } else {
  1314. stack.push(SelectorToken.getInstance(FALSE));
  1315. }
  1316. } else {
  1317. if (between) {
  1318. stack.push(SelectorToken.getInstance(FALSE));
  1319. } else {
  1320. stack.push(SelectorToken.getInstance(TRUE));
  1321. }
  1322. }
  1323. break;
  1324. case IN:
  1325. case NOT_IN:
  1326. // operand2 is the identifier
  1327. operand2 = (SelectorToken)stack.pop();
  1328. if (! (operand2.getValue() instanceof String)) {
  1329. throw new SelectorFormatException(
  1330. "IN requires string operand: " +
  1331. operand2.getValue(), selector);
  1332. }
  1333. // operand1 is the string list
  1334. HashSet set = (HashSet)operand1.getValue();
  1335. if (operand2.getToken() == UNKNOWN) {
  1336. // If operand is unknow, result is unknown.
  1337. stack.push(SelectorToken.getInstance(FALSE));
  1338. } else if (set.contains((String)operand2.getValue())) {
  1339. if (token.getToken() == IN) {
  1340. stack.push(SelectorToken.getInstance(TRUE));
  1341. } else {
  1342. stack.push(SelectorToken.getInstance(FALSE));
  1343. }
  1344. } else {
  1345. if (token.getToken() == IN) {
  1346. stack.push(SelectorToken.getInstance(FALSE));
  1347. } else {
  1348. stack.push(SelectorToken.getInstance(TRUE));
  1349. }
  1350. }
  1351. break;
  1352. case LIKE:
  1353. case NOT_LIKE:
  1354. // operand2 is the identifier
  1355. operand2 = (SelectorToken)stack.pop();
  1356. if (! (operand2.getValue() instanceof String)) {
  1357. throw new SelectorFormatException(
  1358. "LIKE requires string operand: " +
  1359. operand2.getValue(), selector);
  1360. }
  1361. // operand1 is the RE
  1362. RegularExpression re =
  1363. (RegularExpression)operand1.getValue();
  1364. if (operand2.getToken() == UNKNOWN) {
  1365. // If operand is unknow, result is unknown.
  1366. stack.push(SelectorToken.getInstance(FALSE));
  1367. } else if (re.match((String)operand2.getValue())) {
  1368. if (token.getToken() == LIKE) {
  1369. stack.push(SelectorToken.getInstance(TRUE));
  1370. } else {
  1371. stack.push(SelectorToken.getInstance(FALSE));
  1372. }
  1373. } else {
  1374. if (token.getToken() == LIKE) {
  1375. stack.push(SelectorToken.getInstance(FALSE));
  1376. } else {
  1377. stack.push(SelectorToken.getInstance(TRUE));
  1378. }
  1379. }
  1380. break;
  1381. case IS_NULL:
  1382. if (operand1.getToken() == UNKNOWN) {
  1383. stack.push(SelectorToken.getInstance(TRUE));
  1384. } else {
  1385. stack.push(SelectorToken.getInstance(FALSE));
  1386. }
  1387. break;
  1388. case IS_NOT_NULL:
  1389. if (operand1.getToken() != UNKNOWN) {
  1390. stack.push(SelectorToken.getInstance(TRUE));
  1391. } else {
  1392. stack.push(SelectorToken.getInstance(FALSE));
  1393. }
  1394. break;
  1395. default:
  1396. throw new SelectorFormatException(
  1397. "Unknown operator: " + token, selector);
  1398. }
  1399. }
  1400. // All done!
  1401. // The top of the stack better hold a boolean!
  1402. token = (SelectorToken)stack.pop();
  1403. } catch (java.util.EmptyStackException e) {
  1404. throw new SelectorFormatException("Missing operand", selector);
  1405. }
  1406. if (!stack.empty()) {
  1407. throw new SelectorFormatException(
  1408. "Missing operator", selector);
  1409. } else if (token.getToken() == TRUE) {
  1410. return true;
  1411. } else if (token.getToken() == FALSE) {
  1412. return false;
  1413. } else if (token.getToken() == UNKNOWN) {
  1414. return false;
  1415. } else {
  1416. throw new SelectorFormatException(
  1417. "Non-boolean expression", selector);
  1418. }
  1419. }
  1420. private SelectorToken propertyToToken(Object value) {
  1421. if (value instanceof String) {
  1422. return SelectorToken.getInstance(STRING, value);
  1423. } else if (value instanceof Boolean) {
  1424. boolean b = ((Boolean)value).booleanValue();
  1425. if (b) {
  1426. return SelectorToken.getInstance(TRUE);
  1427. } else {
  1428. return SelectorToken.getInstance(FALSE);
  1429. }
  1430. } else if (value instanceof Double) {
  1431. return SelectorToken.getInstance(DOUBLE, value);
  1432. } else if (value instanceof Float) {
  1433. double d = ((Float)value).floatValue();
  1434. return SelectorToken.getInstance(DOUBLE, new Double(d));
  1435. } else if (value instanceof Long) {
  1436. return SelectorToken.getInstance(LONG, value);
  1437. } else if (value instanceof Integer) {
  1438. long l = ((Integer)value).intValue();
  1439. return SelectorToken.getInstance(LONG, new Long(l));
  1440. } else if (value instanceof Short) {
  1441. long l = ((Short)value).shortValue();
  1442. return SelectorToken.getInstance(LONG, new Long(l));
  1443. } else if (value instanceof Byte) {
  1444. long l = ((Byte)value).byteValue();
  1445. return SelectorToken.getInstance(LONG, new Long(l));
  1446. }
  1447. return null;
  1448. }
  1449. private SelectorToken convertStringToNumber(String s)
  1450. throws SelectorFormatException {
  1451. try {
  1452. Long l = Long.valueOf(s);
  1453. return SelectorToken.getInstance(LONG, l);
  1454. } catch (NumberFormatException e) {
  1455. try {
  1456. // Hmmm...maybe it's a double
  1457. Double d = Double.valueOf(s);
  1458. return SelectorToken.getInstance(DOUBLE, d);
  1459. } catch (NumberFormatException e2) {
  1460. throw new SelectorFormatException(
  1461. "Cannot convert string to number '" + s + "'", selector);
  1462. }
  1463. }
  1464. }
  1465. /**
  1466. * Perform a numeric operation. God this is lame. There must
  1467. * be a better way, but it's late and I'm not thinking well.
  1468. *
  1469. * The operands are either Long or Double.
  1470. */
  1471. private SelectorToken doNumericOperation(
  1472. SelectorToken t, SelectorToken op1, SelectorToken op2)
  1473. throws SelectorFormatException {
  1474. boolean b = false;
  1475. boolean is1L = false;
  1476. boolean is2L = false;
  1477. long val1L = 0, val2L = 0;
  1478. double val1D = 0, val2D = 0;
  1479. if ((!isNumeric(op1) && op1.getToken() != UNKNOWN)) {
  1480. if (convertTypes && op1.getToken() == STRING) {
  1481. op1 = convertStringToNumber((String)op1.getValue());
  1482. } else {
  1483. throw new SelectorFormatException(
  1484. "Non-numeric argument '" + op1.getValue() +
  1485. "'", selector);
  1486. }
  1487. }
  1488. if (op2 != null && (!isNumeric(op2) && op2.getToken() != UNKNOWN)) {
  1489. if (convertTypes && op2.getToken() == STRING) {
  1490. op2 = convertStringToNumber((String)op2.getValue());
  1491. } else {
  1492. throw new SelectorFormatException(
  1493. "Non-numeric argument '" + op2.getValue() +
  1494. "'", selector);
  1495. }
  1496. }
  1497. if (op1.getToken() == UNKNOWN ||
  1498. (op2 != null && op2.getToken() == UNKNOWN)) {
  1499. // Operation with a UNKNOWN argument is always UNKNOWN
  1500. return SelectorToken.getInstance(UNKNOWN);
  1501. }
  1502. if (op1.getValue() instanceof Long) {
  1503. is1L = true;
  1504. val1L = ((Long)op1.getValue()).longValue();
  1505. val1D = ((Long)op1.getValue()).doubleValue();
  1506. } else {
  1507. is1L = false;
  1508. val1L = ((Double)op1.getValue()).longValue();
  1509. val1D = ((Double)op1.getValue()).doubleValue();
  1510. }
  1511. if (op2 != null) {
  1512. if (op2.getValue() instanceof Long) {
  1513. is2L = true;
  1514. val2L = ((Long)op2.getValue()).longValue();
  1515. val2D = ((Long)op2.getValue()).doubleValue();
  1516. } else {
  1517. is2L = false;
  1518. val2L = ((Double)op2.getValue()).longValue();
  1519. val2D = ((Double)op2.getValue()).doubleValue();
  1520. }
  1521. }
  1522. switch (t.getToken()) {
  1523. case EQUALS:
  1524. case NOT_EQUALS:
  1525. if (is1L && is2L) {
  1526. b = val1L == val2L;
  1527. } else if (is1L) {
  1528. b = val1L == val2D;
  1529. } else if (is2L) {
  1530. b = val1D == val2L;
  1531. } else {
  1532. b = val1D == val2D;
  1533. }
  1534. if (t.getToken() == EQUALS) {
  1535. return SelectorToken.getInstance(b ? TRUE: FALSE);
  1536. } else {
  1537. return SelectorToken.getInstance(b ? FALSE: TRUE);
  1538. }
  1539. case LT:
  1540. if (is1L && is2L) {
  1541. b = val1L < val2L;
  1542. } else if (is1L) {
  1543. b = val1L < val2D;
  1544. } else if (is2L) {
  1545. b = val1D < val2L;
  1546. } else {
  1547. b = val1D < val2D;
  1548. }
  1549. return SelectorToken.getInstance(b ? TRUE: FALSE);
  1550. case LTE:
  1551. if (is1L && is2L) {
  1552. b = val1L <= val2L;
  1553. } else if (is1L) {
  1554. b = val1L <= val2D;
  1555. } else if (is2L) {
  1556. b = val1D <= val2L;
  1557. } else {
  1558. b = val1D <= val2D;
  1559. }
  1560. return SelectorToken.getInstance(b ? TRUE: FALSE);
  1561. case GT:
  1562. if (is1L && is2L) {
  1563. b = val1L > val2L;
  1564. } else if (is1L) {
  1565. b = val1L > val2D;
  1566. } else if (is2L) {
  1567. b = val1D > val2L;
  1568. } else {
  1569. b = val1D > val2D;
  1570. }
  1571. return SelectorToken.getInstance(b ? TRUE: FALSE);
  1572. case GTE:
  1573. if (is1L && is2L) {
  1574. b = val1L >= val2L;
  1575. } else if (is1L) {
  1576. b = val1L >= val2D;
  1577. } else if (is2L) {
  1578. b = val1D >= val2L;
  1579. } else {
  1580. b = val1D >= val2D;
  1581. }
  1582. return SelectorToken.getInstance(b ? TRUE: FALSE);
  1583. case PLUS:
  1584. if (is1L && is2L) {
  1585. long v = val1L + val2L;
  1586. return SelectorToken.getInstance(LONG, new Long(v));
  1587. } else {
  1588. double d = val1D + val2D;
  1589. return SelectorToken.getInstance(DOUBLE, new Double(d));
  1590. }
  1591. case UNARY_PLUS:
  1592. // Unary plus is a no-op
  1593. return(op1);
  1594. case MINUS:
  1595. if (is1L && is2L) {
  1596. long v = val1L - val2L;
  1597. return SelectorToken.getInstance(LONG, new Long(v));
  1598. } else {
  1599. double d = val1D - val2D;
  1600. return SelectorToken.getInstance(DOUBLE, new Double(d));
  1601. }
  1602. case UNARY_MINUS:
  1603. if (is1L) {
  1604. long v = - val1L;
  1605. return SelectorToken.getInstance(LONG, new Long(v));
  1606. } else {
  1607. double d = - val1D;
  1608. return SelectorToken.getInstance(DOUBLE, new Double(d));
  1609. }
  1610. case MULTIPLY:
  1611. if (is1L && is2L) {
  1612. long v = val1L * val2L;
  1613. return SelectorToken.getInstance(LONG, new Long(v));
  1614. } else {
  1615. double d = val1D * val2D;
  1616. return SelectorToken.getInstance(DOUBLE, new Double(d));
  1617. }
  1618. case DIVIDE:
  1619. if (is1L && is2L) {
  1620. long v = val1L / val2L;
  1621. return SelectorToken.getInstance(LONG, new Long(v));
  1622. } else {
  1623. double d = val1D / val2D;
  1624. return SelectorToken.getInstance(DOUBLE, new Double(d));
  1625. }
  1626. default:
  1627. throw new SelectorFormatException(
  1628. "Unknown numeric operation: " + t, selector);
  1629. }
  1630. }
  1631. private static boolean isNumeric(SelectorToken t) {
  1632. int tok = t.getToken();
  1633. return ((tok == DOUBLE) || (tok == LONG));
  1634. }
  1635. private static boolean isOperator(SelectorToken t) {
  1636. return (t.getToken() < 100);
  1637. }
  1638. private static boolean isOperator(int t) {
  1639. return (t < 100);
  1640. }
  1641. // Return a number representing the precedene of an operator:
  1642. //
  1643. private static int getPrecedence(SelectorToken t) {
  1644. switch (t.getToken()) {
  1645. case OR:
  1646. return 10;
  1647. case AND:
  1648. return 11;
  1649. case NOT:
  1650. return 12;
  1651. case EQUALS:
  1652. case NOT_EQUALS:
  1653. return 20;
  1654. case LT:
  1655. case LTE:
  1656. case GT:
  1657. case GTE:
  1658. return 21;
  1659. case IN:
  1660. case NOT_IN:
  1661. case LIKE:
  1662. case NOT_LIKE:
  1663. case IS_NULL:
  1664. case IS_NOT_NULL:
  1665. case BETWEEN:
  1666. case NOT_BETWEEN:
  1667. return 30;
  1668. case PLUS:
  1669. case MINUS:
  1670. return 40;
  1671. case MULTIPLY:
  1672. case DIVIDE:
  1673. return 41;
  1674. case COMMA:
  1675. return 42;
  1676. case UNARY_PLUS:
  1677. case UNARY_MINUS:
  1678. return 43;
  1679. case LEFT_PAREN:
  1680. case RIGHT_PAREN:
  1681. return 50;
  1682. default:
  1683. return 1;
  1684. }
  1685. }
  1686. /**
  1687. * Two Selector instances are equal if they are the same instance
  1688. * or if the selector strings match.
  1689. */
  1690. public boolean equals(Object o) {
  1691. // Since we cache Selectors it should be the case that Selectors
  1692. // with the same selector string will be the same instance.
  1693. if (this == o) return true;
  1694. if (!(o instanceof Selector)) {
  1695. return false;
  1696. }
  1697. Selector obj = (Selector)o;
  1698. return (this.selector.equals(obj.selector));
  1699. }
  1700. public int hashCode() {
  1701. /* Return the hashCode for the Selector string */
  1702. return selector.hashCode();
  1703. }
  1704. public String toString() {
  1705. return selector;
  1706. }
  1707. /**
  1708. * Check if the Selector uses properties.
  1709. *
  1710. * @return true if the selector expression uses properties
  1711. * false if the selector does not use properties (ie
  1712. * it only uses JMS header fields).
  1713. */
  1714. public boolean usesProperties() {
  1715. return usesProperties;
  1716. }
  1717. /**
  1718. * Check if the Selector uses JMS Header Fiedls.
  1719. *
  1720. * @return true if the selector expression uses JMS fields.
  1721. * false if the selector does not use JMS fields (ie
  1722. * it only uses JMS properties).
  1723. */
  1724. public boolean usesFields() {
  1725. return usesFields;
  1726. }
  1727. private static void dumpTokens(LinkedList l) {
  1728. Iterator iter = l.iterator();
  1729. while (iter.hasNext()) {
  1730. SelectorToken token = (SelectorToken)iter.next();
  1731. System.out.print(token.toString());
  1732. }
  1733. System.out.println();
  1734. }
  1735. public String toDebugString() {
  1736. StringBuffer sb = new StringBuffer();
  1737. for (int i = 0; i < compiledSelector.length; i++) {
  1738. if (compiledSelector[i] != null) {
  1739. sb.append(compiledSelector[i].toString());
  1740. }
  1741. }
  1742. return sb.toString() + " cachesize=" + selectorCache.size() ;
  1743. }
  1744. /**
  1745. * Main for testing Selector class.
  1746. *
  1747. * usage: java Selector [-d] [-D] [-l] [selector string]
  1748. * -d Turn on debug
  1749. * -D Turn on verbose debug
  1750. * -l Loop and generate simple performance info. Only valid if a
  1751. * [selector string] is provide.
  1752. * [selector string] evaluate specified string. If no string is provided
  1753. * then run a simple unit test.
  1754. */
  1755. public static void main(String args[]) {
  1756. HashMap props = new HashMap();
  1757. HashMap fields = new HashMap();
  1758. boolean convert = false;
  1759. int loop = 0;
  1760. /* Dummy up some message properties and fields */
  1761. props.put("color", "red");
  1762. props.put("description", "Galvanized hot dipped wing nuts");
  1763. props.put("size", new Integer(1024));
  1764. props.put("msgnum", new Integer(5));
  1765. props.put("msgnumStr", "5");
  1766. props.put("price", new Float(1.50));
  1767. props.put("quantity", new Long(500));
  1768. props.put("minlong", new Long(Long.MIN_VALUE));
  1769. props.put("maxlong", new Long(Long.MAX_VALUE));
  1770. props.put("trueProp", new Boolean(true));
  1771. props.put("falseProp", new Boolean(false));
  1772. props.put("byteProp", new Byte((byte)4));
  1773. props.put("shortProp", new Short((short)4));
  1774. props.put("intProp", new Integer(4));
  1775. props.put("negIntProp", new Integer(-4));
  1776. props.put("floatProp", new Float(4.0));
  1777. props.put("stringProp", "4");
  1778. props.put("nullProp", null);
  1779. /* Throw in some more complex ones */
  1780. props.put("Event", "*Service Change*Restart*");
  1781. props.put("Region", "*EA*SO*WE*BC*");
  1782. props.put("Airspace", "*ASSS*ARCC*BVNF*");
  1783. props.put("LIDFAC", "*ZDC/ARTCC*EKN/RCAG*");
  1784. props.put("SVCPDC", "*ECOM/CA*YTR/RCG*");
  1785. props.put("CLASS", "*1*2*3*4*5*");
  1786. props.put("USI", "*USISAMPLEUSI000*");
  1787. props.put("JMSXUserID", "testUser");
  1788. fields.put("JMSDeliveryMode", "PERSISTENT");
  1789. fields.put("JMSPriority", new Integer(7));
  1790. fields.put("JMSTimestamp", new Long(System.currentTimeMillis()));
  1791. fields.put("JMSCorrelationID", "123456789");
  1792. fields.put("JMSType", "order");
  1793. fields.put("JMSMessageID", "messageid_" + System.currentTimeMillis());
  1794. System.out.println("\nProperties=" + props + "\n");
  1795. System.out.println("\nFields=" + fields + "\n");
  1796. for (int i = 0; i < args.length; i++) {
  1797. if (args[i].equals("-d")) {
  1798. DEBUG = true;
  1799. continue;
  1800. }
  1801. if (args[i].equals("-D")) {
  1802. DEBUG = true;
  1803. VERBOSE_DEBUG = true;
  1804. continue;
  1805. }
  1806. if (args[i].equals("-l")) {
  1807. loop = 500000;
  1808. continue;
  1809. }
  1810. if (args[i].equals("-n")) {
  1811. Selector.setShortCircuit(false);
  1812. continue;
  1813. }
  1814. if (args[i].equals("-c")) {
  1815. convert = true;
  1816. Selector.setConvertTypes(convert);
  1817. continue;
  1818. }
  1819. System.out.println("\nshortCircuit=" + shortCircuit );
  1820. Selector selector = null;
  1821. try {
  1822. selector = Selector.compile(args[i]);
  1823. } catch (SelectorFormatException e) {
  1824. System.out.println("Compile Error:\n" + e);
  1825. System.exit(1);
  1826. }
  1827. try {
  1828. System.out.println(selector.match(props, fields));
  1829. } catch (SelectorFormatException e) {
  1830. System.out.println("Runtime Error:\n" + e);
  1831. System.exit(1);
  1832. }
  1833. if (loop > 0) {
  1834. long start = System.currentTimeMillis();
  1835. for (int n = 0; n < loop; n++) {
  1836. try {
  1837. selector.match(props, fields);
  1838. } catch (SelectorFormatException e) {
  1839. System.out.println("Runtime Error:\n" + e);
  1840. System.exit(1);
  1841. }
  1842. }
  1843. long stop = System.currentTimeMillis();
  1844. System.out.println("Evaluated " + loop + " matches in " +
  1845. ((stop - start) / 1000.0) + " secs");
  1846. System.out.println((loop / ((stop - start)/1000.0)) +
  1847. " matches/sec");
  1848. }
  1849. System.exit(0);
  1850. }
  1851. // Simple selector tests
  1852. String[][] tests = {
  1853. /* Selector Result */
  1854. {"color = 'red'", "true"},
  1855. {"color = 'blue'", "false"},
  1856. {"color <> 'red'", "false"},
  1857. {"color <> 'blue'", "true"},
  1858. {"color in ('red', 'white', 'blue')", "true"},
  1859. {"color in ('orange', 'white', 'blue')", "false"},
  1860. {"color not in ('orange', 'white', 'blue')", "true"},
  1861. {"description like '%hot%'", "true"},
  1862. {"description not like '%hot%'", "false"},
  1863. {"color like 'r_d'", "true"},
  1864. {"color like 'r_d' or color like 'bl_e'", "true"},
  1865. {"color like 'r_d' and (color like 'b%' or color like '%d')", "true"},
  1866. {"quantity between 400 and 1000.0", "true"},
  1867. {"price between 1.10 and 2", "true"},
  1868. {"price not between 5 and 10e2", "true"},
  1869. {"price not between 5 and 10e2 and price between 1 and 2", "true"},
  1870. {"price not between 5 and 10e2 or price between 1 and 2", "true"},
  1871. {"nullProp is null and price is not null", "true"},
  1872. {"nullProp is null or price is not null", "true"},
  1873. {"price is not null", "true"},
  1874. {"price > 0.75", "true"},
  1875. {"price < 9.75", "true"},
  1876. {"price >= 1.50", "true"},
  1877. {"price <= 1.50", "true"},
  1878. {"price > 9.75", "false"},
  1879. {"price >= 9.75", "false"},
  1880. {"msgnum > 1.75", "true"},
  1881. {"size > msgnum", "true"},
  1882. {"size > price", "true"},
  1883. {"size > price + msgnum", "true"},
  1884. {"size > price * msgnum", "true"},
  1885. {"quantity * price > 3.00", "true"},
  1886. {"JMSXUserID = 'testUser'", "true"},
  1887. {"JMSMessageID like '%~_%' escape '~'", "true"},
  1888. {"JMSTimestamp > 4", "true"},
  1889. {"JMSCorrelationID like '1_34__%9'", "true"},
  1890. {"JMSType <> 'quote'", "true"},
  1891. {"JMSPriority > 5", "true"},
  1892. {"JMSPriority < JMSTimestamp", "true"},
  1893. {"byteProp = 4", "true"},
  1894. {"byteProp <> 5.0", "true"},
  1895. {"shortProp <> 5.0", "true"},
  1896. {"intProp <> 5.0", "true"},
  1897. {"byteProp = shortProp", "true"},
  1898. {"byteProp = floatProp", "true"},
  1899. {"floatProp = 4.0 ", "true"},
  1900. {"floatProp * 2 > byteProp", "true"},
  1901. {"stringProp = '4'", "true"},
  1902. {"stringProp = 4" , "error"},
  1903. {"stringProp <> '5'", "true"},
  1904. {"stringProp <> 5", "error"},
  1905. {"byteProp <> 4", "false"},
  1906. {"byteProp = 5.0", "false"},
  1907. {"shortProp = 5.0", "false"},
  1908. {"intProp = 5.0", "false"},
  1909. {"1 + 4 * 5 = 21", "true"},
  1910. {"1+4*5=21", "true"},
  1911. {"1 + -4 * 5 = -19", "true"},
  1912. {"(1 + 4) * +5 = 25", "true"},
  1913. {"(1 + 4) * -5 = -25", "true"},
  1914. {"(1 + 4) * 5 = (3 + 2) * 5", "true"},
  1915. {"1 + (4 * 5) = 21", "true"},
  1916. {"1 + 4 * 5 = 21", "true"},
  1917. {"(1 + 4) * 5 = 25", "true"},
  1918. {"(1 + 4) - 5 = 0 ", "true"},
  1919. {"2.0 * 4E2 + 5 = 805.0", "true"},
  1920. {"2.0 * 4E2 + 5 = 805", "true"},
  1921. {"2.0 = 2.0", "true"},
  1922. {"1.0+2.0*3.0-4.0/4.0 = 6", "true"},
  1923. {"price > 0.75 OR color <> 'blue'", "true"},
  1924. {"(price > 0.75 OR color <> 'blue') AND color <> 'green'", "true"},
  1925. {" 2 * quantity between msgnum AND msgnum * size", "true"},
  1926. {"NOT (2 * quantity between msgnum AND msgnum * size)", "false"},
  1927. {"-price < 0 AND +negIntProp < 0", "true"},
  1928. {"negIntProp+4 = 0 AND intProp-1 = 3", "true"},
  1929. {"- intProp + intProp = 0", "true"},
  1930. {"intProp between -1 and 5", "true"},
  1931. {"intProp between 1 and 5 AND intProp between -1 and 5", "true"},
  1932. {"minlong=" + Long.MIN_VALUE + " AND maxlong=" + Long.MAX_VALUE,
  1933. "true"},
  1934. // An expression with a NULL property is always NULL
  1935. {"unknownProp NOT IN ('foo','jms','test')", "false"},
  1936. {"nullProp NOT IN ('foo','jms','test')", "false"},
  1937. {"unknownProp NOT LIKE '1_3'", "false"},
  1938. {"nullProp NOT LIKE '1_3'", "false"},
  1939. {"0x1d = 29", "true"},
  1940. {"0x1D = 29", "true"},
  1941. {"035 = 29", "true"},
  1942. {"29L = 29", "true"},
  1943. {"29l = 29", "true"},
  1944. {"18. = 1.8e1", "true"},
  1945. {"18. = .18e2", "true"},
  1946. {"18.0f = .18e2", "true"},
  1947. {"18.0F = .18e2", "true"},
  1948. {"18.0d = .18e2", "true"},
  1949. {"18.0D = .18e2", "true"},
  1950. {".7e4 = 7000.0", "true"},
  1951. {" is null nullProp", "true"},
  1952. {"NOT is null nullProp", "false"},
  1953. {" is null unknownProp", "true"},
  1954. {" is not null nullProp", "false"},
  1955. {"NOT is not null nullProp", "true"},
  1956. {" is not null unknownProp", "false"},
  1957. {"TRUE", "true"},
  1958. {"NOT TRUE", "false"},
  1959. {"(NOT (NOT (NOT (NOT TRUE))))", "true"},
  1960. {"FALSE", "false"},
  1961. {"NOT FALSE", "true"},
  1962. {"trueProp", "true"},
  1963. {"NOT trueProp", "false"},
  1964. {"trueProp = TRUE", "true"},
  1965. {"trueProp = FALSE", "false"},
  1966. {"trueProp <> FALSE", "true"},
  1967. {"falseProp", "false"},
  1968. {"falseProp = TRUE", "false"},
  1969. {"falseProp = FALSE", "true"},
  1970. {"falseProp <> TRUE", "true"},
  1971. {"NOT falseProp", "true"},
  1972. {"description LIKE '%nuts%' AND color in ('black', 'white') OR color = 'blue'", "false"},
  1973. {"description LIKE '%nuts%' OR color in ('black', 'white') OR color = 'blue'", "true"},
  1974. {"Event LIKE '%*Service Change*%' OR Event LIKE '%*Restart*%' AND Region LIKE '%*EA*%' AND Airspace LIKE '%*ARCC*%'", "true"},
  1975. // Make sure we don't have any blatant errors in short circuit code
  1976. {"color = 'red' OR color <> 'blue' AND color <> 'green'", "true"},
  1977. {"color = 'white' OR color <> 'blue' AND color <> 'green'", "true"},
  1978. {"color = 'white' OR color <> 'red' AND color <> 'green'", "false"},
  1979. {"color = 'red' OR color <> 'blue' AND color <> 'red'", "true"},
  1980. {"(color = 'red' OR color <> 'blue') AND color <> 'red'", "false"},
  1981. {"(color = 'red' OR color <> 'blue') AND NOT color <> 'red'", "true"},
  1982. {"true OR true OR true OR true", "true"},
  1983. {"(true OR true) OR (true OR true)", "true"},
  1984. {"true OR false OR true OR false", "true"},
  1985. {"false OR false OR false OR true", "true"},
  1986. {"true OR false OR false OR false", "true"},
  1987. {"false OR false OR false OR false", "false"},
  1988. {"false OR false OR false OR true", "true"},
  1989. {"true AND true AND true AND true", "true"},
  1990. {"(true AND false) AND (true AND false)", "false"},
  1991. {"false AND false AND false AND true", "false"},
  1992. {"true AND false AND false AND false", "false"},
  1993. {"false AND true AND true AND true", "false"},
  1994. {"true AND true AND true AND false", "false"},
  1995. {"true AND false OR true AND true", "true"},
  1996. {"true OR false AND true OR false", "true"},
  1997. {"(true OR false) AND (true OR false)", "true"},
  1998. {"NOT ((true OR false) AND (true OR false))", "false"},
  1999. {"color in ('red', 'white', 'blue'(", "error"},
  2000. {"size not between 'red' and 'green'", "error"},
  2001. {"+ + + +", "error"},
  2002. {"1 2 3 4", "error"},
  2003. {"= = = =", "error"},
  2004. {"((1 + 2) * 4 = 3", "error"},
  2005. {"red red red", "error"},
  2006. {"4 >> 1", "error"},
  2007. {"color = 'red", "error"},
  2008. {"color == 'red'", "error"},
  2009. {"intProp BETWEEN 'foo' and 'test'", "error"},
  2010. {"intProp > 'foo'", "error"},
  2011. {"color > 'foo'", "error"},
  2012. {"unknownProp > 'foo'", "error"},
  2013. {"unknownProp < 'foo'", "error"},
  2014. {"unknownProp =< 'foo'", "error"},
  2015. {"unknownProp >= 'foo'", "error"},
  2016. {"intProp >= 'foo'", "error"},
  2017. {"intProp < 'foo'", "error"},
  2018. {"intProp <= 'foo'", "error"},
  2019. {"intProp between 'foo' and 'bar'", "error"},
  2020. {"color between 1 and 7", "error"},
  2021. {"'color' between 1 and 7", "error"},
  2022. {"7 in ('red', 'blue')", "error"},
  2023. {"intProp in ('red', 'blue')", "error"},
  2024. {"7 not in ('red', 'blue')", "error"},
  2025. {"intProp not in ('red', 'blue')", "error"},
  2026. {"NULL = 0", "error"},
  2027. {"=color 'red'", "error"},
  2028. {"size like '7'", "error"},
  2029. {"size not like '7'", "error"},
  2030. {"4 = 'red'", "error"},
  2031. {"4 <> 'red'", "error"},
  2032. {"'red' <> 4", "error"},
  2033. {"'red' = 4", "error"},
  2034. {"intProp = 'red'", "error"},
  2035. {"intProp <> 'red'", "error"},
  2036. {"'red' = intProp", "error"},
  2037. {"'red' <> intProp", "error"},
  2038. {"msgnumStr = 5", "error"},
  2039. {"msgnum = '5'", "error"},
  2040. };
  2041. int failCnt = 0;
  2042. for (int n = 0; n < tests.length; n++) {
  2043. Selector selector = null;
  2044. String expected = tests[n][1];
  2045. String actual = null;
  2046. String result;
  2047. HashMap _props = null;
  2048. HashMap _fields = null;
  2049. try {
  2050. selector = Selector.compile(tests[n][0]);
  2051. // Test optimization
  2052. if (selector.usesProperties()) {
  2053. _props = props;
  2054. } else {
  2055. _props = null;
  2056. }
  2057. if (selector.usesFields()) {
  2058. _fields = fields;
  2059. } else {
  2060. _fields = null;
  2061. }
  2062. if (selector.match(_props, _fields)) {
  2063. actual = "true";
  2064. } else {
  2065. actual = "false";
  2066. }
  2067. } catch (SelectorFormatException e) {
  2068. actual = "error";
  2069. if (!actual.equals(expected)) {
  2070. System.out.println(e);
  2071. }
  2072. }
  2073. if (actual.equals(expected)) {
  2074. result = " PASS";
  2075. } else {
  2076. result = "***** FAIL";
  2077. failCnt++;
  2078. }
  2079. System.out.println(result + " " + tests[n][0] +
  2080. " : expected=" + expected + " actual=" + actual);
  2081. }
  2082. System.out.println (tests.length + " tests: " + (tests.length - failCnt) +
  2083. " passed " + failCnt + " failed ");
  2084. if (failCnt > 0) {
  2085. System.exit(1);
  2086. } else {
  2087. System.exit(0);
  2088. }
  2089. }
  2090. }