blob: 4798649be6d437d60e8c966b8ba008e571694fe2 [file] [log] [blame]
/*
* 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(Character.toString(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(Character.toString(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(Character.toString(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;
}
}