org.apache.jackrabbit.commons.query.sql2.Parser Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.commons.query.sql2;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.ValueFactory;
import javax.jcr.query.InvalidQueryException;
import javax.jcr.query.qom.BindVariableValue;
import javax.jcr.query.qom.Column;
import javax.jcr.query.qom.Constraint;
import javax.jcr.query.qom.DynamicOperand;
import javax.jcr.query.qom.JoinCondition;
import javax.jcr.query.qom.Literal;
import javax.jcr.query.qom.Ordering;
import javax.jcr.query.qom.PropertyExistence;
import javax.jcr.query.qom.PropertyValue;
import javax.jcr.query.qom.QueryObjectModel;
import javax.jcr.query.qom.QueryObjectModelFactory;
import javax.jcr.query.qom.Selector;
import javax.jcr.query.qom.Source;
import javax.jcr.query.qom.StaticOperand;
import org.apache.jackrabbit.commons.query.qom.JoinType;
import org.apache.jackrabbit.commons.query.qom.Operator;
/**
* The SQL2 parser can convert a JCR-SQL2 query to a QueryObjectModel.
*/
public class Parser {
// Character types, used during the tokenizer phase
private static final int CHAR_END = -1, CHAR_VALUE = 2, CHAR_QUOTED = 3;
private static final int CHAR_NAME = 4, CHAR_SPECIAL_1 = 5, CHAR_SPECIAL_2 = 6;
private static final int CHAR_STRING = 7, CHAR_DECIMAL = 8;
// Token types
private static final int KEYWORD = 1, IDENTIFIER = 2, PARAMETER = 3, END = 4, VALUE = 5;
private static final int MINUS = 12, PLUS = 13, OPEN = 14, CLOSE = 15;
// The query as an array of characters and character types
private String statement;
private char[] statementChars;
private int[] characterTypes;
// The current state of the parser
private int parseIndex;
private int currentTokenType;
private String currentToken;
private boolean currentTokenQuoted;
private Value currentValue;
private ArrayList expected;
// The bind variables
private HashMap bindVariables;
// The list of selectors of this query
private ArrayList selectors;
// SQL injection protection: if disabled, literals are not allowed
private boolean allowTextLiterals = true, allowNumberLiterals = true;
private QueryObjectModelFactory factory;
private ValueFactory valueFactory;
/**
* Create a new parser. A parser can be re-used, but it is not thread safe.
*
* @param factory the query object model factory
* @param valueFactory the value factory
*/
public Parser(QueryObjectModelFactory factory, ValueFactory valueFactory) {
this.factory = factory;
this.valueFactory = valueFactory;
}
/**
* Parse a JCR-SQL2 query and return the query object model
*
* @param query the query string
* @return the query object model
* @throws RepositoryException if parsing failed
*/
public QueryObjectModel createQueryObjectModel(String query) throws RepositoryException {
initialize(query);
selectors = new ArrayList();
expected = new ArrayList();
bindVariables = new HashMap();
read();
read("SELECT");
int columnParseIndex = parseIndex;
ArrayList list = parseColumns();
read("FROM");
Source source = parseSource();
Column[] columnArray = resolveColumns(columnParseIndex, list);
Constraint constraint = null;
if (readIf("WHERE")) {
constraint = parseConstraint();
}
Ordering[] orderings = null;
if (readIf("ORDER")) {
read("BY");
orderings = parseOrder();
}
if (currentToken.length() > 0) {
throw getSyntaxError("");
}
return factory.createQuery(source, constraint, orderings, columnArray);
}
private Selector parseSelector() throws RepositoryException {
String nodeTypeName = readName();
if (readIf("AS")) {
String selectorName = readName();
return factory.selector(nodeTypeName, selectorName);
} else {
return factory.selector(nodeTypeName, nodeTypeName);
}
}
private String readName() throws RepositoryException {
if (readIf("[")) {
if (currentTokenType == VALUE) {
Value value = readString();
read("]");
return value.getString();
} else {
int level = 1;
StringBuilder buff = new StringBuilder();
while (true) {
if (isToken("]")) {
if (--level <= 0) {
read();
break;
}
} else if (isToken("[")) {
level++;
}
buff.append(readAny());
}
return buff.toString();
}
} else {
return readAny();
}
}
private Source parseSource() throws RepositoryException {
Selector selector = parseSelector();
selectors.add(selector);
Source source = selector;
while (true) {
JoinType type;
if (readIf("RIGHT")) {
read("OUTER");
type = JoinType.RIGHT;
} else if (readIf("LEFT")) {
read("OUTER");
type = JoinType.LEFT;
} else if (readIf("INNER")) {
type = JoinType.INNER;
} else {
break;
}
read("JOIN");
selector = parseSelector();
selectors.add(selector);
read("ON");
JoinCondition on = parseJoinCondition();
source = type.join(factory, source, selector, on);
}
return source;
}
private JoinCondition parseJoinCondition() throws RepositoryException {
boolean identifier = currentTokenType == IDENTIFIER;
String name = readName();
JoinCondition c;
if (identifier && readIf("(")) {
if ("ISSAMENODE".equalsIgnoreCase(name)) {
String selector1 = readName();
read(",");
String selector2 = readName();
if (readIf(",")) {
c = factory.sameNodeJoinCondition(selector1, selector2, readPath());
} else {
c = factory.sameNodeJoinCondition(selector1, selector2, ".");
}
} else if ("ISCHILDNODE".equalsIgnoreCase(name)) {
String childSelector = readName();
read(",");
c = factory.childNodeJoinCondition(childSelector, readName());
} else if ("ISDESCENDANTNODE".equalsIgnoreCase(name)) {
String descendantSelector = readName();
read(",");
c = factory.descendantNodeJoinCondition(descendantSelector, readName());
} else {
throw getSyntaxError("ISSAMENODE, ISCHILDNODE, or ISDESCENDANTNODE");
}
read(")");
return c;
} else {
String selector1 = name;
read(".");
String property1 = readName();
read("=");
String selector2 = readName();
read(".");
return factory.equiJoinCondition(selector1, property1, selector2, readName());
}
}
private Constraint parseConstraint() throws RepositoryException {
Constraint a = parseAnd();
while (readIf("OR")) {
a = factory.or(a, parseAnd());
}
return a;
}
private Constraint parseAnd() throws RepositoryException {
Constraint a = parseCondition();
while (readIf("AND")) {
a = factory.and(a, parseCondition());
}
return a;
}
private Constraint parseCondition() throws RepositoryException {
Constraint a;
if (readIf("NOT")) {
a = factory.not(parseConstraint());
} else if (readIf("(")) {
a = parseConstraint();
read(")");
} else if (currentTokenType == IDENTIFIER) {
String identifier = readName();
if (readIf("(")) {
a = parseConditionFuntionIf(identifier);
if (a == null) {
DynamicOperand op = parseExpressionFunction(identifier);
a = parseCondition(op);
}
} else if (readIf(".")) {
a = parseCondition(factory.propertyValue(identifier, readName()));
} else {
a = parseCondition(factory.propertyValue(getOnlySelectorName(identifier), identifier));
}
} else if ("[".equals(currentToken)) {
String name = readName();
if (readIf(".")) {
a = parseCondition(factory.propertyValue(name, readName()));
} else {
a = parseCondition(factory.propertyValue(getOnlySelectorName(name), name));
}
} else {
throw getSyntaxError();
}
return a;
}
private Constraint parseCondition(DynamicOperand left) throws RepositoryException {
Constraint c;
if (readIf("=")) {
c = Operator.EQ.comparison(factory, left, parseStaticOperand());
} else if (readIf("<>")) {
c = Operator.NE.comparison(factory, left, parseStaticOperand());
} else if (readIf("<")) {
c = Operator.LT.comparison(factory, left, parseStaticOperand());
} else if (readIf(">")) {
c = Operator.GT.comparison(factory, left, parseStaticOperand());
} else if (readIf("<=")) {
c = Operator.LE.comparison(factory, left, parseStaticOperand());
} else if (readIf(">=")) {
c = Operator.GE.comparison(factory, left, parseStaticOperand());
} else if (readIf("LIKE")) {
c = Operator.LIKE.comparison(factory, left, parseStaticOperand());
} else if (readIf("IS")) {
boolean not = readIf("NOT");
read("NULL");
if (!(left instanceof PropertyValue)) {
throw getSyntaxError("propertyName (NOT NULL is only supported for properties)");
}
PropertyValue p = (PropertyValue) left;
c = getPropertyExistence(p);
if (!not) {
c = factory.not(c);
}
} else if (readIf("NOT")) {
if (readIf("IS")) {
read("NULL");
if (!(left instanceof PropertyValue)) {
throw new RepositoryException(
"Only property values can be tested for NOT IS NULL; got: "
+ left.getClass().getName());
}
PropertyValue pv = (PropertyValue) left;
c = getPropertyExistence(pv);
} else {
read("LIKE");
c = factory.not(Operator.LIKE.comparison(
factory, left, parseStaticOperand()));
}
} else {
throw getSyntaxError();
}
return c;
}
private PropertyExistence getPropertyExistence(PropertyValue p) throws InvalidQueryException, RepositoryException {
return factory.propertyExistence(p.getSelectorName(), p.getPropertyName());
}
private Constraint parseConditionFuntionIf(String functionName) throws RepositoryException {
Constraint c;
if ("CONTAINS".equalsIgnoreCase(functionName)) {
String name = readName();
if (readIf(".")) {
if (readIf("*")) {
read(",");
c = factory.fullTextSearch(
name, null, parseStaticOperand());
} else {
String selector = name;
name = readName();
read(",");
c = factory.fullTextSearch(
selector, name, parseStaticOperand());
}
} else {
read(",");
c = factory.fullTextSearch(
getOnlySelectorName(name), name,
parseStaticOperand());
}
} else if ("ISSAMENODE".equalsIgnoreCase(functionName)) {
String name = readName();
if (readIf(",")) {
c = factory.sameNode(name, readPath());
} else {
c = factory.sameNode(getOnlySelectorName(name), name);
}
} else if ("ISCHILDNODE".equalsIgnoreCase(functionName)) {
String name = readName();
if (readIf(",")) {
c = factory.childNode(name, readPath());
} else {
c = factory.childNode(getOnlySelectorName(name), name);
}
} else if ("ISDESCENDANTNODE".equalsIgnoreCase(functionName)) {
String name = readName();
if (readIf(",")) {
c = factory.descendantNode(name, readPath());
} else {
c = factory.descendantNode(getOnlySelectorName(name), name);
}
} else {
return null;
}
read(")");
return c;
}
private String readPath() throws RepositoryException {
return readName();
}
private DynamicOperand parseDynamicOperand() throws RepositoryException {
boolean identifier = currentTokenType == IDENTIFIER;
String name = readName();
if (identifier && readIf("(")) {
return parseExpressionFunction(name);
} else {
return parsePropertyValue(name);
}
}
private DynamicOperand parseExpressionFunction(String functionName) throws RepositoryException {
DynamicOperand op;
if ("LENGTH".equalsIgnoreCase(functionName)) {
op = factory.length(parsePropertyValue(readName()));
} else if ("NAME".equalsIgnoreCase(functionName)) {
if (isToken(")")) {
op = factory.nodeName(getOnlySelectorName("NAME()"));
} else {
op = factory.nodeName(readName());
}
} else if ("LOCALNAME".equalsIgnoreCase(functionName)) {
if (isToken(")")) {
op = factory.nodeLocalName(getOnlySelectorName("LOCALNAME()"));
} else {
op = factory.nodeLocalName(readName());
}
} else if ("SCORE".equalsIgnoreCase(functionName)) {
if (isToken(")")) {
op = factory.fullTextSearchScore(getOnlySelectorName("SCORE()"));
} else {
op = factory.fullTextSearchScore(readName());
}
} else if ("LOWER".equalsIgnoreCase(functionName)) {
op = factory.lowerCase(parseDynamicOperand());
} else if ("UPPER".equalsIgnoreCase(functionName)) {
op = factory.upperCase(parseDynamicOperand());
} else {
throw getSyntaxError("LENGTH, NAME, LOCALNAME, SCORE, LOWER, UPPER, or CAST");
}
read(")");
return op;
}
private PropertyValue parsePropertyValue(String name) throws RepositoryException {
if (readIf(".")) {
return factory.propertyValue(name, readName());
} else {
return factory.propertyValue(getOnlySelectorName(name), name);
}
}
private StaticOperand parseStaticOperand() throws RepositoryException {
if (currentTokenType == PLUS) {
read();
} else if (currentTokenType == MINUS) {
read();
if (currentTokenType != VALUE) {
throw getSyntaxError("number");
}
int valueType = currentValue.getType();
switch (valueType) {
case PropertyType.LONG:
currentValue = valueFactory.createValue(-currentValue.getLong());
break;
case PropertyType.DOUBLE:
currentValue = valueFactory.createValue(-currentValue.getDouble());
break;
case PropertyType.BOOLEAN:
currentValue = valueFactory.createValue(!currentValue.getBoolean());
break;
case PropertyType.DECIMAL:
currentValue = valueFactory.createValue(currentValue.getDecimal().negate());
break;
default:
throw getSyntaxError("Illegal operation: -" + currentValue);
}
}
if (currentTokenType == VALUE) {
Literal literal = getUncastLiteral(currentValue);
read();
return literal;
} else if (currentTokenType == PARAMETER) {
read();
String name = readName();
if (readIf(":")) {
name = name + ":" + readName();
}
BindVariableValue var = bindVariables.get(name);
if (var == null) {
var = factory.bindVariable(name);
bindVariables.put(name, var);
}
return var;
} else if (readIf("TRUE")) {
Literal literal = getUncastLiteral(valueFactory.createValue(true));
return literal;
} else if (readIf("FALSE")) {
Literal literal = getUncastLiteral(valueFactory.createValue(false));
return literal;
} else if (readIf("CAST")) {
read("(");
StaticOperand op = parseStaticOperand();
if (!(op instanceof Literal)) {
throw getSyntaxError("literal");
}
Literal literal = (Literal) op;
Value value = literal.getLiteralValue();
read("AS");
value = parseCastAs(value);
read(")");
// CastLiteral
literal = factory.literal(value);
return literal;
} else {
throw getSyntaxError("static operand");
}
}
/**
* Create a literal from a parsed value.
*
* @param value the original value
* @return the literal
*/
private Literal getUncastLiteral(Value value) throws RepositoryException {
return factory.literal(value);
}
private Value parseCastAs(Value value) throws RepositoryException {
if (readIf("STRING")) {
return valueFactory.createValue(value.getString());
} else if(readIf("BINARY")) {
return valueFactory.createValue(value.getBinary());
} else if(readIf("DATE")) {
return valueFactory.createValue(value.getDate());
} else if(readIf("LONG")) {
return valueFactory.createValue(value.getLong());
} else if(readIf("DOUBLE")) {
return valueFactory.createValue(value.getDouble());
} else if(readIf("DECIMAL")) {
return valueFactory.createValue(value.getDecimal());
} else if(readIf("BOOLEAN")) {
return valueFactory.createValue(value.getBoolean());
} else if(readIf("NAME")) {
return valueFactory.createValue(value.getString(), PropertyType.NAME);
} else if(readIf("PATH")) {
return valueFactory.createValue(value.getString(), PropertyType.PATH);
} else if(readIf("REFERENCE")) {
return valueFactory.createValue(value.getString(), PropertyType.REFERENCE);
} else if(readIf("WEAKREFERENCE")) {
return valueFactory.createValue(value.getString(), PropertyType.WEAKREFERENCE);
} else if(readIf("URI")) {
return valueFactory.createValue(value.getString(), PropertyType.URI);
} else {
throw getSyntaxError("data type (STRING|BINARY|...)");
}
}
private Ordering[] parseOrder() throws RepositoryException {
ArrayList orderList = new ArrayList();
do {
Ordering ordering;
DynamicOperand op = parseDynamicOperand();
if (readIf("DESC")) {
ordering = factory.descending(op);
} else {
readIf("ASC");
ordering = factory.ascending(op);
}
orderList.add(ordering);
} while (readIf(","));
Ordering[] orderings = new Ordering[orderList.size()];
orderList.toArray(orderings);
return orderings;
}
private ArrayList parseColumns() throws RepositoryException {
ArrayList list = new ArrayList();
if (readIf("*")) {
list.add(new ColumnOrWildcard());
} else {
do {
ColumnOrWildcard column = new ColumnOrWildcard();
column.propertyName = readName();
if (readIf(".")) {
column.selectorName = column.propertyName;
if (readIf("*")) {
column.propertyName = null;
} else {
column.propertyName = readName();
if (readIf("AS")) {
column.columnName = readName();
} else {
column.columnName = column.selectorName + "."
+ column.propertyName;
}
}
} else {
if (readIf("AS")) {
column.columnName = readName();
}
}
list.add(column);
} while (readIf(","));
}
return list;
}
private Column[] resolveColumns(int columnParseIndex, ArrayList list) throws RepositoryException {
int oldParseIndex = parseIndex;
// set the parse index to the column list, to get a more meaningful error message
// if something is wrong
this.parseIndex = columnParseIndex;
try {
ArrayList columns = new ArrayList();
for (ColumnOrWildcard c : list) {
if (c.propertyName == null) {
for (Selector selector : selectors) {
if (c.selectorName == null
|| c.selectorName
.equals(selector.getSelectorName())) {
Column column = factory.column(selector
.getSelectorName(), null, null);
columns.add(column);
}
}
} else {
Column column;
if (c.selectorName != null) {
column = factory.column(c.selectorName, c.propertyName, c.columnName);
} else if (c.columnName != null) {
column = factory.column(getOnlySelectorName(c.propertyName), c.propertyName, c.columnName);
} else {
column = factory.column(getOnlySelectorName(c.propertyName), c.propertyName, c.propertyName);
}
columns.add(column);
}
}
Column[] array = new Column[columns.size()];
columns.toArray(array);
return array;
} finally {
this.parseIndex = oldParseIndex;
}
}
private boolean readIf(String token) throws RepositoryException {
if (isToken(token)) {
read();
return true;
}
return false;
}
private boolean isToken(String token) {
boolean result = token.equalsIgnoreCase(currentToken) && !currentTokenQuoted;
if (result) {
return true;
}
addExpected(token);
return false;
}
private void read(String expected) throws RepositoryException {
if (!expected.equalsIgnoreCase(currentToken) || currentTokenQuoted) {
throw getSyntaxError(expected);
}
read();
}
private String readAny() throws RepositoryException {
if (currentTokenType == END) {
throw getSyntaxError("a token");
}
String s;
if (currentTokenType == VALUE) {
s = currentValue.getString();
} else {
s = currentToken;
}
read();
return s;
}
private Value readString() throws RepositoryException {
if (currentTokenType != VALUE) {
throw getSyntaxError("string value");
}
Value value = currentValue;
read();
return value;
}
private void addExpected(String token) {
if (expected != null) {
expected.add(token);
}
}
private void initialize(String query) throws InvalidQueryException {
if (query == null) {
query = "";
}
statement = query;
int len = query.length() + 1;
char[] command = new char[len];
int[] types = new int[len];
len--;
query.getChars(0, len, command, 0);
command[len] = ' ';
int startLoop = 0;
for (int i = 0; i < len; i++) {
char c = command[i];
int type = 0;
switch (c) {
case '/':
case '-':
case '(':
case ')':
case '{':
case '}':
case '*':
case ',':
case ';':
case '+':
case '%':
case '?':
case '$':
case '[':
case ']':
type = CHAR_SPECIAL_1;
break;
case '!':
case '<':
case '>':
case '|':
case '=':
case ':':
type = CHAR_SPECIAL_2;
break;
case '.':
type = CHAR_DECIMAL;
break;
case '\'':
type = CHAR_STRING;
types[i] = CHAR_STRING;
startLoop = i;
while (command[++i] != '\'') {
checkRunOver(i, len, startLoop);
}
break;
case '\"':
type = CHAR_QUOTED;
types[i] = CHAR_QUOTED;
startLoop = i;
while (command[++i] != '\"') {
checkRunOver(i, len, startLoop);
}
break;
case '_':
type = CHAR_NAME;
break;
default:
if (c >= 'a' && c <= 'z') {
type = CHAR_NAME;
} else if (c >= 'A' && c <= 'Z') {
type = CHAR_NAME;
} else if (c >= '0' && c <= '9') {
type = CHAR_VALUE;
} else {
if (Character.isJavaIdentifierPart(c)) {
type = CHAR_NAME;
}
}
}
types[i] = (byte) type;
}
statementChars = command;
types[len] = CHAR_END;
characterTypes = types;
parseIndex = 0;
}
private void checkRunOver(int i, int len, int startLoop) throws InvalidQueryException {
if (i >= len) {
parseIndex = startLoop;
throw getSyntaxError();
}
}
private void read() throws RepositoryException {
currentTokenQuoted = false;
if (expected != null) {
expected.clear();
}
int[] types = characterTypes;
int i = parseIndex;
int type = types[i];
while (type == 0) {
type = types[++i];
}
int start = i;
char[] chars = statementChars;
char c = chars[i++];
currentToken = "";
switch (type) {
case CHAR_NAME:
while (true) {
type = types[i];
if (type != CHAR_NAME && type != CHAR_VALUE) {
break;
}
i++;
}
currentToken = statement.substring(start, i);
if (currentToken.length() == 0) {
throw getSyntaxError();
}
currentTokenType = IDENTIFIER;
parseIndex = i;
return;
case CHAR_SPECIAL_2:
if (types[i] == CHAR_SPECIAL_2) {
i++;
}
// fall through
case CHAR_SPECIAL_1:
currentToken = statement.substring(start, i);
switch (c) {
case '$':
currentTokenType = PARAMETER;
break;
case '+':
currentTokenType = PLUS;
break;
case '-':
currentTokenType = MINUS;
break;
case '(':
currentTokenType = OPEN;
break;
case ')':
currentTokenType = CLOSE;
break;
default:
currentTokenType = KEYWORD;
}
parseIndex = i;
return;
case CHAR_VALUE:
long number = c - '0';
while (true) {
c = chars[i];
if (c < '0' || c > '9') {
if (c == '.') {
readDecimal(start, i);
break;
}
if (c == 'E' || c == 'e') {
readDecimal(start, i);
break;
}
checkLiterals(false);
currentValue = valueFactory.createValue(number);
currentTokenType = VALUE;
currentToken = "0";
parseIndex = i;
break;
}
number = number * 10 + (c - '0');
if (number > Integer.MAX_VALUE) {
readDecimal(start, i);
break;
}
i++;
}
return;
case CHAR_DECIMAL:
if (types[i] != CHAR_VALUE) {
currentTokenType = KEYWORD;
currentToken = ".";
parseIndex = i;
return;
}
readDecimal(i - 1, i);
return;
case CHAR_STRING:
readString(i, '\'');
return;
case CHAR_QUOTED:
readString(i, '\"');
return;
case CHAR_END:
currentToken = "";
currentTokenType = END;
parseIndex = i;
return;
default:
throw getSyntaxError();
}
}
private void readString(int i, char end) throws RepositoryException {
char[] chars = statementChars;
String result = null;
while (true) {
for (int begin = i;; i++) {
if (chars[i] == end) {
if (result == null) {
result = statement.substring(begin, i);
} else {
result += statement.substring(begin - 1, i);
}
break;
}
}
if (chars[++i] != end) {
break;
}
i++;
}
currentToken = "'";
checkLiterals(false);
currentValue = valueFactory.createValue(result);
parseIndex = i;
currentTokenType = VALUE;
}
private void checkLiterals(boolean text) throws InvalidQueryException {
if (text && !allowTextLiterals || (!text && !allowNumberLiterals)) {
throw getSyntaxError("bind variable (literals of this type not allowed)");
}
}
private void readDecimal(int start, int i) throws RepositoryException {
char[] chars = statementChars;
int[] types = characterTypes;
while (true) {
int t = types[i];
if (t != CHAR_DECIMAL && t != CHAR_VALUE) {
break;
}
i++;
}
if (chars[i] == 'E' || chars[i] == 'e') {
i++;
if (chars[i] == '+' || chars[i] == '-') {
i++;
}
if (types[i] != CHAR_VALUE) {
throw getSyntaxError();
}
do {
i++; // go until the first non-number
} while (types[i] == CHAR_VALUE);
}
parseIndex = i;
String sub = statement.substring(start, i);
BigDecimal bd;
try {
bd = new BigDecimal(sub);
} catch (NumberFormatException e) {
throw new InvalidQueryException("Data conversion error converting " + sub + " to BigDecimal: " + e);
}
checkLiterals(false);
currentValue = valueFactory.createValue(bd);
currentTokenType = VALUE;
}
private InvalidQueryException getSyntaxError() {
if (expected == null || expected.size() == 0) {
return getSyntaxError(null);
} else {
StringBuilder buff = new StringBuilder();
for (String exp : expected) {
if (buff.length() > 0) {
buff.append(", ");
}
buff.append(exp);
}
return getSyntaxError(buff.toString());
}
}
private InvalidQueryException getSyntaxError(String expected) {
int index = Math.min(parseIndex, statement.length() - 1);
String query = statement.substring(0, index) + "(*)" + statement.substring(index).trim();
if (expected != null) {
query += "; expected: " + expected;
}
return new InvalidQueryException("Query:\n" + query);
}
/**
* Represents a column or a wildcard in a SQL expression.
* This class is temporarily used during parsing.
*/
static class ColumnOrWildcard {
String selectorName;
String propertyName;
String columnName;
}
/**
* Get the selector name if only one selector exists in the query.
* If more than one selector exists, an exception is thrown.
*
* @param name the property name
* @return the selector name
*/
private String getOnlySelectorName(String propertyName) throws RepositoryException {
if (selectors.size() > 1) {
throw getSyntaxError("Need to specify the selector name for \"" + propertyName + "\" because the query contains more than one selector.");
}
return selectors.get(0).getSelectorName();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy