All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sap.cds.impl.parser.AbstractCqnExpressionParser Maven / Gradle / Ivy

There is a newer version: 3.8.0
Show newest version
/*******************************************************************
 * © 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