PageRenderTime 59ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/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

Large files files are truncated, but you can click here to view the full 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;

Large files files are truncated, but you can click here to view the full file