Index: modeshape-graph/src/main/java/org/modeshape/graph/query/parse/SqlQueryParser.java
===================================================================
--- modeshape-graph/src/main/java/org/modeshape/graph/query/parse/SqlQueryParser.java (revision 1779)
+++ modeshape-graph/src/main/java/org/modeshape/graph/query/parse/SqlQueryParser.java (working copy)
@@ -587,15 +587,15 @@ public class SqlQueryParser implements QueryParser {
List columns = new ArrayList();
do {
Position position = tokens.nextPosition();
- String propertyName = removeBracketsAndQuotes(tokens.consume());
+ String propertyName = parseName(tokens, typeSystem);
SelectorName selectorName = null;
if (tokens.canConsume('.')) {
// We actually read the selector name, so now read the property name ...
selectorName = new SelectorName(propertyName);
- propertyName = removeBracketsAndQuotes(tokens.consume());
+ propertyName = parseName(tokens, typeSystem);
}
String alias = propertyName;
- if (tokens.canConsume("AS")) alias = removeBracketsAndQuotes(tokens.consume());
+ if (tokens.canConsume("AS")) alias = parseName(tokens, typeSystem);
columns.add(new ColumnExpression(selectorName, propertyName, alias, position));
} while (tokens.canConsume(','));
return columns;
@@ -605,7 +605,7 @@ public class SqlQueryParser implements QueryParser {
TypeSystem typeSystem ) {
Source source = null;
tokens.consume("FROM");
- source = parseNamedSelector(tokens);
+ source = parseNamedSelector(tokens, typeSystem);
while (tokens.hasNext()) {
JoinType joinType = null;
if (tokens.canConsume("JOIN") || tokens.canConsume("INNER", "JOIN")) {
@@ -622,7 +622,7 @@ public class SqlQueryParser implements QueryParser {
}
if (joinType == null) break;
// Read the name of the selector on the right side of the join ...
- NamedSelector right = parseNamedSelector(tokens);
+ NamedSelector right = parseNamedSelector(tokens, typeSystem);
// Read the join condition ...
JoinCondition joinCondition = parseJoinCondition(tokens, typeSystem);
// Create the join ...
@@ -635,9 +635,9 @@ public class SqlQueryParser implements QueryParser {
TypeSystem typeSystem ) {
tokens.consume("ON");
if (tokens.canConsume("ISSAMENODE", "(")) {
- SelectorName selector1Name = parseSelectorName(tokens);
+ SelectorName selector1Name = parseSelectorName(tokens, typeSystem);
tokens.consume(',');
- SelectorName selector2Name = parseSelectorName(tokens);
+ SelectorName selector2Name = parseSelectorName(tokens, typeSystem);
if (tokens.canConsume('.')) {
String path = parsePath(tokens, typeSystem);
tokens.consume(')');
@@ -647,24 +647,24 @@ public class SqlQueryParser implements QueryParser {
return new SameNodeJoinCondition(selector1Name, selector2Name);
}
if (tokens.canConsume("ISCHILDNODE", "(")) {
- SelectorName child = parseSelectorName(tokens);
+ SelectorName child = parseSelectorName(tokens, typeSystem);
tokens.consume(',');
- SelectorName parent = parseSelectorName(tokens);
+ SelectorName parent = parseSelectorName(tokens, typeSystem);
tokens.consume(')');
return new ChildNodeJoinCondition(parent, child);
}
if (tokens.canConsume("ISDESCENDANTNODE", "(")) {
- SelectorName descendant = parseSelectorName(tokens);
+ SelectorName descendant = parseSelectorName(tokens, typeSystem);
tokens.consume(',');
- SelectorName ancestor = parseSelectorName(tokens);
+ SelectorName ancestor = parseSelectorName(tokens, typeSystem);
tokens.consume(')');
return new DescendantNodeJoinCondition(ancestor, descendant);
}
- SelectorName selector1 = parseSelectorName(tokens);
+ SelectorName selector1 = parseSelectorName(tokens, typeSystem);
tokens.consume('.');
String property1 = parseName(tokens, typeSystem);
tokens.consume('=');
- SelectorName selector2 = parseSelectorName(tokens);
+ SelectorName selector2 = parseSelectorName(tokens, typeSystem);
tokens.consume('.');
String property2 = parseName(tokens, typeSystem);
return new EquiJoinCondition(selector1, property1, selector2, property2);
@@ -725,7 +725,7 @@ public class SqlQueryParser implements QueryParser {
}
selectorName = ((Selector)source).getName();
} else {
- selectorName = parseSelectorName(tokens);
+ selectorName = parseSelectorName(tokens, typeSystem);
tokens.consume(',');
}
String path = parsePath(tokens, typeSystem);
@@ -740,7 +740,7 @@ public class SqlQueryParser implements QueryParser {
}
selectorName = ((Selector)source).getName();
} else {
- selectorName = parseSelectorName(tokens);
+ selectorName = parseSelectorName(tokens, typeSystem);
tokens.consume(',');
}
String path = parsePath(tokens, typeSystem);
@@ -755,7 +755,7 @@ public class SqlQueryParser implements QueryParser {
}
selectorName = ((Selector)source).getName();
} else {
- selectorName = parseSelectorName(tokens);
+ selectorName = parseSelectorName(tokens, typeSystem);
tokens.consume(',');
}
String path = parsePath(tokens, typeSystem);
@@ -1081,7 +1081,7 @@ public class SqlQueryParser implements QueryParser {
String msg = GraphI18n.functionIsAmbiguous.text("NAME()", pos.getLine(), pos.getColumn());
throw new ParsingException(pos, msg);
}
- result = new NodeName(parseSelectorName(tokens));
+ result = new NodeName(parseSelectorName(tokens, typeSystem));
tokens.consume(")");
} else if (tokens.canConsume("LOCALNAME", "(")) {
if (tokens.canConsume(")")) {
@@ -1091,7 +1091,7 @@ public class SqlQueryParser implements QueryParser {
String msg = GraphI18n.functionIsAmbiguous.text("LOCALNAME()", pos.getLine(), pos.getColumn());
throw new ParsingException(pos, msg);
}
- result = new NodeLocalName(parseSelectorName(tokens));
+ result = new NodeLocalName(parseSelectorName(tokens, typeSystem));
tokens.consume(")");
} else if (tokens.canConsume("SCORE", "(")) {
if (tokens.canConsume(")")) {
@@ -1101,7 +1101,7 @@ public class SqlQueryParser implements QueryParser {
String msg = GraphI18n.functionIsAmbiguous.text("SCORE()", pos.getLine(), pos.getColumn());
throw new ParsingException(pos, msg);
}
- result = new FullTextSearchScore(parseSelectorName(tokens));
+ result = new FullTextSearchScore(parseSelectorName(tokens, typeSystem));
tokens.consume(")");
} else if (tokens.canConsume("DEPTH", "(")) {
if (tokens.canConsume(")")) {
@@ -1111,7 +1111,7 @@ public class SqlQueryParser implements QueryParser {
String msg = GraphI18n.functionIsAmbiguous.text("DEPTH()", pos.getLine(), pos.getColumn());
throw new ParsingException(pos, msg);
}
- result = new NodeDepth(parseSelectorName(tokens));
+ result = new NodeDepth(parseSelectorName(tokens, typeSystem));
tokens.consume(")");
} else if (tokens.canConsume("PATH", "(")) {
if (tokens.canConsume(")")) {
@@ -1121,7 +1121,7 @@ public class SqlQueryParser implements QueryParser {
String msg = GraphI18n.functionIsAmbiguous.text("PATH()", pos.getLine(), pos.getColumn());
throw new ParsingException(pos, msg);
}
- result = new NodePath(parseSelectorName(tokens));
+ result = new NodePath(parseSelectorName(tokens, typeSystem));
tokens.consume(")");
} else if (tokens.canConsume("REFERENCE", "(")) {
result = parseReferenceValue(tokens, typeSystem, source);
@@ -1173,7 +1173,7 @@ public class SqlQueryParser implements QueryParser {
TypeSystem typeSystem,
Source source ) {
Position pos = tokens.nextPosition();
- String firstWord = removeBracketsAndQuotes(tokens.consume());
+ String firstWord = parseName(tokens, typeSystem);
SelectorName selectorName = null;
if (tokens.canConsume('.')) {
// We actually read the selector name, so now read the property name ...
@@ -1205,7 +1205,7 @@ public class SqlQueryParser implements QueryParser {
throw new ParsingException(pos, msg);
}
// Otherwise, there is at least one word inside the parentheses ...
- String firstWord = removeBracketsAndQuotes(tokens.consume());
+ String firstWord = parseName(tokens, typeSystem);
if (tokens.canConsume('.')) {
// We actually read the selector name, so now read the property name ...
selectorName = new SelectorName(firstWord);
@@ -1281,15 +1281,17 @@ public class SqlQueryParser implements QueryParser {
return text;
}
- protected NamedSelector parseNamedSelector( TokenStream tokens ) {
- SelectorName name = parseSelectorName(tokens);
+ protected NamedSelector parseNamedSelector( TokenStream tokens,
+ TypeSystem typeSystem ) {
+ SelectorName name = parseSelectorName(tokens, typeSystem);
SelectorName alias = null;
- if (tokens.canConsume("AS")) alias = parseSelectorName(tokens);
+ if (tokens.canConsume("AS")) alias = parseSelectorName(tokens, typeSystem);
return new NamedSelector(name, alias);
}
- protected SelectorName parseSelectorName( TokenStream tokens ) {
- return new SelectorName(removeBracketsAndQuotes(tokens.consume()));
+ protected SelectorName parseSelectorName( TokenStream tokens,
+ TypeSystem typeSystem ) {
+ return new SelectorName(parseName(tokens, typeSystem));
}
protected String parsePath( TokenStream tokens,
Index: modeshape-jcr/src/main/java/org/modeshape/jcr/sql/JcrSqlQueryParser.java
new file mode 100644
===================================================================
--- /dev/null (revision 1779)
+++ modeshape-jcr/src/main/java/org/modeshape/jcr/sql/JcrSqlQueryParser.java (working copy)
@@ -0,0 +1,499 @@
+/*
+ * ModeShape (http://www.modeshape.org)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * ModeShape is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.modeshape.jcr.sql;
+
+import org.modeshape.common.text.ParsingException;
+import org.modeshape.common.text.Position;
+import org.modeshape.common.text.TokenStream;
+import org.modeshape.graph.GraphI18n;
+import org.modeshape.graph.property.ValueFormatException;
+import org.modeshape.graph.query.model.Comparison;
+import org.modeshape.graph.query.model.Constraint;
+import org.modeshape.graph.query.model.DynamicOperand;
+import org.modeshape.graph.query.model.FullTextSearchScore;
+import org.modeshape.graph.query.model.NodePath;
+import org.modeshape.graph.query.model.PropertyValue;
+import org.modeshape.graph.query.model.Source;
+import org.modeshape.graph.query.model.TypeSystem;
+import org.modeshape.graph.query.model.TypeSystem.TypeFactory;
+import org.modeshape.graph.query.parse.FullTextSearchParser;
+import org.modeshape.graph.query.parse.SqlQueryParser;
+
+/**
+ * Parser for JCR-SQL queries that produces {@link org.modeshape.graph.query.model abstract query model (AQM)} objects.
+ *
+ * JCR-SQL grammar
+ *
+ * This section defines the complete grammar for the JCR-SQL dialect supported by this parser, as defined by the
+ * JCR 1.0.1 specification. This parser actually extends the {@link SqlQueryParser (extended) JCR-SQL2 parser},
+ * and thus allows many of the JCR-SQL2 standard and extended features, although there are several key differences:
+ *
+ * - Names are not enclosed by square brackets.
+ * - Criteria on scores use
jcr:score
as a pseudo-column.
+ * - Criteria on path use
jcr:path
as a pseudo-column.
+ * - Joins are specified with comma-separated table names in the FROM clause and join criteria in the WHERE clause.
+ *
+ *
+ * Queries
+ *
+ *
+ * QueryCommand ::= Query | SetQuery
+ *
+ * SetQuery ::= Query ('UNION'|'INTERSECT'|'EXCEPT') [ALL] Query
+ * { ('UNION'|'INTERSECT'|'EXCEPT') [ALL] Query }
+ *
+ * Query ::= 'SELECT' ['DISINCT'] columns
+ * 'FROM' Source
+ * ['WHERE' Constraint]
+ * ['ORDER BY' orderings]
+ * [Limit]
+ *
+ *
+ * Sources
+ *
+ *
+ * Source ::= Selector | Join
+ *
+ * Selector ::= nodeTypeName ['AS' selectorName]
+ *
+ * nodeTypeName ::= Name
+ *
+ *
+ * Joins
+ *
+ *
+ * Join ::= left [JoinType] 'JOIN' right 'ON' JoinCondition
+ * // If JoinType is omitted INNER is assumed.
+ *
+ * left ::= Source
+ * right ::= Source
+ *
+ * JoinType ::= Inner | LeftOuter | RightOuter | FullOuter | Cross
+ *
+ * Inner ::= 'INNER' ['JOIN']
+ *
+ * LeftOuter ::= 'LEFT JOIN' | 'OUTER JOIN' | 'LEFT OUTER JOIN'
+ *
+ * RightOuter ::= 'RIGHT OUTER' ['JOIN']
+ *
+ * RightOuter ::= 'FULL OUTER' ['JOIN']
+ *
+ * RightOuter ::= 'CROSS' ['JOIN']
+ *
+ * JoinCondition ::= EquiJoinCondition | SameNodeJoinCondition | ChildNodeJoinCondition | DescendantNodeJoinCondition
+ *
+ *
+ * Equi-join conditions
+ *
+ *
+ * EquiJoinCondition ::= selector1Name'.'property1Name '=' selector2Name'.'property2Name
+ *
+ * selector1Name ::= selectorName
+ * selector2Name ::= selectorName
+ * property1Name ::= propertyName
+ * property2Name ::= propertyName
+ *
+ *
+ * Same-node join condition
+ *
+ *
+ * SameNodeJoinCondition ::= 'ISSAMENODE(' selector1Name ',' selector2Name [',' selector2Path] ')'
+ *
+ * selector2Path ::= Path
+ *
+ *
+ * Child-node join condition
+ *
+ *
+ * ChildNodeJoinCondition ::= 'ISCHILDNODE(' childSelectorName ',' parentSelectorName ')'
+ *
+ * childSelectorName ::= selectorName
+ * parentSelectorName ::= selectorName
+ *
+ *
+ * Descendant-node join condition
+ *
+ *
+ * DescendantNodeJoinCondition ::= 'ISDESCENDANTNODE(' descendantSelectorName ',' ancestorSelectorName ')'
+ * descendantSelectorName ::= selectorName
+ * ancestorSelectorName ::= selectorName
+ *
+ *
+ * Constraints
+ *
+ *
+ * Constraint ::= ConstraintItem | '(' ConstraintItem ')'
+ *
+ * ConstraintItem ::= And | Or | Not | Comparison | Between | PropertyExistence | SetConstraint | FullTextSearch |
+ * SameNode | ChildNode | DescendantNode
+ *
+ *
+ * And constraint
+ *
+ *
+ * And ::= constraint1 'AND' constraint2
+ *
+ * constraint1 ::= Constraint
+ * constraint2 ::= Constraint
+ *
+ *
+ * Or constraint
+ *
+ *
+ * Or ::= constraint1 'OR' constraint2
+ *
+ *
+ * Not constraint
+ *
+ *
+ * Not ::= 'NOT' Constraint
+ *
+ *
+ * Comparison constraint
+ *
+ *
+ * Comparison ::= DynamicOperand Operator StaticOperand
+ *
+ * Operator ::= '=' | '!=' | '<' | '<=' | '>' | '>=' | 'LIKE'
+ *
+ *
+ * Between constraint
+ *
+ *
+ * Between ::= DynamicOperand ['NOT'] 'BETWEEN' lowerBound ['EXCLUSIVE'] 'AND' upperBound ['EXCLUSIVE']
+ *
+ * lowerBound ::= StaticOperand
+ * upperBound ::= StaticOperand
+ *
+ *
+ * Property existence constraint
+ *
+ *
+ * PropertyExistence ::= selectorName'.'propertyName 'IS' ['NOT'] 'NULL' |
+ * propertyName 'IS' ['NOT'] 'NULL' /* If only one selector exists in this query */
+ *
+ *
+ *
+ * Set constraint
+ *
+ *
+ * SetConstraint ::= selectorName'.'propertyName ['NOT'] 'IN' |
+ * propertyName ['NOT'] 'IN' /* If only one selector exists in this query */
+ * '(' firstStaticOperand {',' additionalStaticOperand } ')'
+ * firstStaticOperand ::= StaticOperand
+ * additionalStaticOperand ::= StaticOperand
+ *
+ *
+ * Full-text search constraint
+ *
+ *
+ * FullTextSearch ::= 'CONTAINS(' ([selectorName'.']propertyName | selectorName'.*')
+ * ',' ''' fullTextSearchExpression''' ')'
+ * /* If only one selector exists in this query, explicit specification of the selectorName
+ * preceding the propertyName is optional */
+ * fullTextSearchExpression ::= /* a full-text search expression, see {@link FullTextSearchParser} */
+ *
+ *
+ * Same-node constraint
+ *
+ *
+ * SameNode ::= 'ISSAMENODE(' [selectorName ','] Path ')'
+ * /* If only one selector exists in this query, explicit specification of the selectorName
+ * preceding the propertyName is optional */
+ *
+ *
+ * Child-node constraint
+ *
+ *
+ * ChildNode ::= 'ISCHILDNODE(' [selectorName ','] Path ')'
+ * /* If only one selector exists in this query, explicit specification of the selectorName
+ * preceding the propertyName is optional */
+ *
+ *
+ * Descendant-node constraint
+ *
+ *
+ * DescendantNode ::= 'ISDESCENDANTNODE(' [selectorName ','] Path ')'
+ * /* If only one selector exists in this query, explicit specification of the selectorName
+ * preceding the propertyName is optional */
+ *
+ *
+ * Paths and names
+ *
+ *
+ *
+ * Name ::= '[' quotedName ']' | '[' simpleName ']' | simpleName
+ *
+ * quotedName ::= /* A JCR Name (see the JCR specification) */
+ * simpleName ::= /* A JCR Name that contains only SQL-legal characters (namely letters, digits, and underscore) */
+ *
+ * Path ::= '[' quotedPath ']' | '[' simplePath ']' | simplePath
+ *
+ * quotedPath ::= /* A JCR Path that contains non-SQL-legal characters */
+ * simplePath ::= /* A JCR Path (rather Name) that contains only SQL-legal characters (namely letters, digits, and underscore) */
+ *
+ *
+ * Static operands
+ *
+ *
+ * StaticOperand ::= Literal | BindVariableValue
+ *
+ *
+ * Literal
+ *
+ *
+ * Literal ::= CastLiteral | UncastLiteral
+ *
+ * CastLiteral ::= 'CAST(' UncastLiteral ' AS ' PropertyType ')'
+ *
+ * PropertyType ::= 'STRING' | 'BINARY' | 'DATE' | 'LONG' | 'DOUBLE' | 'DECIMAL' | 'BOOLEAN' | 'NAME' | 'PATH' |
+ * 'REFERENCE' | 'WEAKREFERENCE' | 'URI'
+ * /* 'WEAKREFERENCE' is not currently supported in JCR 1.0 */
+ *
+ * UncastLiteral ::= UnquotedLiteral | ''' UnquotedLiteral ''' | '"' UnquotedLiteral '"'
+ *
+ * UnquotedLiteral ::= /* String form of a JCR Value, as defined in the JCR specification */
+ *
+ *
+ * Bind variables
+ *
+ *
+ * BindVariableValue ::= '$'bindVariableName
+ *
+ * bindVariableName ::= /* A string that conforms to the JCR Name syntax, though the prefix does not need to be
+ * a registered namespace prefix. */
+ *
+ *
+ * Dynamic operands
+ *
+ *
+ * DynamicOperand ::= PropertyValue | ReferenceValue | Length | NodeName | NodeLocalName | NodePath | NodeDepth |
+ * FullTextSearchScore | LowerCase | UpperCase | Arithmetic |
+ * '(' DynamicOperand ')'
+ *
+ * Property value
+ *
+ * PropertyValue ::= [selectorName'.'] propertyName
+ * /* If only one selector exists in this query, explicit specification of the selectorName
+ * preceding the propertyName is optional */
+ *
+ * Reference value
+ *
+ * ReferenceValue ::= 'REFERENCE(' selectorName '.' propertyName ')' |
+ * 'REFERENCE(' selectorName ')' |
+ * 'REFERENCE()' |
+ * /* If only one selector exists in this query, explicit specification of the selectorName
+ * preceding the propertyName is optional. Also, the property name may be excluded
+ * if the constraint should apply to any reference property. */
+ *
+ * Property length
+ *
+ * Length ::= 'LENGTH(' PropertyValue ')'
+ *
+ * Node name
+ *
+ * NodeName ::= 'NAME(' [selectorName] ')'
+ * /* If only one selector exists in this query, explicit specification of the selectorName
+ * is optional */
+ *
+ * Node local name
+ *
+ * NodeLocalName ::= 'LOCALNAME(' [selectorName] ')'
+ * /* If only one selector exists in this query, explicit specification of the selectorName
+ * is optional */
+ *
+ * Node path
+ *
+ * NodePath ::= 'PATH(' [selectorName] ')'
+ * /* If only one selector exists in this query, explicit specification of the selectorName
+ * is optional */
+ *
+ * Node depth
+ *
+ * NodeDepth ::= 'DEPTH(' [selectorName] ')'
+ * /* If only one selector exists in this query, explicit specification of the selectorName
+ * is optional */
+ *
+ * Full-text search score
+ *
+ * FullTextSearchScore ::= 'SCORE(' [selectorName] ')'
+ * /* If only one selector exists in this query, explicit specification of the selectorName
+ * is optional */
+ *
+ * Lowercase
+ *
+ * LowerCase ::= 'LOWER(' DynamicOperand ')'
+ *
+ * Uppercase
+ *
+ * UpperCase ::= 'UPPER(' DynamicOperand ')'
+ *
+ * Arithmetic
+ *
+ * Arithmetic ::= DynamicOperand ('+'|'-'|'*'|'/') DynamicOperand
+ *
+ *
+ * Ordering
+ *
+ *
+ * orderings ::= Ordering {',' Ordering}
+ *
+ * Ordering ::= DynamicOperand [Order]
+ *
+ * Order ::= 'ASC' | 'DESC'
+ *
+ *
+ * Columns
+ *
+ *
+ * columns ::= (Column ',' {Column}) | '*'
+ *
+ * Column ::= ([selectorName'.']propertyName ['AS' columnName]) | (selectorName'.*')
+ * /* If only one selector exists in this query, explicit specification of the selectorName
+ * preceding the propertyName is optional */
+ * selectorName ::= Name
+ * propertyName ::= Name
+ * columnName ::= Name
+ *
+ *
+ * Limit
+ *
+ *
+ * Limit ::= 'LIMIT' count [ 'OFFSET' offset ]
+ * count ::= /* Positive integer value */
+ * offset ::= /* Non-negative integer value */
+ *
+ */
+public class JcrSqlQueryParser extends SqlQueryParser {
+
+ /**
+ *
+ */
+ public JcrSqlQueryParser() {
+ }
+
+ /**
+ * Parse a constraint clause. This method inherits all of the functionality from JCR-SQL2, except that JCR-SQL allows
+ * constraints that use "jcr:path
" and "jcr:score
" pseudo-columns. In these special cases, the
+ * resulting {@link Comparison comparison} will have a {@link NodePath} or {@link FullTextSearchScore} dynamic operand.
+ *
+ * @see org.modeshape.graph.query.parse.SqlQueryParser#parseConstraint(org.modeshape.common.text.TokenStream,
+ * org.modeshape.graph.query.model.TypeSystem, org.modeshape.graph.query.model.Source)
+ */
+ @Override
+ protected Constraint parseConstraint( TokenStream tokens,
+ TypeSystem typeSystem,
+ Source source ) {
+ Constraint constraint = super.parseConstraint(tokens, typeSystem, source);
+ if (constraint instanceof Comparison) {
+ Comparison comparison = (Comparison)constraint;
+ DynamicOperand left = comparison.getOperand1();
+ if (left instanceof PropertyValue) {
+ PropertyValue propValue = (PropertyValue)left;
+ if ("jcr:path".equals(propValue.getPropertyName())) {
+ // Rewrite this constraint as a PATH criteria ...
+ NodePath path = new NodePath(propValue.getSelectorName());
+ return new Comparison(path, comparison.getOperator(), comparison.getOperand2());
+ }
+ if ("jcr:score".equals(propValue.getPropertyName())) {
+ // Rewrite this constraint as a SCORE criteria ...
+ FullTextSearchScore score = new FullTextSearchScore(propValue.getSelectorName());
+ return new Comparison(score, comparison.getOperator(), comparison.getOperand2());
+ }
+ }
+ }
+
+ return constraint;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Parsing behavior is overridden to that JCR-SQL style (unquoted prefixed) names are allowed. This method parses the selector
+ * name, which may be of the form "unprefixedName
" (consisting of a single token) or "prefix:name
"
+ * (consisting of three tokens).
+ *
+ *
+ * @see org.modeshape.graph.query.parse.SqlQueryParser#parseName(org.modeshape.common.text.TokenStream,
+ * org.modeshape.graph.query.model.TypeSystem)
+ */
+ @Override
+ protected String parseName( TokenStream tokens,
+ TypeSystem typeSystem ) {
+ String token1 = tokens.consume();
+ token1 = removeBracketsAndQuotes(token1);
+ if (tokens.canConsume(':')) {
+ String token2 = tokens.consume();
+ token2 = removeBracketsAndQuotes(token2);
+ return token1 + ':' + token2;
+ }
+ return token1;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.modeshape.graph.query.parse.SqlQueryParser#parseLiteralValue(org.modeshape.common.text.TokenStream,
+ * org.modeshape.graph.query.model.TypeSystem)
+ */
+ @Override
+ protected String parseLiteralValue( TokenStream tokens,
+ TypeSystem typeSystem ) {
+ if (tokens.canConsume("TIMESTAMP")) {
+ Position pos = tokens.previousPosition();
+ // This should be a timestamp represented as a single-quoted string ...
+ String value = removeBracketsAndQuotes(tokens.consume());
+ TypeFactory> dateTimeFactory = typeSystem.getDateTimeFactory();
+ try {
+ // Convert to a date and then back to a string to get canonical form ...
+ Object dateTime = dateTimeFactory.create(value);
+ return dateTimeFactory.asString(dateTime);
+ } catch (ValueFormatException e) {
+ String msg = GraphI18n.expectingLiteralAndUnableToParseAsDate.text(value, pos.getLine(), pos.getColumn());
+ throw new ParsingException(pos, msg);
+ }
+ }
+ return super.parseLiteralValue(tokens, typeSystem);
+ }
+
+ /**
+ * Remove any leading and trailing single-quotes.
+ *
+ * @param text the input text; may not be null
+ * @return the text without leading and trailing quotes, or text
if there were no quotes
+ */
+ @Override
+ protected String removeBracketsAndQuotes( String text ) {
+ if (text.length() > 0) {
+ char firstChar = text.charAt(0);
+ switch (firstChar) {
+ case '\'':
+ assert text.charAt(text.length() - 1) == firstChar;
+ return removeBracketsAndQuotes(text.substring(1, text.length() - 1));
+ }
+ }
+ return text;
+ }
+
+}