/** * {@inheritDoc} * * @see * org.modeshape.jcr.query.parse.BasicSqlQueryParser#parseFrom(org.modeshape.common.text.TokenStream, * org.modeshape.jcr.query.model.TypeSystem) */ @Override protected Source parseFrom(TokenStream tokens, TypeSystem typeSystem) { Position firstSourcePosition = tokens.nextPosition(); Source source = super.parseFrom(tokens, typeSystem); if (tokens.matches(',') && source instanceof NamedSelector) { NamedSelector selector = (NamedSelector) source; JoinableSources joinedSources = new JoinableSources(selector, firstSourcePosition); while (tokens.canConsume(',')) { // This is a JCR-SQL-style JOIN ... Position nextSourcePosition = tokens.nextPosition(); NamedSelector nextSource = parseNamedSelector(tokens, typeSystem); joinedSources.add(nextSource, nextSourcePosition); } source = joinedSources; } return source; }
protected Source rewrite(JoinableSources joinableSources) { // Find the order of the joins ... List<Join> joins = new LinkedList<Join>(); for (SameNodeJoinCondition joinCondition : joinableSources.getJoinConditions()) { SelectorName selector1 = joinCondition.selector1Name(); SelectorName selector2 = joinCondition.selector2Name(); boolean found = false; ListIterator<Join> iter = joins.listIterator(); while (iter.hasNext()) { Join next = iter.next(); Join replacement = null; if (usesSelector(next, selector1)) { Source right = joinableSources.getSelectors().get(selector2.name()); replacement = new Join(next, JoinType.INNER, right, joinCondition); } else if (usesSelector(next, selector2)) { Source left = joinableSources.getSelectors().get(selector1.name()); replacement = new Join(left, JoinType.INNER, next, joinCondition); } if (replacement != null) { iter.previous(); iter.remove(); joins.add(replacement); found = true; break; } } if (!found) { // Nothing matched, so add a new join ... Source left = joinableSources.getSelectors().get(selector1.name()); Source right = joinableSources.getSelectors().get(selector2.name()); if (left == null) { Position pos = joinableSources.getJoinCriteriaPosition(); String msg = JcrI18n.selectorUsedInEquiJoinCriteriaDoesNotExistInQuery.text( selector1.name(), pos.getLine(), pos.getColumn()); throw new ParsingException(pos, msg); } if (right == null) { Position pos = joinableSources.getJoinCriteriaPosition(); String msg = JcrI18n.selectorUsedInEquiJoinCriteriaDoesNotExistInQuery.text( selector2.name(), pos.getLine(), pos.getColumn()); throw new ParsingException(pos, msg); } joins.add(new Join(left, JoinType.INNER, right, joinCondition)); } } if (joins.size() == 1) { return joins.get(0); } // Otherwise the join conditions were not sufficient return null; }
/** * Parse a constraint clause. This method inherits all of the functionality from JCR-SQL2, except * that JCR-SQL allows constraints that use "<code>jcr:path</code>" and "<code>jcr:score</code>" * pseudo-columns. In these special cases, the resulting {@link Comparison comparison} will have a * {@link NodePath} or {@link FullTextSearchScore} dynamic operand. * * @see * org.modeshape.jcr.query.parse.BasicSqlQueryParser#parseConstraint(org.modeshape.common.text.TokenStream, * org.modeshape.jcr.query.model.TypeSystem, org.modeshape.jcr.query.model.Source) */ @Override protected Constraint parseConstraint(TokenStream tokens, TypeSystem typeSystem, Source source) { Constraint constraint = null; if (tokens.canConsume("JCR", ":", "PATH")) { // It is a property constraint on "jcr:path" ... SelectorName selector = getSelectorNameFor(source); PropertyValue value = new PropertyValue(selector, "jcr:path"); Operator operator = parseComparisonOperator(tokens); StaticOperand right = parseStaticOperand(tokens, typeSystem); constraint = rewriteConstraint(new Comparison(value, operator, right)); } else if (tokens.matches(ANY_VALUE, "IN")) { // This is a "... 'value' IN prop ..." pattern used in the JCR TCK tests but not in the JCR // 1.0.1 specification // ... Literal value = parseLiteral(tokens, typeSystem); tokens.consume("IN"); PropertyValue propertyValue = parsePropertyValue(tokens, typeSystem, source); constraint = new SetCriteria(propertyValue, value); } else if (source instanceof JoinableSources && !(tokens.matches("(") || tokens.matches("NOT") || tokens.matches("CONTAINS", "(") || tokens.matches("ISSAMENODE", "(") || tokens.matches("ISCHILDNODE", "(") || tokens.matches("ISDESCENDANTNODE", "("))) { JoinableSources joinableSources = (JoinableSources) source; // See if this is a join condition ... if (tokens.matches(ANY_VALUE, ":", ANY_VALUE, ".", "JCR", ":", "PATH", "=") || tokens.matches(ANY_VALUE, ".", "JCR", ":", "PATH", "=")) { Position position = tokens.nextPosition(); SelectorName selector1 = parseSelectorName(tokens, typeSystem); tokens.consume('.'); parseName(tokens, typeSystem); // jcr:path tokens.consume('='); SelectorName selector2 = parseSelectorName(tokens, typeSystem); tokens.consume('.'); parseName(tokens, typeSystem); // jcr:path joinableSources.add(new SameNodeJoinCondition(selector1, selector2), position); // AND has higher precedence than OR, so we need to evaluate it first ... while (tokens.canConsume("AND")) { Constraint rhs = parseConstraint(tokens, typeSystem, source); if (rhs != null) constraint = constraint != null ? new And(constraint, rhs) : rhs; } while (tokens.canConsume("OR")) { Constraint rhs = parseConstraint(tokens, typeSystem, source); if (rhs != null) constraint = constraint != null ? new And(constraint, rhs) : rhs; } return constraint; } } if (constraint != null) { // AND has higher precedence than OR, so we need to evaluate it first ... while (tokens.canConsume("AND")) { Constraint rhs = parseConstraint(tokens, typeSystem, source); if (rhs != null) constraint = new And(constraint, rhs); } while (tokens.canConsume("OR")) { Constraint rhs = parseConstraint(tokens, typeSystem, source); if (rhs != null) constraint = new Or(constraint, rhs); } return constraint; } constraint = super.parseConstraint(tokens, typeSystem, source); constraint = rewriteConstraint(constraint); return constraint; }