
com.sap.cds.impl.parser.AbstractCqnExpressionParser Maven / Gradle / Ivy
/*******************************************************************
* © 2020 SAP SE or an SAP affiliate company. All rights reserved. *
*******************************************************************/
package com.sap.cds.impl.parser;
import static com.sap.cds.impl.parser.token.CqnPlainImpl.plain;
import static com.sap.cds.ql.CQL.and;
import static com.sap.cds.ql.CQL.not;
import static com.sap.cds.ql.CQL.or;
import static java.util.stream.Collectors.toList;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Stream;
import com.sap.cds.impl.parser.token.CqnPlainImpl;
import com.sap.cds.ql.cqn.CqnLiteral;
import com.sap.cds.ql.cqn.CqnPlain;
import com.sap.cds.ql.cqn.CqnPredicate;
import com.sap.cds.ql.cqn.CqnReference;
import com.sap.cds.ql.cqn.CqnSyntaxException;
import com.sap.cds.ql.cqn.CqnToken;
import com.sap.cds.ql.cqn.CqnValue;
/**
*
* GRAMMAR:
*
* search expression = boolean_term | boolean_term OR search_expression
*
* boolean_term = boolean_factor | boolean_factor AND boolean_term
*
* boolean_factor = [ NOT ] boolean_test
*
* boolean test = predicate | ( search_expression )
*/
public abstract class AbstractCqnExpressionParser {
private static final NumberFormat number = NumberFormat.getInstance();
protected LinkedList tokens;
protected CqnToken aheadToken;
protected int pos;
public CqnPredicate parsePredicate(Stream expressionTokens) {
return parsePredicate(expressionTokens.collect(toList()));
}
public CqnPredicate parsePredicate(List tokenList) {
if (tokenList.isEmpty()) {
return null;
}
tokens = new LinkedList<>(tokenList);
aheadToken = tokens.getFirst();
pos = 0;
CqnPredicate expression = searchCondition();
if (aheadToken != null) {
throw unexpected();
}
return expression;
}
private CqnPredicate searchCondition() {
CqnPredicate term = booleanTerm();
if (is("or")) {
CqnPredicate expr = searchCondition();
return or(term, expr);
}
return term;
}
private CqnPredicate booleanTerm() {
CqnPredicate factor = booleanFactor();
if (is("and")) {
CqnPredicate term = booleanTerm();
return and(factor, term);
}
return factor;
}
private CqnPredicate booleanFactor() {
if (is("not")) {
return not(booleanTest());
}
return booleanTest();
}
private CqnPredicate booleanTest() {
if (is("(")) {
CqnPredicate expr = searchCondition();
expect(")");
return expr;
}
return predicate();
}
protected abstract CqnPredicate predicate();
protected void nextToken() {
tokens.pop();
if (tokens.isEmpty()) {
aheadToken = null;
} else {
aheadToken = tokens.getFirst();
pos++;
}
}
protected boolean hasNext() {
return aheadToken != null;
}
protected CqnPredicate getPredicate() {
return get(CqnPredicate.class);
}
protected CqnPlain getPlain() {
return get(CqnPlain.class);
}
protected CqnLiteral> getLiteral() {
return get(CqnLiteral.class);
}
protected CqnValue getValue() {
return get(CqnValue.class);
}
@SuppressWarnings("unchecked")
protected T get(Class clazz) {
if (null == aheadToken) {
throw new CqnSyntaxException("Unexpected end of token stream.");
}
if (clazz.isAssignableFrom(aheadToken.getClass())) {
T value = (T) aheadToken;
nextToken();
return value;
}
throw unexpected();
}
protected boolean is(String value) {
if (peek(value)) {
nextToken();
return true;
}
return false;
}
protected boolean peek(String... values) {
if (!isPlain()) {
return false;
}
String plain = ((CqnPlain) aheadToken).plain();
return Arrays.stream(values).anyMatch(plain::equalsIgnoreCase);
}
protected boolean isPredicate() {
return aheadToken instanceof CqnPredicate;
}
protected boolean isPlain() {
return aheadToken instanceof CqnPlain;
}
protected boolean isLiteral() {
return aheadToken instanceof CqnLiteral;
}
protected boolean isRef() {
return aheadToken instanceof CqnReference;
}
protected boolean isNumeric() {
if (isLiteral()) {
return ((CqnLiteral>) aheadToken).isNumeric();
}
if (isPlain()) {
try {
number.parse(((CqnPlain) aheadToken).plain());
} catch (ParseException e) {
return false;
}
}
return true;
}
protected CqnSyntaxException unexpected() {
String msg = MessageFormat.format("Unexpected token at position {0}: {1}", pos, aheadToken.toJson());
return new CqnSyntaxException(msg);
}
protected void expect(String expected) {
if (!is(expected)) {
throw expecting(plain(expected));
}
}
private CqnSyntaxException expecting(CqnPlainImpl plain) {
String msg;
if (aheadToken != null) {
msg = MessageFormat.format("Unexpeceted token {0} at position {1}. Expecting {2}.", aheadToken.toJson(),
pos, plain.toJson());
} else {
msg = MessageFormat.format("Expecting token {0} at position {1}.", plain.toJson(), pos);
}
return new CqnSyntaxException(msg);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy