Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.lithium.ldn.starql.parsers.JparsecQueryMarkupManager Maven / Gradle / Ivy
package com.lithium.ldn.starql.parsers;
import static java.util.regex.Pattern.CASE_INSENSITIVE;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.codehaus.jparsec.Parser;
import org.codehaus.jparsec.Parsers;
import org.codehaus.jparsec.Scanners;
import org.codehaus.jparsec.error.ParserException;
import org.codehaus.jparsec.functors.Map;
import org.codehaus.jparsec.functors.Pair;
import org.codehaus.jparsec.functors.Tuple3;
import org.codehaus.jparsec.functors.Tuple5;
import org.codehaus.jparsec.pattern.Patterns;
import com.fasterxml.jackson.databind.util.ISO8601Utils;
import com.google.common.collect.Lists;
import com.lithium.ldn.starql.exceptions.InvalidQueryException;
import com.lithium.ldn.starql.exceptions.QueryValidationException;
import com.lithium.ldn.starql.models.QlBooleanConstraintNode;
import com.lithium.ldn.starql.models.QlConstraint;
import com.lithium.ldn.starql.models.QlConstraintOperator;
import com.lithium.ldn.starql.models.QlConstraintPairOperator;
import com.lithium.ldn.starql.models.QlConstraintValue;
import com.lithium.ldn.starql.models.QlConstraintValueCollection;
import com.lithium.ldn.starql.models.QlConstraintValueDate;
import com.lithium.ldn.starql.models.QlConstraintValueNumber;
import com.lithium.ldn.starql.models.QlConstraintValueString;
import com.lithium.ldn.starql.models.QlField;
import com.lithium.ldn.starql.models.QlPageConstraints;
import com.lithium.ldn.starql.models.QlSelectStatement;
import com.lithium.ldn.starql.models.QlSortClause;
import com.lithium.ldn.starql.models.QlSortOrderType;
import com.lithium.ldn.starql.models.QlWhereClause;
import com.lithium.ldn.starql.validation.NoOpValidator;
import com.lithium.ldn.starql.validation.QlConstraintsClauseValidator;
import com.lithium.ldn.starql.validation.QlSelectStatementValidator;
/**
* JParsec implementation of QueryMarkupManger.
*
* @author David Esposito
* @testing When making changes to this file, make sure to run JparsecQlParserTest.
*/
public class JparsecQueryMarkupManager implements QueryMarkupManager {
private static final NoOpValidator NO_OP_VALIDATOR = new NoOpValidator();
@Override
public QlSelectStatement parseQlSelect(String query) throws InvalidQueryException,
QueryValidationException {
return parseQlSelect(query, NO_OP_VALIDATOR);
}
@Override
public QlSelectStatement parseQlSelect(String query, QlSelectStatementValidator validator)
throws InvalidQueryException, QueryValidationException {
if (query == null) {
throw new IllegalArgumentException("query cannot be null");
}
if (validator == null) {
throw new IllegalArgumentException("validator cannot be null");
}
try {
QlSelectStatement selectStatement = qlSelectParser().parse(query);
validator.validate(selectStatement);
return selectStatement;
} catch (ParserException e) {
throw new InvalidQueryException(e.getMessage(), query);
}
}
@Override
public QlWhereClause parseQlConstraintsClause(String query) throws InvalidQueryException,
QueryValidationException {
return parseQlConstraintsClause(query, NO_OP_VALIDATOR);
}
@Override
public QlWhereClause parseQlConstraintsClause(String query, QlConstraintsClauseValidator validator)
throws InvalidQueryException, QueryValidationException {
if (query == null) {
throw new IllegalArgumentException("query cannot be null");
}
if (validator == null) {
throw new IllegalArgumentException("validator cannot be null");
}
try {
QlBooleanConstraintNode constraintsRootNode = constraintsParser().parse(query);
if (constraintsRootNode != null) {
QlWhereClause clause = new QlWhereClause.Builder()
.setRoot(constraintsRootNode)
.build();
validator.validate(clause);
return clause;
}
throw new InvalidQueryException("", query);
} catch (ParserException e) {
throw new InvalidQueryException(e.getMessage(), query);
}
}
/*
* ====================================================
* SELECT STATEMENT
* ====================================================
*/
protected Parser qlSelectParser() {
return paddedRegex("SELECT", true, false)
.next(Parsers.tuple(fieldsParser().followedBy(paddedRegex("FROM", true, false)),
alphaNumeric(),
padWithWhitespace(whereClauseParser().optional(), true),
padWithWhitespace(orderByParser().optional(), false),
padWithWhitespace(pageConstraintParser().optional(), false))
.map(new Map, String, QlBooleanConstraintNode, QlSortClause, QlPageConstraints>,
QlSelectStatement>() {
@Override
public QlSelectStatement map(Tuple5, String, QlBooleanConstraintNode,
QlSortClause, QlPageConstraints> arg0) {
return new QlSelectStatement.Builder()
.setFields(arg0.a)
.setCollection(arg0.b)
.setConstraints(arg0.c)
.setSortConstraint(arg0.d)
.setPageConstraints(arg0.e)
.build();
}
}));
}
/*
* ====================================================
* FIELDS
* ====================================================
*/
protected Parser> fieldsParser() {
return Parsers.or(fieldStarParser(), fieldCollectionParser());
}
protected Parser> fieldCollectionParser() {
return qualifiedFieldOrFunctionParser().sepBy1(padWithWhitespace(regex(",", true), false));
}
protected Parser fieldOrFunctionParser() {
Parser.Reference fieldOrFuncParserRef = Parser.newReference();
Parser> simpleParser = simpleFieldParser();
Parser> fieldParser = fieldParser(fieldOrFuncParserRef);
Parser fieldOrFuncParser =
Parsers.or(functionParser(fieldOrFuncParserRef), fieldParser,
simpleParser).map(
new Map, QlField>() {
@Override
public QlField map(Tuple3 fieldInfo) {
return QlField.create(null, fieldInfo.a, fieldInfo.b,
fieldInfo.c);
}
});
fieldOrFuncParserRef.lazySet(fieldOrFuncParser);
return fieldOrFuncParser;
}
protected Parser qualifiedFieldOrFunctionParser() {
Parser.Reference fieldOrFuncParserRef = Parser.newReference();
Parser> simpleParser = simpleFieldParser();
Parser> fieldParser = fieldParser(fieldOrFuncParserRef);
Parser qualifierParser = regex("[a-zA-Z]+\\s+", false)
.followedBy(regex("from", false).not().peek()).atomic().optional(null);
Parser fieldOrFuncParser = Parsers.pair(qualifierParser,
Parsers.or(functionParser(fieldOrFuncParserRef), fieldParser,
simpleParser)).map(
new Map>, QlField>() {
@Override
public QlField map(
Pair> fieldInfo) {
String qualifier = fieldInfo.a == null ? null : fieldInfo.a.trim();
return QlField.create(qualifier, fieldInfo.b.a, fieldInfo.b.b,
fieldInfo.b.c);
}
});
fieldOrFuncParserRef.lazySet(fieldOrFuncParser);
return fieldOrFuncParser;
}
protected Parser> functionParser(Parser.Reference fieldOrFuncParserRef) {
return Parsers.tuple(alphaNumeric().followedBy(padWithWhitespace(regex("\\(", true), false)),
insideFunctionParser(fieldOrFuncParserRef),
Parsers.constant(true));
}
protected Parser insideFunctionParser(Parser.Reference fieldOrFuncParserRef) {
return Parsers.or(fieldSingleStarParser(), fieldOrFuncParserRef.lazy()).followedBy(
padWithWhitespace(regex("\\)", true), true));
}
protected Parser> fieldParser(
Parser.Reference fieldOrFuncParserRef) {
return Parsers.tuple(
alphaNumeric().followedBy(padWithWhitespace(regex("\\.", true), false)),
fieldOrFuncParserRef.lazy(),
Parsers.constant(false));
}
protected Parser> simpleFieldParser() {
return Parsers.tuple(alphaNumeric(),
Parsers.constant((QlField)null),
Parsers.constant(false));
}
protected Parser fieldSingleStarParser() {
return paddedRegex("\\*", true, true)
.map(new Map() {
@Override
public QlField map(String arg0) {
return QlField.create(arg0);
}
});
}
protected Parser> fieldStarParser() {
return paddedRegex("\\*", true, true)
.map(new Map>() {
@Override
public List map(String arg0) {
return Lists.newArrayList(QlField.create(arg0));
}
});
}
/*
* ====================================================
* ORDER BY
* ====================================================
*/
protected Parser orderByParser() {
return paddedRegex("ORDER BY", false, false)
.next(Parsers.tuple(fieldOrFunctionParser(), sortOrderTypeParser()))
.map(new Map, QlSortClause>() {
@Override
public QlSortClause map(Pair arg0) {
return new QlSortClause(arg0.a, arg0.b);
}
});
}
protected Parser sortOrderTypeParser() {
return paddedRegex("[a-z]+", true, false)
.map(new Map() {
@Override
public QlSortOrderType map(String arg0) {
return QlSortOrderType.get(arg0);
}
});
}
/*
* ====================================================
* LIMIT OFFSET
* ====================================================
*/
protected Parser pageConstraintParser() {
return Parsers.tuple(padWithWhitespace(limitParser().optional(-1), false),
offsetParser().optional(-1))
.map(new Map, QlPageConstraints>() {
@Override
public QlPageConstraints map(Pair arg0) {
return new QlPageConstraints(arg0.a, arg0.b);
}
});
}
protected Parser limitParser() {
return regexIntegerPair("LIMIT", false);
}
protected Parser offsetParser() {
return regexIntegerPair("OFFSET", false);
}
/*
* ====================================================
* WHERE
* ====================================================
*/
protected Parser whereClauseParser() {
return paddedRegex("WHERE", false, false).next(constraintsParser());
}
protected Parser constraintsParser() {
return padWithWhitespace(constraintParser(), false)
.infixl(constraintPairOperatorParser());
}
protected Parser constraintPairOperatorParser() {
return regex("(AND|OR) ", false)
.map(new Map() {
@Override
public QlConstraintPairOperator map(String arg0) {
return QlConstraintPairOperator.get(arg0.trim());
}
});
}
protected Parser constraintParser() {
return Parsers.tuple(padWithWhitespace(fieldOrFunctionParser(), false), constraintOperatorParser(),
constraintValueParser())
.map(new Map, QlBooleanConstraintNode>() {
@Override
public QlBooleanConstraintNode map(Tuple3 arg0) {
return new QlConstraint(arg0.a, arg0.c, arg0.b);
}
});
}
protected Parser constraintOperatorParser() {
return paddedRegex("(!=|=|<=|>=|<|>|IN|MATCHES|LIKE)", false, false)
.map(new Map() {
@Override
public QlConstraintOperator map(String arg0) {
return QlConstraintOperator.get(arg0.toString());
}
});
}
/*
* ====================================================
* MISC
* ====================================================
*/
/**
* All added whitespace is optional. To require white space, you should add it to your parser inline.
* @param parser The parser to wrap in optional white space.
* @param leadingWhitespaces If optional white space should prepend the provided parser.
* @return The parser that was wrapped in optional white space.
*/
protected Parser padWithWhitespace(Parser parser, boolean leadingWhitespaces) {
return leadingWhitespaces
? Scanners.WHITESPACES.optional().next(parser).followedBy(Scanners.WHITESPACES.optional())
: parser.followedBy(Scanners.WHITESPACES.optional());
}
/**
* This value is not padded with leading/trailing whitespace. Values can start with [a-zA-Z_].
*
* @return The alpha-numberic string that was parsed.
*/
protected Parser alphaNumeric() {
return regex("[a-zA-Z_][a-zA-Z0-9_]*", false);
}
protected Parser regexIntegerPair(String keyword, boolean isCaseSensitive) {
return paddedRegex(keyword, false, isCaseSensitive).next(Scanners.INTEGER)
.map(new Map() {
@Override
public Integer map(String arg0) {
return Integer.parseInt(arg0);
}
});
}
protected Parser paddedRegex(String pattern, boolean leadingWhitespace, boolean isCaseSensitive) {
return paddedRegex(pattern, "regexParser: " + pattern, leadingWhitespace, isCaseSensitive);
}
protected Parser regex(String pattern, boolean isCaseSensitive) {
return regex(pattern, "regexParser: " + pattern, isCaseSensitive);
}
protected Parser paddedRegex(String pattern, String name, boolean leadingWhitespace, boolean isCaseSensitive) {
return padWithWhitespace(regex(pattern, isCaseSensitive).source(), leadingWhitespace);
}
protected Parser regex(String pattern, String name, boolean isCaseSensitive) {
Pattern p = Pattern.compile(pattern, isCaseSensitive ? 0 : CASE_INSENSITIVE);
return Scanners.pattern(Patterns.regex(p), name).source();
}
/**
* Parser for any type of constraint value.
*
* @return The value, an integer, quoted string without the quotes, or collection.
*/
protected Parser constraintValueParser() {
return Parsers.or(dateValueParser(),
numericalValueParser(),
collectionValueParser(),
stringValueParser()
);
}
/**
* String variables may be in single or double quotes (regardless, quotes on either end must match).
* Single quoted strings have single quotes escaped by repeating, so use two single quotes ('').
* Double quoted strings are escaped with the character '\'. Only the double quote character needs escaping.
*
* @return A quoted string without the surrounding single quotes.
*/
protected Parser stringValueParser() {
return Parsers.or(Scanners.DOUBLE_QUOTE_STRING
.map(new Map() {
@Override
public QlConstraintValueString map(String arg0) {
String ret = arg0;
// Remove beginning and ending double quote.
ret = ret.substring(1);
ret = ret.substring(0, ret.length()-1);
// Unescape double quotes, ie. "\"" becomes """, "\\"" becomes "\"".
Pattern p = Pattern.compile("(\\\\(.))");
Matcher m = p.matcher(ret);
StringBuffer sb = new StringBuffer();
while(m.find()) {
if (m.group(2).equals("\"")) {
m.appendReplacement(sb, "\"");
} else if (m.group(2).equals("\\")) {
m.appendReplacement(sb, "\\");
} else {
throw new ParserException(null, "stringValueParser", null);
}
}
m.appendTail(sb);
ret = sb.toString();
return new QlConstraintValueString(ret);
}
}),Scanners.SINGLE_QUOTE_STRING
.map(new Map(){
@Override
public QlConstraintValueString map(String arg0) {
String ret = arg0;
// Remove beginning and ending single quote.
ret = ret.substring(1);
ret = ret.substring(0, ret.length()-1);
// Unescape single quotes, ie. "''" becomes "'".
ret = ret.replaceAll("''", "'");
return new QlConstraintValueString(ret);
}
})
);
}
/**
* A collection is a constraint value consisting of a pair of parentheses around a comma-separated
* list of constraint values, as defined above excluding collections, so it is not recursive.
*
* @return ConstraintValueCollection containing ConstraintValues *MAY BE OF DIFFERENT TYPES*
*/
protected Parser> collectionValueParser() {
return padWithWhitespace(Parsers.or(stringValueParser(), numericalValueParser()), false).sepBy(padWithWhitespace(regex(",", true), false))
.between(padWithWhitespace(regex("\\(", true), false), padWithWhitespace(regex("\\)", true), false))
.map(new Map, QlConstraintValueCollection extends QlConstraintValue>>(){
@Override
public QlConstraintValueCollection extends QlConstraintValue> map(List arg0) {
return new QlConstraintValueCollection(arg0);
}
});
}
/**
* Supports
* - int: [1-9][0-9]*, 0
* - long: [1-9][0-9]*L, 0L
* - double: [0-9]+\.[0-9]+
* - float: [0-9]+\.[0-9]+f, 0f
*
* @return The matched string
*/
protected Parser numericalValueParser() {
return regex("([0-9]+\\.[0-9]+f?|[1-9][0-9]*L?|0[fL]?)", false)
.map(new Map(){
@Override
public QlConstraintValueNumber map(String arg0) {
try {
if (arg0.contains("f")) {
return new QlConstraintValueNumber(Float.parseFloat(arg0));
}
if (arg0.contains(".")) {
return new QlConstraintValueNumber(Double.parseDouble(arg0));
}
if (arg0.contains("L")) {
return new QlConstraintValueNumber(Long.parseLong(arg0.substring(0, arg0.length()-1)));
}
try {
return new QlConstraintValueNumber(Integer.parseInt(arg0));
}
catch(NumberFormatException e) {
return new QlConstraintValueNumber(Long.parseLong(arg0.substring(0, arg0.length())));
}
} catch (NumberFormatException e) {
throw new RuntimeException("Could not convert the following string to a nubmer: " + arg0);
}
}
});
}
protected Parser dateValueParser() {
return regex("[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|(\\+|-)[0-9]{2}:[0-9]{2})?", true)
.map(new Map(){
@Override
public QlConstraintValueDate map(String arg0) {
return new QlConstraintValueDate(ISO8601Utils.parse(arg0));
}
});
}
}