| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.sdo.helper.extension; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Stack; |
| |
| /** |
| * Utility class used for processing an XPath Expression into postfix |
| * notation. For example, consider the following expression: |
| * |
| * quantity=5 and (specialOrder='false' or productName='Lawnmower') |
| * |
| * The operations performed to create a postfix expression are: |
| * 1) add "quantity" to the list |
| * 2) push "=" onto the stack |
| * 3) add "5" to the list |
| * 4) pop "=" off the stack, and add to the list |
| * 5) push "and" onto the stack |
| * 6) push "(" onto the stack |
| * 7) add "specialOrder" to the list |
| * 8) push "=" onto the stack |
| * 9) add "'false'" to the list |
| * 10) pop "=" off the stack, and add to the list |
| * 11) push "or" onto the stack |
| * 12) add "productName" to the list |
| * 13) push "=" onto the stack |
| * 14) add "'Lawnmower'" to the list |
| * 15) pop "=" off the stack, and add to the list |
| * 16) pop "or" off the stack, and add to the list |
| * 17) push ")" onto the stack |
| * 18) pop "(" ")" pair off the stack |
| * 19) pop "and" off the stack, and add to the list |
| * |
| * The resulting postfix expression is: |
| * |
| * quantity 5 = specialOrder 'false' = productName 'Lawnmower' = or and |
| */ |
| public class OPStack { |
| |
| /** Visibility reduced from [public] in 2.1.0. May 15 2007 */ |
| private int state; |
| |
| /** Visibility reduced from [public] in 2.1.0. May 15 2007 */ |
| private Stack stack; |
| |
| /** Visibility reduced from [public] in 2.1.0. May 15 2007 */ |
| |
| //private String expression; |
| |
| /** Visibility reduced from [public] in 2.1.0. May 15 2007 */ |
| private ArrayList tokens; |
| |
| /** Visibility reduced from [public] in 2.1.0. May 15 2007 */ |
| private List out; |
| |
| /** |
| * The default constructor initializes the stack and |
| * Token list. |
| */ |
| public OPStack() { |
| stack = new Stack(); |
| tokens = new ArrayList(); |
| } |
| |
| /** |
| * Removes a '(' ')' pair from the stack. |
| */ |
| private void removeOCpair() { |
| stack.pop(); |
| stack.pop(); |
| } |
| |
| /** |
| * Removes lower priority tokens from the stack (adding them to the |
| * List that will be returned), then push the new token onto the |
| * stack. |
| * |
| * @param outList |
| * @param t |
| */ |
| private void pushSpew(List outList, Token t) { |
| while (!stack.empty() && (((Token)stack.peek()).getPriority() >= t.getPriority())) { |
| outList.add(stack.pop()); |
| } |
| stack.push(t); |
| } |
| |
| /** |
| * Returns the next Token in the list. |
| * |
| * @return |
| */ |
| private Token getNextToken() { |
| if (tokens.size() > 0) { |
| Token tok = (Token)tokens.get(0); |
| tokens.remove(0); |
| return tok; |
| } |
| return null; |
| } |
| |
| /** |
| * Parse an XPath Expression into Token objects. |
| * |
| * @param exp |
| */ |
| private void parseExpression(String exp) { |
| StringBuffer sbuf = new StringBuffer(); |
| char ch; |
| Token tok; |
| for (int i = 0; i < exp.length(); i++) { |
| ch = exp.charAt(i); |
| // 'and' and 'or' should be preceded by whitespace |
| if (ch == ' ') { |
| if (((i + 2) < exp.length()) && ((exp.charAt(i + 1) == 'o') && (exp.charAt(i + 2) == 'r'))) { |
| tok = new Token(sbuf.toString(), Token.ARG); |
| tokens.add(tok); |
| sbuf = new StringBuffer(); |
| sbuf.append(exp.charAt(++i)); |
| sbuf.append(exp.charAt(++i)); |
| tok = new Token(sbuf.toString(), Token.DI, Token.OR_PR); |
| tokens.add(tok); |
| sbuf = new StringBuffer(); |
| } else if (((i + 3) < exp.length()) && ((exp.charAt(i + 1) == 'a') && (exp.charAt(i + 2) == 'n') && (exp.charAt(i + 3) == 'd'))) { |
| tok = new Token(sbuf.toString(), Token.ARG); |
| tokens.add(tok); |
| sbuf = new StringBuffer(); |
| sbuf.append(exp.charAt(++i)); |
| sbuf.append(exp.charAt(++i)); |
| sbuf.append(exp.charAt(++i)); |
| tok = new Token(sbuf.toString(), Token.DI, Token.AND_PR); |
| tokens.add(tok); |
| sbuf = new StringBuffer(); |
| } |
| } else if (ch == '=') { |
| tok = new Token(sbuf.toString(), Token.ARG); |
| tokens.add(tok); |
| tok = new Token(Character.toString(ch), Token.DI, Token.EQ_PR); |
| tokens.add(tok); |
| sbuf = new StringBuffer(); |
| } else if ((ch == '!') && (((i + 1) < exp.length()) && (exp.charAt(i + 1) == '='))) { |
| tok = new Token(sbuf.toString(), Token.ARG); |
| tokens.add(tok); |
| sbuf = new StringBuffer(); |
| sbuf.append(ch); |
| sbuf.append(exp.charAt(++i)); |
| tok = new Token(sbuf.toString().trim(), Token.DI, Token.NEQ_PR); |
| tokens.add(tok); |
| sbuf = new StringBuffer(); |
| } else if (ch == '<') { |
| tok = new Token(sbuf.toString(), Token.ARG); |
| tokens.add(tok); |
| sbuf = new StringBuffer(); |
| sbuf.append(ch); |
| if (((i + 1) < exp.length()) && (exp.charAt(i + 1) == '=')) { |
| sbuf.append(exp.charAt(++i)); |
| tok = new Token(sbuf.toString().trim(), Token.DI, Token.LEQ_PR); |
| } else { |
| tok = new Token(sbuf.toString().trim(), Token.DI, Token.LT_PR); |
| } |
| tokens.add(tok); |
| sbuf = new StringBuffer(); |
| } else if (ch == '>') { |
| tok = new Token(sbuf.toString(), Token.ARG); |
| tokens.add(tok); |
| sbuf = new StringBuffer(); |
| sbuf.append(ch); |
| if (((i + 1) < exp.length()) && (exp.charAt(i + 1) == '=')) { |
| sbuf.append(exp.charAt(++i)); |
| tok = new Token(sbuf.toString().trim(), Token.DI, Token.GEQ_PR); |
| } else { |
| tok = new Token(sbuf.toString().trim(), Token.DI, Token.GT_PR); |
| } |
| tokens.add(tok); |
| sbuf = new StringBuffer(); |
| } else if (ch == '(') { |
| if (sbuf.length() > 0) { |
| tok = new Token(sbuf.toString(), Token.ARG); |
| tokens.add(tok); |
| sbuf = new StringBuffer(); |
| } |
| tok = new Token(Character.toString(ch), Token.OPEN); |
| tokens.add(tok); |
| } else if (ch == ')') { |
| if (sbuf.length() > 0) { |
| tok = new Token(sbuf.toString(), Token.ARG); |
| tokens.add(tok); |
| sbuf = new StringBuffer(); |
| } |
| tok = new Token(Character.toString(ch), Token.CLOSE); |
| tokens.add(tok); |
| } else { |
| if (ch != ' ') { |
| sbuf.append(ch); |
| } |
| } |
| } |
| if (sbuf.length() > 0) { |
| tok = new Token(sbuf.toString(), Token.ARG); |
| tokens.add(tok); |
| sbuf = new StringBuffer(); |
| } |
| } |
| |
| /** |
| * Process an XPath Expression into postfix notation. |
| * |
| * @param exp |
| * @return |
| */ |
| public List processExpression(String exp) { |
| parseExpression(exp); |
| out = new ArrayList(); |
| state = 0; |
| stack.push(Token.OpenExp);// start off with an open of the expression |
| Token t; |
| while ((t = getNextToken()) != null) { |
| switch (t.getType()) { |
| case Token.ARG: |
| if (state != 0) { |
| out.clear(); |
| return out; |
| } |
| out.add(t); |
| state = 1; |
| break; |
| case Token.PRE: |
| if (state != 0) { |
| out.clear(); |
| return out; |
| } |
| stack.push(t); |
| break; |
| case Token.OPEN: |
| stack.push(t); |
| break; |
| case Token.DI: |
| if (state != 1) { |
| out.clear(); |
| return out; |
| } |
| pushSpew(out, t); |
| state = 0; |
| break; |
| case Token.POST: |
| if (state != 1) { |
| out.clear(); |
| return out; |
| } |
| pushSpew(out, t); |
| out.add(stack.pop()); |
| break; |
| case Token.CLOSE: |
| pushSpew(out, t); |
| removeOCpair(); |
| } |
| } |
| pushSpew(out, Token.CloseExp); |
| removeOCpair(); |
| if (state != 1) { |
| out.clear(); |
| } |
| return out; |
| } |
| } |